statistiques, ajouter une colonne commune à wcs_all_form avec les données nécessaires (#73770) #63
|
@ -501,3 +501,38 @@ def test_block_duplicate(pub):
|
|||
|
||||
block_copy = BlockDef.get_by_slug('other_copy')
|
||||
assert len(block_copy.fields) == 2
|
||||
|
||||
|
||||
def test_block_field_statistics_data_update(pub):
|
||||
create_superuser(pub)
|
||||
|
||||
BlockDef.wipe()
|
||||
block = BlockDef()
|
||||
block.name = 'Foobar'
|
||||
block.fields = [fields.BoolField(id='1', label='Bool', varname='bool', type='comment')]
|
||||
block.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.fields = [
|
||||
fields.BlockField(id='0', label='test', type='block:%s' % block.slug),
|
||||
]
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.data['0'] = {'data': [{'1': True}]}
|
||||
formdata.store()
|
||||
|
||||
assert not formdata.statistics_data
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/forms/blocks/%s/1/' % block.id)
|
||||
|
||||
resp.form['display_locations$element3'] = True
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'Statistics data will be collected in the background.' in resp.text
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.statistics_data == {'bool': [True]}
|
||||
|
|
|
@ -3765,3 +3765,30 @@ def test_form_import_fields(pub):
|
|||
('5', 'field 4'),
|
||||
('3', 'Page 2'),
|
||||
]
|
||||
|
||||
|
||||
def test_form_field_statistics_data_update(pub):
|
||||
create_superuser(pub)
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.fields = [fields.BoolField(id='1', label='Bool', varname='bool', type='comment')]
|
||||
formdef.store()
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
formdata.data['1'] = True
|
||||
formdata.store()
|
||||
|
||||
assert not formdata.statistics_data
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/forms/1/fields/1/')
|
||||
|
||||
resp.form['display_locations$element3'] = True
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'Statistics data will be collected in the background.' in resp.text
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.statistics_data == {'bool': [True]}
|
||||
|
|
|
@ -3745,3 +3745,41 @@ def test_remove_tracking_code_details(pub):
|
|||
workflow.store()
|
||||
resp = app.get('/backoffice/workflows/%s/status/%s/' % (workflow.id, baz_status.id))
|
||||
assert 'Remove Tracking Code (replace with a new one)' in resp.text
|
||||
|
||||
|
||||
def test_workflow_backoffice_field_statistics_data_update(pub):
|
||||
create_superuser(pub)
|
||||
|
||||
CardDef.wipe()
|
||||
FormDef.wipe()
|
||||
Workflow.wipe()
|
||||
workflow = Workflow(name='foo')
|
||||
workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
|
||||
workflow.backoffice_fields_formdef.fields = [
|
||||
fields.BoolField(id='1', label='Bool', varname='bool', type='comment')
|
||||
]
|
||||
workflow.store()
|
||||
|
||||
FormDef.wipe()
|
||||
formdef = FormDef()
|
||||
formdef.name = 'form title'
|
||||
formdef.workflow = workflow
|
||||
formdef.store()
|
||||
|
||||
app = login(get_app(pub))
|
||||
|
||||
formdata = formdef.data_class()()
|
||||
formdata.data['1'] = True
|
||||
formdata.store()
|
||||
|
||||
assert not formdata.statistics_data
|
||||
|
||||
app = login(get_app(pub))
|
||||
resp = app.get('/backoffice/workflows/1/backoffice-fields/fields/1/')
|
||||
|
||||
resp.form['display_locations$element2'] = True
|
||||
resp = resp.form.submit('submit').follow()
|
||||
assert 'Statistics data will be collected in the background.' in resp.text
|
||||
|
||||
formdata.refresh_from_storage()
|
||||
assert formdata.statistics_data == {'bool': [True]}
|
||||
|
|
|
@ -563,6 +563,23 @@ 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()
|
||||
|
@ -587,7 +604,7 @@ 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}]}
|
||||
formdata.data['4'] = {'data': [{'1': True}, {'1': False}]}
|
||||
elif i % 2:
|
||||
formdata.data['1'] = False
|
||||
formdata.data['2'] = 'baz'
|
||||
|
@ -646,7 +663,7 @@ def test_statistics_forms_count_subfilters_query(pub, formdef):
|
|||
assert resp.json['data']['series'][0]['data'][0] == 13
|
||||
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_bool=false'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 3
|
||||
assert resp.json['data']['series'][0]['data'][0] == 16
|
||||
|
||||
# filter on status
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-status=_all'))
|
||||
|
@ -691,6 +708,7 @@ def test_statistics_forms_count_subfilters_query_same_varname(pub, formdef):
|
|||
]
|
||||
formdef.store()
|
||||
|
||||
formdatas = []
|
||||
for i in range(5):
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
|
@ -701,6 +719,7 @@ def test_statistics_forms_count_subfilters_query_same_varname(pub, formdef):
|
|||
formdata.data['1'] = 'bar'
|
||||
formdata.data['2'] = 'foo'
|
||||
formdata.store()
|
||||
formdatas.append(formdata)
|
||||
|
||||
url = '/api/statistics/forms/count/?form=%s' % formdef.url_name
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-test=foo'))
|
||||
|
@ -708,14 +727,34 @@ def test_statistics_forms_count_subfilters_query_same_varname(pub, formdef):
|
|||
|
||||
formdef.fields[0].display_locations = ['statistics']
|
||||
formdef.store()
|
||||
for formdata in formdatas:
|
||||
formdata.store() # refresh statistics_data column
|
||||
|
||||
# filter criterias is "f1 == 'foo' and f2 == 'foo'" hence one result, this should be improved
|
||||
# first non empty value is used : 4 are 'foo' and one is 'bar' hence 4 results
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-test=foo'))
|
||||
assert resp.json['data']['series'] == [{'data': [4], 'label': 'Forms Count'}]
|
||||
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-test=bar'))
|
||||
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'}]
|
||||
|
||||
def test_statistics_forms_count_subfilters_query_integer_items(pub, formdef):
|
||||
for i in range(10):
|
||||
formdata = formdef.data_class()()
|
||||
formdata.just_created()
|
||||
if i % 2:
|
||||
formdata.data['3'] = ['1', '2']
|
||||
else:
|
||||
formdata.data['3'] = ['1']
|
||||
formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
|
||||
formdata.store()
|
||||
|
||||
url = '/api/statistics/forms/count/?form=%s' % formdef.url_name
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-test-items=1'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 10
|
||||
|
||||
resp = get_app(pub).get(sign_uri(url + '&filter-test-items=2'))
|
||||
assert resp.json['data']['series'][0]['data'][0] == 5
|
||||
|
||||
|
||||
@pytest.mark.parametrize('anonymise', [False, True])
|
||||
|
@ -907,6 +946,7 @@ def test_statistics_forms_count_group_by_same_varname(pub, formdef):
|
|||
|
||||
formdef.fields[0].display_locations = ['statistics']
|
||||
formdef.store()
|
||||
formdata.store() # refresh statistics_data column
|
||||
|
||||
# group by uses first field marked for statistics
|
||||
resp = get_app(pub).get(sign_uri(url + '&group-by=test'))
|
||||
|
@ -918,9 +958,8 @@ def test_statistics_forms_count_group_by_same_varname(pub, formdef):
|
|||
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'}]
|
||||
assert resp.json['data']['series'] == [{'data': [2], 'label': 'foo'}]
|
||||
|
||||
|
||||
def test_statistics_cards_count(pub):
|
||||
|
|
|
@ -24,6 +24,7 @@ from wcs.admin.fields import FieldDefPage, FieldsDirectory
|
|||
from wcs.backoffice.snapshots import SnapshotsDirectory
|
||||
from wcs.blocks import BlockDef, BlockdefImportError
|
||||
from wcs.categories import BlockCategory
|
||||
from wcs.formdef import UpdateStatisticsDataAfterJob
|
||||
from wcs.qommon import _, misc, template
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
from wcs.qommon.errors import AccessForbiddenError, TraversalError
|
||||
|
@ -35,6 +36,11 @@ class BlockFieldDefPage(FieldDefPage):
|
|||
anchor = '#itemId_%s' % field.id if field else ''
|
||||
return redirect('../%s' % anchor)
|
||||
|
||||
def schedule_statistics_data_update(self):
|
||||
get_response().add_after_job(
|
||||
UpdateStatisticsDataAfterJob(formdefs=self.objectdef.get_usage_formdefs())
|
||||
|
||||
)
|
||||
|
||||
|
||||
class BlockDirectory(FieldsDirectory):
|
||||
_q_exports = [
|
||||
|
|
|
@ -25,7 +25,7 @@ from wcs import fields
|
|||
from wcs.admin import utils
|
||||
from wcs.carddef import CardDef
|
||||
from wcs.fields import BlockField, get_field_options
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.formdef import FormDef, UpdateStatisticsDataAfterJob
|
||||
from wcs.qommon import _, errors, get_cfg, misc
|
||||
from wcs.qommon.admin.menu import command_icon
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
|
@ -70,6 +70,7 @@ class FieldDefPage(Directory):
|
|||
def _q_index(self):
|
||||
form = self.form()
|
||||
redo = False
|
||||
old_display_locations = self.field.display_locations.copy()
|
||||
|
||||
if form.get_submit() == 'cancel':
|
||||
return redirect('../#itemId_%s' % self.field.id)
|
||||
|
@ -111,6 +112,11 @@ class FieldDefPage(Directory):
|
|||
return r.getvalue()
|
||||
|
||||
self.submit(form)
|
||||
|
||||
if 'statistics' in self.field.display_locations and 'statistics' not in old_display_locations:
|
||||
self.schedule_statistics_data_update()
|
||||
get_session().message = ('info', _('Statistics data will be collected in the background.'))
|
||||
|
||||
if form.get_widget('items') is None and self.field.type == 'item':
|
||||
return redirect('.')
|
||||
|
||||
|
@ -128,6 +134,9 @@ class FieldDefPage(Directory):
|
|||
|
||||
return redirect('../#itemId_%s' % self.field.id)
|
||||
|
||||
def schedule_statistics_data_update(self):
|
||||
get_response().add_after_job(UpdateStatisticsDataAfterJob(formdefs=[self.objectdef]))
|
||||
|
||||
def submit(self, form):
|
||||
for f in self.field.get_admin_attributes():
|
||||
widget = form.get_widget(f)
|
||||
|
|
|
@ -32,7 +32,7 @@ from wcs.backoffice.snapshots import SnapshotsDirectory
|
|||
from wcs.carddef import CardDef
|
||||
from wcs.categories import WorkflowCategory
|
||||
from wcs.formdata import Evolution
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.formdef import FormDef, UpdateStatisticsDataAfterJob
|
||||
from wcs.qommon import _, errors, force_str, misc, template
|
||||
from wcs.qommon.admin.menu import command_icon
|
||||
from wcs.qommon.backoffice.menu import html_top
|
||||
|
@ -1083,6 +1083,14 @@ class WorkflowBackofficeFieldDefPage(FieldDefPage):
|
|||
display_locations.options = display_locations.options[1:]
|
||||
return form
|
||||
|
||||
def schedule_statistics_data_update(self):
|
||||
formdefs = [
|
||||
x
|
||||
for x in FormDef.select(lightweight=True) + CardDef.select(lightweight=True)
|
||||
if x.workflow_id == self.objectdef.workflow.id
|
||||
]
|
||||
get_response().add_after_job(UpdateStatisticsDataAfterJob(formdefs=formdefs))
|
||||
|
||||
|
||||
class WorkflowVariablesFieldsDirectory(FieldsDirectory):
|
||||
_q_exports = ['', 'update_order', 'new']
|
||||
|
|
|
@ -302,6 +302,7 @@ class FormData(StorableObject):
|
|||
workflow_data = None
|
||||
workflow_roles = None
|
||||
geolocations = None
|
||||
statistics_data = None
|
||||
|
||||
_formdef = None
|
||||
|
||||
|
@ -521,8 +522,9 @@ class FormData(StorableObject):
|
|||
|
||||
changed = False
|
||||
|
||||
def get_all_fields():
|
||||
for field in self.formdef.fields:
|
||||
def get_all_fields(with_backoffice_fields=False):
|
||||
fields = self.formdef.get_all_fields() if with_backoffice_fields else self.formdef.fields
|
||||
for field in fields:
|
||||
yield field
|
||||
if field.key == 'block':
|
||||
try:
|
||||
|
@ -608,6 +610,33 @@ class FormData(StorableObject):
|
|||
if digests:
|
||||
self.digests = digests
|
||||
changed = True
|
||||
|
||||
new_statistics_data = {}
|
||||
for field in get_all_fields(with_backoffice_fields=True):
|
||||
if 'statistics' not in field.display_locations:
|
||||
continue
|
||||
|
||||
if new_statistics_data.get(field.varname):
|
||||
continue # ignore fields with duplicated varname if we already have data
|
||||
|
||||
block = getattr(field, 'block', None)
|
||||
if block:
|
||||
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 = list(items)
|
||||
else:
|
||||
values = self.data.get(field.id)
|
||||
if not isinstance(values, list):
|
||||
values = [values]
|
||||
new_statistics_data[field.varname] = [x for x in values if x is not None]
|
||||
|
||||
if new_statistics_data != self.statistics_data:
|
||||
self.statistics_data = new_statistics_data
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
def get_lateral_block(self):
|
||||
|
|
|
@ -2163,3 +2163,7 @@ class UpdateDigestAfterJob(AfterJob):
|
|||
formdef = formdef_class.get(formdef_id)
|
||||
for formdata in formdef.data_class().select(order_by='id'):
|
||||
formdata.store()
|
||||
|
||||
|
||||
class UpdateStatisticsDataAfterJob(UpdateDigestAfterJob):
|
||||
label = _('Updating statistics data')
|
||||
|
|
34
wcs/sql.py
34
wcs/sql.py
|
@ -181,7 +181,9 @@ def pickle_loads(value):
|
|||
|
||||
class Criteria(qommon.storage.Criteria):
|
||||
def __init__(self, attribute, value, **kwargs):
|
||||
self.attribute = attribute.replace('-', '_')
|
||||
self.attribute = attribute
|
||||
if '->' not in attribute:
|
||||
self.attribute = self.attribute.replace('-', '_')
|
||||
self.value = value
|
||||
self.field = kwargs.get('field')
|
||||
|
||||
|
@ -925,7 +927,8 @@ BEGIN
|
|||
NEW.criticality_level - {criticality_levels},
|
||||
{geoloc_base_x},
|
||||
{geoloc_base_y},
|
||||
NEW.anonymised);
|
||||
NEW.anonymised,
|
||||
NEW.statistics_data);
|
||||
RETURN NEW;
|
||||
ELSE
|
||||
UPDATE wcs_all_forms SET
|
||||
|
@ -948,7 +951,8 @@ BEGIN
|
|||
criticality_level = NEW.criticality_level - {criticality_levels},
|
||||
geoloc_base_x = {geoloc_base_x},
|
||||
geoloc_base_y = {geoloc_base_y},
|
||||
anonymised = NEW.anonymised
|
||||
anonymised = NEW.anonymised,
|
||||
statistics_data = NEW.statistics_data
|
||||
WHERE formdef_id = {formdef_id} AND id = OLD.id;
|
||||
RETURN NEW;
|
||||
END IF;
|
||||
|
@ -1790,7 +1794,8 @@ def do_global_views(conn, cur):
|
|||
criticality_level integer,
|
||||
geoloc_base_x double precision,
|
||||
geoloc_base_y double precision,
|
||||
anonymised timestamp with time zone
|
||||
anonymised timestamp with time zone,
|
||||
statistics_data jsonb
|
||||
, PRIMARY KEY(formdef_id, id)
|
||||
)"""
|
||||
)
|
||||
|
@ -1894,7 +1899,8 @@ def init_global_table(conn=None, cur=None):
|
|||
criticality_level - {criticality_levels},
|
||||
{geoloc_base_x},
|
||||
{geoloc_base_y},
|
||||
anonymised
|
||||
anonymised,
|
||||
statistics_data
|
||||
FROM {table_name}
|
||||
ON CONFLICT DO NOTHING;
|
||||
""".format(
|
||||
|
@ -2502,6 +2508,7 @@ class SqlDataMixin(SqlMixin):
|
|||
('digests', 'jsonb'),
|
||||
('user_label', 'varchar'),
|
||||
('auto_geoloc', 'point'),
|
||||
('statistics_data', 'jsonb'),
|
||||
]
|
||||
|
||||
def __init__(self, id=None):
|
||||
|
@ -2598,7 +2605,8 @@ class SqlDataMixin(SqlMixin):
|
|||
'''UPDATE %s
|
||||
SET id_display = %%(id_display)s,
|
||||
digests = %%(digests)s,
|
||||
user_label = %%(user_label)s
|
||||
user_label = %%(user_label)s,
|
||||
statistics_data = %%(statistics_data)s
|
||||
WHERE id = %%(id)s'''
|
||||
% self._table_name
|
||||
)
|
||||
|
@ -2609,6 +2617,7 @@ class SqlDataMixin(SqlMixin):
|
|||
'id_display': self.id_display,
|
||||
'digests': self.digests,
|
||||
'user_label': self.user_label,
|
||||
'statistics_data': self.statistics_data,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -2630,6 +2639,7 @@ class SqlDataMixin(SqlMixin):
|
|||
'submission_channel': self.submission_channel,
|
||||
'criticality_level': self.criticality_level,
|
||||
'workflow_merged_roles_dict': self.workflow_merged_roles_dict,
|
||||
'statistics_data': self.statistics_data or {},
|
||||
}
|
||||
if self.last_update_time:
|
||||
sql_dict['last_update_time'] = datetime.datetime.fromtimestamp(time.mktime(self.last_update_time))
|
||||
|
@ -4639,7 +4649,11 @@ def get_period_query(
|
|||
formdef_class = criteria.value
|
||||
continue
|
||||
|
||||
if criteria.__class__.__name__ == 'Equal' and criteria.attribute == 'formdef_id':
|
||||
if (
|
||||
formdef_class
|
||||
and criteria.__class__.__name__ == 'Equal'
|
||||
and criteria.attribute == 'formdef_id'
|
||||
):
|
||||
# if there's a formdef_id specified, switch to using the
|
||||
# specific table so we have access to all fields
|
||||
table_name = get_formdef_table_name(formdef_class.get(criteria.value))
|
||||
|
@ -4936,7 +4950,7 @@ def get_period_total(
|
|||
# latest migration, number + description (description is not used
|
||||
# programmaticaly but will make sure git conflicts if two migrations are
|
||||
# separately added with the same number)
|
||||
SQL_LEVEL = (79, 'add translatable column to TranslatableMessage table')
|
||||
SQL_LEVEL = (80, 'add statistics data column to formdata')
|
||||
|
||||
|
||||
def migrate_global_views(conn, cur):
|
||||
|
@ -5199,10 +5213,12 @@ def migrate():
|
|||
continue
|
||||
for formdata in formdef.data_class().select_iterator():
|
||||
formdata._set_auto_fields(cur) # build digests
|
||||
if sql_level < 69:
|
||||
if sql_level < 80:
|
||||
# 58: add workflow_merged_roles_dict as a jsonb column with
|
||||
# combined formdef and formdata value.
|
||||
# 69: add auto_geoloc field to form/card tables
|
||||
# 80: add jsonb column to hold statistics data
|
||||
|
||||
for formdef in FormDef.select() + CardDef.select():
|
||||
do_formdef_tables(formdef, rebuild_views=False, rebuild_global_views=False)
|
||||
migrate_views(conn, cur)
|
||||
|
|
|
@ -30,7 +30,7 @@ from wcs.categories import Category
|
|||
from wcs.formdata import FormData
|
||||
from wcs.formdef import FormDef
|
||||
from wcs.qommon import _, misc, pgettext_lazy
|
||||
from wcs.qommon.storage import Contains, Equal, GreaterOrEqual, Less, NotNull, Null, Or, StrictNotEqual
|
||||
from wcs.qommon.storage import Contains, Equal, GreaterOrEqual, Less, Null, Or, StrictNotEqual
|
||||
|
||||
|
||||
class RestrictedView(View):
|
||||
|
@ -252,11 +252,7 @@ class FormsCountView(RestrictedView):
|
|||
except KeyError:
|
||||
return HttpResponseBadRequest('invalid form')
|
||||
form_page = self.formpage_class(formdef=formdef, update_breadcrumbs=False)
|
||||
|
||||
# formdef_klass is a fake criteria, it will be used in time interval functions
|
||||
# to switch to appropriate class, it must appear before formdef_id.
|
||||
totals_kwargs['criterias'].append(Equal('formdef_klass', self.formdef_class))
|
||||
totals_kwargs['criterias'].append(Equal('formdef_id', formdef.id))
|
||||
self.set_formdef_parameters(totals_kwargs, formdef)
|
||||
totals_kwargs['criterias'].extend(self.get_filters_criterias(formdef, form_page))
|
||||
self.set_group_by_parameters(group_by, formdef, form_page, totals_kwargs, group_labels)
|
||||
subfilters = self.get_subfilters(form_page, group_by)
|
||||
|
@ -314,8 +310,27 @@ class FormsCountView(RestrictedView):
|
|||
{'data': {'x_labels': x_labels, 'series': series, 'subfilters': subfilters}, 'err': 0}
|
||||
)
|
||||
|
||||
def set_formdef_parameters(self, totals_kwargs, formdef):
|
||||
# set formdef_klass to None to deactivate switching to formdef specific table
|
||||
totals_kwargs['criterias'].append(Equal('formdef_klass', None))
|
||||
totals_kwargs['criterias'].append(Equal('formdef_id', formdef.id))
|
||||
|
||||
def transform_criteria(self, criteria):
|
||||
if not hasattr(criteria, 'field'):
|
||||
return criteria
|
||||
|
||||
attribute = "statistics_data->'%s'" % criteria.field.varname
|
||||
|
||||
if isinstance(criteria.value, bool):
|
||||
value = str(criteria.value).lower()
|
||||
else:
|
||||
value = '"%s"' % criteria.value
|
||||
|
||||
return sql.ArrayContains(attribute, value)
|
||||
|
||||
def get_filters_criterias(self, formdef, form_page):
|
||||
criterias = form_page.get_criterias_from_query(statistics_fields_only=True)
|
||||
criterias = [self.transform_criteria(criteria) for criteria in criterias]
|
||||
|
||||
selected_status = self.request.GET.get('filter-status')
|
||||
applied_filters = None
|
||||
|
@ -471,10 +486,10 @@ class FormsCountView(RestrictedView):
|
|||
if group_by_field.type == 'status':
|
||||
totals_kwargs['group_by'] = 'status'
|
||||
else:
|
||||
totals_kwargs['group_by'] = sql.get_field_id(group_by_field)
|
||||
totals_kwargs['group_by'] = "statistics_data->'%s'" % group_by_field.varname
|
||||
|
||||
if self.request.GET.get('hide_none_label') == 'true':
|
||||
totals_kwargs['criterias'].append(NotNull(totals_kwargs['group_by']))
|
||||
totals_kwargs['criterias'].append(StrictNotEqual(totals_kwargs['group_by'], '[]'))
|
||||
|
||||
group_labels.update(self.get_group_labels(group_by_field, formdef, form_page, group_by))
|
||||
|
||||
|
@ -494,6 +509,8 @@ class FormsCountView(RestrictedView):
|
|||
groups = total[1]
|
||||
if not isinstance(groups, list):
|
||||
groups = [groups]
|
||||
if not groups:
|
||||
groups = [None]
|
||||
for group in groups:
|
||||
totals_by_group[group] += total[2]
|
||||
seen_group_values.add(group)
|
||||
|
@ -516,6 +533,8 @@ class FormsCountView(RestrictedView):
|
|||
for groups, total in totals:
|
||||
if not isinstance(groups, list):
|
||||
groups = [groups]
|
||||
if not groups:
|
||||
groups = [None]
|
||||
for group in groups:
|
||||
totals_by_group[group] += total
|
||||
|
||||
|
@ -562,6 +581,12 @@ class CardsCountView(FormsCountView):
|
|||
has_global_count_support = False
|
||||
label = _('Cards Count')
|
||||
|
||||
def set_formdef_parameters(self, totals_kwargs, formdef):
|
||||
# formdef_klass is a fake criteria, it will be used in time interval functions
|
||||
# to switch to appropriate class, it must appear before formdef_id.
|
||||
totals_kwargs['criterias'].append(Equal('formdef_klass', CardDef))
|
||||
totals_kwargs['criterias'].append(Equal('formdef_id', formdef.id))
|
||||
|
||||
|
||||
class ResolutionTimeView(RestrictedView):
|
||||
formdef_class = FormDef
|
||||
|
|
Loading…
Reference in New Issue
Plutôt que tel quel le UpdateDigestAfterJob je préfererais un nouveau job (très simple, ça peut juste être une classe qui hérite de UpdateDigestAfterJob et lui met un attribut label adapté aux statistiques).
Ajouté « UpdateStatisticsDataAfterJob ».