statistiques, ne pas (trop) confondre les champs ayant le même identifiant dans le group_by (#73562) #55

Merged
vdeniaud merged 2 commits from wip/73562-statistiques-ne-pas-trop-confond into main 2023-02-13 10:36:06 +01:00
3 changed files with 97 additions and 27 deletions

View File

@ -94,15 +94,7 @@ def formdef(pub):
)
items_field.display_locations = ['statistics']
block_field = fields.BlockField(id='4', label='Block Data', varname='blockdata', type='block:foobar')
computed_field = fields.ComputedField(
id='5',
label='computed',
varname='computed',
value_template='{{ "xxx" }}',
type='computed',
anonymise=False,
)
formdef.fields = [item_field, items_field, block_field, computed_field]
formdef.fields = [item_field, items_field, block_field]
formdef.store()
formdef.data_class().wipe()
return formdef
@ -683,6 +675,49 @@ def test_statistics_forms_count_subfilters_query(pub, formdef):
assert resp.json['data']['series'][0]['data'] == []
def test_statistics_forms_count_subfilters_query_same_varname(pub, formdef):
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ItemField(id='1', varname='test', label='Test', type='item', items=['foo', 'bar']),
fields.ItemField(
id='2',
varname='test',
label='Test',
type='item',
items=['foo', 'bar'],
display_locations=['statistics'],
),
]
formdef.store()
for i in range(5):
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
if i == 0:
formdata.data['1'] = 'foo'
if i == 1:
formdata.data['1'] = 'bar'
formdata.data['2'] = 'foo'
formdata.store()
url = '/api/statistics/forms/count/?form=%s' % formdef.url_name
resp = get_app(pub).get(sign_uri(url + '&filter-test=foo'))
assert resp.json['data']['series'] == [{'data': [5], 'label': 'Forms Count'}]
formdef.fields[0].display_locations = ['statistics']
formdef.store()
# filter criterias is "f1 == 'foo' and f2 == 'foo'" hence one result, this should be improved
resp = get_app(pub).get(sign_uri(url + '&filter-test=foo'))
assert resp.json['data']['series'] == [{'data': [1], 'label': 'Forms Count'}]
# filter criterias is "f1 == 'bar' and f2 == 'bar'" hence no results, this should be improved
resp = get_app(pub).get(sign_uri(url + '&filter-test=bar'))
assert resp.json['data']['series'] == [{'data': [], 'label': 'Forms Count'}]
@pytest.mark.parametrize('anonymise', [False, True])
def test_statistics_forms_count_group_by(pub, formdef, anonymise):
for i in range(20):
@ -702,7 +737,6 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
formdata.submission_channel = ''
else:
formdata.submission_channel = None
formdata.data['5'] = 'Computed 1'
elif i % 2:
formdata.data['1'] = False
formdata.data['2'] = 'baz'
@ -715,7 +749,6 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
else:
formdata.jump_status('2')
formdata.submission_channel = 'mail'
formdata.data['5'] = 'Computed 2'
else:
formdata.receipt_time = datetime.datetime(2021, 3, 1, 2, 0).timetuple()
formdata.store()
@ -808,15 +841,6 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
{'data': [13, None, 4], 'label': 'Web'},
]
# group by computed field
resp = get_app(pub).get(sign_uri(url + '&group-by=computed'))
assert resp.json['data']['x_labels'] == ['2021-01', '2021-02', '2021-03']
assert resp.json['data']['series'] == [
{'data': [3, None, None], 'label': 'Computed 2'},
{'data': [13, None, None], 'label': 'Computed 1'},
{'data': [None, None, 4], 'label': 'None'},
]
# 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
@ -859,6 +883,46 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
assert resp.json['data']['series'] == [{'data': [16, 0, 4], 'label': 'Forms Count'}]
def test_statistics_forms_count_group_by_same_varname(pub, formdef):
formdef = FormDef()
formdef.name = 'test'
formdef.fields = [
fields.ItemField(id='1', varname='test', label='Test', type='item', items=['foo']),
fields.ItemField(
id='2', varname='test', label='Test', type='item', items=['bar'], display_locations=['statistics']
),
]
formdef.store()
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
formdata.data['1'] = 'foo'
formdata.data['2'] = 'bar'
formdata.store()
url = '/api/statistics/forms/count/?form=%s' % formdef.url_name
resp = get_app(pub).get(sign_uri(url + '&group-by=test'))
assert resp.json['data']['series'] == [{'data': [1], 'label': 'bar'}]
formdef.fields[0].display_locations = ['statistics']
formdef.store()
# group by uses first field marked for statistics
resp = get_app(pub).get(sign_uri(url + '&group-by=test'))
assert resp.json['data']['series'] == [{'data': [1], 'label': 'foo'}]
formdata = formdef.data_class()()
formdata.just_created()
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
formdata.data['2'] = 'foo'
formdata.store()
# second field is ignored
resp = get_app(pub).get(sign_uri(url + '&group-by=test'))
assert resp.json['data']['series'] == [{'data': [1], 'label': 'foo'}, {'data': [1], 'label': 'None'}]
def test_statistics_cards_count(pub):
carddef = CardDef()
carddef.name = 'test 1'

View File

@ -1732,9 +1732,11 @@ class FormPage(FormdefDirectoryBase):
raise RequestError('Invalid operator "%s" for "filter-operator"' % operator)
return operator
def get_criterias_from_query(self):
def get_criterias_from_query(self, statistics_fields_only=False):
query_overrides = get_request().form
return self.get_view_criterias(query_overrides, request=get_request())
return self.get_view_criterias(
query_overrides, request=get_request(), statistics_fields_only=statistics_fields_only
)
def get_field_allowed_operators(self, field):
operators = [
@ -1776,6 +1778,7 @@ class FormPage(FormdefDirectoryBase):
custom_view=None,
compile_templates=False,
keep_templates=False,
statistics_fields_only=False,
):
fake_fields = [
FakeField('internal-id', 'internal-id', _('Identifier')),
@ -1825,6 +1828,9 @@ class FormPage(FormdefDirectoryBase):
if filter_field.type not in self.get_filterable_field_types():
continue
if statistics_fields_only and not getattr(filter_field, 'include_in_statistics', False):
continue
filter_field_key = None
if filter_field.contextual_varname:

View File

@ -315,7 +315,7 @@ class FormsCountView(RestrictedView):
)
def get_filters_criterias(self, formdef, form_page):
criterias = form_page.get_criterias_from_query()
criterias = form_page.get_criterias_from_query(statistics_fields_only=True)
selected_status = self.request.GET.get('filter-status')
applied_filters = None
@ -417,7 +417,10 @@ class FormsCountView(RestrictedView):
def get_group_by_field(self, form_page, group_by):
fields = [
x for x in form_page.get_formdef_fields() if getattr(x, 'contextual_varname', None) == group_by
x
for x in form_page.get_formdef_fields()
if getattr(x, 'contextual_varname', None) == group_by
and getattr(x, 'include_in_statistics', False)
]
if fields:
if not hasattr(fields[0], 'block_field'): # block fields are not supported
@ -470,9 +473,6 @@ class FormsCountView(RestrictedView):
else:
totals_kwargs['group_by'] = sql.get_field_id(group_by_field)
if group_by_field.type == 'computed':
totals_kwargs['group_by'] = totals_kwargs['group_by'] + "->'data'"
if self.request.GET.get('hide_none_label') == 'true':
totals_kwargs['criterias'].append(NotNull(totals_kwargs['group_by']))