manager: import/export resources (#62890)

This commit is contained in:
Lauréline Guérin 2022-04-01 14:25:41 +02:00
parent 96b5dc941f
commit cdada0578b
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 90 additions and 9 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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'),

View File

@ -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)

View File

@ -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

View File

@ -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)