Projet

Général

Profil

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

Valentin Deniaud, 06 juillet 2021 16:27

Télécharger (12,2 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                  | 68 +++++++++++++------
 tests/test_dataviz.py                         | 44 ++++++++++--
 5 files changed, 93 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)
......
364 364
        elif self.time_range == 'previous-year':
365 365
            params['start'] = date(year=now.year - 1, month=1, day=1)
366 366
            params['end'] = date(year=now.year, month=1, day=1)
367
        elif self.time_range == 'next-year':
368
            params['start'] = date(year=now.year + 1, month=1, day=1)
369
            params['end'] = date(year=now.year + 2, month=1, day=1)
367 370
        elif self.time_range == 'current-month':
368 371
            params['start'] = date(year=now.year, month=now.month, day=1)
369 372
        elif self.time_range == 'previous-month':
370 373
            params['start'] = date(year=now.year, month=now.month - 1, day=1)
371 374
            params['end'] = date(year=now.year, month=now.month, day=1)
375
        elif self.time_range == 'next-month':
376
            params['start'] = date(year=now.year, month=now.month + 1, day=1)
377
            params['end'] = date(year=now.year, month=now.month + 2, day=1)
378
        elif self.time_range == 'current-week':
379
            params['start'] = now + relativedelta(weekday=MO(-1))
380
            params['end'] = now + relativedelta(weekday=MO(+1), days=+1)
381
        elif self.time_range == 'previous-week':
382
            params['start'] = now + relativedelta(weekday=MO(-2))
383
            params['end'] = now + relativedelta(weekday=MO(-1))
384
        elif self.time_range == 'next-week':
385
            params['start'] = now + relativedelta(weekday=MO(+1), days=+1)
386
            params['end'] = now + relativedelta(weekday=MO(+2), days=+1)
372 387
        elif self.time_range == 'range':
373 388
            if self.time_range_start:
374 389
                params['start'] = self.time_range_start
......
567 582
        dates = [datetime.strptime(label, '%Y-%m-%d') for label in data['x_labels']]
568 583
        min_date, max_date = min(dates), max(dates)
569 584

  
585
        date_formats = {
586
            'day': 'd-m-Y',
587
            '_week': gettext('\WW-Y'),
588
            '_month': 'm-Y',
589
            '_year': 'Y',
590
            '_weekday': 'l',
591
        }
570 592
        if interval == 'day':
571 593
            x_labels = [
572
                (min_date + timedelta(days=i)).strftime('%d-%m-%Y')
594
                format_date(min_date + timedelta(days=i), date_formats['day'])
573 595
                for i in range((max_date - min_date).days + 1)
574 596
            ]
575 597
        elif interval == '_month':
576 598
            month_difference = max_date.month - min_date.month + (max_date.year - min_date.year) * 12
577 599
            x_labels = [
578
                (min_date + relativedelta(months=i)).strftime('%m-%Y') for i in range(month_difference + 1)
600
                format_date(min_date + relativedelta(months=i), date_formats['_month'])
601
                for i in range(month_difference + 1)
579 602
            ]
580 603
        elif interval == '_year':
581 604
            x_labels = [str(year) for year in range(min_date.year, max_date.year + 1)]
582 605
        elif interval == '_weekday':
583 606
            x_labels = [str(label) for label in WEEKDAYS.values()]
607
        elif interval == '_week':
608
            week_difference = (
609
                max_date.isocalendar()[1] - min_date.isocalendar()[1] + (max_date.year - min_date.year) * 53
610
            )
611
            x_labels = [
612
                format_date(min_date + relativedelta(weeks=i), date_formats['_week'])
613
                for i in range(week_difference)
614
            ]
584 615

  
585 616
        aggregates = OrderedDict((label, [0] * len(series_data)) for label in x_labels)
586
        date_formats = {'day': 'd-m-Y', '_month': 'm-Y', '_year': 'Y', '_weekday': 'l'}
587 617
        for i, date in enumerate(dates):
588 618
            key = format_date(date, date_formats[interval])
589 619
            for j in range(len(series_data)):
tests/test_dataviz.py
1341 1341

  
1342 1342

  
1343 1343
@with_httmock(new_api_mock)
1344
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer):
1344
@pytest.mark.parametrize('date', ['2020-03-02 12:01', '2020-03-05 12:01'])  # Monday and Thursday
1345
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer, date):
1345 1346
    page = Page.objects.create(title='One', slug='index')
1346 1347
    cell = ChartNgCell(page=page, order=1, placeholder='content')
1347 1348
    cell.statistic = Statistic.objects.get(slug='one-serie')
......
1359 1360
    assert 'time_interval=month' in request.url
1360 1361
    assert 'ou=default' in request.url
1361 1362

  
1362
    freezer.move_to('2020-03-02 12:01')
1363
    freezer.move_to(date)
1363 1364
    cell.time_range = 'previous-year'
1364 1365
    cell.save()
1365 1366
    chart = cell.get_chart()
......
1368 1369
    assert 'ou=default' in request.url
1369 1370
    assert 'start=2019-01-01' in request.url and 'end=2020-01-01' in request.url
1370 1371

  
1372
    cell.time_range = 'current-week'
1373
    cell.save()
1374
    chart = cell.get_chart()
1375
    request = new_api_mock.call['requests'][-1]
1376
    assert 'start=2020-03-02' in request.url and 'end=2020-03-09' in request.url
1377

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

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

  
1371 1390
    cell.time_range = 'range'
1372 1391
    cell.save()
1373 1392
    chart = cell.get_chart()
1374
    request = new_api_mock.call['requests'][3]
1393
    request = new_api_mock.call['requests'][-1]
1375 1394
    assert 'start' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1376 1395
    assert 'end' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1377 1396

  
1378 1397
    cell.time_range_start = '2020-10-01'
1379 1398
    cell.save()
1380 1399
    chart = cell.get_chart()
1381
    request = new_api_mock.call['requests'][4]
1400
    request = new_api_mock.call['requests'][-1]
1382 1401
    assert 'start=2020-10-01' in request.url
1383 1402

  
1384 1403
    cell.time_range_end = '2020-11-03'
1385 1404
    cell.save()
1386 1405
    chart = cell.get_chart()
1387
    request = new_api_mock.call['requests'][5]
1406
    request = new_api_mock.call['requests'][-1]
1388 1407
    assert 'start=2020-10-01' in request.url and 'end=2020-11-03' in request.url
1389 1408

  
1390 1409

  
......
1424 1443
    assert time_interval_field.value == 'day'
1425 1444
    assert time_interval_field.options == [
1426 1445
        ('day', True, 'Day'),
1446
        ('_week', False, 'Week'),
1427 1447
        ('_month', False, 'Month'),
1428 1448
        ('_year', False, 'Year'),
1429 1449
        ('_weekday', False, 'Week day'),
......
1475 1495
        ([16, 3, 0, 0, 0, 0, 0], {'title': 'Serie 1'}),
1476 1496
        ([1, 4, 0, 0, 0, 0, 0], {'title': 'Serie 2'}),
1477 1497
    ]
1498

  
1499
    time_interval_field.value = '_week'
1500
    resp.form.submit()
1501
    cell.refresh_from_db()
1502

  
1503
    chart = cell.get_chart()
1504
    assert 'time_interval=day' in new_api_mock.call['requests'][1].url
1505
    assert len(chart.x_labels) == 70
1506
    assert chart.x_labels[:3] == ['W41-2020', 'W42-2020', 'W43-2020']
1507
    assert chart.x_labels[-6:] == ['W52-2021', 'W1-2022', 'W2-2022', 'W3-2022', 'W4-2022', 'W5-2022']
1508
    assert chart.raw_series == [
1509
        ([0, 1, 0, 0, 0, 0, 0, 0, 16] + [0] * 60 + [2], {'title': 'Serie 1'}),
1510
        ([2, 2, 0, 0, 0, 0, 0, 0, 1] + [0] * 61, {'title': 'Serie 2'}),
1511
    ]
1478
-