Projet

Général

Profil

0001-dataviz-add-week-filters-55417.patch

Valentin Deniaud, 15 juillet 2021 10:56

Télécharger (13 ko)

Voir les différences:

Subject: [PATCH] dataviz: add week filters (#55417)

 combo/apps/dataviz/forms.py                   |  1 +
 .../migrations/0012_auto_20201126_1557.py     |  1 +
 .../migrations/0016_auto_20201215_1624.py     | 10 +--
 combo/apps/dataviz/models.py                  | 73 ++++++++++++++-----
 tests/test_dataviz.py                         | 45 ++++++++++--
 5 files changed, 99 insertions(+), 31 deletions(-)
combo/apps/dataviz/forms.py
56 56
class ChartNgForm(forms.ModelForm):
57 57
    blank_choice = ('', '---------')
58 58
    time_intervals = (
59
        ('_week', _('Week')),
59 60
        ('_month', _('Month')),
60 61
        ('_year', _('Year')),
61 62
        ('_weekday', _('Week day')),
combo/apps/dataviz/migrations/0012_auto_20201126_1557.py
46 46
                related_name='cells',
47 47
                to='dataviz.Statistic',
48 48
                verbose_name='Data',
49
                help_text='This list may take a few seconds to be updated, please refresh the page if an item is missing.',
49 50
            ),
50 51
        ),
51 52
    ]
combo/apps/dataviz/migrations/0016_auto_20201215_1624.py
4 4

  
5 5
from django.db import migrations, models
6 6

  
7
from combo.apps.dataviz.models import TIME_FILTERS
8

  
7 9

  
8 10
class Migration(migrations.Migration):
9 11

  
......
17 19
            name='time_range',
18 20
            field=models.CharField(
19 21
                blank=True,
20
                choices=[
21
                    ('current-year', 'Current year'),
22
                    ('previous-year', 'Previous year'),
23
                    ('current-month', 'Current month'),
24
                    ('previous-month', 'Previous month'),
25
                    ('range', 'Free range'),
26
                ],
22
                choices=TIME_FILTERS,
27 23
                max_length=20,
28 24
                verbose_name='Filtering (time)',
29 25
            ),
combo/apps/dataviz/models.py
22 22

  
23 23
import pygal
24 24
import pygal.util
25
from dateutil.relativedelta import relativedelta
25
from dateutil.relativedelta import MO, relativedelta
26 26
from django.conf import settings
27 27
from django.contrib.postgres.fields import JSONField
28 28
from django.db import models, transaction
......
149 149
        return (self.slug, self.site_slug, self.service_slug)
150 150

  
151 151

  
152
TIME_FILTERS = (
153
    ('previous-year', _('Previous year')),
154
    ('current-year', _('Current year')),
155
    ('next-year', _('Next year')),
156
    ('previous-month', _('Previous month')),
157
    ('current-month', _('Current month')),
158
    ('next-month', _('Next month')),
159
    ('previous-week', _('Previous week')),
160
    ('current-week', _('Current week')),
161
    ('next-week', _('Next week')),
162
    ('range', _('Free range')),
163
)
164

  
165

  
152 166
@register_cell_class
153 167
class ChartNgCell(CellBase):
154
    TIME_FILTERS = (
155
        ('current-year', _('Current year')),
156
        ('previous-year', _('Previous year')),
157
        ('current-month', _('Current month')),
158
        ('previous-month', _('Previous month')),
159
        ('range', _('Free range')),
160
    )
161

  
162 168
    statistic = models.ForeignKey(
163 169
        verbose_name=_('Data'),
164 170
        to=Statistic,
......
176 182
        _('Filtering (time)'),
177 183
        max_length=20,
178 184
        blank=True,
179
        choices=(
180
            ('current-year', _('Current year')),
181
            ('previous-year', _('Previous year')),
182
            ('current-month', _('Current month')),
183
            ('previous-month', _('Previous month')),
184
            ('range', _('Free range')),
185
        ),
185
        choices=TIME_FILTERS,
186 186
    )
187 187
    time_range_start = models.DateField(_('From'), null=True, blank=True)
188 188
    time_range_end = models.DateField(_('To'), null=True, blank=True)
......
368 368
        elif self.time_range == 'previous-year':
369 369
            params['start'] = date(year=now.year - 1, month=1, day=1)
370 370
            params['end'] = date(year=now.year, month=1, day=1)
371
        elif self.time_range == 'next-year':
372
            params['start'] = date(year=now.year + 1, month=1, day=1)
373
            params['end'] = date(year=now.year + 2, month=1, day=1)
371 374
        elif self.time_range == 'current-month':
372 375
            params['start'] = date(year=now.year, month=now.month, day=1)
373 376
        elif self.time_range == 'previous-month':
374 377
            params['start'] = date(year=now.year, month=now.month - 1, day=1)
375 378
            params['end'] = date(year=now.year, month=now.month, day=1)
379
        elif self.time_range == 'next-month':
380
            params['start'] = date(year=now.year, month=now.month + 1, day=1)
381
            params['end'] = date(year=now.year, month=now.month + 2, day=1)
382
        elif self.time_range == 'current-week':
383
            params['start'] = now + relativedelta(weekday=MO(-1))
384
            params['end'] = now + relativedelta(weekday=MO(+1), days=+1)
385
        elif self.time_range == 'previous-week':
386
            params['start'] = now + relativedelta(weekday=MO(-2))
387
            params['end'] = now + relativedelta(weekday=MO(-1))
388
        elif self.time_range == 'next-week':
389
            params['start'] = now + relativedelta(weekday=MO(+1), days=+1)
390
            params['end'] = now + relativedelta(weekday=MO(+2), days=+1)
376 391
        elif self.time_range == 'range':
377 392
            if self.time_range_start:
378 393
                params['start'] = self.time_range_start
......
571 586
        dates = [datetime.strptime(label, '%Y-%m-%d') for label in data['x_labels']]
572 587
        min_date, max_date = min(dates), max(dates)
573 588

  
589
        date_formats = {
590
            'day': 'd-m-Y',
591
            # Translators: This indicates week number followed by year, for example it can yield W2-2021.
592
            # First W is the first letter of the word "week" and should be translated accordingly, second W
593
            # and Y are interpreted by Django's date filter and should be left as is. First W is backslash
594
            # escaped to prevent it from being interpreted, translators should refer to Django's documentation
595
            # in order to know if the new letter resulting of translation should be escaped or not.
596
            '_week': gettext('\WW-Y'),
597
            '_month': 'm-Y',
598
            '_year': 'Y',
599
            '_weekday': 'l',
600
        }
574 601
        if interval == 'day':
575 602
            x_labels = [
576
                (min_date + timedelta(days=i)).strftime('%d-%m-%Y')
603
                format_date(min_date + timedelta(days=i), date_formats['day'])
577 604
                for i in range((max_date - min_date).days + 1)
578 605
            ]
579 606
        elif interval == '_month':
580 607
            month_difference = max_date.month - min_date.month + (max_date.year - min_date.year) * 12
581 608
            x_labels = [
582
                (min_date + relativedelta(months=i)).strftime('%m-%Y') for i in range(month_difference + 1)
609
                format_date(min_date + relativedelta(months=i), date_formats['_month'])
610
                for i in range(month_difference + 1)
583 611
            ]
584 612
        elif interval == '_year':
585 613
            x_labels = [str(year) for year in range(min_date.year, max_date.year + 1)]
586 614
        elif interval == '_weekday':
587 615
            x_labels = [str(label) for label in WEEKDAYS.values()]
616
        elif interval == '_week':
617
            week_difference = (
618
                max_date.isocalendar()[1] - min_date.isocalendar()[1] + (max_date.year - min_date.year) * 53
619
            )
620
            x_labels = [
621
                format_date(min_date + relativedelta(weeks=i), date_formats['_week'])
622
                for i in range(week_difference)
623
            ]
588 624

  
589 625
        aggregates = OrderedDict((label, [0] * len(series_data)) for label in x_labels)
590
        date_formats = {'day': 'd-m-Y', '_month': 'm-Y', '_year': 'Y', '_weekday': 'l'}
591 626
        for i, date in enumerate(dates):
592 627
            key = format_date(date, date_formats[interval])
593 628
            for j in range(len(series_data)):
tests/test_dataviz.py
1120 1120
        ('day', False, 'Day'),
1121 1121
        ('month', True, 'Month'),
1122 1122
        ('year', False, 'Year'),
1123
        ('_week', False, 'Week'),
1123 1124
        ('_weekday', False, 'Week day'),
1124 1125
    ]
1125 1126

  
......
1346 1347

  
1347 1348

  
1348 1349
@with_httmock(new_api_mock)
1349
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer):
1350
@pytest.mark.parametrize('date', ['2020-03-02 12:01', '2020-03-05 12:01'])  # Monday and Thursday
1351
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer, date):
1350 1352
    page = Page.objects.create(title='One', slug='index')
1351 1353
    cell = ChartNgCell(page=page, order=1, placeholder='content')
1352 1354
    cell.statistic = Statistic.objects.get(slug='one-serie')
......
1364 1366
    assert 'time_interval=month' in request.url
1365 1367
    assert 'ou=default' in request.url
1366 1368

  
1367
    freezer.move_to('2020-03-02 12:01')
1369
    freezer.move_to(date)
1368 1370
    cell.time_range = 'previous-year'
1369 1371
    cell.save()
1370 1372
    chart = cell.get_chart()
......
1373 1375
    assert 'ou=default' in request.url
1374 1376
    assert 'start=2019-01-01' in request.url and 'end=2020-01-01' in request.url
1375 1377

  
1378
    cell.time_range = 'current-week'
1379
    cell.save()
1380
    chart = cell.get_chart()
1381
    request = new_api_mock.call['requests'][-1]
1382
    assert 'start=2020-03-02' in request.url and 'end=2020-03-09' in request.url
1383

  
1384
    cell.time_range = 'previous-week'
1385
    cell.save()
1386
    chart = cell.get_chart()
1387
    request = new_api_mock.call['requests'][-1]
1388
    assert 'start=2020-02-24' in request.url and 'end=2020-03-02' in request.url
1389

  
1390
    cell.time_range = 'next-week'
1391
    cell.save()
1392
    chart = cell.get_chart()
1393
    request = new_api_mock.call['requests'][-1]
1394
    assert 'start=2020-03-09' in request.url and 'end=2020-03-16' in request.url
1395

  
1376 1396
    cell.time_range = 'range'
1377 1397
    cell.save()
1378 1398
    chart = cell.get_chart()
1379
    request = new_api_mock.call['requests'][3]
1399
    request = new_api_mock.call['requests'][-1]
1380 1400
    assert 'start' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1381 1401
    assert 'end' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1382 1402

  
1383 1403
    cell.time_range_start = '2020-10-01'
1384 1404
    cell.save()
1385 1405
    chart = cell.get_chart()
1386
    request = new_api_mock.call['requests'][4]
1406
    request = new_api_mock.call['requests'][-1]
1387 1407
    assert 'start=2020-10-01' in request.url
1388 1408

  
1389 1409
    cell.time_range_end = '2020-11-03'
1390 1410
    cell.save()
1391 1411
    chart = cell.get_chart()
1392
    request = new_api_mock.call['requests'][5]
1412
    request = new_api_mock.call['requests'][-1]
1393 1413
    assert 'start=2020-10-01' in request.url and 'end=2020-11-03' in request.url
1394 1414

  
1395 1415

  
......
1435 1455
    assert time_interval_field.value == 'day'
1436 1456
    assert time_interval_field.options == [
1437 1457
        ('day', True, 'Day'),
1458
        ('_week', False, 'Week'),
1438 1459
        ('_month', False, 'Month'),
1439 1460
        ('_year', False, 'Year'),
1440 1461
        ('_weekday', False, 'Week day'),
......
1486 1507
        ([16, 3, 0, 0, 0, 0, 0], {'title': 'Serie 1'}),
1487 1508
        ([1, 4, 0, 0, 0, 0, 0], {'title': 'Serie 2'}),
1488 1509
    ]
1510

  
1511
    time_interval_field.value = '_week'
1512
    resp.form.submit()
1513
    cell.refresh_from_db()
1514

  
1515
    chart = cell.get_chart()
1516
    assert 'time_interval=day' in new_api_mock.call['requests'][1].url
1517
    assert len(chart.x_labels) == 70
1518
    assert chart.x_labels[:3] == ['W41-2020', 'W42-2020', 'W43-2020']
1519
    assert chart.x_labels[-6:] == ['W52-2021', 'W1-2022', 'W2-2022', 'W3-2022', 'W4-2022', 'W5-2022']
1520
    assert chart.raw_series == [
1521
        ([0, 1, 0, 0, 0, 0, 0, 0, 16] + [0] * 60 + [2], {'title': 'Serie 1'}),
1522
        ([2, 2, 0, 0, 0, 0, 0, 0, 1] + [0] * 61, {'title': 'Serie 2'}),
1523
    ]
1489
-