From 6ab7a285864f17c24ffe9d854346acfa52152325 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Thu, 8 Dec 2022 17:21:41 +0100 Subject: [PATCH] statistics: add group by simplified status (#71665) --- tests/api/test_statistics.py | 45 ++++++++++++++++++++++++++++++------ wcs/statistics/views.py | 41 ++++++++++++++++++++++++++------ 2 files changed, 72 insertions(+), 14 deletions(-) diff --git a/tests/api/test_statistics.py b/tests/api/test_statistics.py index c201076ea..2b0603c25 100644 --- a/tests/api/test_statistics.py +++ b/tests/api/test_statistics.py @@ -50,9 +50,17 @@ def formdef(pub): workflow = Workflow(name='Workflow One') new_status = workflow.add_status(name='New status') workflow.add_status(name='End status') + middle_status1 = workflow.add_status(name='Middle status 1') + middle_status2 = workflow.add_status(name='Middle status 2') jump = new_status.add_action('jump', id='_jump') jump.status = '2' jump.timeout = 86400 + jump = new_status.add_action('jump', id='_jump') + jump.status = '3' + jump = middle_status1.add_action('jump', id='_jump') + jump.status = '4' + jump = middle_status2.add_action('jump', id='_jump') + jump.status = '2' workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) workflow.backoffice_fields_formdef.fields = [ fields.BoolField( @@ -432,6 +440,7 @@ def test_statistics_forms_count_subfilters(pub, formdef): 'label': 'Group by', 'options': [ {'id': 'channel', 'label': 'Channel'}, + {'id': 'simple-status', 'label': 'Simplified status'}, {'id': 'test-item', 'label': 'Test item'}, {'id': 'test-items', 'label': 'Test items'}, {'id': 'checkbox', 'label': 'Checkbox'}, @@ -661,7 +670,13 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise): formdata.data['1'] = False formdata.data['2'] = 'baz' formdata.data['3'] = ['baz'] - formdata.jump_status('2') + if i == 3: + formdata.jump_status('3') + elif i == 9: + formdata.jump_status('3') + formdata.jump_status('4') + else: + formdata.jump_status('2') formdata.submission_channel = 'mail' else: formdata.receipt_time = datetime.datetime(2021, 3, 1, 2, 0).timetuple() @@ -724,8 +739,19 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise): resp = get_app(pub).get(sign_uri(url + '&group-by=status')) assert resp.json['data']['x_labels'] == ['2021-01', '2021-02', '2021-03'] assert resp.json['data']['series'] == [ - {'data': [13, None, 4], 'label': 'New status'}, - {'data': [3, None, None], 'label': 'End status'}, + {'label': 'New status', 'data': [13, None, 4]}, + {'label': 'End status', 'data': [1, None, None]}, + {'label': 'Middle status 1', 'data': [1, None, None]}, + {'label': 'Middle status 2', 'data': [1, None, None]}, + ] + + # group by simplified status + resp = get_app(pub).get(sign_uri(url + '&group-by=simple-status')) + assert resp.json['data']['x_labels'] == ['2021-01', '2021-02', '2021-03'] + assert resp.json['data']['series'] == [ + {'label': 'New', 'data': [13, None, 4]}, + {'label': 'Done', 'data': [1, None, None]}, + {'label': 'In progress', 'data': [2, None, None]}, ] # group by channel @@ -754,15 +780,20 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise): # group by status without time interval resp = get_app(pub).get(sign_uri(url + '&group-by=status&time_interval=none')) - assert resp.json['data']['x_labels'] == ['New status', 'End status'] - assert resp.json['data']['series'] == [{'data': [17, 3], 'label': 'Forms Count'}] + assert resp.json['data']['x_labels'] == ['New status', 'End status', 'Middle status 1', 'Middle status 2'] + assert resp.json['data']['series'] == [{'data': [17, 1, 1, 1], 'label': 'Forms Count'}] + + # group by simplfified status without time interval + resp = get_app(pub).get(sign_uri(url + '&group-by=simple-status&time_interval=none')) + assert resp.json['data']['x_labels'] == ['New', 'Done', 'In progress'] + assert resp.json['data']['series'] == [{'label': 'Forms Count', 'data': [17, 1, 2]}] # check statuses order formdef.workflow.possible_status = list(reversed(formdef.workflow.possible_status)) formdef.workflow.store() resp = get_app(pub).get(sign_uri(url + '&group-by=status&time_interval=none')) - assert resp.json['data']['x_labels'] == ['End status', 'New status'] - assert resp.json['data']['series'] == [{'data': [3, 17], 'label': 'Forms Count'}] + assert resp.json['data']['x_labels'] == ['Middle status 2', 'Middle status 1', 'End status', 'New status'] + assert resp.json['data']['series'] == [{'data': [1, 1, 1, 17], 'label': 'Forms Count'}] # group by on block field is not supported resp = get_app(pub).get(sign_uri(url + '&group-by=blockdata_bool')) diff --git a/wcs/statistics/views.py b/wcs/statistics/views.py index 3ff02f0eb..ae51dfd4e 100644 --- a/wcs/statistics/views.py +++ b/wcs/statistics/views.py @@ -376,7 +376,10 @@ class FormsCountView(RestrictedView): { 'id': 'group-by', 'label': _('Group by'), - 'options': [{'id': 'channel', 'label': _('Channel')}] + 'options': [ + {'id': 'channel', 'label': _('Channel')}, + {'id': 'simple-status', 'label': _('Simplified status')}, + ] + [{'id': x[0], 'label': x[1]} for x in field_choices], }, ) @@ -391,10 +394,17 @@ class FormsCountView(RestrictedView): if not hasattr(fields[0], 'block_field'): # block fields are not supported return fields[0] - def get_group_labels(self, group_by_field, formdef, form_page): + def get_group_labels(self, group_by_field, formdef, form_page, group_by): group_labels = {} - if group_by_field.type == 'status': + if group_by == 'status': group_labels = {'wf-%s' % status.id: status.name for status in formdef.workflow.possible_status} + elif group_by == 'simple-status': + group_labels['wf-%s' % formdef.workflow.possible_status[0].id] = _('New') + for status in formdef.workflow.possible_status[1:]: + if status.is_endpoint(): + group_labels['wf-%s' % status.id] = _('Done') + else: + group_labels['wf-%s' % status.id] = _('In progress') elif group_by_field.type == 'bool': group_labels = {True: _('Yes'), False: _('No')} elif group_by_field.type in ('item', 'items'): @@ -418,8 +428,11 @@ class FormsCountView(RestrictedView): group_labels[None] = _('Web') group_labels[''] = _('Web') return + elif group_by == 'simple-status': + group_by_field = self.get_group_by_field(form_page, 'status') + else: + group_by_field = self.get_group_by_field(form_page, group_by) - group_by_field = self.get_group_by_field(form_page, group_by) if not group_by_field: return @@ -428,7 +441,7 @@ class FormsCountView(RestrictedView): else: totals_kwargs['group_by'] = sql.get_field_id(group_by_field) - group_labels.update(self.get_group_labels(group_by_field, formdef, form_page)) + group_labels.update(self.get_group_labels(group_by_field, formdef, form_page, group_by)) def get_grouped_time_data(self, totals, group_labels): totals_by_time = collections.OrderedDict( @@ -490,8 +503,22 @@ class FormsCountView(RestrictedView): return len(group_label_indexes) return group_label_indexes[group] - groups.sort(key=get_group_order) - return {group_labels.get(group, group): totals_by_group[group] for group in groups} + totals_by_label = {} + for group in sorted(groups, key=get_group_order): + label = group_labels.get(group, group) + if label in totals_by_label: + if isinstance(totals_by_label[label], list): + for i, (x, y) in enumerate(zip(totals_by_group[group], totals_by_label[label])): + totals_by_label[label][i] = (x or 0) + (y or 0) if x or y else None + totals_by_label[label][i] = ((x or 0) + (y or 0)) or None + else: + totals_by_label[label] = ( + (totals_by_label[label] or 0) + (totals_by_group[group] or 0) + ) or None + else: + totals_by_label[label] = totals_by_group[group] + + return totals_by_label class CardsCountView(FormsCountView): -- 2.35.1