From 28445341f1f28b64453d1bc0eed9c362e05d11ce Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Wed, 11 May 2022 15:50:25 +0200 Subject: [PATCH] statistics: merge category selection into form options (#62532) --- tests/api/test_statistics.py | 112 ++++++++++++++++++++++++++++++++++- wcs/statistics/views.py | 58 ++++++++++++++---- 2 files changed, 156 insertions(+), 14 deletions(-) diff --git a/tests/api/test_statistics.py b/tests/api/test_statistics.py index 0e8829be2..73121347e 100644 --- a/tests/api/test_statistics.py +++ b/tests/api/test_statistics.py @@ -6,7 +6,7 @@ import pytest from wcs import fields from wcs.blocks import BlockDef from wcs.carddef import CardDef -from wcs.categories import Category +from wcs.categories import CardDefCategory, Category from wcs.formdef import FormDef from wcs.qommon.http_request import HTTPRequest from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef @@ -112,6 +112,7 @@ def test_statistics_index_categories(pub): {'id': 'category-a', 'label': 'Category A'}, {'id': 'category-b', 'label': 'Category B'}, ] + assert category_filter['deprecated'] is True def test_statistics_index_forms(pub): @@ -135,6 +136,107 @@ def test_statistics_index_forms(pub): {'id': 'test-2', 'label': 'test 2'}, ] + category_a = Category(name='Category A') + category_a.store() + category_b = Category(name='Category B') + category_b.store() + formdef2.category_id = category_a.id + formdef2.store() + + formdef3 = FormDef() + formdef3.name = 'test 3' + formdef3.category_id = category_b.id + formdef3.store() + formdef3.data_class().wipe() + + resp = get_app(pub).get(sign_uri('/api/statistics/')) + form_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'form'][0] + assert form_filter['options'] == [ + [None, [{'id': '_all', 'label': 'All Forms'}]], + [ + 'Category A', + [ + {'id': 'category:category-a', 'label': 'All forms of category Category A'}, + {'id': 'test-2', 'label': 'test 2'}, + ], + ], + [ + 'Category B', + [ + {'id': 'category:category-b', 'label': 'All forms of category Category B'}, + {'id': 'test-3', 'label': 'test 3'}, + ], + ], + ['Misc', [{'id': 'test-1', 'label': 'test 1'}]], + ] + + # check Misc is not shown if all forms have categories + formdef.category_id = category_a.id + formdef.store() + + resp = get_app(pub).get(sign_uri('/api/statistics/')) + form_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'form'][0] + assert form_filter['options'] == [ + [None, [{'id': '_all', 'label': 'All Forms'}]], + [ + 'Category A', + [ + {'id': 'category:category-a', 'label': 'All forms of category Category A'}, + {'id': 'test-1', 'label': 'test 1'}, + {'id': 'test-2', 'label': 'test 2'}, + ], + ], + [ + 'Category B', + [ + {'id': 'category:category-b', 'label': 'All forms of category Category B'}, + {'id': 'test-3', 'label': 'test 3'}, + ], + ], + ] + + +def test_statistics_index_cards(pub): + carddef = CardDef() + carddef.name = 'test 1' + carddef.fields = [] + carddef.store() + carddef.data_class().wipe() + + carddef2 = CardDef() + carddef2.name = 'test 2' + carddef2.fields = [] + carddef2.store() + carddef2.data_class().wipe() + + resp = get_app(pub).get(sign_uri('/api/statistics/')) + form_filter = [x for x in resp.json['data'][1]['filters'] if x['id'] == 'form'][0] + assert form_filter['options'] == [ + {'id': 'test-1', 'label': 'test 1'}, + {'id': 'test-2', 'label': 'test 2'}, + ] + + category_a = CardDefCategory(name='Category A') + category_a.store() + category_b = CardDefCategory(name='Category B') + category_b.store() + carddef2.category_id = category_a.id + carddef2.store() + + carddef3 = CardDef() + carddef3.name = 'test 3' + carddef3.category_id = category_b.id + carddef3.store() + carddef3.data_class().wipe() + + resp = get_app(pub).get(sign_uri('/api/statistics/')) + form_filter = [x for x in resp.json['data'][1]['filters'] if x['id'] == 'form'][0] + assert form_filter['options'] == [ + ['Category A', [{'id': 'test-2', 'label': 'test 2'}]], + ['Category B', [{'id': 'test-3', 'label': 'test 3'}]], + ['Misc', [{'id': 'test-1', 'label': 'test 1'}]], + ] + def test_statistics_forms_count(pub): category_a = Category(name='Category A') @@ -222,8 +324,8 @@ def test_statistics_forms_count(pub): # time_interval=day is not supported get_app(pub).get(sign_uri('/api/statistics/forms/count/?time_interval=day'), status=400) - # apply category filter - resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?category=category-a')) + # apply category filter through form parameter + resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=category:category-a')) assert resp.json == { 'data': { 'series': [{'data': [20], 'label': 'Forms Count'}], @@ -233,6 +335,10 @@ def test_statistics_forms_count(pub): 'err': 0, } + # apply category filter (legacy) + new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?category=category-a')) + assert new_resp.json == resp.json + # apply category id filter (legacy) new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?category=%s' % category_a.id)) assert new_resp.json == resp.json diff --git a/wcs/statistics/views.py b/wcs/statistics/views.py index e67af8ca8..bf4916492 100644 --- a/wcs/statistics/views.py +++ b/wcs/statistics/views.py @@ -44,19 +44,12 @@ class IndexView(RestrictedView): def get(self, request, *args, **kwargs): if not get_publisher().is_using_postgresql(): return JsonResponse({'data': [], 'err': 0}) + categories = Category.select() categories.sort(key=lambda x: misc.simplify(x.name)) category_options = [{'id': '_all', 'label': C_('categories|All')}] + [ {'id': x.url_name, 'label': x.name} for x in categories ] - forms = FormDef.select(lightweight=True) - forms.sort(key=lambda x: misc.simplify(x.name)) - form_options = [{'id': '_all', 'label': _('All Forms')}] + [ - {'id': x.url_name, 'label': x.name} for x in forms - ] - cards = CardDef.select(lightweight=True) - cards.sort(key=lambda x: misc.simplify(x.name)) - card_options = [{'id': x.url_name, 'label': x.name} for x in cards] return JsonResponse( { 'data': [ @@ -95,11 +88,15 @@ class IndexView(RestrictedView): 'options': category_options, 'required': True, 'default': '_all', + 'deprecated': True, + 'deprecation_hint': _( + 'Category should now be selected using the Form field below.' + ), }, { 'id': 'form', 'label': _('Form'), - 'options': form_options, + 'options': self.get_form_options(FormDef), 'required': True, 'default': '_all', 'has_subfilters': True, @@ -138,7 +135,7 @@ class IndexView(RestrictedView): { 'id': 'form', 'label': _('Card'), - 'options': card_options, + 'options': self.get_form_options(CardDef, include_all_option=False), 'required': True, 'default': '', 'has_subfilters': True, @@ -149,6 +146,43 @@ class IndexView(RestrictedView): } ) + @staticmethod + def get_form_options(formdef_class, include_all_option=True): + all_forms_option = [{'id': '_all', 'label': _('All Forms')}] + + forms = formdef_class.select(lightweight=True) + forms.sort(key=lambda x: misc.simplify(x.name)) + + forms_with_category = [x for x in forms if x.category] + if not forms_with_category: + form_options = [{'id': x.url_name, 'label': x.name} for x in forms] + return all_forms_option + form_options if include_all_option else form_options + + form_options = collections.defaultdict(list) + for x in forms_with_category: + if x.category.name not in form_options and include_all_option: + form_options[x.category.name] = [ + { + 'id': 'category:' + x.category.url_name, + 'label': _('All forms of category %s') % x.category.name, + } + ] + form_options[x.category.name].append({'id': x.url_name, 'label': x.name}) + form_options = sorted( + ((category, forms) for category, forms in form_options.items()), key=lambda x: misc.simplify(x[0]) + ) + + forms_without_category_options = [ + {'id': x.url_name, 'label': x.name} for x in forms if not x.category + ] + if forms_without_category_options: + form_options.append((_('Misc'), forms_without_category_options)) + + if include_all_option: + form_options = [(None, all_forms_option)] + form_options + + return form_options + class FormsCountView(RestrictedView): formdef_class = FormDef @@ -167,7 +201,7 @@ class FormsCountView(RestrictedView): formdef_slug = request.GET.get('form', '_all' if self.has_global_count_support else '_nothing') group_by = request.GET.get('group-by') subfilters = [] - if formdef_slug != '_all': + if formdef_slug != '_all' and not formdef_slug.startswith('category:'): try: formdef = self.formdef_class.get_by_urlname(formdef_slug, ignore_migration=True) except KeyError: @@ -189,6 +223,8 @@ class FormsCountView(RestrictedView): else: totals_kwargs['criterias'].append(StrictNotEqual('status', 'draft')) + if formdef_slug.startswith('category:'): + category_slug = formdef_slug.split(':', 1)[1] if category_slug != '_all': try: category = Category.get_by_urlname(category_slug) -- 2.30.2