Projet

Général

Profil

0001-api-allow-multiple-grouping-in-statistics-57817.patch

Valentin Deniaud, 13 octobre 2021 14:51

Télécharger (7,14 ko)

Voir les différences:

Subject: [PATCH] api: allow multiple grouping in statistics (#57817)

 chrono/api/serializers.py    |  4 +++-
 chrono/api/views.py          | 42 ++++++++++++++++++++++--------------
 tests/api/test_statistics.py | 25 +++++++++++++++++----
 3 files changed, 50 insertions(+), 21 deletions(-)
chrono/api/serializers.py
100 100
    end = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d'])
101 101
    category = serializers.SlugField(required=False, allow_blank=False, max_length=256)
102 102
    agenda = serializers.SlugField(required=False, allow_blank=False, max_length=256)
103
    group_by = serializers.SlugField(required=False, allow_blank=False, max_length=256)
103
    group_by = serializers.ListField(
104
        required=False, child=serializers.SlugField(allow_blank=False, max_length=256)
105
    )
104 106

  
105 107

  
106 108
class DateRangeSerializer(serializers.Serializer):
chrono/api/views.py
31 31
from django.utils.encoding import force_text
32 32
from django.utils.formats import date_format
33 33
from django.utils.timezone import localtime, make_aware, now
34
from django.utils.translation import gettext_noop
34
from django.utils.translation import gettext, gettext_noop
35 35
from django.utils.translation import ugettext_lazy as _
36 36
from django_filters import rest_framework as filters
37 37
from rest_framework import permissions, status
......
2460 2460
                                'label': _('Group by'),
2461 2461
                                'options': group_by_options,
2462 2462
                                'required': False,
2463
                                'multiple': True,
2463 2464
                            },
2464 2465
                        ],
2465 2466
                    }
......
2509 2510
                series = []
2510 2511
        else:
2511 2512
            group_by = data['group_by']
2512
            if group_by not in ('user_was_present',):
2513
                group_by = 'extra_data__%s' % group_by
2514
            bookings = bookings.values('day', group_by).annotate(total=Count('id')).order_by('day')
2513
            if not isinstance(group_by, list):  # legacy support
2514
                group_by = [group_by]
2515

  
2516
            lookups = [
2517
                'extra_data__%s' % field if field != 'user_was_present' else field for field in group_by
2518
            ]
2519
            bookings = bookings.values('day', *lookups).annotate(total=Count('id')).order_by('day')
2515 2520

  
2516 2521
            days = bookings_by_day = collections.OrderedDict(
2517 2522
                # day1: {group1: total_11, group2: total_12},
......
2522 2527
            )
2523 2528
            for booking in bookings:
2524 2529
                totals_by_group = bookings_by_day.setdefault(booking['day'], {})
2525
                group_value = booking[group_by]
2530
                group_value = tuple(booking[field] for field in lookups)
2526 2531
                totals_by_group[group_value] = booking['total']
2527 2532
                seen_group_values.add(group_value)
2528 2533

  
......
2533 2538
            for group in seen_group_values:
2534 2539
                bookings_by_group[group] = [bookings.get(group) for bookings in bookings_by_day.values()]
2535 2540

  
2536
            if group_by == 'user_was_present':
2537
                labels = {None: _('Booked'), True: _('Present'), False: _('Absent')}
2538
                series = [
2539
                    {'label': labels[k], 'data': data} for k, data in bookings_by_group.items() if any(data)
2540
                ]
2541
            else:
2542
                series = [
2543
                    {'label': k or _('None'), 'data': data}
2544
                    for k, data in bookings_by_group.items()
2545
                    if any(data)
2546
                ]
2541
            def build_label(group):
2542
                group_labels = []
2543
                for field, value in zip(group_by, group):
2544
                    if field == 'user_was_present':
2545
                        label = {None: gettext('Booked'), True: gettext('Present'), False: gettext('Absent')}[
2546
                            value
2547
                        ]
2548
                    else:
2549
                        label = value or gettext('None')
2550
                    group_labels.append(label)
2551
                return ' / '.join(group_labels)
2552

  
2553
            series = [
2554
                {'label': build_label(k), 'data': data} for k, data in bookings_by_group.items() if any(data)
2555
            ]
2556
            series.sort(key=lambda x: x['label'])
2547 2557

  
2548 2558
        return Response(
2549 2559
            {
tests/api/test_statistics.py
95 95

  
96 96
    resp = app.get(url + '?group_by=user_was_present')
97 97
    data = resp.json['data']
98
    data['series'].sort(key=lambda x: x['label'])
99 98
    assert data == {
100 99
        'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
101 100
        'series': [
......
110 109
    agenda.save()
111 110

  
112 111
    for i in range(9):
113
        Booking.objects.create(event=event3 if i % 2 else event4, extra_data={'menu': 'vegetables'})
112
        Booking.objects.create(
113
            event=event3 if i % 2 else event4, extra_data={'menu': 'vegetables'}, user_was_present=bool(i % 3)
114
        )
114 115
    for i in range(5):
115
        Booking.objects.create(event=event3 if i % 2 else event4, extra_data={'menu': 'meet'})
116
        Booking.objects.create(
117
            event=event3 if i % 2 else event4, extra_data={'menu': 'meet'}, user_was_present=bool(i % 3)
118
        )
116 119

  
117 120
    resp = app.get(url + '?group_by=menu')
118 121
    data = resp.json['data']
119
    data['series'].sort(key=lambda x: x['label'])
120 122
    assert data == {
121 123
        'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
122 124
        'series': [
......
125 127
            {'label': 'vegetables', 'data': [None, None, 4, 5]},
126 128
        ],
127 129
    }
130

  
131
    resp = app.get(url + '?group_by=user_was_present&group_by=menu')
132
    data = resp.json['data']
133
    assert data == {
134
        'x_labels': ['2020-10-10', '2020-10-15', '2020-10-25', '2020-11-01'],
135
        'series': [
136
            {'label': 'Absent / None', 'data': [None, None, 5, None]},
137
            {'label': 'Absent / meet', 'data': [None, None, 1, 1]},
138
            {'label': 'Absent / vegetables', 'data': [None, None, 1, 2]},
139
            {'label': 'Booked / None', 'data': [10, 1, 1, None]},
140
            {'label': 'Present / None', 'data': [None, None, 5, 1]},
141
            {'label': 'Present / meet', 'data': [None, None, 1, 2]},
142
            {'label': 'Present / vegetables', 'data': [None, None, 3, 3]},
143
        ],
144
    }
128
-