diff --git a/chrono/manager/forms.py b/chrono/manager/forms.py index 6c41df84..592bcb31 100644 --- a/chrono/manager/forms.py +++ b/chrono/manager/forms.py @@ -67,6 +67,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 @@ -444,19 +445,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, @@ -502,31 +500,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 072b2fa3..54e48d04 100644 --- a/chrono/manager/views.py +++ b/chrono/manager/views.py @@ -2503,12 +2503,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( @@ -3266,12 +3265,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 'Checked' 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 '' 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 '' % check_type_presence.slug in resp + assert '' 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 '' 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 '' % check_type_absence.slug in resp + assert '' 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(