Projet

Général

Profil

0001-statistics-filter-forms-count-by-fields-60777.patch

Valentin Deniaud, 15 février 2022 17:27

Télécharger (26,5 ko)

Voir les différences:

Subject: [PATCH 1/2] statistics: filter forms count by fields (#60777)

 tests/admin_pages/test_form.py |  34 ++++
 tests/api/test_statistics.py   | 298 +++++++++++++++++++++++++++++++++
 wcs/backoffice/management.py   |   7 +-
 wcs/fields.py                  |  24 ++-
 wcs/statistics/views.py        | 106 +++++++++++-
 5 files changed, 462 insertions(+), 7 deletions(-)
tests/admin_pages/test_form.py
3144 3144
    ]
3145 3145
    resp = resp.click('edit page fields', index=0)
3146 3146
    assert '<h2>form title - page 1 - first page</h2>' in resp.text
3147

  
3148

  
3149
def test_field_display_locations_statistics_choice(pub):
3150
    create_superuser(pub)
3151
    create_role(pub)
3152

  
3153
    FormDef.wipe()
3154
    formdef = FormDef()
3155
    formdef.name = 'form title'
3156
    formdef.fields = [
3157
        fields.StringField(id='0', label='String field', varname='var_1'),
3158
        fields.ItemField(id='1', label='Item field', type='item'),
3159
        fields.ItemsField(id='2', label='Items field', type='items'),
3160
        fields.BoolField(id='3', label='Bool field', type='bool'),
3161
    ]
3162
    formdef.store()
3163
    formdef.data_class().wipe()
3164

  
3165
    app = login(get_app(pub))
3166
    resp = app.get('/backoffice/forms/%s/fields/0/' % formdef.id)
3167
    assert 'Statistics' not in resp.text
3168

  
3169
    for i in range(1, 4):
3170
        resp = app.get('/backoffice/forms/%s/fields/%s/' % (formdef.id, i))
3171
        assert 'Statistics' in resp.text
3172

  
3173
        resp.form['display_locations$element3'] = True
3174
        resp = resp.form.submit('submit')
3175
        assert 'Field must have a varname in order to be displayed in statistics.' in resp.text
3176
        assert 'statistics' not in FormDef.get(formdef.id).fields[i].display_locations
3177

  
3178
        resp.form['varname'] = 'var_%s' % i
3179
        resp = resp.form.submit('submit')
3180
        assert 'statistics' in FormDef.get(formdef.id).fields[i].display_locations
tests/api/test_statistics.py
3 3

  
4 4
import pytest
5 5

  
6
from wcs import fields
7
from wcs.blocks import BlockDef
6 8
from wcs.categories import Category
7 9
from wcs.formdef import FormDef
8 10
from wcs.qommon.http_request import HTTPRequest
11
from wcs.wf.jump import JumpWorkflowStatusItem
12
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef
9 13

  
10 14
from ..utilities import clean_temporary_pub, create_temporary_pub, get_app
11 15
from .utils import sign_uri
......
14 18
@pytest.fixture
15 19
def pub():
16 20
    pub = create_temporary_pub(sql_mode=True)
21
    BlockDef.wipe()
17 22
    Category.wipe()
18 23
    FormDef.wipe()
24
    Workflow.wipe()
19 25

  
20 26
    req = HTTPRequest(None, {'SCRIPT_NAME': '/', 'SERVER_NAME': 'example.net'})
21 27
    pub.set_app_dir(req)
......
34 40
    return pub
35 41

  
36 42

  
43
@pytest.fixture
44
def formdef(pub):
45
    workflow = Workflow(name='Workflow One')
46
    new_status = workflow.add_status(name='New status')
47
    workflow.add_status(name='End status')
48
    jump = JumpWorkflowStatusItem()
49
    jump.id = '_jump'
50
    jump.status = '2'
51
    jump.timeout = 86400
52
    new_status.items.append(jump)
53
    jump.parent = new_status
54
    workflow.backoffice_fields_formdef = WorkflowBackofficeFieldsFormDef(workflow)
55
    workflow.backoffice_fields_formdef.fields = [
56
        fields.BoolField(
57
            id='1', varname='checkbox', label='Checkbox', type='bool', display_locations=['statistics']
58
        ),
59
    ]
60
    workflow.store()
61

  
62
    block = BlockDef()
63
    block.name = 'foobar'
64
    block.fields = [
65
        fields.BoolField(id='1', label='Bool', type='bool', varname='bool', display_locations=['statistics'])
66
    ]
67
    block.store()
68

  
69
    formdef = FormDef()
70
    formdef.name = 'test'
71
    formdef.workflow_id = workflow.id
72
    item_field = fields.ItemField(
73
        id='2', varname='test-item', label='Test item', type='item', items=['foo', 'bar', 'baz']
74
    )
75
    item_field.display_locations = ['statistics']
76
    items_field = fields.ItemsField(
77
        id='3', varname='test-items', label='Test items', type='items', items=['foo', 'bar', 'baz']
78
    )
79
    items_field.display_locations = ['statistics']
80
    block_field = fields.BlockField(id='4', label='Block Data', varname='blockdata', type='block:foobar')
81
    formdef.fields = [item_field, items_field, block_field]
82
    formdef.store()
83
    formdef.data_class().wipe()
84
    return formdef
85

  
86

  
37 87
def teardown_module(module):
38 88
    clean_temporary_pub()
39 89

  
......
58 108
    assert len(category_filter['options']) == 3
59 109

  
60 110

  
111
def test_statistics_index_forms(pub):
112
    formdef = FormDef()
113
    formdef.name = 'test 1'
114
    formdef.fields = []
115
    formdef.store()
116
    formdef.data_class().wipe()
117

  
118
    formdef2 = FormDef()
119
    formdef2.name = 'test 2'
120
    formdef2.fields = []
121
    formdef2.store()
122
    formdef2.data_class().wipe()
123

  
124
    resp = get_app(pub).get(sign_uri('/api/statistics/'))
125
    form_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'form'][0]
126
    assert form_filter['options'] == [
127
        {'id': '_all', 'label': 'All'},
128
        {'id': 'test-1', 'label': 'test 1'},
129
        {'id': 'test-2', 'label': 'test 2'},
130
    ]
131

  
132

  
61 133
def test_statistics_forms_count(pub):
62 134
    category_a = Category(name='Category A')
63 135
    category_a.store()
......
95 167
        'data': {
96 168
            'series': [{'data': [20, 0, 30], 'label': 'Forms Count'}],
97 169
            'x_labels': ['2021-01', '2021-02', '2021-03'],
170
            'subfilters': [],
98 171
        },
99 172
        'err': 0,
100 173
    }
......
104 177
        'data': {
105 178
            'series': [{'data': [50], 'label': 'Forms Count'}],
106 179
            'x_labels': ['2021'],
180
            'subfilters': [],
107 181
        },
108 182
        'err': 0,
109 183
    }
......
113 187
        'data': {
114 188
            'series': [{'data': [30, 0, 0, 0, 20, 0, 0], 'label': 'Forms Count'}],
115 189
            'x_labels': ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
190
            'subfilters': [],
116 191
        },
117 192
        'err': 0,
118 193
    }
......
127 202
                }
128 203
            ],
129 204
            'x_labels': list(range(24)),
205
            'subfilters': [],
130 206
        },
131 207
        'err': 0,
132 208
    }
......
140 216
        'data': {
141 217
            'series': [{'data': [20], 'label': 'Forms Count'}],
142 218
            'x_labels': ['2021-01'],
219
            'subfilters': [],
143 220
        },
144 221
        'err': 0,
145 222
    }
146 223

  
224
    # apply form filter
225
    resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
226
    assert resp.json['data']['series'] == [{'data': [20], 'label': 'Forms Count'}]
227
    assert resp.json['data']['x_labels'] == ['2021-01']
228

  
229
    resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % 'invalid'), status=400)
230
    assert resp.text == 'invalid form'
231

  
147 232
    # apply period filter
148 233
    resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?end=2021-02-01'))
149 234
    assert resp.json == {
150 235
        'data': {
151 236
            'series': [{'data': [20], 'label': 'Forms Count'}],
152 237
            'x_labels': ['2021-01'],
238
            'subfilters': [],
153 239
        },
154 240
        'err': 0,
155 241
    }
242

  
243

  
244
def test_statistics_forms_count_subfilters(pub, formdef):
245
    for i in range(2):
246
        formdata = formdef.data_class()()
247
        formdata.data['2'] = 'foo' if i % 2 else 'baz'
248
        formdata.data['2_display'] = 'Foo' if i % 2 else 'Baz'
249
        formdata.data['3'] = ['foo'] if i % 2 else ['bar', 'baz']
250
        formdata.data['3_display'] = 'Foo' if i % 2 else 'Bar, Baz'
251
        formdata.just_created()
252
        formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
253
        formdata.store()
254

  
255
    resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
256

  
257
    # check item field subfilter
258
    assert resp.json['data']['subfilters'][0] == {
259
        'id': 'filter-test-item',
260
        'label': 'Test item',
261
        'options': [{'id': 'baz', 'label': 'Baz'}, {'id': 'foo', 'label': 'Foo'}],
262
        'required': False,
263
    }
264

  
265
    # check items field subfilter
266
    assert resp.json['data']['subfilters'][1] == {
267
        'id': 'filter-test-items',
268
        'label': 'Test items',
269
        'options': [
270
            {'id': 'bar', 'label': 'Bar'},
271
            {'id': 'baz', 'label': 'Baz'},
272
            {'id': 'foo', 'label': 'Foo'},
273
        ],
274
        'required': False,
275
    }
276

  
277
    # check block boolean field subfilter
278
    assert resp.json['data']['subfilters'][2] == {
279
        'id': 'filter-blockdata_bool',
280
        'label': 'Bool',
281
        'options': [{'id': 'true', 'label': 'Yes'}, {'id': 'false', 'label': 'No'}],
282
        'required': False,
283
    }
284

  
285
    # check boolean backoffice field subfilter
286
    assert resp.json['data']['subfilters'][3] == {
287
        'id': 'filter-checkbox',
288
        'label': 'Checkbox',
289
        'options': [{'id': 'true', 'label': 'Yes'}, {'id': 'false', 'label': 'No'}],
290
        'required': False,
291
    }
292

  
293
    # check status subfilter
294
    assert resp.json['data']['subfilters'][-1] == {
295
        'default': '_all',
296
        'id': 'filter-status',
297
        'label': 'Status',
298
        'options': [
299
            {'id': '_all', 'label': 'All'},
300
            {'id': 'pending', 'label': 'Open'},
301
            {'id': 'done', 'label': 'Done'},
302
            {'id': '1', 'label': 'New status'},
303
            {'id': '2', 'label': 'End status'},
304
        ],
305
        'required': True,
306
    }
307

  
308
    # add item field with no formdata, it should not appear
309
    item_field = fields.ItemField(
310
        id='20',
311
        varname='test-item-no-formdata',
312
        label='Test item no formdata',
313
        type='item',
314
        items=['foo', 'bar', 'baz'],
315
        display_locations=['statistics'],
316
    )
317
    formdef.fields.append(item_field)
318
    formdef.store()
319
    new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
320
    assert new_resp.json == resp.json
321

  
322
    # add boolean field with no varname, it should not appear
323
    bool_field = fields.BoolField(id='21', label='Checkbox', type='bool', display_locations=['statistics'])
324
    formdef.fields.append(bool_field)
325
    formdef.store()
326
    new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
327
    assert new_resp.json == resp.json
328

  
329
    # add boolean field with no display location, it should not appear
330
    bool_field = fields.BoolField(
331
        id='22', varname='checkbox', label='Checkbox', type='bool', display_locations=['validation']
332
    )
333
    formdef.fields.append(bool_field)
334
    formdef.store()
335
    new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
336
    assert new_resp.json == resp.json
337

  
338
    # add not filterable field, it should not appear
339
    formdef.fields.append(fields.StringField(id='23', varname='test string', label='Test', type='string'))
340
    formdef.store()
341
    new_resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
342
    assert new_resp.json == resp.json
343

  
344
    # remove fields and statuses
345
    workflow = Workflow(name='Empty wf')
346
    workflow.store()
347
    formdef.workflow = workflow
348
    formdef.fields.clear()
349
    formdef.store()
350
    formdef.data_class().wipe()
351

  
352
    resp = get_app(pub).get(sign_uri('/api/statistics/forms/count/?form=%s' % formdef.url_name))
353
    assert resp.json['data'] == {
354
        'series': [{'data': [], 'label': 'Forms Count'}],
355
        'subfilters': [],
356
        'x_labels': [],
357
    }
358

  
359

  
360
def test_statistics_forms_count_subfilters_query(pub, formdef):
361
    for i in range(20):
362
        formdata = formdef.data_class()()
363
        formdata.just_created()
364
        if i % 3:
365
            formdata.data['1'] = True
366
            formdata.data['2'] = 'foo'
367
            formdata.data['3'] = ['bar', 'baz']
368
            formdata.data['4'] = {'data': [{'1': True}]}
369
        elif i % 2:
370
            formdata.data['1'] = False
371
            formdata.data['2'] = 'baz'
372
            formdata.data['3'] = ['baz']
373
            formdata.data['4'] = {'data': [{'1': False}]}
374
            formdata.jump_status('2')
375
        formdata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
376
        formdata.store()
377

  
378
    # query all formdata
379
    url = '/api/statistics/forms/count/?form=%s' % formdef.url_name
380
    resp = get_app(pub).get(sign_uri(url))
381
    assert resp.json['data']['series'][0]['data'][0] == 20
382

  
383
    # filter on boolean field
384
    resp = get_app(pub).get(sign_uri(url + '&filter-checkbox=true'))
385
    assert resp.json['data']['series'][0]['data'][0] == 13
386

  
387
    resp = get_app(pub).get(sign_uri(url + '&filter-checkbox=false'))
388
    assert resp.json['data']['series'][0]['data'][0] == 3
389

  
390
    resp = get_app(pub).get(sign_uri(url + '&filter-checkbox='))
391
    assert resp.json['data']['series'][0]['data'][0] == 20
392

  
393
    resp = get_app(pub).get(sign_uri(url + '&filter-checkbox=xxx'), status=400)
394
    assert resp.text == 'Invalid value "xxx" for "filter-checkbox"'
395

  
396
    # filter on item field
397
    resp = get_app(pub).get(sign_uri(url + '&filter-test-item=foo'))
398
    assert resp.json['data']['series'][0]['data'][0] == 13
399

  
400
    resp = get_app(pub).get(sign_uri(url + '&filter-test-item=baz'))
401
    assert resp.json['data']['series'][0]['data'][0] == 3
402

  
403
    resp = get_app(pub).get(sign_uri(url + '&filter-test-item=bar'))
404
    assert resp.json['data']['series'][0]['data'] == []
405

  
406
    resp = get_app(pub).get(sign_uri(url + '&filter-test-item='))
407
    assert resp.json['data']['series'][0]['data'][0] == 20
408

  
409
    resp = get_app(pub).get(sign_uri(url + '&filter-test-item=xxx'))
410
    assert resp.json['data']['series'][0]['data'] == []
411

  
412
    # filter on items field
413
    resp = get_app(pub).get(sign_uri(url + '&filter-test-items=foo'))
414
    assert resp.json['data']['series'][0]['data'] == []
415

  
416
    resp = get_app(pub).get(sign_uri(url + '&filter-test-items=bar'))
417
    assert resp.json['data']['series'][0]['data'][0] == 13
418

  
419
    resp = get_app(pub).get(sign_uri(url + '&filter-test-items=baz'))
420
    assert resp.json['data']['series'][0]['data'][0] == 16
421

  
422
    # filter on block boolean field
423
    resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_bool=true'))
424
    assert resp.json['data']['series'][0]['data'][0] == 13
425

  
426
    resp = get_app(pub).get(sign_uri(url + '&filter-blockdata_bool=false'))
427
    assert resp.json['data']['series'][0]['data'][0] == 3
428

  
429
    # filter on status
430
    resp = get_app(pub).get(sign_uri(url + '&filter-status=_all'))
431
    assert resp.json['data']['series'][0]['data'][0] == 20
432

  
433
    resp = get_app(pub).get(sign_uri(url + '&filter-status=1'))
434
    assert resp.json['data']['series'][0]['data'][0] == 17
435

  
436
    resp = get_app(pub).get(sign_uri(url + '&filter-status=pending'))
437
    assert resp.json['data']['series'][0]['data'][0] == 17
438

  
439
    resp = get_app(pub).get(sign_uri(url + '&filter-status=2'))
440
    assert resp.json['data']['series'][0]['data'][0] == 3
441

  
442
    resp = get_app(pub).get(sign_uri(url + '&filter-status=done'))
443
    assert resp.json['data']['series'][0]['data'][0] == 3
444

  
445
    resp = get_app(pub).get(sign_uri(url + '&filter-status='))
446
    assert resp.json['data']['series'][0]['data'][0] == 20
447

  
448
    resp = get_app(pub).get(sign_uri(url + '&filter-status=xxx'))
449
    assert resp.json['data']['series'][0]['data'][0] == 20
450

  
451
    # invalid filter
452
    resp = get_app(pub).get(sign_uri(url + '&filter-xxx=yyy'))
453
    assert resp.json['data']['series'][0]['data'] == []
wcs/backoffice/management.py
928 928
            return ('start', 'end')
929 929
        return ()
930 930

  
931
    def get_item_filter_options(self, filter_field, selected_filter, criterias):
931
    def get_item_filter_options(self, filter_field, selected_filter, criterias=None):
932 932
        criterias = (criterias or [])[:]
933 933
        # remove potential filter on self (Equal for item, Intersects for items)
934 934
        criterias = [
......
1596 1596
                field.has_relations = True
1597 1597
                yield RelatedField(carddef, card_field, field)
1598 1598

  
1599
        yield FakeField('status', 'status', _('Status'))
1599
        yield FakeField('status', 'status', _('Status'), include_in_statistics=True)
1600 1600
        yield FakeField('anonymised', 'anonymised', _('Anonymised'))
1601 1601

  
1602 1602
    def get_default_columns(self):
......
3508 3508

  
3509 3509

  
3510 3510
class FakeField:
3511
    def __init__(self, id, type_, label, addable=True):
3511
    def __init__(self, id, type_, label, addable=True, include_in_statistics=False):
3512 3512
        self.id = id
3513 3513
        self.contextual_id = self.id
3514 3514
        self.type = type_
......
3518 3518
        self.contextual_varname = self.varname
3519 3519
        self.store_display_value = None
3520 3520
        self.addable = addable
3521
        self.include_in_statistics = include_in_statistics
3521 3522

  
3522 3523
    def get_view_value(self, value):
3523 3524
        # just here to quack like a duck
wcs/fields.py
255 255
    convert_value_to_str = None
256 256
    convert_value_from_anything = None
257 257
    allow_complex = False
258
    allow_statistics = False
258 259
    display_locations = []
259 260
    prefill = None
260 261
    keep_raw_value = True
......
295 296
    def include_in_summary_page(self):
296 297
        return 'summary' in (self.display_locations or [])
297 298

  
299
    @property
300
    def include_in_statistics(self):
301
        return self.varname and 'statistics' in (self.display_locations or [])
302

  
298 303
    @property
299 304
    def unhtmled_label(self):
300 305
        return force_str(html.unescape(force_text(re.sub('<.*?>', ' ', self.label or ''))).strip())
......
743 748
                widget.extra_css_class = self.extra_css_class
744 749

  
745 750
    def get_display_locations_options(self):
746
        return [
751
        options = [
747 752
            ('validation', _('Validation Page')),
748 753
            ('summary', _('Summary Page')),
749 754
            ('listings', _('Management Listings')),
750 755
        ]
751 756

  
757
        if self.allow_statistics:
758
            options.append(('statistics', _('Statistics')))
759

  
760
        return options
761

  
752 762
    def fill_admin_form(self, form):
753 763
        form.add(StringWidget, 'label', title=_('Label'), value=self.label, required=True, size=50)
754 764
        form.add(CheckboxWidget, 'required', title=_('Required'), value=self.required)
......
815 825
            )
816 826

  
817 827
    def check_admin_form(self, form):
818
        return
828
        display_locations = form.get_widget('display_locations').parse()
829
        varname = form.get_widget('varname').parse()
830
        if 'statistics' in display_locations and not varname:
831
            form.set_error(
832
                'display_locations', _('Field must have a varname in order to be displayed in statistics.')
833
            )
819 834

  
820 835
    def get_admin_attributes(self):
821 836
        return Field.get_admin_attributes(self) + [
......
1249 1264
    key = 'bool'
1250 1265
    description = _('Check Box (single choice)')
1251 1266
    allow_complex = True
1267
    allow_statistics = True
1252 1268

  
1253 1269
    widget_class = CheckboxWidget
1254 1270
    required = False
......
1888 1904
    key = 'item'
1889 1905
    description = _('List')
1890 1906
    allow_complex = True
1907
    allow_statistics = True
1891 1908

  
1892 1909
    items = []
1893 1910
    show_as_radio = None
......
2215 2232
        ]
2216 2233

  
2217 2234
    def check_admin_form(self, form):
2235
        super().check_admin_form(form)
2218 2236
        self.check_items_admin_form(form)
2219 2237
        self.check_zoom_admin_form(form)
2220 2238

  
......
2273 2291
    key = 'items'
2274 2292
    description = _('Multiple choice list')
2275 2293
    allow_complex = True
2294
    allow_statistics = True
2276 2295

  
2277 2296
    items = []
2278 2297
    min_choices = 0
......
2358 2377
        ]
2359 2378

  
2360 2379
    def check_admin_form(self, form):
2380
        super().check_admin_form(form)
2361 2381
        self.check_items_admin_form(form)
2362 2382

  
2363 2383
    def get_prefill_value(self, user=None, force_string=True):
wcs/statistics/views.py
20 20
from quixote import get_publisher
21 21

  
22 22
from wcs.api_utils import is_url_signed
23
from wcs.backoffice.management import FormPage
23 24
from wcs.categories import Category
25
from wcs.formdef import FormDef
24 26
from wcs.qommon import _, misc
25 27
from wcs.qommon.misc import C_
26
from wcs.qommon.storage import Equal
28
from wcs.qommon.storage import Equal, NotEqual, Or
27 29

  
28 30

  
29 31
class RestrictedView(View):
......
42 44
        category_options = [{'id': '_all', 'label': C_('categories|All')}] + [
43 45
            {'id': x.id, 'label': x.name} for x in categories
44 46
        ]
47
        forms = FormDef.select()
48
        forms.sort(key=lambda x: misc.simplify(x.name))
49
        form_options = [{'id': '_all', 'label': _('All')}] + [
50
            {'id': x.url_name, 'label': x.name} for x in forms
51
        ]
45 52
        return JsonResponse(
46 53
            {
47 54
                'data': [
......
81 88
                                'required': True,
82 89
                                'default': '_all',
83 90
                            },
91
                            {
92
                                'id': 'form',
93
                                'label': _('Form'),
94
                                'options': form_options,
95
                                'required': True,
96
                                'default': '_all',
97
                                'has_subfilters': True,
98
                            },
84 99
                        ],
85 100
                    }
86 101
                ]
......
99 114
            'criterias': [],
100 115
        }
101 116
        category_id = request.GET.get('category', '_all')
102
        if category_id != '_all':
117
        formdef_slug = request.GET.get('form', '_all')
118
        subfilters = []
119
        if formdef_slug != '_all':
120
            try:
121
                formdef = FormDef.get_by_urlname(formdef_slug, ignore_migration=True)
122
            except KeyError:
123
                return HttpResponseBadRequest('invalid form')
124
            form_page = FormPage(formdef=formdef, update_breadcrumbs=False)
125

  
126
            totals_kwargs['criterias'].append(Equal('formdef_id', formdef.id))
127
            totals_kwargs['criterias'].extend(self.get_filters_criterias(formdef, form_page))
128

  
129
            subfilters = self.get_subfilters(form_page)
130
        elif category_id != '_all':
103 131
            totals_kwargs['criterias'].append(Equal('category_id', category_id))
132

  
104 133
        time_interval_methods = {
105 134
            'month': sql.get_monthly_totals,
106 135
            'year': sql.get_yearly_totals,
......
122 151
                            'data': [x[1] for x in totals],
123 152
                        }
124 153
                    ],
154
                    'subfilters': subfilters,
125 155
                },
126 156
                'err': 0,
127 157
            }
128 158
        )
159

  
160
    def get_filters_criterias(self, formdef, form_page):
161
        criterias = form_page.get_criterias_from_query()
162

  
163
        selected_status = self.request.GET.get('filter-status')
164
        applied_filters = None
165
        if selected_status and selected_status != '_all':
166
            if selected_status == 'pending':
167
                applied_filters = ['wf-%s' % x.id for x in formdef.workflow.get_not_endpoint_status()]
168
            elif selected_status == 'done':
169
                applied_filters = ['wf-%s' % x.id for x in formdef.workflow.get_endpoint_status()]
170
            else:
171
                try:
172
                    formdef.workflow.get_status(selected_status)
173
                    applied_filters = ['wf-%s' % selected_status]
174
                except KeyError:
175
                    pass
176

  
177
        if applied_filters:
178
            criterias.append(Or([Equal('status', x) for x in applied_filters]))
179
        else:
180
            criterias = [NotEqual('status', 'draft')] + criterias
181

  
182
        return criterias
183

  
184
    @staticmethod
185
    def get_subfilters(form_page):
186
        subfilters = []
187
        for field in form_page.get_formdef_fields():
188
            if not getattr(field, 'include_in_statistics', False) or not field.contextual_varname:
189
                continue
190

  
191
            field_key = 'filter-%s' % field.contextual_varname
192
            field.required = False
193

  
194
            if field.type == 'status':
195
                waitpoint_status = form_page.formdef.workflow.get_waitpoint_status()
196
                if not waitpoint_status:
197
                    continue
198

  
199
                field.required = True
200
                field.default_filter_value = '_all'
201
                options = [
202
                    ('_all', _('All')),
203
                    ('pending', C_('statistics|Open')),
204
                    ('done', _('Done')),
205
                ]
206
                for status in waitpoint_status:
207
                    options.append((status.id, status.name))
208
            elif field.type in ('item', 'items'):
209
                if not get_publisher().is_using_postgresql():
210
                    continue
211
                options = form_page.get_item_filter_options(field, selected_filter='all')
212
                if not options:
213
                    continue
214
            elif field.type == 'bool':
215
                options = [('true', _('Yes')), ('false', _('No'))]
216
            else:
217
                continue
218

  
219
            filter_description = {
220
                'id': field_key,
221
                'label': field.label,
222
                'options': [{'id': x[0], 'label': x[1]} for x in options],
223
                'required': field.required,
224
            }
225
            if hasattr(field, 'default_filter_value'):
226
                filter_description['default'] = field.default_filter_value
227

  
228
            subfilters.append(filter_description)
229

  
230
        return subfilters
129
-