0001-statistics-add-group-by-simplified-status-71665.patch
tests/api/test_statistics.py | ||
---|---|---|
50 | 50 |
workflow = Workflow(name='Workflow One') |
51 | 51 |
new_status = workflow.add_status(name='New status') |
52 | 52 |
workflow.add_status(name='End status') |
53 |
middle_status1 = workflow.add_status(name='Middle status 1') |
|
54 |
middle_status2 = workflow.add_status(name='Middle status 2') |
|
53 | 55 |
jump = new_status.add_action('jump', id='_jump') |
54 | 56 |
jump.status = '2' |
55 | 57 |
jump.timeout = 86400 |
58 |
jump = new_status.add_action('jump', id='_jump') |
|
59 |
jump.status = '3' |
|
60 |
jump = middle_status1.add_action('jump', id='_jump') |
|
61 |
jump.status = '4' |
|
62 |
jump = middle_status2.add_action('jump', id='_jump') |
|
63 |
jump.status = '2' |
|
56 | 64 |
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow) |
57 | 65 |
workflow.backoffice_fields_formdef.fields = [ |
58 | 66 |
fields.BoolField( |
... | ... | |
432 | 440 |
'label': 'Group by', |
433 | 441 |
'options': [ |
434 | 442 |
{'id': 'channel', 'label': 'Channel'}, |
443 |
{'id': 'simple-status', 'label': 'Simplified status'}, |
|
435 | 444 |
{'id': 'test-item', 'label': 'Test item'}, |
436 | 445 |
{'id': 'test-items', 'label': 'Test items'}, |
437 | 446 |
{'id': 'checkbox', 'label': 'Checkbox'}, |
... | ... | |
661 | 670 |
formdata.data['1'] = False |
662 | 671 |
formdata.data['2'] = 'baz' |
663 | 672 |
formdata.data['3'] = ['baz'] |
664 |
formdata.jump_status('2') |
|
673 |
if i == 3: |
|
674 |
formdata.jump_status('3') |
|
675 |
elif i == 9: |
|
676 |
formdata.jump_status('3') |
|
677 |
formdata.jump_status('4') |
|
678 |
else: |
|
679 |
formdata.jump_status('2') |
|
665 | 680 |
formdata.submission_channel = 'mail' |
666 | 681 |
else: |
667 | 682 |
formdata.receipt_time = datetime.datetime(2021, 3, 1, 2, 0).timetuple() |
... | ... | |
724 | 739 |
resp = get_app(pub).get(sign_uri(url + '&group-by=status')) |
725 | 740 |
assert resp.json['data']['x_labels'] == ['2021-01', '2021-02', '2021-03'] |
726 | 741 |
assert resp.json['data']['series'] == [ |
727 |
{'data': [13, None, 4], 'label': 'New status'}, |
|
728 |
{'data': [3, None, None], 'label': 'End status'}, |
|
742 |
{'label': 'New status', 'data': [13, None, 4]}, |
|
743 |
{'label': 'End status', 'data': [1, None, None]}, |
|
744 |
{'label': 'Middle status 1', 'data': [1, None, None]}, |
|
745 |
{'label': 'Middle status 2', 'data': [1, None, None]}, |
|
746 |
] |
|
747 | ||
748 |
# group by simplified status |
|
749 |
resp = get_app(pub).get(sign_uri(url + '&group-by=simple-status')) |
|
750 |
assert resp.json['data']['x_labels'] == ['2021-01', '2021-02', '2021-03'] |
|
751 |
assert resp.json['data']['series'] == [ |
|
752 |
{'label': 'New', 'data': [13, None, 4]}, |
|
753 |
{'label': 'Done', 'data': [1, None, None]}, |
|
754 |
{'label': 'In progress', 'data': [2, None, None]}, |
|
729 | 755 |
] |
730 | 756 | |
731 | 757 |
# group by channel |
... | ... | |
754 | 780 | |
755 | 781 |
# group by status without time interval |
756 | 782 |
resp = get_app(pub).get(sign_uri(url + '&group-by=status&time_interval=none')) |
757 |
assert resp.json['data']['x_labels'] == ['New status', 'End status'] |
|
758 |
assert resp.json['data']['series'] == [{'data': [17, 3], 'label': 'Forms Count'}] |
|
783 |
assert resp.json['data']['x_labels'] == ['New status', 'End status', 'Middle status 1', 'Middle status 2'] |
|
784 |
assert resp.json['data']['series'] == [{'data': [17, 1, 1, 1], 'label': 'Forms Count'}] |
|
785 | ||
786 |
# group by simplfified status without time interval |
|
787 |
resp = get_app(pub).get(sign_uri(url + '&group-by=simple-status&time_interval=none')) |
|
788 |
assert resp.json['data']['x_labels'] == ['New', 'Done', 'In progress'] |
|
789 |
assert resp.json['data']['series'] == [{'label': 'Forms Count', 'data': [17, 1, 2]}] |
|
759 | 790 | |
760 | 791 |
# check statuses order |
761 | 792 |
formdef.workflow.possible_status = list(reversed(formdef.workflow.possible_status)) |
762 | 793 |
formdef.workflow.store() |
763 | 794 |
resp = get_app(pub).get(sign_uri(url + '&group-by=status&time_interval=none')) |
764 |
assert resp.json['data']['x_labels'] == ['End status', 'New status'] |
|
765 |
assert resp.json['data']['series'] == [{'data': [3, 17], 'label': 'Forms Count'}]
|
|
795 |
assert resp.json['data']['x_labels'] == ['Middle status 2', 'Middle status 1', 'End status', 'New status']
|
|
796 |
assert resp.json['data']['series'] == [{'data': [1, 1, 1, 17], 'label': 'Forms Count'}]
|
|
766 | 797 | |
767 | 798 |
# group by on block field is not supported |
768 | 799 |
resp = get_app(pub).get(sign_uri(url + '&group-by=blockdata_bool')) |
wcs/statistics/views.py | ||
---|---|---|
376 | 376 |
{ |
377 | 377 |
'id': 'group-by', |
378 | 378 |
'label': _('Group by'), |
379 |
'options': [{'id': 'channel', 'label': _('Channel')}] |
|
379 |
'options': [ |
|
380 |
{'id': 'channel', 'label': _('Channel')}, |
|
381 |
{'id': 'simple-status', 'label': _('Simplified status')}, |
|
382 |
] |
|
380 | 383 |
+ [{'id': x[0], 'label': x[1]} for x in field_choices], |
381 | 384 |
}, |
382 | 385 |
) |
... | ... | |
391 | 394 |
if not hasattr(fields[0], 'block_field'): # block fields are not supported |
392 | 395 |
return fields[0] |
393 | 396 | |
394 |
def get_group_labels(self, group_by_field, formdef, form_page): |
|
397 |
def get_group_labels(self, group_by_field, formdef, form_page, group_by):
|
|
395 | 398 |
group_labels = {} |
396 |
if group_by_field.type == 'status':
|
|
399 |
if group_by == 'status': |
|
397 | 400 |
group_labels = {'wf-%s' % status.id: status.name for status in formdef.workflow.possible_status} |
401 |
elif group_by == 'simple-status': |
|
402 |
group_labels['wf-%s' % formdef.workflow.possible_status[0].id] = _('New') |
|
403 |
for status in formdef.workflow.possible_status[1:]: |
|
404 |
if status.is_endpoint(): |
|
405 |
group_labels['wf-%s' % status.id] = _('Done') |
|
406 |
else: |
|
407 |
group_labels['wf-%s' % status.id] = _('In progress') |
|
398 | 408 |
elif group_by_field.type == 'bool': |
399 | 409 |
group_labels = {True: _('Yes'), False: _('No')} |
400 | 410 |
elif group_by_field.type in ('item', 'items'): |
... | ... | |
418 | 428 |
group_labels[None] = _('Web') |
419 | 429 |
group_labels[''] = _('Web') |
420 | 430 |
return |
431 |
elif group_by == 'simple-status': |
|
432 |
group_by_field = self.get_group_by_field(form_page, 'status') |
|
433 |
else: |
|
434 |
group_by_field = self.get_group_by_field(form_page, group_by) |
|
421 | 435 | |
422 |
group_by_field = self.get_group_by_field(form_page, group_by) |
|
423 | 436 |
if not group_by_field: |
424 | 437 |
return |
425 | 438 | |
... | ... | |
428 | 441 |
else: |
429 | 442 |
totals_kwargs['group_by'] = sql.get_field_id(group_by_field) |
430 | 443 | |
431 |
group_labels.update(self.get_group_labels(group_by_field, formdef, form_page)) |
|
444 |
group_labels.update(self.get_group_labels(group_by_field, formdef, form_page, group_by))
|
|
432 | 445 | |
433 | 446 |
def get_grouped_time_data(self, totals, group_labels): |
434 | 447 |
totals_by_time = collections.OrderedDict( |
... | ... | |
490 | 503 |
return len(group_label_indexes) |
491 | 504 |
return group_label_indexes[group] |
492 | 505 | |
493 |
groups.sort(key=get_group_order) |
|
494 |
return {group_labels.get(group, group): totals_by_group[group] for group in groups} |
|
506 |
totals_by_label = {} |
|
507 |
for group in sorted(groups, key=get_group_order): |
|
508 |
label = group_labels.get(group, group) |
|
509 |
if label in totals_by_label: |
|
510 |
if isinstance(totals_by_label[label], list): |
|
511 |
for i, (x, y) in enumerate(zip(totals_by_group[group], totals_by_label[label])): |
|
512 |
totals_by_label[label][i] = (x or 0) + (y or 0) if x or y else None |
|
513 |
totals_by_label[label][i] = ((x or 0) + (y or 0)) or None |
|
514 |
else: |
|
515 |
totals_by_label[label] = ( |
|
516 |
(totals_by_label[label] or 0) + (totals_by_group[group] or 0) |
|
517 |
) or None |
|
518 |
else: |
|
519 |
totals_by_label[label] = totals_by_group[group] |
|
520 | ||
521 |
return totals_by_label |
|
495 | 522 | |
496 | 523 | |
497 | 524 |
class CardsCountView(FormsCountView): |
498 |
- |