From 86135c1d007b28deacd3f9b3ff7b9df2ebb4283c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 9 Jun 2022 17:01:20 +0200
Subject: [PATCH 4/5] manager: use check_types from lingo (#66015)

---
 chrono/manager/forms.py     |  48 ++++++------
 chrono/manager/views.py     |  18 ++---
 tests/manager/test_event.py | 144 +++++++++++++++++-------------------
 3 files changed, 96 insertions(+), 114 deletions(-)

diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py
index 80c95bec..f6473108 100644
--- a/chrono/manager/forms.py
+++ b/chrono/manager/forms.py
@@ -64,6 +64,7 @@ from chrono.agendas.models import (
     VirtualMember,
     generate_slug,
 )
+from chrono.utils.lingo import get_agenda_check_types
 
 from . import widgets
 from .widgets import SplitDateTimeField, WeekdaysWidget
@@ -441,19 +442,16 @@ class BookingCheckFilterSet(django_filters.FilterSet):
             ('not-checked', _('Not checked')),
             ('presence', _('Presence')),
         ]
-        if self.agenda.check_type_group:
-            status_choices += [
-                ('presence::%s' % r.slug, _('Presence (%s)') % r.label)
-                for r in self.agenda.check_type_group.check_types.all()
-                if r.kind == 'presence' and not r.disabled
-            ]
+        check_types = get_agenda_check_types(self.agenda)
+        absence_check_types = [ct for ct in check_types if ct.kind == 'absence']
+        presence_check_types = [ct for ct in check_types if ct.kind == 'presence']
+        status_choices += [
+            ('presence::%s' % ct.slug, _('Presence (%s)') % ct.label) for ct in presence_check_types
+        ]
         status_choices += [('absence', _('Absence'))]
-        if self.agenda.check_type_group:
-            status_choices += [
-                ('absence::%s' % r.slug, _('Absence (%s)') % r.label)
-                for r in self.agenda.check_type_group.check_types.all()
-                if r.kind == 'absence' and not r.disabled
-            ]
+        status_choices += [
+            ('absence::%s' % ct.slug, _('Absence (%s)') % ct.label) for ct in absence_check_types
+        ]
         self.filters['booking-status'] = django_filters.ChoiceFilter(
             label=_('Filter by status'),
             choices=status_choices,
@@ -499,31 +497,29 @@ class SubscriptionCheckFilterSet(BookingCheckFilterSet):
 
 
 class BookingCheckAbsenceForm(forms.Form):
-    check_type = forms.ChoiceField(required=False)  # and not ModelChoiceField, to reduce querysets
+    check_type = forms.ChoiceField(required=False)
 
     def __init__(self, *args, **kwargs):
         agenda = kwargs.pop('agenda')
         super().__init__(*args, **kwargs)
-        if agenda.check_type_group:
-            self.fields['check_type'].choices = [('', '---------')] + [
-                (r.slug, r.label)
-                for r in agenda.check_type_group.check_types.all()
-                if r.kind == 'absence' and not r.disabled
-            ]
+        check_types = get_agenda_check_types(agenda)
+        self.absence_check_types = [ct for ct in check_types if ct.kind == 'absence']
+        self.fields['check_type'].choices = [('', '---------')] + [
+            (ct.slug, ct.label) for ct in self.absence_check_types
+        ]
 
 
 class BookingCheckPresenceForm(forms.Form):
-    check_type = forms.ChoiceField(required=False)  # and not ModelChoiceField, to reduce querysets
+    check_type = forms.ChoiceField(required=False)
 
     def __init__(self, *args, **kwargs):
         agenda = kwargs.pop('agenda')
         super().__init__(*args, **kwargs)
-        if agenda.check_type_group:
-            self.fields['check_type'].choices = [('', '---------')] + [
-                (r.slug, r.label)
-                for r in agenda.check_type_group.check_types.all()
-                if r.kind == 'presence' and not r.disabled
-            ]
+        check_types = get_agenda_check_types(agenda)
+        self.presence_check_types = [ct for ct in check_types if ct.kind == 'presence']
+        self.fields['check_type'].choices = [('', '---------')] + [
+            (ct.slug, ct.label) for ct in self.presence_check_types
+        ]
 
 
 class EventsTimesheetForm(forms.Form):
diff --git a/chrono/manager/views.py b/chrono/manager/views.py
index fa659a36..132df095 100644
--- a/chrono/manager/views.py
+++ b/chrono/manager/views.py
@@ -2498,12 +2498,11 @@ class EventCheckMixin:
 
     def get_check_type(self, kind):
         form = self.get_form()
-        check_type = None
         if form.is_valid() and form.cleaned_data['check_type']:
-            check_type = CheckType.objects.filter(
-                slug=form.cleaned_data['check_type'], kind=kind, group=self.agenda.check_type_group
-            ).first()
-        return check_type
+            check_types = getattr(form, '%s_check_types' % kind)
+            for ct in check_types:
+                if ct.slug == form.cleaned_data['check_type']:
+                    return ct
 
     def response(self, request):
         return HttpResponseRedirect(
@@ -3261,12 +3260,11 @@ class BookingCheckMixin:
 
     def get_check_type(self, kind):
         form = self.get_form()
-        check_type = None
         if form.is_valid() and form.cleaned_data['check_type']:
-            check_type = CheckType.objects.filter(
-                slug=form.cleaned_data['check_type'], kind=kind, group=self.agenda.check_type_group
-            ).first()
-        return check_type
+            check_types = getattr(form, '%s_check_types' % kind)
+            for ct in check_types:
+                if ct.slug == form.cleaned_data['check_type']:
+                    return ct
 
     def response(self, request, booking):
         if request.is_ajax():
diff --git a/tests/manager/test_event.py b/tests/manager/test_event.py
index 13fd0aab..0cca4c19 100644
--- a/tests/manager/test_event.py
+++ b/tests/manager/test_event.py
@@ -10,16 +10,8 @@ from django.test.utils import CaptureQueriesContext
 from django.utils.timezone import localtime, make_aware, now
 from webtest import Upload
 
-from chrono.agendas.models import (
-    Agenda,
-    Booking,
-    CheckType,
-    CheckTypeGroup,
-    Desk,
-    Event,
-    EventsType,
-    Subscription,
-)
+from chrono.agendas.models import Agenda, Booking, Desk, Event, EventsType, Subscription
+from chrono.utils.lingo import CheckType
 from tests.utils import login
 
 pytestmark = pytest.mark.django_db
@@ -1648,18 +1640,16 @@ def test_event_checked(app, admin_user):
     assert '<span class="tag">Checked</span>' in resp
 
 
-def test_event_check_filters(app, admin_user):
-    group = CheckTypeGroup.objects.create(label='Foo bar')
-    check_type_absence = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
-    check_type_presence = CheckType.objects.create(label='Bar reason', group=group, kind='presence')
-    check_type_absence_disabled = CheckType.objects.create(
-        label='disabled', group=group, kind='absence', disabled=True
-    )
-    check_type_presence_disabled = CheckType.objects.create(
-        label='disabled too', group=group, kind='presence', disabled=True
-    )
+@mock.patch('chrono.manager.forms.get_agenda_check_types')
+def test_event_check_filters(check_types, app, admin_user):
+    check_types.return_value = [
+        CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
+        CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
+    ]
     agenda = Agenda.objects.create(
-        label='Events', kind='events', booking_check_filters='foo,bar', check_type_group=group
+        label='Events',
+        kind='events',
+        booking_check_filters='foo,bar',
     )
     event = Event.objects.create(
         label='xyz',
@@ -1707,7 +1697,7 @@ def test_event_check_filters(app, admin_user):
         user_last_name='foo-none bar-val2 reason-foo',
         extra_data={'bar': 'val2'},
         user_was_present=False,
-        user_check_type_slug=check_type_absence.slug,
+        user_check_type_slug='foo-reason',
     )
     Booking.objects.create(
         event=event,
@@ -1716,7 +1706,7 @@ def test_event_check_filters(app, admin_user):
         user_last_name='foo-none bar-val2 reason-bar',
         extra_data={'bar': 'val2'},
         user_was_present=True,
-        user_check_type_slug=check_type_presence.slug,
+        user_check_type_slug='bar-reason',
     )
     Booking.objects.create(
         event=event,
@@ -1725,7 +1715,7 @@ def test_event_check_filters(app, admin_user):
         user_last_name='foo-none bar-val2 cancelled-absence',
         extra_data={'bar': 'val2'},
         user_was_present=False,
-        user_check_type_slug=check_type_absence.slug,
+        user_check_type_slug='foo-reason',
         cancellation_datetime=now(),
     )
     Booking.objects.create(
@@ -1735,7 +1725,7 @@ def test_event_check_filters(app, admin_user):
         user_last_name='foo-none bar-val2 cancelled-presence',
         extra_data={'bar': 'val2'},
         user_was_present=True,
-        user_check_type_slug=check_type_presence.slug,
+        user_check_type_slug='bar-reason',
         cancellation_datetime=now(),
     )
 
@@ -1816,14 +1806,12 @@ def test_event_check_filters(app, admin_user):
         assert 'Subscription foo-val2 bar-val1' in resp
         assert 'Subscription foo-val1 bar-val2' in resp
         assert 'Subscription foo-none bar-val2' in resp
-        assert len(resp.pyquery.find('input[value=absence-%s]' % check_type_absence_disabled.slug)) == 0
-        assert len(resp.pyquery.find('input[value=absence-%s]' % check_type_presence_disabled.slug)) == 0
 
     with CaptureQueriesContext(connection) as ctx:
         resp = app.get(
             '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk), params={'extra-data-foo': 'val1'}
         )
-        assert len(ctx.captured_queries) == 12
+        assert len(ctx.captured_queries) == 11
     assert 'User none' not in resp
     assert 'User empty' not in resp
     assert 'User foo-val1 bar-none presence' in resp
@@ -1996,7 +1984,7 @@ def test_event_check_filters(app, admin_user):
 
     resp = app.get(
         '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
-        params={'booking-status': 'absence::%s' % check_type_absence.slug},
+        params={'booking-status': 'absence::foo-reason'},
     )
     assert 'User none' not in resp
     assert 'User empty' not in resp
@@ -2016,7 +2004,7 @@ def test_event_check_filters(app, admin_user):
 
     resp = app.get(
         '/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk),
-        params={'booking-status': 'presence::%s' % check_type_presence.slug},
+        params={'booking-status': 'presence::bar-reason'},
     )
     assert 'User none' not in resp
     assert 'User empty' not in resp
@@ -2073,8 +2061,9 @@ def test_event_check_ordering(app, admin_user):
     assert resp.text.index('AA YY') < resp.text.index('BB XX') < resp.text.index('CC WW')
 
 
-def test_event_check_booking(app, admin_user):
-    group = CheckTypeGroup.objects.create(label='Foo bar')
+@mock.patch('chrono.manager.forms.get_agenda_check_types')
+def test_event_check_booking(check_types, app, admin_user):
+    check_types.return_value = []
     agenda = Agenda.objects.create(label='Events', kind='events')
     event = Event.objects.create(
         label='xyz',
@@ -2138,15 +2127,9 @@ def test_event_check_booking(app, admin_user):
     event.refresh_from_db()
     assert event.checked is True
 
-    agenda.check_type_group = group
-    agenda.save()
-    CheckType.objects.create(label='disabled', group=group, kind='absence', disabled=True)
-    CheckType.objects.create(label='disabled too', group=group, kind='presence', disabled=True)
-    resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
-    assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 0
-    assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 0
-
-    check_type_absence = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
+    check_types.return_value = [
+        CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
+    ]
     resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
     assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
     assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 0
@@ -2154,17 +2137,17 @@ def test_event_check_booking(app, admin_user):
     # set as absent with check_type
     resp = app.post(
         '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_absence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
     ).follow()
     assert 'Foo reason' in resp
     booking.refresh_from_db()
     assert booking.user_was_present is False
-    assert booking.user_check_type_slug == check_type_absence.slug
-    assert booking.user_check_type_label == check_type_absence.label
+    assert booking.user_check_type_slug == 'foo-reason'
+    assert booking.user_check_type_label == 'Foo reason'
     secondary_booking.refresh_from_db()
     assert secondary_booking.user_was_present is False
-    assert secondary_booking.user_check_type_slug == check_type_absence.slug
-    assert secondary_booking.user_check_type_label == check_type_absence.label
+    assert secondary_booking.user_check_type_slug == 'foo-reason'
+    assert secondary_booking.user_check_type_label == 'Foo reason'
 
     # set as present without check_type
     resp = app.post(
@@ -2185,13 +2168,14 @@ def test_event_check_booking(app, admin_user):
     event.refresh_from_db()
     assert event.checked is True
 
-    agenda.check_type_group = group
-    agenda.save()
     resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
     assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
     assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 0
 
-    check_type_presence = CheckType.objects.create(label='Bar reason', group=group, kind='presence')
+    check_types.return_value = [
+        CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
+        CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
+    ]
     resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
     assert len(resp.pyquery.find('td.booking-actions form.absence select')) == 1
     assert len(resp.pyquery.find('td.booking-actions form.presence select')) == 1
@@ -2199,17 +2183,17 @@ def test_event_check_booking(app, admin_user):
     # set as present with check_type
     resp = app.post(
         '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_presence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
     ).follow()
     assert 'Bar reason' in resp
     booking.refresh_from_db()
     assert booking.user_was_present is True
-    assert booking.user_check_type_slug == check_type_presence.slug
-    assert booking.user_check_type_label == check_type_presence.label
+    assert booking.user_check_type_slug == 'bar-reason'
+    assert booking.user_check_type_label == 'Bar reason'
     secondary_booking.refresh_from_db()
     assert secondary_booking.user_was_present is True
-    assert secondary_booking.user_check_type_slug == check_type_presence.slug
-    assert secondary_booking.user_check_type_label == check_type_presence.label
+    assert secondary_booking.user_check_type_slug == 'bar-reason'
+    assert secondary_booking.user_check_type_label == 'Bar reason'
 
     # mark the event as checked
     event.checked = True
@@ -2246,11 +2230,13 @@ def test_event_check_booking(app, admin_user):
     )
 
 
-def test_event_check_booking_ajax(app, admin_user):
-    group = CheckTypeGroup.objects.create(label='Foo bar')
-    check_type_absence = CheckType.objects.create(label='Foo reason', group=group)
-    check_type_presence = CheckType.objects.create(label='Bar reason', group=group, kind='presence')
-    agenda = Agenda.objects.create(label='Events', kind='events', check_type_group=group)
+@mock.patch('chrono.manager.forms.get_agenda_check_types')
+def test_event_check_booking_ajax(check_types, app, admin_user):
+    check_types.return_value = [
+        CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
+        CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
+    ]
+    agenda = Agenda.objects.create(label='Events', kind='events')
     event = Event.objects.create(
         label='xyz',
         start_datetime=now() - datetime.timedelta(days=1),
@@ -2267,33 +2253,35 @@ def test_event_check_booking_ajax(app, admin_user):
     # set as present
     resp = app.post(
         '/manage/agendas/%s/bookings/%s/presence' % (agenda.pk, booking.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_presence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
         extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
     )
     assert '<tr>' not in resp  # because this is a fragment
     assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Present\n  \n    (Bar reason)'
     assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
     assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text == 'Presence'
-    assert '<option value="%s" selected>Bar reason</option>' % check_type_presence.slug in resp
+    assert '<option value="bar-reason" selected>Bar reason</option>' in resp
 
     # set as absent
     resp = app.post(
         '/manage/agendas/%s/bookings/%s/absence' % (agenda.pk, booking.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_absence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
         extra_environ={'HTTP_X_REQUESTED_WITH': 'XMLHttpRequest'},
     )
     assert '<tr>' not in resp  # because this is a fragment
     assert resp.pyquery.find('td.booking-status')[0].text.strip() == 'Absent\n  \n    (Foo reason)'
     assert len(resp.pyquery.find('td.booking-actions button[disabled]')) == 1
     assert resp.pyquery.find('td.booking-actions button[disabled]')[0].text.startswith('Absence')
-    assert '<option value="%s" selected>Foo reason</option>' % check_type_absence.slug in resp
+    assert '<option value="foo-reason" selected>Foo reason</option>' in resp
 
 
-def test_event_check_all_bookings(app, admin_user):
-    group = CheckTypeGroup.objects.create(label='Foo bar')
-    check_type_absence = CheckType.objects.create(label='Foo reason', group=group)
-    check_type_presence = CheckType.objects.create(label='Bar reason', group=group, kind='presence')
-    agenda = Agenda.objects.create(label='Events', kind='events', check_type_group=group)
+@mock.patch('chrono.manager.forms.get_agenda_check_types')
+def test_event_check_all_bookings(check_types, app, admin_user):
+    check_types.return_value = [
+        CheckType(slug='foo-reason', label='Foo reason', kind='absence'),
+        CheckType(slug='bar-reason', label='Bar reason', kind='presence'),
+    ]
+    agenda = Agenda.objects.create(label='Events', kind='events')
     event = Event.objects.create(
         label='xyz',
         start_datetime=now() - datetime.timedelta(days=1),
@@ -2362,7 +2350,7 @@ def test_event_check_all_bookings(app, admin_user):
     assert 'Mark all bookings without status' in resp
     app.post(
         '/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_absence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
     )
     booking1.refresh_from_db()
     assert booking1.user_was_present is False
@@ -2374,15 +2362,15 @@ def test_event_check_all_bookings(app, admin_user):
     assert booking2.user_check_type_label is None
     booking3.refresh_from_db()
     assert booking3.user_was_present is False
-    assert booking3.user_check_type_slug == check_type_absence.slug
-    assert booking3.user_check_type_label == check_type_absence.label
+    assert booking3.user_check_type_slug == 'foo-reason'
+    assert booking3.user_check_type_label == 'Foo reason'
 
     booking4 = Booking.objects.create(event=event, user_first_name='User', user_last_name='52')
     resp = app.get('/manage/agendas/%s/events/%s/check' % (agenda.pk, event.pk))
     assert 'Mark all bookings without status' in resp
     app.post(
         '/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_presence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
     )
     booking1.refresh_from_db()
     assert booking1.user_was_present is False
@@ -2394,12 +2382,12 @@ def test_event_check_all_bookings(app, admin_user):
     assert booking2.user_check_type_label is None
     booking3.refresh_from_db()
     assert booking3.user_was_present is False
-    assert booking3.user_check_type_slug == check_type_absence.slug
-    assert booking3.user_check_type_label == check_type_absence.label
+    assert booking3.user_check_type_slug == 'foo-reason'
+    assert booking3.user_check_type_label == 'Foo reason'
     booking4.refresh_from_db()
     assert booking4.user_was_present is True
-    assert booking4.user_check_type_slug == check_type_presence.slug
-    assert booking4.user_check_type_label == check_type_presence.label
+    assert booking4.user_check_type_slug == 'bar-reason'
+    assert booking4.user_check_type_label == 'Bar reason'
 
     # now disable check update
     agenda.disable_check_update = True
@@ -2409,7 +2397,7 @@ def test_event_check_all_bookings(app, admin_user):
     assert 'Mark all bookings without status' not in resp
     app.post(
         '/manage/agendas/%s/events/%s/absence' % (agenda.pk, event.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_absence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'foo-reason'},
         status=404,
     )
     resp = app.post(
@@ -2419,7 +2407,7 @@ def test_event_check_all_bookings(app, admin_user):
     )
     app.post(
         '/manage/agendas/%s/events/%s/presence' % (agenda.pk, event.pk),
-        params={'csrfmiddlewaretoken': token, 'check_type': check_type_presence.slug},
+        params={'csrfmiddlewaretoken': token, 'check_type': 'bar-reason'},
         status=404,
     )
     resp = app.post(
-- 
2.35.1

