api: filter statistics by extra_data (#55424)
This commit is contained in:
parent
68b0691daa
commit
c0e2726e67
|
@ -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,
|
||||
|
|
|
@ -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]},
|
||||
],
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue