Projet

Général

Profil

0001-manager-import-export-categories-57424.patch

Valentin Deniaud, 09 décembre 2021 10:14

Télécharger (11,5 ko)

Voir les différences:

Subject: [PATCH 1/2] manager: import/export categories (#57424)

 chrono/agendas/models.py                      | 13 +++++
 chrono/manager/forms.py                       |  1 +
 chrono/manager/utils.py                       | 18 ++++++-
 tests/manager/test_all.py                     | 36 --------------
 .../{test_import.py => test_import_export.py} | 43 ++++++++++++++++
 tests/test_import_export.py                   | 49 +++++++++++++++++--
 6 files changed, 117 insertions(+), 43 deletions(-)
 rename tests/manager/{test_import.py => test_import_export.py} (89%)
chrono/agendas/models.py
2166 2166
    def base_slug(self):
2167 2167
        return slugify(self.label)
2168 2168

  
2169
    @classmethod
2170
    def import_json(cls, data, overwrite=False):
2171
        data = clean_import_data(cls, data)
2172
        slug = data.pop('slug')
2173
        category, created = cls.objects.update_or_create(slug=slug, defaults=data)
2174
        return created, category
2175

  
2176
    def export_json(self):
2177
        return {
2178
            'label': self.label,
2179
            'slug': self.slug,
2180
        }
2181

  
2169 2182

  
2170 2183
def ics_directory_path(instance, filename):
2171 2184
    return f'ics/{str(uuid.uuid4())}/{filename}'
chrono/manager/forms.py
922 922
        label=_('Unavailability calendars'), required=False, initial=True
923 923
    )
924 924
    absence_reason_groups = forms.BooleanField(label=_('Absence reason groups'), required=False, initial=True)
925
    categories = forms.BooleanField(label=_('Categories'), required=False, initial=True)
chrono/manager/utils.py
21 21
from django.db import transaction
22 22
from django.db.models import Q
23 23

  
24
from chrono.agendas.models import AbsenceReasonGroup, Agenda, AgendaImportError, UnavailabilityCalendar
24
from chrono.agendas.models import (
25
    AbsenceReasonGroup,
26
    Agenda,
27
    AgendaImportError,
28
    Category,
29
    UnavailabilityCalendar,
30
)
25 31

  
26 32

  
27
def export_site(agendas=True, unavailability_calendars=True, absence_reason_groups=True):
33
def export_site(agendas=True, unavailability_calendars=True, absence_reason_groups=True, categories=True):
28 34
    '''Dump site objects to JSON-dumpable dictionnary'''
29 35
    data = collections.OrderedDict()
36
    if categories:
37
        data['categories'] = [x.export_json() for x in Category.objects.all()]
30 38
    if absence_reason_groups:
31 39
        data['absence_reason_groups'] = [x.export_json() for x in AbsenceReasonGroup.objects.all()]
32 40
    if unavailability_calendars:
......
43 51
        Agenda.objects.exists()
44 52
        or UnavailabilityCalendar.objects.exists()
45 53
        or AbsenceReasonGroup.objects.exists()
54
        or Category.objects.exists()
46 55
    ):
47 56
        return
48 57

  
......
50 59
        Agenda.objects.all().delete()
51 60
        UnavailabilityCalendar.objects.all().delete()
52 61
        AbsenceReasonGroup.objects.all().delete()
62
        Category.objects.all().delete()
53 63

  
54 64
    results = {
55 65
        'agendas': collections.defaultdict(list),
......
59 69
    agendas = data.get('agendas', [])
60 70
    unavailability_calendars = data.get('unavailability_calendars', [])
61 71
    absence_reason_groups = data.get('absence_reason_groups', [])
72
    categories = data.get('categories', [])
62 73

  
63 74
    role_names = set()
64 75
    for objs in (agendas, unavailability_calendars):
......
71 82
        existing_roles_names = set(existing_roles.values_list('name', flat=True))
72 83
        raise AgendaImportError('Missing roles: "%s"' % ', '.join(role_names - existing_roles_names))
73 84

  
85
    for category in categories:
86
        Category.import_json(category, overwrite=overwrite)
87

  
74 88
    with transaction.atomic():
75 89
        for objs, cls, label in (
76 90
            (absence_reason_groups, AbsenceReasonGroup, 'absence_reason_groups'),
tests/manager/test_all.py
1 1
import datetime
2
import json
3 2
from unittest import mock
4 3

  
5 4
import freezegun
......
2914 2913
    )
2915 2914

  
2916 2915

  
2917
def test_export_site(app, admin_user):
2918
    login(app)
2919
    resp = app.get('/manage/')
2920
    resp = resp.click('Export')
2921

  
2922
    with freezegun.freeze_time('2020-06-15'):
2923
        resp = resp.form.submit()
2924
    assert resp.headers['content-type'] == 'application/json'
2925
    assert resp.headers['content-disposition'] == 'attachment; filename="export_agendas_20200615.json"'
2926

  
2927
    site_json = json.loads(resp.text)
2928
    assert site_json == {'unavailability_calendars': [], 'agendas': [], 'absence_reason_groups': []}
2929

  
2930
    agenda = Agenda.objects.create(label='Foo Bar', kind='events')
2931
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
2932
    UnavailabilityCalendar.objects.create(label='Calendar 1')
2933
    resp = app.get('/manage/agendas/export/')
2934
    resp = resp.form.submit()
2935

  
2936
    site_json = json.loads(resp.text)
2937
    assert len(site_json['agendas']) == 1
2938
    assert len(site_json['unavailability_calendars']) == 1
2939
    assert len(site_json['absence_reason_groups']) == 0
2940

  
2941
    resp = app.get('/manage/agendas/export/')
2942
    resp.form['agendas'] = False
2943
    resp.form['absence_reason_groups'] = False
2944
    resp = resp.form.submit()
2945

  
2946
    site_json = json.loads(resp.text)
2947
    assert 'agendas' not in site_json
2948
    assert 'unavailability_calendars' in site_json
2949
    assert 'absence_reason_groups' not in site_json
2950

  
2951

  
2952 2916
def test_manager_agenda_roles(app, admin_user, manager_user):
2953 2917
    agenda = Agenda.objects.create(label='Events', kind='events')
2954 2918
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
tests/manager/test_import.py → tests/manager/test_import_export.py
23 23
pytestmark = pytest.mark.django_db
24 24

  
25 25

  
26
def test_export_site(app, admin_user):
27
    login(app)
28
    resp = app.get('/manage/')
29
    resp = resp.click('Export')
30

  
31
    with freezegun.freeze_time('2020-06-15'):
32
        resp = resp.form.submit()
33
    assert resp.headers['content-type'] == 'application/json'
34
    assert resp.headers['content-disposition'] == 'attachment; filename="export_agendas_20200615.json"'
35

  
36
    site_json = json.loads(resp.text)
37
    assert site_json == {
38
        'unavailability_calendars': [],
39
        'agendas': [],
40
        'absence_reason_groups': [],
41
        'categories': [],
42
    }
43

  
44
    agenda = Agenda.objects.create(label='Foo Bar', kind='events')
45
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
46
    UnavailabilityCalendar.objects.create(label='Calendar 1')
47
    resp = app.get('/manage/agendas/export/')
48
    resp = resp.form.submit()
49

  
50
    site_json = json.loads(resp.text)
51
    assert len(site_json['agendas']) == 1
52
    assert len(site_json['unavailability_calendars']) == 1
53
    assert len(site_json['absence_reason_groups']) == 0
54
    assert len(site_json['categories']) == 0
55

  
56
    resp = app.get('/manage/agendas/export/')
57
    resp.form['agendas'] = False
58
    resp.form['absence_reason_groups'] = False
59
    resp.form['categories'] = False
60
    resp = resp.form.submit()
61

  
62
    site_json = json.loads(resp.text)
63
    assert 'agendas' not in site_json
64
    assert 'unavailability_calendars' in site_json
65
    assert 'absence_reason_groups' not in site_json
66
    assert 'categories' not in site_json
67

  
68

  
26 69
def test_import_agenda_as_manager(app, manager_user):
27 70
    # open /manage/ access to manager_user, and check agenda import is not
28 71
    # allowed.
tests/test_import_export.py
374 374
    assert list(agenda.resources.all()) == [resource]
375 375

  
376 376

  
377
def test_import_export_categorys(app):
377
def test_import_export_categories(app):
378 378
    category = Category.objects.create(label='foo')
379 379
    agenda = Agenda.objects.create(label='Foo Bar', category=category)
380 380
    Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
......
382 382

  
383 383
    import_site(data={}, clean=True)
384 384
    assert Agenda.objects.count() == 0
385
    category.delete()
385
    assert Category.objects.count() == 0
386
    data = json.loads(output)
387
    del data['categories']
386 388

  
387 389
    with pytest.raises(AgendaImportError) as excinfo:
388
        import_site(json.loads(output), overwrite=True)
390
        import_site(data, overwrite=True)
389 391
    assert str(excinfo.value) == 'Missing "foo" category'
390 392

  
391 393
    category = Category.objects.create(label='foobar')
392 394
    with pytest.raises(AgendaImportError) as excinfo:
393
        import_site(json.loads(output), overwrite=True)
395
        import_site(data, overwrite=True)
394 396
    assert str(excinfo.value) == 'Missing "foo" category'
395 397

  
396 398
    category = Category.objects.create(label='foo')
397
    import_site(json.loads(output), overwrite=True)
399
    import_site(data, overwrite=True)
398 400
    agenda = Agenda.objects.get(slug=agenda.slug)
399 401
    assert agenda.category == category
400 402

  
......
932 934
    assert AbsenceReason.objects.get(group=group, label='Baz', slug='baz')
933 935

  
934 936

  
937
def test_import_export_category(app):
938
    output = get_output_of_command('export_site')
939
    payload = json.loads(output)
940
    assert len(payload['categories']) == 0
941

  
942
    category = Category.objects.create(label='Foo bar')
943

  
944
    output = get_output_of_command('export_site')
945
    payload = json.loads(output)
946
    assert len(payload['categories']) == 1
947

  
948
    category.delete()
949
    assert not Category.objects.exists()
950

  
951
    import_site(copy.deepcopy(payload))
952
    assert Category.objects.count() == 1
953
    category = Category.objects.first()
954
    assert category.label == 'Foo bar'
955
    assert category.slug == 'foo-bar'
956

  
957
    # update
958
    update_payload = copy.deepcopy(payload)
959
    update_payload['categories'][0]['label'] = 'Foo bar Updated'
960
    import_site(update_payload)
961
    category.refresh_from_db()
962
    assert category.label == 'Foo bar Updated'
963

  
964
    # insert another category
965
    category.slug = 'foo-bar-updated'
966
    category.save()
967
    import_site(copy.deepcopy(payload))
968
    assert Category.objects.count() == 2
969
    category = Category.objects.latest('pk')
970
    assert category.label == 'Foo bar'
971
    assert category.slug == 'foo-bar'
972

  
973

  
935 974
@mock.patch('chrono.agendas.models.Agenda.is_available_for_simple_management')
936 975
def test_import_export_desk_simple_management(available_mock):
937 976
    agenda = Agenda.objects.create(label='Foo bar', kind='meetings', desk_simple_management=True)
938
-