manager: import/export events types (#63285)

This commit is contained in:
Lauréline Guérin 2022-04-01 10:13:52 +02:00
parent 597d88cce7
commit 96b5dc941f
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
6 changed files with 122 additions and 21 deletions

View File

@ -1853,6 +1853,20 @@ class EventsType(models.Model):
custom_fields.append(values)
return custom_fields
@classmethod
def import_json(cls, data, overwrite=False):
data = clean_import_data(cls, data)
slug = data.pop('slug')
events_type, created = cls.objects.update_or_create(slug=slug, defaults=data)
return created, events_type
def export_json(self):
return {
'slug': self.slug,
'label': self.label,
'custom_fields': self.custom_fields,
}
class BookingColor(models.Model):
COLOR_COUNT = 8

View File

@ -1343,6 +1343,7 @@ class AgendasExportForm(forms.Form):
)
absence_reason_groups = forms.BooleanField(label=_('Absence reason groups'), required=False, initial=True)
categories = forms.BooleanField(label=_('Categories'), required=False, initial=True)
events_types = forms.BooleanField(label=_('Events types'), required=False, initial=True)
class SharedCustodyRuleForm(forms.ModelForm):

View File

@ -26,15 +26,24 @@ from chrono.agendas.models import (
Agenda,
AgendaImportError,
Category,
EventsType,
UnavailabilityCalendar,
)
def export_site(agendas=True, unavailability_calendars=True, absence_reason_groups=True, categories=True):
def export_site(
agendas=True,
unavailability_calendars=True,
absence_reason_groups=True,
events_types=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 events_types:
data['events_types'] = [x.export_json() for x in EventsType.objects.all()]
if absence_reason_groups:
data['absence_reason_groups'] = [x.export_json() for x in AbsenceReasonGroup.objects.all()]
if unavailability_calendars:
@ -47,10 +56,12 @@ def export_site(agendas=True, unavailability_calendars=True, absence_reason_grou
def import_site(data, if_empty=False, clean=False, overwrite=False):
# pylint: disable=too-many-boolean-expressions
if if_empty and (
Agenda.objects.exists()
or UnavailabilityCalendar.objects.exists()
or AbsenceReasonGroup.objects.exists()
or EventsType.objects.exists()
or Category.objects.exists()
):
return
@ -59,20 +70,23 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
Agenda.objects.all().delete()
UnavailabilityCalendar.objects.all().delete()
AbsenceReasonGroup.objects.all().delete()
EventsType.objects.all().delete()
Category.objects.all().delete()
results = {
'agendas': collections.defaultdict(list),
'unavailability_calendars': collections.defaultdict(list),
'absence_reason_groups': collections.defaultdict(list),
key: collections.defaultdict(list)
for key in [
'agendas',
'unavailability_calendars',
'absence_reason_groups',
'events_types',
'categories',
]
}
agendas = data.get('agendas', [])
unavailability_calendars = data.get('unavailability_calendars', [])
absence_reason_groups = data.get('absence_reason_groups', [])
categories = data.get('categories', [])
role_names = set()
for objs in (agendas, unavailability_calendars):
for key in ['agendas', 'unavailability_calendars']:
objs = data.get(key, [])
role_names = role_names.union(
{name for data in objs for _, name in data.get('permissions', {}).items() if name}
)
@ -82,20 +96,20 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
existing_roles_names = set(existing_roles.values_list('name', flat=True))
raise AgendaImportError('Missing roles: "%s"' % ', '.join(role_names - existing_roles_names))
for category in categories:
Category.import_json(category, overwrite=overwrite)
with transaction.atomic():
for objs, cls, label in (
(absence_reason_groups, AbsenceReasonGroup, 'absence_reason_groups'),
(unavailability_calendars, UnavailabilityCalendar, 'unavailability_calendars'),
(agendas, Agenda, 'agendas'),
for cls, key in (
(Category, 'categories'),
(EventsType, 'events_types'),
(AbsenceReasonGroup, 'absence_reason_groups'),
(UnavailabilityCalendar, 'unavailability_calendars'),
(Agenda, 'agendas'),
):
for data in objs:
created, obj = cls.import_json(data, overwrite=overwrite)
results[label]['all'].append(obj)
objs = data.get(key, [])
for obj in objs:
created, obj = cls.import_json(obj, overwrite=overwrite)
results[key]['all'].append(obj)
if created:
results[label]['created'].append(obj)
results[key]['created'].append(obj)
else:
results[label]['updated'].append(obj)
results[key]['updated'].append(obj)
return results

View File

@ -995,6 +995,34 @@ class AgendasImportView(FormView):
x,
),
},
'events_types': {
'create_noop': _('No events type created.'),
'create': lambda x: ungettext(
'An events type has been created.',
'%(count)d events types have been created.',
x,
),
'update_noop': _('No events type updated.'),
'update': lambda x: ungettext(
'An events type has been updated.',
'%(count)d events types have been updated.',
x,
),
},
'categories': {
'create_noop': _('No category created.'),
'create': lambda x: ungettext(
'A category has been created.',
'%(count)d categories have been created.',
x,
),
'update_noop': _('No category updated.'),
'update': lambda x: ungettext(
'A category has been updated.',
'%(count)d categories have been updated.',
x,
),
},
}
global_noop = True
@ -1043,6 +1071,8 @@ class AgendasImportView(FormView):
messages.info(self.request, results['agendas']['messages'])
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['categories']['messages'])
return super().form_valid(form)

View File

@ -37,6 +37,7 @@ def test_export_site(app, admin_user):
'unavailability_calendars': [],
'agendas': [],
'absence_reason_groups': [],
'events_types': [],
'categories': [],
}
@ -50,11 +51,13 @@ def test_export_site(app, admin_user):
assert len(site_json['agendas']) == 1
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['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['categories'] = False
resp = resp.form.submit()
@ -62,6 +65,7 @@ def test_export_site(app, admin_user):
assert 'agendas' not in site_json
assert 'unavailability_calendars' in site_json
assert 'absence_reason_groups' not in site_json
assert 'events_types' not in site_json
assert 'categories' not in site_json

View File

@ -25,6 +25,7 @@ from chrono.agendas.models import (
Category,
Desk,
Event,
EventsType,
MeetingType,
Resource,
TimePeriod,
@ -996,6 +997,43 @@ def test_import_export_category(app):
assert category.slug == 'foo-bar'
def test_import_export_events_type(app):
output = get_output_of_command('export_site')
payload = json.loads(output)
assert len(payload['events_types']) == 0
events_type = EventsType.objects.create(label='Foo bar')
output = get_output_of_command('export_site')
payload = json.loads(output)
assert len(payload['events_types']) == 1
events_type.delete()
assert not EventsType.objects.exists()
import_site(copy.deepcopy(payload))
assert EventsType.objects.count() == 1
events_type = EventsType.objects.first()
assert events_type.label == 'Foo bar'
assert events_type.slug == 'foo-bar'
# update
update_payload = copy.deepcopy(payload)
update_payload['events_types'][0]['label'] = 'Foo bar Updated'
import_site(update_payload)
events_type.refresh_from_db()
assert events_type.label == 'Foo bar Updated'
# insert another events_type
events_type.slug = 'foo-bar-updated'
events_type.save()
import_site(copy.deepcopy(payload))
assert EventsType.objects.count() == 2
events_type = EventsType.objects.latest('pk')
assert events_type.label == 'Foo bar'
assert events_type.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)