api: add support for statistics on cards (#62029)

This commit is contained in:
Frédéric Péters 2022-05-03 20:05:06 +02:00
parent 74673b9e25
commit bde49d22b7
4 changed files with 103 additions and 11 deletions

View File

@ -5,6 +5,7 @@ import pytest
from wcs import fields
from wcs.blocks import BlockDef
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.qommon.http_request import HTTPRequest
@ -129,7 +130,7 @@ def test_statistics_index_forms(pub):
resp = get_app(pub).get(sign_uri('/api/statistics/'))
form_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'form'][0]
assert form_filter['options'] == [
{'id': '_all', 'label': 'All'},
{'id': '_all', 'label': 'All Forms'},
{'id': 'test-1', 'label': 'test 1'},
{'id': 'test-2', 'label': 'test 2'},
]
@ -569,3 +570,25 @@ def test_statistics_forms_count_group_by(pub, formdef, anonymise):
# invalid field
resp = get_app(pub).get(sign_uri(url + '&group-by=xxx'))
assert resp.json['data']['series'] == [{'data': [16, 0, 4], 'label': 'Forms Count'}]
def test_statistics_cards_count(pub):
carddef = CardDef()
carddef.name = 'test 1'
carddef.fields = []
carddef.store()
carddef.data_class().wipe()
for _i in range(20):
carddata = carddef.data_class()()
carddata.just_created()
carddata.receipt_time = datetime.datetime(2021, 1, 1, 0, 0).timetuple()
carddata.store()
# apply (required) card filter
resp = get_app(pub).get(sign_uri('/api/statistics/cards/count/?form=%s' % carddef.url_name))
assert resp.json['data']['series'] == [{'data': [20], 'label': 'Cards Count'}]
assert resp.json['data']['x_labels'] == ['2021-01']
resp = get_app(pub).get(sign_uri('/api/statistics/cards/count/?card=%s' % 'invalid'), status=400)
assert resp.text == 'invalid form'

View File

@ -3801,13 +3801,18 @@ def get_period_query(
clause = [NotNull('receipt_time')]
table_name = 'wcs_all_forms'
if criterias:
from wcs.formdef import FormDef
formdef_class = FormDef
for criteria in criterias:
if criteria.__class__.__name__ == 'Equal' and criteria.attribute == 'formdef_klass':
formdef_class = criteria.value
continue
if 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
from wcs.formdef import FormDef
table_name = get_formdef_table_name(FormDef.get(criteria.value))
table_name = get_formdef_table_name(formdef_class.get(criteria.value))
continue
clause.append(criteria)
if period_start:

View File

@ -23,7 +23,9 @@ from quixote import get_publisher
from wcs import sql
from wcs.api_utils import is_url_signed
from wcs.backoffice.data_management import CardPage
from wcs.backoffice.management import FormPage
from wcs.carddef import CardDef
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.qommon import _, misc
@ -47,11 +49,14 @@ class IndexView(RestrictedView):
category_options = [{'id': '_all', 'label': C_('categories|All')}] + [
{'id': x.url_name, 'label': x.name} for x in categories
]
forms = FormDef.select()
forms = FormDef.select(lightweight=True)
forms.sort(key=lambda x: misc.simplify(x.name))
form_options = [{'id': '_all', 'label': _('All')}] + [
form_options = [{'id': '_all', 'label': _('All Forms')}] + [
{'id': x.url_name, 'label': x.name} for x in forms
]
cards = CardDef.select(lightweight=True)
cards.sort(key=lambda x: misc.simplify(x.name))
card_options = [{'id': x.url_name, 'label': x.name} for x in cards]
return JsonResponse(
{
'data': [
@ -100,13 +105,57 @@ class IndexView(RestrictedView):
'has_subfilters': True,
},
],
}
},
{
'name': _('Cards Count'),
'url': request.build_absolute_uri(reverse('api-statistics-cards-count')),
'id': 'cards_counts',
'filters': [
{
'id': 'time_interval',
'label': _('Interval'),
'options': [
{
'id': 'month',
'label': _('Month'),
},
{
'id': 'year',
'label': _('Year'),
},
{
'id': 'weekday',
'label': _('Week day'),
},
{
'id': 'hour',
'label': _('Hour'),
},
],
'required': True,
'default': 'month',
},
{
'id': 'form',
'label': _('Card'),
'options': card_options,
'required': True,
'default': '',
'has_subfilters': True,
},
],
},
]
}
)
class FormsCountView(RestrictedView):
formdef_class = FormDef
formpage_class = FormPage
has_global_count_support = True
label = _('Forms Count')
def get(self, request, *args, **kwargs):
time_interval = request.GET.get('time_interval', 'month')
totals_kwargs = {
@ -115,16 +164,19 @@ class FormsCountView(RestrictedView):
'criterias': [],
}
category_slug = request.GET.get('category', '_all')
formdef_slug = request.GET.get('form', '_all')
formdef_slug = request.GET.get('form', '_all' if self.has_global_count_support else '_nothing')
group_by = request.GET.get('group-by')
subfilters = []
if formdef_slug != '_all':
try:
formdef = FormDef.get_by_urlname(formdef_slug, ignore_migration=True)
formdef = self.formdef_class.get_by_urlname(formdef_slug, ignore_migration=True)
except KeyError:
return HttpResponseBadRequest('invalid form')
form_page = FormPage(formdef=formdef, update_breadcrumbs=False)
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))
totals_kwargs['criterias'].extend(self.get_filters_criterias(formdef, form_page))
if group_by:
@ -161,7 +213,7 @@ class FormsCountView(RestrictedView):
if 'group_by' not in totals_kwargs:
x_labels = [x[0] for x in totals]
series = [{'label': _('Forms Count'), 'data': [x[1] for x in totals]}]
series = [{'label': self.label, 'data': [x[1] for x in totals]}]
else:
x_labels, series = self.get_grouped_data(totals, group_by_field, formdef, form_page)
@ -307,3 +359,10 @@ class FormsCountView(RestrictedView):
]
series.sort(key=lambda x: x['label'].lower())
return x_labels, series
class CardsCountView(FormsCountView):
formdef_class = CardDef
formpage_class = CardPage
has_global_count_support = False
label = _('Cards Count')

View File

@ -51,6 +51,11 @@ urlpatterns = [
statistics_views.FormsCountView.as_view(),
name='api-statistics-forms-count',
),
url(
r'^api/statistics/cards/count/$',
statistics_views.CardsCountView.as_view(),
name='api-statistics-cards-count',
),
# provide django.contrib.auth view names for compatibility with
# templates created for classic django applications.
url(r'^login/$', compat.quixote, name='auth_login'),