0001-api-add-suppot-for-statistics-on-cards-62029.patch
tests/api/test_statistics.py | ||
---|---|---|
5 | 5 | |
6 | 6 |
from wcs import fields |
7 | 7 |
from wcs.blocks import BlockDef |
8 |
from wcs.carddef import CardDef |
|
8 | 9 |
from wcs.categories import Category |
9 | 10 |
from wcs.formdef import FormDef |
10 | 11 |
from wcs.qommon.http_request import HTTPRequest |
... | ... | |
129 | 130 |
resp = get_app(pub).get(sign_uri('/api/statistics/')) |
130 | 131 |
form_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'form'][0] |
131 | 132 |
assert form_filter['options'] == [ |
132 |
{'id': '_all', 'label': 'All'}, |
|
133 |
{'id': '_all', 'label': 'All Forms'},
|
|
133 | 134 |
{'id': 'test-1', 'label': 'test 1'}, |
134 | 135 |
{'id': 'test-2', 'label': 'test 2'}, |
135 | 136 |
] |
... | ... | |
569 | 570 |
# invalid field |
570 | 571 |
resp = get_app(pub).get(sign_uri(url + '&group-by=xxx')) |
571 | 572 |
assert resp.json['data']['series'] == [{'data': [16, 0, 4], 'label': 'Forms Count'}] |
573 | ||
574 | ||
575 |
def test_statistics_cards_count(pub): |
|
576 |
carddef = CardDef() |
|
577 |
carddef.name = 'test 1' |
|
578 |
carddef.fields = [] |
|
579 |
carddef.store() |
|
580 |
carddef.data_class().wipe() |
|
581 | ||
582 |
for _i in range(20): |
|
583 |
carddata = carddef.data_class()() |
|
584 |
carddata.just_created() |
|
585 |
carddata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple() |
|
586 |
carddata.store() |
|
587 | ||
588 |
# apply (required) card filter |
|
589 |
resp = get_app(pub).get(sign_uri('/api/statistics/cards/count/?form=%s' % carddef.url_name)) |
|
590 |
assert resp.json['data']['series'] == [{'data': [20], 'label': 'Cards Count'}] |
|
591 |
assert resp.json['data']['x_labels'] == ['2021-01'] |
|
592 | ||
593 |
resp = get_app(pub).get(sign_uri('/api/statistics/cards/count/?card=%s' % 'invalid'), status=400) |
|
594 |
assert resp.text == 'invalid form' |
wcs/sql.py | ||
---|---|---|
3801 | 3801 |
clause = [NotNull('receipt_time')] |
3802 | 3802 |
table_name = 'wcs_all_forms' |
3803 | 3803 |
if criterias: |
3804 |
from wcs.formdef import FormDef |
|
3805 | ||
3806 |
formdef_class = FormDef |
|
3804 | 3807 |
for criteria in criterias: |
3808 |
if criteria.__class__.__name__ == 'Equal' and criteria.attribute == 'formdef_klass': |
|
3809 |
formdef_class = criteria.value |
|
3810 |
continue |
|
3811 | ||
3805 | 3812 |
if criteria.__class__.__name__ == 'Equal' and criteria.attribute == 'formdef_id': |
3806 | 3813 |
# if there's a formdef_id specified, switch to using the |
3807 | 3814 |
# specific table so we have access to all fields |
3808 |
from wcs.formdef import FormDef |
|
3809 | ||
3810 |
table_name = get_formdef_table_name(FormDef.get(criteria.value)) |
|
3815 |
table_name = get_formdef_table_name(formdef_class.get(criteria.value)) |
|
3811 | 3816 |
continue |
3812 | 3817 |
clause.append(criteria) |
3813 | 3818 |
if period_start: |
wcs/statistics/views.py | ||
---|---|---|
23 | 23 | |
24 | 24 |
from wcs import sql |
25 | 25 |
from wcs.api_utils import is_url_signed |
26 |
from wcs.backoffice.data_management import CardPage |
|
26 | 27 |
from wcs.backoffice.management import FormPage |
28 |
from wcs.carddef import CardDef |
|
27 | 29 |
from wcs.categories import Category |
28 | 30 |
from wcs.formdef import FormDef |
29 | 31 |
from wcs.qommon import _, misc |
... | ... | |
47 | 49 |
category_options = [{'id': '_all', 'label': C_('categories|All')}] + [ |
48 | 50 |
{'id': x.url_name, 'label': x.name} for x in categories |
49 | 51 |
] |
50 |
forms = FormDef.select() |
|
52 |
forms = FormDef.select(lightweight=True)
|
|
51 | 53 |
forms.sort(key=lambda x: misc.simplify(x.name)) |
52 |
form_options = [{'id': '_all', 'label': _('All')}] + [ |
|
54 |
form_options = [{'id': '_all', 'label': _('All Forms')}] + [
|
|
53 | 55 |
{'id': x.url_name, 'label': x.name} for x in forms |
54 | 56 |
] |
57 |
cards = CardDef.select(lightweight=True) |
|
58 |
cards.sort(key=lambda x: misc.simplify(x.name)) |
|
59 |
card_options = [{'id': x.url_name, 'label': x.name} for x in cards] |
|
55 | 60 |
return JsonResponse( |
56 | 61 |
{ |
57 | 62 |
'data': [ |
... | ... | |
100 | 105 |
'has_subfilters': True, |
101 | 106 |
}, |
102 | 107 |
], |
103 |
} |
|
108 |
}, |
|
109 |
{ |
|
110 |
'name': _('Cards Count'), |
|
111 |
'url': request.build_absolute_uri(reverse('api-statistics-cards-count')), |
|
112 |
'id': 'cards_counts', |
|
113 |
'filters': [ |
|
114 |
{ |
|
115 |
'id': 'time_interval', |
|
116 |
'label': _('Interval'), |
|
117 |
'options': [ |
|
118 |
{ |
|
119 |
'id': 'month', |
|
120 |
'label': _('Month'), |
|
121 |
}, |
|
122 |
{ |
|
123 |
'id': 'year', |
|
124 |
'label': _('Year'), |
|
125 |
}, |
|
126 |
{ |
|
127 |
'id': 'weekday', |
|
128 |
'label': _('Week day'), |
|
129 |
}, |
|
130 |
{ |
|
131 |
'id': 'hour', |
|
132 |
'label': _('Hour'), |
|
133 |
}, |
|
134 |
], |
|
135 |
'required': True, |
|
136 |
'default': 'month', |
|
137 |
}, |
|
138 |
{ |
|
139 |
'id': 'form', |
|
140 |
'label': _('Card'), |
|
141 |
'options': card_options, |
|
142 |
'required': True, |
|
143 |
'default': '', |
|
144 |
'has_subfilters': True, |
|
145 |
}, |
|
146 |
], |
|
147 |
}, |
|
104 | 148 |
] |
105 | 149 |
} |
106 | 150 |
) |
107 | 151 | |
108 | 152 | |
109 | 153 |
class FormsCountView(RestrictedView): |
154 |
formdef_class = FormDef |
|
155 |
formpage_class = FormPage |
|
156 |
has_global_count_support = True |
|
157 |
label = _('Forms Count') |
|
158 | ||
110 | 159 |
def get(self, request, *args, **kwargs): |
111 | 160 |
time_interval = request.GET.get('time_interval', 'month') |
112 | 161 |
totals_kwargs = { |
... | ... | |
115 | 164 |
'criterias': [], |
116 | 165 |
} |
117 | 166 |
category_slug = request.GET.get('category', '_all') |
118 |
formdef_slug = request.GET.get('form', '_all') |
|
167 |
formdef_slug = request.GET.get('form', '_all' if self.has_global_count_support else '_nothing')
|
|
119 | 168 |
group_by = request.GET.get('group-by') |
120 | 169 |
subfilters = [] |
121 | 170 |
if formdef_slug != '_all': |
122 | 171 |
try: |
123 |
formdef = FormDef.get_by_urlname(formdef_slug, ignore_migration=True)
|
|
172 |
formdef = self.formdef_class.get_by_urlname(formdef_slug, ignore_migration=True)
|
|
124 | 173 |
except KeyError: |
125 | 174 |
return HttpResponseBadRequest('invalid form') |
126 |
form_page = FormPage(formdef=formdef, update_breadcrumbs=False)
|
|
175 |
form_page = self.formpage_class(formdef=formdef, update_breadcrumbs=False)
|
|
127 | 176 | |
177 |
# formdef_klass is a fake criteria, it will be used in time interval functions |
|
178 |
# to switch to appropriate class, it must appear before formdef_id. |
|
179 |
totals_kwargs['criterias'].append(Equal('formdef_klass', self.formdef_class)) |
|
128 | 180 |
totals_kwargs['criterias'].append(Equal('formdef_id', formdef.id)) |
129 | 181 |
totals_kwargs['criterias'].extend(self.get_filters_criterias(formdef, form_page)) |
130 | 182 |
if group_by: |
... | ... | |
161 | 213 | |
162 | 214 |
if 'group_by' not in totals_kwargs: |
163 | 215 |
x_labels = [x[0] for x in totals] |
164 |
series = [{'label': _('Forms Count'), 'data': [x[1] for x in totals]}]
|
|
216 |
series = [{'label': self.label, 'data': [x[1] for x in totals]}]
|
|
165 | 217 |
else: |
166 | 218 |
x_labels, series = self.get_grouped_data(totals, group_by_field, formdef, form_page) |
167 | 219 | |
... | ... | |
307 | 359 |
] |
308 | 360 |
series.sort(key=lambda x: x['label'].lower()) |
309 | 361 |
return x_labels, series |
362 | ||
363 | ||
364 |
class CardsCountView(FormsCountView): |
|
365 |
formdef_class = CardDef |
|
366 |
formpage_class = CardPage |
|
367 |
has_global_count_support = False |
|
368 |
label = _('Cards Count') |
wcs/urls.py | ||
---|---|---|
51 | 51 |
statistics_views.FormsCountView.as_view(), |
52 | 52 |
name='api-statistics-forms-count', |
53 | 53 |
), |
54 |
url( |
|
55 |
r'^api/statistics/cards/count/$', |
|
56 |
statistics_views.CardsCountView.as_view(), |
|
57 |
name='api-statistics-cards-count', |
|
58 |
), |
|
54 | 59 |
# provide django.contrib.auth view names for compatibility with |
55 | 60 |
# templates created for classic django applications. |
56 | 61 |
url(r'^login/$', compat.quixote, name='auth_login'), |
57 |
- |