manager: import/export categories (#57424)
This commit is contained in:
parent
842d7629d8
commit
f974cfe604
|
@ -2178,6 +2178,19 @@ class Category(models.Model):
|
|||
def base_slug(self):
|
||||
return slugify(self.label)
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data, overwrite=False):
|
||||
data = clean_import_data(cls, data)
|
||||
slug = data.pop('slug')
|
||||
category, created = cls.objects.update_or_create(slug=slug, defaults=data)
|
||||
return created, category
|
||||
|
||||
def export_json(self):
|
||||
return {
|
||||
'label': self.label,
|
||||
'slug': self.slug,
|
||||
}
|
||||
|
||||
|
||||
def ics_directory_path(instance, filename):
|
||||
return f'ics/{str(uuid.uuid4())}/{filename}'
|
||||
|
|
|
@ -922,3 +922,4 @@ class AgendasExportForm(forms.Form):
|
|||
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)
|
||||
|
|
|
@ -21,12 +21,20 @@ from django.contrib.auth.models import Group
|
|||
from django.db import transaction
|
||||
from django.db.models import Q
|
||||
|
||||
from chrono.agendas.models import AbsenceReasonGroup, Agenda, AgendaImportError, UnavailabilityCalendar
|
||||
from chrono.agendas.models import (
|
||||
AbsenceReasonGroup,
|
||||
Agenda,
|
||||
AgendaImportError,
|
||||
Category,
|
||||
UnavailabilityCalendar,
|
||||
)
|
||||
|
||||
|
||||
def export_site(agendas=True, unavailability_calendars=True, absence_reason_groups=True):
|
||||
def export_site(agendas=True, unavailability_calendars=True, absence_reason_groups=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 absence_reason_groups:
|
||||
data['absence_reason_groups'] = [x.export_json() for x in AbsenceReasonGroup.objects.all()]
|
||||
if unavailability_calendars:
|
||||
|
@ -43,6 +51,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
Agenda.objects.exists()
|
||||
or UnavailabilityCalendar.objects.exists()
|
||||
or AbsenceReasonGroup.objects.exists()
|
||||
or Category.objects.exists()
|
||||
):
|
||||
return
|
||||
|
||||
|
@ -50,6 +59,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
Agenda.objects.all().delete()
|
||||
UnavailabilityCalendar.objects.all().delete()
|
||||
AbsenceReasonGroup.objects.all().delete()
|
||||
Category.objects.all().delete()
|
||||
|
||||
results = {
|
||||
'agendas': collections.defaultdict(list),
|
||||
|
@ -59,6 +69,7 @@ def import_site(data, if_empty=False, clean=False, overwrite=False):
|
|||
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):
|
||||
|
@ -71,6 +82,9 @@ 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'),
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import datetime
|
||||
import json
|
||||
from unittest import mock
|
||||
|
||||
import freezegun
|
||||
|
@ -2914,41 +2913,6 @@ def test_manager_reminders_preview(app, admin_user):
|
|||
)
|
||||
|
||||
|
||||
def test_export_site(app, admin_user):
|
||||
login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export')
|
||||
|
||||
with freezegun.freeze_time('2020-06-15'):
|
||||
resp = resp.form.submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_agendas_20200615.json"'
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
assert site_json == {'unavailability_calendars': [], 'agendas': [], 'absence_reason_groups': []}
|
||||
|
||||
agenda = Agenda.objects.create(label='Foo Bar', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
UnavailabilityCalendar.objects.create(label='Calendar 1')
|
||||
resp = app.get('/manage/agendas/export/')
|
||||
resp = resp.form.submit()
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
assert len(site_json['agendas']) == 1
|
||||
assert len(site_json['unavailability_calendars']) == 1
|
||||
assert len(site_json['absence_reason_groups']) == 0
|
||||
|
||||
resp = app.get('/manage/agendas/export/')
|
||||
resp.form['agendas'] = False
|
||||
resp.form['absence_reason_groups'] = False
|
||||
resp = resp.form.submit()
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
assert 'agendas' not in site_json
|
||||
assert 'unavailability_calendars' in site_json
|
||||
assert 'absence_reason_groups' not in site_json
|
||||
|
||||
|
||||
def test_manager_agenda_roles(app, admin_user, manager_user):
|
||||
agenda = Agenda.objects.create(label='Events', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
|
|
@ -23,6 +23,49 @@ from .test_all import login
|
|||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
def test_export_site(app, admin_user):
|
||||
login(app)
|
||||
resp = app.get('/manage/')
|
||||
resp = resp.click('Export')
|
||||
|
||||
with freezegun.freeze_time('2020-06-15'):
|
||||
resp = resp.form.submit()
|
||||
assert resp.headers['content-type'] == 'application/json'
|
||||
assert resp.headers['content-disposition'] == 'attachment; filename="export_agendas_20200615.json"'
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
assert site_json == {
|
||||
'unavailability_calendars': [],
|
||||
'agendas': [],
|
||||
'absence_reason_groups': [],
|
||||
'categories': [],
|
||||
}
|
||||
|
||||
agenda = Agenda.objects.create(label='Foo Bar', kind='events')
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
UnavailabilityCalendar.objects.create(label='Calendar 1')
|
||||
resp = app.get('/manage/agendas/export/')
|
||||
resp = resp.form.submit()
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
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['categories']) == 0
|
||||
|
||||
resp = app.get('/manage/agendas/export/')
|
||||
resp.form['agendas'] = False
|
||||
resp.form['absence_reason_groups'] = False
|
||||
resp.form['categories'] = False
|
||||
resp = resp.form.submit()
|
||||
|
||||
site_json = json.loads(resp.text)
|
||||
assert 'agendas' not in site_json
|
||||
assert 'unavailability_calendars' in site_json
|
||||
assert 'absence_reason_groups' not in site_json
|
||||
assert 'categories' not in site_json
|
||||
|
||||
|
||||
def test_import_agenda_as_manager(app, manager_user):
|
||||
# open /manage/ access to manager_user, and check agenda import is not
|
||||
# allowed.
|
|
@ -374,7 +374,7 @@ def test_import_export_resources(app):
|
|||
assert list(agenda.resources.all()) == [resource]
|
||||
|
||||
|
||||
def test_import_export_categorys(app):
|
||||
def test_import_export_categories(app):
|
||||
category = Category.objects.create(label='foo')
|
||||
agenda = Agenda.objects.create(label='Foo Bar', category=category)
|
||||
Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
|
||||
|
@ -382,19 +382,21 @@ def test_import_export_categorys(app):
|
|||
|
||||
import_site(data={}, clean=True)
|
||||
assert Agenda.objects.count() == 0
|
||||
category.delete()
|
||||
assert Category.objects.count() == 0
|
||||
data = json.loads(output)
|
||||
del data['categories']
|
||||
|
||||
with pytest.raises(AgendaImportError) as excinfo:
|
||||
import_site(json.loads(output), overwrite=True)
|
||||
import_site(data, overwrite=True)
|
||||
assert str(excinfo.value) == 'Missing "foo" category'
|
||||
|
||||
category = Category.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" category'
|
||||
|
||||
category = Category.objects.create(label='foo')
|
||||
import_site(json.loads(output), overwrite=True)
|
||||
import_site(data, overwrite=True)
|
||||
agenda = Agenda.objects.get(slug=agenda.slug)
|
||||
assert agenda.category == category
|
||||
|
||||
|
@ -932,6 +934,43 @@ def test_import_export_absence_reason_group(app):
|
|||
assert AbsenceReason.objects.get(group=group, label='Baz', slug='baz')
|
||||
|
||||
|
||||
def test_import_export_category(app):
|
||||
output = get_output_of_command('export_site')
|
||||
payload = json.loads(output)
|
||||
assert len(payload['categories']) == 0
|
||||
|
||||
category = Category.objects.create(label='Foo bar')
|
||||
|
||||
output = get_output_of_command('export_site')
|
||||
payload = json.loads(output)
|
||||
assert len(payload['categories']) == 1
|
||||
|
||||
category.delete()
|
||||
assert not Category.objects.exists()
|
||||
|
||||
import_site(copy.deepcopy(payload))
|
||||
assert Category.objects.count() == 1
|
||||
category = Category.objects.first()
|
||||
assert category.label == 'Foo bar'
|
||||
assert category.slug == 'foo-bar'
|
||||
|
||||
# update
|
||||
update_payload = copy.deepcopy(payload)
|
||||
update_payload['categories'][0]['label'] = 'Foo bar Updated'
|
||||
import_site(update_payload)
|
||||
category.refresh_from_db()
|
||||
assert category.label == 'Foo bar Updated'
|
||||
|
||||
# insert another category
|
||||
category.slug = 'foo-bar-updated'
|
||||
category.save()
|
||||
import_site(copy.deepcopy(payload))
|
||||
assert Category.objects.count() == 2
|
||||
category = Category.objects.latest('pk')
|
||||
assert category.label == 'Foo bar'
|
||||
assert category.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