From a7cb4475c650d0ad3e3bb7597a6ab2c98bfb97b2 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 16 Feb 2023 10:56:07 +0100 Subject: [PATCH 1/4] api: drop legacy category filter (#74327) --- chrono/api/serializers.py | 1 - chrono/api/views.py | 18 ------------------ tests/api/test_statistics.py | 11 ++--------- 3 files changed, 2 insertions(+), 28 deletions(-) diff --git a/chrono/api/serializers.py b/chrono/api/serializers.py index c99daad7..84fbb0d9 100644 --- a/chrono/api/serializers.py +++ b/chrono/api/serializers.py @@ -304,7 +304,6 @@ class StatisticsFiltersSerializer(serializers.Serializer): time_interval = serializers.ChoiceField(choices=('day', _('Day')), default='day') start = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d']) end = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d']) - category = serializers.SlugField(required=False, allow_blank=False, max_length=256) agenda = serializers.CharField(required=False, allow_blank=False, max_length=256) group_by = serializers.ListField( required=False, child=serializers.SlugField(allow_blank=False, max_length=256) diff --git a/chrono/api/views.py b/chrono/api/views.py index e6c50f9b..d76629b5 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -47,7 +47,6 @@ from chrono.agendas.models import ( Agenda, Booking, BookingColor, - Category, Desk, Event, MeetingType, @@ -3072,10 +3071,6 @@ class StatisticsList(APIView): permission_classes = (permissions.IsAuthenticated,) def get(self, request, *args, **kwargs): - categories = Category.objects.all() - category_options = [{'id': '_all', 'label': pgettext('categories', 'All')}] + [ - {'id': x.slug, 'label': x.label} for x in categories - ] return Response( { 'data': [ @@ -3092,18 +3087,6 @@ class StatisticsList(APIView): 'required': True, 'default': 'day', }, - { - 'id': 'category', - 'label': _('Category'), - 'options': category_options, - 'required': True, - 'default': '_all', - 'has_subfilters': True, - 'deprecated': True, - 'deprecation_hint': _( - 'Category should now be selected using the Agenda field below.' - ), - }, { 'id': 'agenda', 'label': _('Agenda'), @@ -3168,7 +3151,6 @@ class BookingsStatistics(APIView): bookings = bookings.filter(event__start_datetime__lte=data['end']) agenda_slug = data.get('agenda', '_all') - category_slug = data.get('category', '_all') if agenda_slug.startswith('category:'): category_slug = agenda_slug.split(':', 1)[1] diff --git a/tests/api/test_statistics.py b/tests/api/test_statistics.py index 9a7d726c..1e46e858 100644 --- a/tests/api/test_statistics.py +++ b/tests/api/test_statistics.py @@ -17,8 +17,6 @@ def test_statistics_list(app, user): app.authorization = ('Basic', ('john.doe', 'password')) resp = app.get('/api/statistics/') - category_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'category'][0] - assert len(category_filter['options']) == 3 agenda_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'agenda'][0] assert agenda_filter['options'] == [ {'id': '_all', 'label': 'All'}, @@ -106,11 +104,6 @@ def test_statistics_bookings(app, user, freezer): assert resp.json['data']['x_labels'] == ['2020-10-25'] assert resp.json['data']['series'] == [{'label': 'Bookings Count', 'data': [1]}] - # legacy, category filter - resp = app.get(url + '?category=category-a') - assert resp.json['data']['x_labels'] == ['2020-10-25'] - assert resp.json['data']['series'] == [{'label': 'Bookings Count', 'data': [1]}] - # agenda filter resp = app.get(url + '?agenda=bar-foo') assert resp.json['data']['x_labels'] == ['2020-10-25'] @@ -198,7 +191,7 @@ def test_statistics_bookings_subfilters_list(app, user): } assert resp.json['data']['subfilters'][0]['options'][1] == {'id': 'test', 'label': 'Test'} - resp = app.get(url + '?category=category-a') + resp = app.get(url + '?agenda=category:category-a') assert len(resp.json['data']['subfilters'][0]['options']) == 2 Category.objects.create(label='Category B') @@ -207,5 +200,5 @@ def test_statistics_bookings_subfilters_list(app, user): resp = app.get(url + '?agenda=other-agenda') assert len(resp.json['data']['subfilters'][0]['options']) == 1 - resp = app.get(url + '?category=category-b') + resp = app.get(url + '?agenda=category:category-b') assert len(resp.json['data']['subfilters'][0]['options']) == 1 -- 2.39.2 From 253bf7af6e334adf695b670ce1136802a15f3aed Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 16 Feb 2023 10:57:48 +0100 Subject: [PATCH 2/4] api: force correct agendas ordering in statistics (#74327) --- chrono/api/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chrono/api/views.py b/chrono/api/views.py index d76629b5..de1546b5 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -3105,7 +3105,7 @@ class StatisticsList(APIView): def get_agenda_options(): all_agendas_option = [{'id': '_all', 'label': pgettext('agendas', 'All')}] - agendas = Agenda.objects.all().order_by('category__name') + agendas = Agenda.objects.all().order_by('category__name', 'label') agendas_with_category = [x for x in agendas if x.category] if not agendas_with_category: return all_agendas_option + [{'id': x.slug, 'label': x.label} for x in agendas] -- 2.39.2 From 331fdfa4a1cba7f936ba2d650ae5ea733b7d7f3d Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 16 Feb 2023 11:14:59 +0100 Subject: [PATCH 3/4] api: ensure valid slug in statistics agenda filter (#74327) --- chrono/api/views.py | 19 +++++++++++-------- tests/api/test_statistics.py | 12 +++++++++--- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/chrono/api/views.py b/chrono/api/views.py index de1546b5..821e6c10 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -3151,15 +3151,18 @@ class BookingsStatistics(APIView): bookings = bookings.filter(event__start_datetime__lte=data['end']) agenda_slug = data.get('agenda', '_all') - if agenda_slug.startswith('category:'): - category_slug = agenda_slug.split(':', 1)[1] + if agenda_slug != '_all': + if agenda_slug.startswith('category:'): + category_slug = agenda_slug.split(':', 1)[1] + agendas = Agenda.objects.filter(category__slug=category_slug) + else: + agendas = Agenda.objects.filter(slug=agenda_slug) - if category_slug != '_all': - bookings = bookings.filter(event__agenda__category__slug=category_slug) - subfilters = self.get_subfilters(agendas=Agenda.objects.filter(category__slug=category_slug)) - elif agenda_slug != '_all': - bookings = bookings.filter(event__agenda__slug=agenda_slug) - subfilters = self.get_subfilters(agendas=Agenda.objects.filter(slug=agenda_slug)) + if not agendas: + raise APIErrorBadRequest(_('No agendas found.')) + + bookings = bookings.filter(event__agenda__in=agendas) + subfilters = self.get_subfilters(agendas=agendas) bookings = bookings.annotate(day=TruncDay('event__start_datetime')) diff --git a/tests/api/test_statistics.py b/tests/api/test_statistics.py index 1e46e858..9d9b348c 100644 --- a/tests/api/test_statistics.py +++ b/tests/api/test_statistics.py @@ -194,11 +194,17 @@ def test_statistics_bookings_subfilters_list(app, user): resp = app.get(url + '?agenda=category:category-a') assert len(resp.json['data']['subfilters'][0]['options']) == 2 - Category.objects.create(label='Category B') - Agenda.objects.create(label='Other', kind='events', category=category) + category_b = Category.objects.create(label='Category B') + Agenda.objects.create(label='Other', kind='events', category=category_b) - resp = app.get(url + '?agenda=other-agenda') + resp = app.get(url + '?agenda=other') assert len(resp.json['data']['subfilters'][0]['options']) == 1 resp = app.get(url + '?agenda=category:category-b') assert len(resp.json['data']['subfilters'][0]['options']) == 1 + + resp = app.get(url + '?agenda=xxx', status=400) + assert resp.json['err_desc'] == 'No agendas found.' + + resp = app.get(url + '?agenda=category:xxx', status=400) + assert resp.json['err_desc'] == 'No agendas found.' -- 2.39.2 From 518124ac1a011abbb77fa5f8ffdbb30f97c8a16e Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 16 Feb 2023 11:33:49 +0100 Subject: [PATCH 4/4] api: only show presence filter for events agendas in statistics (#74327) --- chrono/api/views.py | 11 ++++++++--- tests/api/test_statistics.py | 8 ++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/chrono/api/views.py b/chrono/api/views.py index 821e6c10..bd183d54 100644 --- a/chrono/api/views.py +++ b/chrono/api/views.py @@ -3240,9 +3240,14 @@ class BookingsStatistics(APIView): .values_list('extra_data_keys', flat=True) ) - group_by_options = [{'id': 'user_was_present', 'label': _('Presence/Absence')}] + [ - {'id': x, 'label': x.capitalize()} for x in extra_data_keys - ] + group_by_options = [{'id': x, 'label': x.capitalize()} for x in extra_data_keys] + + if any(x.kind == 'events' for x in agendas): + group_by_options = [{'id': 'user_was_present', 'label': _('Presence/Absence')}] + group_by_options + + if not group_by_options: + return [] + return [ { 'id': 'group_by', diff --git a/tests/api/test_statistics.py b/tests/api/test_statistics.py index 9d9b348c..639a0d1a 100644 --- a/tests/api/test_statistics.py +++ b/tests/api/test_statistics.py @@ -203,6 +203,14 @@ def test_statistics_bookings_subfilters_list(app, user): resp = app.get(url + '?agenda=category:category-b') assert len(resp.json['data']['subfilters'][0]['options']) == 1 + Agenda.objects.create(label='Meetings', kind='meetings', category=category_b) + + resp = app.get(url + '?agenda=meetings') + assert resp.json['data']['subfilters'] == [] + + resp = app.get(url + '?agenda=category:category-b') + assert len(resp.json['data']['subfilters'][0]['options']) == 1 + resp = app.get(url + '?agenda=xxx', status=400) assert resp.json['err_desc'] == 'No agendas found.' -- 2.39.2