statistics: allow filtering by items field inside block (#75573) #205
|
@ -74,7 +74,16 @@ def formdef(pub):
|
|||
block = BlockDef()
|
||||
block.name = 'foobar'
|
||||
block.fields = [
|
||||
fields.BoolField(id='1', label='Bool', type='bool', varname='bool', display_locations=['statistics'])
|
||||
fields.BoolField(id='1', label='Bool', type='bool', varname='bool', display_locations=['statistics']),
|
||||
fields.ItemsField(
|
||||
id='2',
|
||||
varname='block-items',
|
||||
label='Block items',
|
||||
type='items',
|
||||
items=['foo', 'bar', 'baz'],
|
||||
anonymise=False,
|
||||
display_locations=['statistics'],
|
||||
),
|
||||
]
|
||||
block.store()
|
||||
|
||||
|
@ -415,6 +424,7 @@ def test_statistics_forms_count_subfilters(pub, formdef):
|
|||
formdata.data['2_display'] = 'Foo' if i % 2 else 'Baz'
|
||||
formdata.data['3'] = ['foo'] if i % 2 else ['bar', 'baz']
|
||||
formdata.data['3_display'] = 'Foo' if i % 2 else 'Bar, Baz'
|
||||
formdata.data['4'] = {'data': [{'2': ['foo', 'bar'], '2_display': 'Foo, Bar'}]}
|
||||
formdata.just_created()
|
||||
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
|
||||
formdata.store()
|
||||
|
@ -464,8 +474,16 @@ def test_statistics_forms_count_subfilters(pub, formdef):
|
|||
'required': False,
|
||||
}
|
||||
|
||||
# check boolean backoffice field subfilter
|
||||
# check block items field subfilter
|
||||
assert resp.json['data']['subfilters'][4] == {
|
||||
'id': 'filter-blockdata_block-items',
|
||||
'label': 'Block items',
|
||||
'options': [{'id': 'bar', 'label': 'Bar'}, {'id': 'foo', 'label': 'Foo'}],
|
||||
'required': False,
|
||||
}
|
||||
|
||||
# check boolean backoffice field subfilter
|
||||
assert resp.json['data']['subfilters'][5] == {
|
||||
'id': 'filter-checkbox',
|
||||
'label': 'Checkbox',
|
||||
'options': [{'id': 'true', 'label': 'Yes'}, {'id': 'false', 'label': 'No'}],
|
||||
|
@ -537,23 +555,6 @@ def test_statistics_forms_count_subfilters(pub, formdef):
|
|||
new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
|
||||
assert new_resp.json == resp.json
|
||||
|
||||
# add items field inside block field, it should not appear
|
||||
items_field = fields.ItemsField(
|
||||
id='2',
|
||||
varname='items',
|
||||
label='Block items',
|
||||
type='items',
|
||||
items=['foo', 'bar', 'baz'],
|
||||
anonymise=False,
|
||||
display_locations=['statistics'],
|
||||
)
|
||||
formdef.fields[2].block.fields.append(bool_field)
|
||||
formdef.store()
|
||||
formdata.data['4'] = {'data': [{'2': ['bar']}]}
|
||||
formdata.store()
|
||||
new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
|
||||
assert new_resp.json == resp.json
|
||||
|
||||
# remove fields and statuses
|
||||
workflow = Workflow(name='Empty wf')
|
||||
workflow.store()
|
||||
|
@ -578,12 +579,17 @@ def test_statistics_forms_count_subfilters_query(pub, formdef):
|
|||
formdata.data['1'] = True
|
||||
formdata.data['2'] = 'foo'
|
||||
formdata.data['3'] = ['bar', 'baz']
|
||||
formdata.data['4'] = {'data': [{'1': True}, {'1': False}]}
|
||||
formdata.data['4'] = {
|
||||
'data': [
|
||||
{'1': True, '2': ['baz'], '2_display': 'Baz'},
|
||||
{'1': False, '2': ['foo'], '2_display': 'Foo'},
|
||||
]
|
||||
}
|
||||
elif i % 2:
|
||||
formdata.data['1'] = False
|
||||
formdata.data['2'] = 'baz'
|
||||
formdata.data['3'] = ['baz']
|
||||
formdata.data['4'] = {'data': [{'1': False}]}
|
||||
formdata.data['4'] = {'data': [{'1': False, '2': ['foo', 'bar'], '2_display': 'Foo, Bar'}]}
|
||||
formdata.jump_status('2')
|
||||
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
|
||||
formdata.store()
|
||||
|
@ -639,6 +645,16 @@ def test_statistics_forms_count_subfilters_query(pub, formdef):
|
|||
resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_bool=false'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 16
|
||||
|
||||
# filter on block items field
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_block-items=foo'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 16
|
||||
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_block-items=bar'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 3
|
||||
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_block-items=baz'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 13
|
||||
|
||||
# filter on status
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-status=_all'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 20
|
||||
|
|
|
@ -994,7 +994,7 @@ class FormPage(FormdefDirectoryBase):
|
|||
# values used in repeated blocks, ex:
|
||||
# {"data": [{"FOOBAR": "value1"}, {"FOOBAR": "value2}]}
|
||||
# → ["value1", "value2"}
|
||||
field1 = "jsonb_array_elements(%s->'data')->> '%s'" % (
|
||||
field1 = "jsonb_array_elements(%s->'data')-> '%s'" % (
|
||||
|
||||
sql.get_field_id(filter_field.block_field),
|
||||
filter_field.id,
|
||||
)
|
||||
|
@ -1630,7 +1630,7 @@ class FormPage(FormdefDirectoryBase):
|
|||
self.view.remove_self()
|
||||
return redirect('..')
|
||||
|
||||
def get_formdef_fields(self):
|
||||
def get_formdef_fields(self, include_block_items_fields=False):
|
||||
yield FakeField('id', 'id', _('Number'))
|
||||
if get_publisher().get_site_option('welco_url', 'variables'):
|
||||
yield FakeField('submission_channel', 'submission_channel', _('Channel'))
|
||||
|
@ -1651,7 +1651,7 @@ class FormPage(FormdefDirectoryBase):
|
|||
|
||||
for field in self.formdef.iter_fields(include_block_fields=True):
|
||||
if getattr(field, 'block_field', None):
|
||||
if field.key == 'items':
|
||||
if field.key == 'items' and not include_block_items_fields:
|
||||
# not yet
|
||||
continue
|
||||
yield field
|
||||
|
@ -1828,7 +1828,7 @@ class FormPage(FormdefDirectoryBase):
|
|||
get_session().message = ('warning', error_message)
|
||||
criterias.append(Nothing())
|
||||
|
||||
for filter_field in fake_fields + list(self.get_formdef_fields()):
|
||||
for filter_field in fake_fields + list(self.get_formdef_fields(include_block_items_fields=True)):
|
||||
if filter_field.type not in self.get_filterable_field_types():
|
||||
continue
|
||||
|
||||
|
|
|
@ -621,8 +621,10 @@ class FormData(StorableObject):
|
|||
sub_data = self.data.get(block.id) or {}
|
||||
items = set()
|
||||
for data in sub_data.get('data', []):
|
||||
value = data.get(field.id)
|
||||
items.add(value)
|
||||
values = data.get(field.id)
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
items.update(values)
|
||||
values = list(items)
|
||||
else:
|
||||
values = self.data.get(field.id)
|
||||
|
|
|
@ -380,7 +380,7 @@ class FormsCountView(RestrictedView):
|
|||
def get_form_subfilters(self, form_page, group_by):
|
||||
subfilters = []
|
||||
field_choices = []
|
||||
for field in form_page.get_formdef_fields():
|
||||
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
|
||||
|
||||
|
@ -458,7 +458,7 @@ class FormsCountView(RestrictedView):
|
|||
def get_group_by_field(self, form_page, group_by):
|
||||
fields = [
|
||||
x
|
||||
for x in form_page.get_formdef_fields()
|
||||
for x in form_page.get_formdef_fields(include_block_items_fields=True)
|
||||
if getattr(x, 'contextual_varname', None) == group_by
|
||||
and getattr(x, 'include_in_statistics', False)
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue
Ce bout me reste mystérieux et à chaque fois j'abandonne la relecture quand j'arrive ici :/
(mais Pierre m'a dit que c'était ok donc je vais valider)
Ma compréhension c'est que
->>
sort toujours l'élément du JSON sous forme de string, or la valeur d'un ItemsField c'est une liste, donc ça sort n'imp genre un truc qui vaut"['a', 'b']"
, enlever un chevron donne bien['a', 'b']
une vraie liste.Là où ça pourrait poser problème c'est si il y avait genre des entiers dans le JSON, et que derrière du code qui attendait des string reçoit maintenant des entiers. Mais je crois qu'il y a suffisamment de cast python str() explicites partout que ça a peu de chance d'arriver (confiance aux tests, aussi).