manager: import/export resources (#62890)
This commit is contained in:
parent
96b5dc941f
commit
cdada0578b
|
@ -2243,6 +2243,20 @@ class Resource(models.Model):
|
|||
group_ids = [x.id for x in user.groups.all()]
|
||||
return self.agenda_set.filter(edit_role_id__in=group_ids).exists()
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
data = clean_import_data(cls, data)
|
||||
slug = data.pop('slug')
|
||||
resource, created = cls.objects.update_or_create(slug=slug, defaults=data)
|
||||
return created, resource
|
||||
|
||||
def export_json(self):
|
||||
return {
|
||||
'slug': self.slug,
|
||||
'label': self.label,
|
||||
'description': self.description,
|
||||
}
|
||||
|
||||
|
||||
class Category(models.Model):
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
|
|
|
@ -1338,11 +1338,12 @@ class AgendaReminderTestForm(forms.Form):
|
|||
|
||||
class AgendasExportForm(forms.Form):
|
||||
agendas = forms.BooleanField(label=_('Agendas'), required=False, initial=True)
|
||||
resources = forms.BooleanField(label=_('Resources'), required=False, initial=True)
|
||||
unavailability_calendars = forms.BooleanField(
|
||||
label=_('Unavailability calendars'), required=False, initial=True
|
||||
)
|
||||
absence_reason_groups = forms.BooleanField(label=_('Absence reason groups'), required=False, initial=True)
|
||||
categories = forms.BooleanField(label=_('Categories'), required=False, initial=True)
|
||||
absence_reason_groups = forms.BooleanField(label=_('Absence reason groups'), required=False, initial=True)
|
||||
events_types = forms.BooleanField(label=_('Events types'), required=False, initial=True)
|
||||
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ from chrono.agendas.models import (
|
|||
AgendaImportError,
|
||||
Category,
|
||||
EventsType,
|
||||
Resource,
|
||||
UnavailabilityCalendar,
|
||||
)
|
||||
|
||||
|
@ -36,12 +37,15 @@ def export_site(
|
|||
unavailability_calendars=True,
|
||||
absence_reason_groups=True,
|
||||
events_types=True,
|
||||
resources=True,
|
||||
categories=True,
|
||||
):
|
||||
'''Dump site objects to JSON-dumpable dictionnary'''
|
||||
data = collections.OrderedDict()
|
||||
if categories:
|
||||
data['categories'] = [x.export_json() for x in Category.objects.all()]
|
||||
if resources:
|
||||
data['resources'] = [x.export_json() for x in Resource.objects.all()]
|
||||
if events_types:
|
||||
data['events_types'] = [x.export_json() for x in EventsType.objects.all()]
|
||||
if absence_reason_groups:
|
||||
|
@ -62,6 +66,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
or UnavailabilityCalendar.objects.exists()
|
||||
or AbsenceReasonGroup.objects.exists()
|
||||
or EventsType.objects.exists()
|
||||
or Resource.objects.exists()
|
||||
or Category.objects.exists()
|
||||
):
|
||||
return
|
||||
|
@ -71,6 +76,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
UnavailabilityCalendar.objects.all().delete()
|
||||
AbsenceReasonGroup.objects.all().delete()
|
||||
EventsType.objects.all().delete()
|
||||
Resource.objects.all().delete()
|
||||
Category.objects.all().delete()
|
||||
|
||||
results = {
|
||||
|
@ -80,6 +86,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
'unavailability_calendars',
|
||||
'absence_reason_groups',
|
||||
'events_types',
|
||||
'resources',
|
||||
'categories',
|
||||
]
|
||||
}
|
||||
|
@ -99,6 +106,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
with transaction.atomic():
|
||||
for cls, key in (
|
||||
(Category, 'categories'),
|
||||
(Resource, 'resources'),
|
||||
(EventsType, 'events_types'),
|
||||
(AbsenceReasonGroup, 'absence_reason_groups'),
|
||||
(UnavailabilityCalendar, 'unavailability_calendars'),
|
||||
|
|
|
@ -1009,6 +1009,20 @@ class AgendasImportView(FormView):
|
|||
x,
|
||||
),
|
||||
},
|
||||
'resources': {
|
||||
'create_noop': _('No resource created.'),
|
||||
'create': lambda x: ungettext(
|
||||
'A resource has been created.',
|
||||
'%(count)d resources have been created.',
|
||||
x,
|
||||
),
|
||||
'update_noop': _('No resource updated.'),
|
||||
'update': lambda x: ungettext(
|
||||
'A resource has been updated.',
|
||||
'%(count)d resources have been updated.',
|
||||
x,
|
||||
),
|
||||
},
|
||||
'categories': {
|
||||
'create_noop': _('No category created.'),
|
||||
'create': lambda x: ungettext(
|
||||
|
@ -1072,6 +1086,7 @@ class AgendasImportView(FormView):
|
|||
messages.info(self.request, results['unavailability_calendars']['messages'])
|
||||
messages.info(self.request, results['absence_reason_groups']['messages'])
|
||||
messages.info(self.request, results['events_types']['messages'])
|
||||
messages.info(self.request, results['resources']['messages'])
|
||||
messages.info(self.request, results['categories']['messages'])
|
||||
|
||||
return super().form_valid(form)
|
||||
|
|
|
@ -38,6 +38,7 @@ def test_export_site(app, admin_user):
|
|||
'agendas': [],
|
||||
'absence_reason_groups': [],
|
||||
'events_types': [],
|
||||
'resources': [],
|
||||
'categories': [],
|
||||
}
|
||||
|
||||
|
@ -52,12 +53,14 @@ def test_export_site(app, admin_user):
|
|||
assert len(site_json['unavailability_calendars']) == 1
|
||||
assert len(site_json['absence_reason_groups']) == 0
|
||||
assert len(site_json['events_types']) == 0
|
||||
assert len(site_json['resources']) == 0
|
||||
assert len(site_json['categories']) == 0
|
||||
|
||||
resp = app.get('/manage/agendas/export/')
|
||||
resp.form['agendas'] = False
|
||||
resp.form['absence_reason_groups'] = False
|
||||
resp.form['events_types'] = False
|
||||
resp.form['resources'] = False
|
||||
resp.form['categories'] = False
|
||||
resp = resp.form.submit()
|
||||
|
||||
|
@ -66,6 +69,7 @@ def test_export_site(app, admin_user):
|
|||
assert 'unavailability_calendars' in site_json
|
||||
assert 'absence_reason_groups' not in site_json
|
||||
assert 'events_types' not in site_json
|
||||
assert 'resources' not in site_json
|
||||
assert 'categories' not in site_json
|
||||
|
||||
|
||||
|
|
|
@ -377,7 +377,7 @@ def test_import_export_permissions(app):
|
|||
assert agenda.edit_role == group2
|
||||
|
||||
|
||||
def test_import_export_resources(app):
|
||||
def test_import_export_agenda_with_resources(app):
|
||||
meetings_agenda = Agenda.objects.create(label='Foo Bar', kind='meetings')
|
||||
resource = Resource.objects.create(label='foo')
|
||||
meetings_agenda.resources.add(resource)
|
||||
|
@ -385,24 +385,26 @@ def test_import_export_resources(app):
|
|||
|
||||
import_site(data={}, clean=True)
|
||||
assert Agenda.objects.count() == 0
|
||||
resource.delete()
|
||||
assert Resource.objects.count() == 0
|
||||
data = json.loads(output)
|
||||
del data['resources']
|
||||
|
||||
with pytest.raises(AgendaImportError) as excinfo:
|
||||
import_site(json.loads(output), overwrite=True)
|
||||
import_site(data, overwrite=True)
|
||||
assert str(excinfo.value) == 'Missing "foo" resource'
|
||||
|
||||
resource = Resource.objects.create(label='foobar')
|
||||
Resource.objects.create(label='foobar')
|
||||
with pytest.raises(AgendaImportError) as excinfo:
|
||||
import_site(json.loads(output), overwrite=True)
|
||||
import_site(data, overwrite=True)
|
||||
assert str(excinfo.value) == 'Missing "foo" resource'
|
||||
|
||||
resource = Resource.objects.create(label='foo')
|
||||
import_site(json.loads(output), overwrite=True)
|
||||
import_site(data, overwrite=True)
|
||||
agenda = Agenda.objects.get(slug=meetings_agenda.slug)
|
||||
assert list(agenda.resources.all()) == [resource]
|
||||
|
||||
|
||||
def test_import_export_categories(app):
|
||||
def test_import_export_agenda_with_category(app):
|
||||
category = Category.objects.create(label='foo')
|
||||
agenda = Agenda.objects.create(label='Foo Bar', category=category)
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
@ -429,7 +431,7 @@ def test_import_export_categories(app):
|
|||
assert agenda.category == category
|
||||
|
||||
|
||||
def test_import_export_absence_reasons(app):
|
||||
def test_import_export_agenda_with_absence_reasons(app):
|
||||
group = AbsenceReasonGroup.objects.create(label='foo')
|
||||
agenda = Agenda.objects.create(label='Foo Bar', kind='events', absence_reasons_group=group)
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
@ -1034,6 +1036,43 @@ def test_import_export_events_type(app):
|
|||
assert events_type.slug == 'foo-bar'
|
||||
|
||||
|
||||
def test_import_export_resource(app):
|
||||
output = get_output_of_command('export_site')
|
||||
payload = json.loads(output)
|
||||
assert len(payload['resources']) == 0
|
||||
|
||||
resource = Resource.objects.create(label='Foo bar')
|
||||
|
||||
output = get_output_of_command('export_site')
|
||||
payload = json.loads(output)
|
||||
assert len(payload['resources']) == 1
|
||||
|
||||
resource.delete()
|
||||
assert not Resource.objects.exists()
|
||||
|
||||
import_site(copy.deepcopy(payload))
|
||||
assert Resource.objects.count() == 1
|
||||
resource = Resource.objects.first()
|
||||
assert resource.label == 'Foo bar'
|
||||
assert resource.slug == 'foo-bar'
|
||||
|
||||
# update
|
||||
update_payload = copy.deepcopy(payload)
|
||||
update_payload['resources'][0]['label'] = 'Foo bar Updated'
|
||||
import_site(update_payload)
|
||||
resource.refresh_from_db()
|
||||
assert resource.label == 'Foo bar Updated'
|
||||
|
||||
# insert another resource
|
||||
resource.slug = 'foo-bar-updated'
|
||||
resource.save()
|
||||
import_site(copy.deepcopy(payload))
|
||||
assert Resource.objects.count() == 2
|
||||
resource = Resource.objects.latest('pk')
|
||||
assert resource.label == 'Foo bar'
|
||||
assert resource.slug == 'foo-bar'
|
||||
|
||||
|
||||
@mock.patch('chrono.agendas.models.Agenda.is_available_for_simple_management')
|
||||
def test_import_export_desk_simple_management(available_mock):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True)
|
||||
|
|
Loading…
Reference in New Issue