api: filter statistics by extra_data (#55424)

This commit is contained in:
Valentin Deniaud 2021-07-07 11:30:56 +02:00
parent 68b0691daa
commit c0e2726e67
2 changed files with 90 additions and 19 deletions

View File

@ -2235,6 +2235,12 @@ class StatisticsList(APIView):
agenda_options = [{'id': '_all', 'label': _('All')}] + [
{'id': x.slug, 'label': x.label} for x in agendas
]
booking_check_filters = set()
for agenda in Agenda.objects.exclude(booking_check_filters=''):
booking_check_filters.update(agenda.get_booking_check_filters())
group_by_options = [{'id': 'user_was_present', 'label': _('Presence/Absence')}] + [
{'id': x, 'label': x.capitalize()} for x in sorted(list(booking_check_filters))
]
return Response(
{
'data': [
@ -2264,6 +2270,12 @@ class StatisticsList(APIView):
'required': False,
'default': '_all',
},
{
'id': 'group_by',
'label': _('Group by'),
'options': group_by_options,
'required': False,
},
],
}
]
@ -2280,6 +2292,7 @@ class StatisticsFiltersSerializer(serializers.Serializer):
end = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d'])
category = serializers.SlugField(required=False, allow_blank=False, max_length=256)
agenda = serializers.SlugField(required=False, allow_blank=False, max_length=256)
group_by = serializers.SlugField(required=False, allow_blank=False, max_length=256)
class BookingsStatistics(APIView):
@ -2309,28 +2322,56 @@ class BookingsStatistics(APIView):
bookings = bookings.filter(event__agenda__slug=data['agenda'])
bookings = bookings.annotate(day=TruncDay('event__start_datetime'))
bookings = bookings.values('day', 'user_was_present').annotate(total=Count('id')).order_by('day')
bookings_by_day = collections.OrderedDict()
for booking in bookings:
totals_by_presence = bookings_by_day.setdefault(booking['day'], {})
totals_by_presence[booking['user_was_present']] = booking['total']
if 'group_by' not in data:
bookings = bookings.values('day').annotate(total=Count('id')).order_by('day')
days = [booking['day'] for booking in bookings]
if bookings:
series = [{'label': _('Bookings Count'), 'data': [booking['total'] for booking in bookings]}]
else:
series = []
else:
group_by = data['group_by']
if group_by not in ('user_was_present',):
group_by = 'extra_data__%s' % group_by
bookings = bookings.values('day', group_by).annotate(total=Count('id')).order_by('day')
bookings_by_presence = {None: [], True: [], False: []}
for bookings in bookings_by_day.values():
for presence, data in bookings_by_presence.items():
data.append(bookings.get(presence))
days = bookings_by_day = collections.OrderedDict(
# day1: {group1: total_11, group2: total_12},
# day2: {group1: total_21}
)
seen_group_values = set(
# group1, group2
)
for booking in bookings:
totals_by_group = bookings_by_day.setdefault(booking['day'], {})
group_value = booking[group_by]
totals_by_group[group_value] = booking['total']
seen_group_values.add(group_value)
labels = {None: _('Booked'), True: _('Present'), False: _('Absent')}
series = [{'label': labels[k], 'data': data} for k, data in bookings_by_presence.items() if any(data)]
bookings_by_group = {
# group1: [total_11, total_21],
# group2: [total_12, None],
}
for group in seen_group_values:
bookings_by_group[group] = [bookings.get(group) for bookings in bookings_by_day.values()]
if len(series) == 1 and series[0]['label'] == _('Booked'):
series[0]['label'] = _('Bookings Count')
if group_by == 'user_was_present':
labels = {None: _('Booked'), True: _('Present'), False: _('Absent')}
series = [
{'label': labels[k], 'data': data} for k, data in bookings_by_group.items() if any(data)
]
else:
series = [
{'label': k or _('None'), 'data': data}
for k, data in bookings_by_group.items()
if any(data)
]
return Response(
{
'data': {
'x_labels': [day.strftime('%Y-%m-%d') for day in bookings_by_day],
'x_labels': [day.strftime('%Y-%m-%d') for day in days],
'series': series,
},
'err': 0,

View File

@ -9,8 +9,8 @@ pytestmark = pytest.mark.django_db
def test_statistics_list(app, user):
Agenda.objects.create(label='Foo bar')
Agenda.objects.create(label='Bar foo')
Agenda.objects.create(label='Foo bar', booking_check_filters='menu,allergies')
Agenda.objects.create(label='Bar foo', booking_check_filters='menu,special')
Category.objects.create(label='Category A')
Category.objects.create(label='Category B')
@ -23,6 +23,13 @@ def test_statistics_list(app, user):
assert len(category_filter['options']) == 3
agenda_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'agenda'][0]
assert len(agenda_filter['options']) == 3
group_by_filter = [x for x in resp.json['data'][0]['filters'] if x['id'] == 'group_by'][0]
assert group_by_filter['options'] == [
{'id': 'user_was_present', 'label': 'Presence/Absence'},
{'id': 'allergies', 'label': 'Allergies'},
{'id': 'menu', 'label': 'Menu'},
{'id': 'special', 'label': 'Special'},
]
def test_statistics_bookings(app, user, freezer):
@ -88,12 +95,35 @@ def test_statistics_bookings(app, user, freezer):
event4 = Event.objects.create(start_datetime=now().replace(month=11, day=1), places=5, agenda=agenda)
Booking.objects.create(event=event4, user_was_present=True)
resp = app.get(url)
assert resp.json['data'] == {
resp = app.get(url + '?group_by=user_was_present')
data = resp.json['data']
data['series'].sort(key=lambda x: x['label'])
assert data == {
'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
'series': [
{'label': 'Absent', 'data': [None, None, 5, None]},
{'label': 'Booked', 'data': [10, 1, 1, None]},
{'label': 'Present', 'data': [None, None, 5, 1]},
{'label': 'Absent', 'data': [None, None, 5, None]},
],
}
# any booking check filter
agenda.booking_check_filters = 'menu'
agenda.save()
for i in range(9):
Booking.objects.create(event=event3 if i % 2 else event4, extra_data={'menu': 'vegetables'})
for i in range(5):
Booking.objects.create(event=event3 if i % 2 else event4, extra_data={'menu': 'meet'})
resp = app.get(url + '?group_by=menu')
data = resp.json['data']
data['series'].sort(key=lambda x: x['label'])
assert data == {
'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
'series': [
{'label': 'None', 'data': [10, 1, 11, 1]},
{'label': 'meet', 'data': [None, None, 2, 3]},
{'label': 'vegetables', 'data': [None, None, 4, 5]},
],
}