stats permettre plus de regroupements (#85530) #1180

Merged
vdeniaud merged 5 commits from wip/85530-Stats-Pouvoir-faire-un-regroupem into main 2024-02-26 10:21:01 +01:00
2 changed files with 113 additions and 51 deletions

View File

@ -569,6 +569,7 @@ def test_statistics_forms_count_subfilters(pub, formdef):
# remove fields and statuses
workflow = Workflow(name='Empty wf')
workflow.add_status('New')
workflow.store()
formdef.workflow = workflow
formdef.fields.clear()
@ -578,8 +579,31 @@ def test_statistics_forms_count_subfilters(pub, formdef):
resp = get_app(pub).get(sign_uri(url))
assert resp.json['data'] == {
'series': [{'data': [], 'label': 'Forms Count'}],
'subfilters': [],
'x_labels': [],
'subfilters': [
{
'has_subfilters': True,
'id': 'group-by',
'label': 'Group by',
'options': [
{'id': 'channel', 'label': 'Channel'},
{'id': 'simple-status', 'label': 'Simplified status'},
{'id': 'status', 'label': 'Status'},
],
},
{
'default': '_all',
'id': 'filter-status',
'label': 'Status',
'options': [
{'id': '_all', 'label': 'All'},
{'id': 'pending', 'label': 'Open'},
{'id': 'done', 'label': 'Done'},
{'id': '1', 'label': 'New'},
],
'required': True,
},
],
}
@ -967,6 +991,10 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
{'data': [6, None, None], 'label': 'Backoffice'},
]
# group by channel without form filter
new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?group-by=channel'))
assert new_resp.json['data']['series'] == resp.json['data']['series']
# group by item field without time interval
resp = get_app(pub).get(sign_uri(url + '&group-by=test-item&time_interval=none'))
# Foo is first because it has a display value, baz is second because it has not, None is always last
@ -1046,8 +1074,12 @@ def test_statistics_forms_count_group_by_same_varname(pub, formdef):
def test_statistics_forms_count_group_by_form(pub):
category_a = Category(name='Category A')
category_a.store()
formdef = FormDef()
formdef.name = 'A'
formdef.category_id = category_a.id
formdef.store()
for i in range(10):
@ -1058,6 +1090,7 @@ def test_statistics_forms_count_group_by_form(pub):
formdef = FormDef()
formdef.name = 'B'
formdef.category_id = category_a.id
formdef.store()
for i in range(5):
@ -1071,7 +1104,11 @@ def test_statistics_forms_count_group_by_form(pub):
assert resp.json['data']['subfilters'][1] == {
'id': 'group-by',
'label': 'Group by',
'options': [{'id': 'form', 'label': 'Form'}],
'options': [
{'id': 'channel', 'label': 'Channel'},
{'id': 'form', 'label': 'Form'},
],
'has_subfilters': True,
}
resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?time_interval=year'))
@ -1089,6 +1126,18 @@ def test_statistics_forms_count_group_by_form(pub):
assert resp.json['data']['x_labels'] == ['A', 'B']
assert resp.json['data']['series'] == [{'data': [10, 5], 'label': 'Forms Count'}]
resp = get_app(pub).get(
sign_uri('/api/statistics/forms/count/?time_interval=none&group-by=form&form=category:category-a')
)
assert resp.json['data']['x_labels'] == ['A', 'B']
assert resp.json['data']['series'] == [{'data': [10, 5], 'label': 'Forms Count'}]
resp = get_app(pub).get(
sign_uri('/api/statistics/forms/count/?time_interval=none&group-by=form&form=a&form=b')
)
assert resp.json['data']['x_labels'] == ['A', 'B']
assert resp.json['data']['series'] == [{'data': [10, 5], 'label': 'Forms Count'}]
def test_statistics_forms_count_months_to_show(pub, formdef):
for i in range(24):
@ -1685,6 +1734,7 @@ def test_statistics_multiple_forms_count_subfilters(pub, formdef):
group_by_filter = [x for x in resp.json['data']['subfilters'] if x['id'] == 'group-by'][0]
assert group_by_filter['options'] == [
{'id': 'channel', 'label': 'Channel'},
{'id': 'form', 'label': 'Form'},
{'id': 'simple-status', 'label': 'Simplified status'},
{'id': 'test-item', 'label': 'Test item'},
{'id': 'checkbox', 'label': 'Checkbox'},
@ -1708,3 +1758,10 @@ def test_statistics_multiple_forms_count_subfilters(pub, formdef):
category_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=category:category-a'))
assert category_resp.json == resp.json
# cannot group by form if single form is selected
form_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=test'))
form_group_by_filter = [x for x in form_resp.json['data']['subfilters'] if x['id'] == 'group-by'][0]
assert [x for x in group_by_filter['options'] if x not in form_group_by_filter['options']] == [
{'id': 'form', 'label': 'Form'}
]

View File

@ -232,8 +232,8 @@ class FormsCountView(RestrictedView):
}
group_by = request.GET.get('group-by')
group_labels = {}
subfilters = self.get_common_subfilters(time_interval)
formdefs = []
slugs = request.GET.getlist('form', ['_all'] if self.has_global_count_support else ['_nothing'])
if slugs != ['_all']:
formdef_slugs = [x for x in slugs if not x.startswith('category:')]
@ -244,22 +244,17 @@ class FormsCountView(RestrictedView):
if not formdefs:
raise TraversalError()
subfilters = self.get_common_subfilters(time_interval, formdefs)
if formdefs:
for formdef in formdefs:
formdef.form_page = self.formpage_class(formdef=formdef, update_breadcrumbs=False)
self.set_formdef_parameters(totals_kwargs, formdefs)
totals_kwargs['criterias'].extend(self.get_filters_criterias(formdefs))
self.set_group_by_parameters(group_by, formdefs, totals_kwargs, group_labels)
subfilters += self.get_formdefs_subfilters(formdefs, group_by, time_interval)
else:
subfilters += [
{'id': 'group-by', 'label': _('Group by'), 'options': [{'id': 'form', 'label': _('Form')}]}
]
if group_by == 'form':
totals_kwargs['group_by'] = 'formdef_id'
group_labels = {
int(x.id): x.name for x in self.formdef_class.select(lightweight=True, order_by='name')
}
self.add_formdefs_subfilters(subfilters, formdefs, group_by, time_interval)
self.set_group_by_parameters(group_by, formdefs, totals_kwargs, group_labels)
channel = request.GET.get('channel', '_all')
if channel in ('web', 'backoffice'):
@ -366,7 +361,7 @@ class FormsCountView(RestrictedView):
return criterias
def get_common_subfilters(self, time_interval):
def get_common_subfilters(self, time_interval, formdefs):
subfilters = []
if time_interval == 'month':
@ -384,12 +379,27 @@ class FormsCountView(RestrictedView):
}
)
group_by_filter = {
'id': 'group-by',
'label': _('Group by'),
'has_subfilters': True,
'options': [
{'id': 'channel', 'label': _('Channel')},
],
}
if len(formdefs) != 1:
# allow grouping by form only if no form is selected or many are selected
group_by_filter['options'].append({'id': 'form', 'label': _('Form')})
subfilters.append(group_by_filter)
return subfilters
def get_formdefs_subfilters(self, formdefs, group_by, time_interval):
def add_formdefs_subfilters(self, common_subfilters, formdefs, group_by, time_interval):
subfilters = None
for formdef in formdefs:
new_subfilters = self.get_form_subfilters(formdef.form_page, group_by)
new_subfilters = self.get_form_subfilters(formdef.form_page)
if not subfilters:
subfilters = new_subfilters
@ -400,7 +410,7 @@ class FormsCountView(RestrictedView):
for filter_id, subfilter in subfilters.copy().items():
if subfilter['options'] != new_subfilters[filter_id]['options']:
if filter_id in ('filter-status', 'group-by'):
if filter_id in ('filter-status'):
# keep only common options
subfilter['options'] = {
k: v
@ -421,11 +431,29 @@ class FormsCountView(RestrictedView):
if needs_sorting:
subfilter['options'].sort(key=lambda x: x['label'])
return subfilters
group_by_filter = [x for x in common_subfilters if x['id'] == 'group-by'][0]
group_by_filter['options'].append({'id': 'simple-status', 'label': _('Simplified status')})
group_by_filter['options'].extend(
[{'id': x['id'].removeprefix('filter-'), 'label': x['label']} for x in subfilters]
)
def get_form_subfilters(self, form_page, group_by):
if group_by not in (None, 'channel', 'simple-status', 'status'):
group_by_field = self.get_group_by_field(formdefs[0].form_page, group_by)
if group_by_field:
common_subfilters.append(
{
'id': 'hide_none_label',
'label': _('Ignore forms where "%s" is empty.') % group_by_field.label,
'options': [{'id': 'true', 'label': _('Yes')}, {'id': 'false', 'label': _('No')}],
'required': True,
'default': 'false',
}
)
common_subfilters.extend(subfilters)
def get_form_subfilters(self, form_page):
subfilters = []
field_choices = []
for field in form_page.get_formdef_fields(include_block_items_fields=True):
if not getattr(field, 'include_in_statistics', False) or not field.contextual_varname:
continue
@ -466,36 +494,6 @@ class FormsCountView(RestrictedView):
filter_description['default'] = field.default_filter_value
subfilters.append(filter_description)
field_choices.append((field.contextual_varname, field.label))
if field_choices:
additionnal_filters = [
{
'id': 'group-by',
'label': _('Group by'),
'options': {
'channel': _('Channel'),
'simple-status': _('Simplified status'),
**{x[0]: x[1] for x in field_choices},
},
'has_subfilters': True,
}
]
if group_by not in (None, 'channel', 'simple-status', 'status'):
group_by_field = self.get_group_by_field(form_page, group_by)
if group_by_field:
additionnal_filters.append(
{
'id': 'hide_none_label',
'label': _('Ignore forms where "%s" is empty.') % group_by_field.label,
'options': {'true': _('Yes'), 'false': _('No')},
'required': True,
'default': 'false',
}
)
subfilters = additionnal_filters + subfilters
subfilters = {x['id']: x for x in subfilters}
return subfilters
@ -536,6 +534,13 @@ class FormsCountView(RestrictedView):
if not group_by:
return
if group_by == 'form':
totals_kwargs['group_by'] = 'formdef_id'
group_labels.update(
{int(x.id): x.name for x in self.formdef_class.select(lightweight=True, order_by='name')}
)
return
if group_by == 'channel':
totals_kwargs['group_by'] = 'submission_channel_new'
totals_kwargs['group_by_clause'] = (