Projet

Général

Profil

0001-dataviz-allow-setting-time-range-using-template-5761.patch

Valentin Deniaud, 07 octobre 2021 10:58

Télécharger (13,6 ko)

Voir les différences:

Subject: [PATCH] dataviz: allow setting time range using template (#57617)

 combo/apps/dataviz/forms.py                   | 30 ++++++++-
 .../migrations/0019_auto_20211006_1525.py     | 36 ++++++++++
 combo/apps/dataviz/models.py                  | 31 ++++++++-
 .../templates/combo/chartngcell_form.html     |  9 +++
 combo/context_processors.py                   |  1 -
 tests/test_dataviz.py                         | 67 +++++++++++++++++++
 6 files changed, 168 insertions(+), 6 deletions(-)
 create mode 100644 combo/apps/dataviz/migrations/0019_auto_20211006_1525.py
combo/apps/dataviz/forms.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import datetime
17 18
from collections import OrderedDict
18 19

  
19 20
from django import forms
20 21
from django.conf import settings
21 22
from django.db import transaction
22 23
from django.db.models import Q
24
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
23 25
from django.utils.translation import ugettext_lazy as _
24 26

  
25 27
from combo.utils import cache_during_request, requests, spooler
......
69 71
            'time_range',
70 72
            'time_range_start',
71 73
            'time_range_end',
74
            'time_range_start_template',
75
            'time_range_end_template',
72 76
            'chart_type',
73 77
            'height',
74 78
            'sort_order',
......
85 89

  
86 90
        field_ids = list(self._meta.fields)
87 91
        if not self.instance.statistic or self.instance.statistic.service_slug == 'bijoe':
88
            field_ids = [
89
                x for x in field_ids if x not in ('time_range', 'time_range_start', 'time_range_end')
90
            ]
92
            exclude = (
93
                'time_range',
94
                'time_range_start',
95
                'time_range_end',
96
                'time_range_start_template',
97
                'time_range_end_template',
98
            )
99
            field_ids = [x for x in field_ids if x not in exclude]
91 100

  
92 101
        stat_field = self.fields['statistic']
93 102
        if not self.instance.statistic:
......
142 151
        for choice in self.time_intervals:
143 152
            if choice[0].strip('_') not in choice_ids:
144 153
                self.fields['time_interval'].choices.append(choice)
154

  
155
    def clean(self):
156
        for template_field in ('time_range_start_template', 'time_range_end_template'):
157
            if not self.cleaned_data.get(template_field):
158
                continue
159
            context = {'now': datetime.datetime.now, 'today': datetime.datetime.now}
160
            try:
161
                date = Template('{{ %s|date:"Y-m-d" }}' % self.cleaned_data[template_field]).render(
162
                    Context(context)
163
                )
164
            except (VariableDoesNotExist, TemplateSyntaxError) as e:
165
                self.add_error(template_field, e)
166
            else:
167
                if not date:
168
                    self.add_error(template_field, _('Template does not evaluate to a valid date.'))
combo/apps/dataviz/migrations/0019_auto_20211006_1525.py
1
# Generated by Django 2.2.19 on 2021-10-06 13:25
2

  
3
from django.db import migrations, models
4

  
5
import combo.data.models
6

  
7

  
8
class Migration(migrations.Migration):
9

  
10
    dependencies = [
11
        ('dataviz', '0018_auto_20210723_1318'),
12
    ]
13

  
14
    operations = [
15
        migrations.AddField(
16
            model_name='chartngcell',
17
            name='time_range_end_template',
18
            field=models.CharField(
19
                blank=True,
20
                max_length=200,
21
                validators=[combo.data.models.django_template_validator],
22
                verbose_name='To',
23
            ),
24
        ),
25
        migrations.AddField(
26
            model_name='chartngcell',
27
            name='time_range_start_template',
28
            field=models.CharField(
29
                blank=True,
30
                max_length=200,
31
                validators=[combo.data.models.django_template_validator],
32
                verbose_name='From',
33
                help_text='Template code returning a date. For example, Monday in two weeks would be today|add_days:"14"|adjust_to_week_monday.',
34
            ),
35
        ),
36
    ]
combo/apps/dataviz/models.py
25 25
from django.conf import settings
26 26
from django.contrib.postgres.fields import JSONField
27 27
from django.db import models, transaction
28
from django.template import Context, Template
28 29
from django.template.defaultfilters import date as format_date
29 30
from django.urls import reverse
30 31
from django.utils import timezone
......
36 37
from requests.exceptions import HTTPError, RequestException
37 38

  
38 39
from combo.data.library import register_cell_class
39
from combo.data.models import CellBase
40
from combo.data.models import CellBase, django_template_validator
40 41
from combo.utils import get_templated_url, requests, spooler
41 42

  
42 43

  
......
158 159
    ('previous-week', _('Previous week')),
159 160
    ('current-week', _('Current week')),
160 161
    ('next-week', _('Next week')),
161
    ('range', _('Free range')),
162
    ('range', _('Free range (date)')),
163
    ('range-template', _('Free range (template)')),
162 164
)
163 165

  
164 166

  
......
185 187
    )
186 188
    time_range_start = models.DateField(_('From'), null=True, blank=True)
187 189
    time_range_end = models.DateField(_('To'), null=True, blank=True)
190
    time_range_start_template = models.CharField(
191
        _('From'),
192
        max_length=200,
193
        blank=True,
194
        validators=[django_template_validator],
195
        help_text=_(
196
            'Template code returning a date. For example, Monday in two weeks would be today|add_days:"14"|adjust_to_week_monday.'
197
        ),
198
    )
199
    time_range_end_template = models.CharField(
200
        _('To'),
201
        max_length=200,
202
        blank=True,
203
        validators=[django_template_validator],
204
    )
188 205
    chart_type = models.CharField(
189 206
        _('Chart Type'),
190 207
        max_length=20,
......
394 411
                params['start'] = self.time_range_start
395 412
            if self.time_range_end:
396 413
                params['end'] = self.time_range_end
414
        elif self.time_range == 'range-template':
415
            context = {'now': datetime.now, 'today': datetime.now}
416
            if self.time_range_start_template:
417
                params['start'] = Template('{{ %s|date:"Y-m-d" }}' % self.time_range_start_template).render(
418
                    Context(context)
419
                )
420
            if self.time_range_end_template:
421
                params['end'] = Template('{{ %s|date:"Y-m-d" }}' % self.time_range_end_template).render(
422
                    Context(context)
423
                )
397 424
        if 'time_interval' in params and params['time_interval'].startswith('_'):
398 425
            params['time_interval'] = 'day'
399 426
        return params
combo/apps/dataviz/templates/combo/chartngcell_form.html
11 11
	$(function () {
12 12
		start_field = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_start');
13 13
		end_field = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_end');
14
		start_field_template = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_start_template');
15
		end_field_template = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_end_template');
14 16
		$('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range').change(function() {
15 17
			if(this.value == 'range') {
16 18
				start_field.parent().show();
......
19 21
				start_field.parent().hide();
20 22
				end_field.parent().hide();
21 23
			}
24
			if(this.value == 'range_template') {
25
				start_field_template.parent().show();
26
				end_field_template.parent().show();
27
			} else {
28
				start_field_template.parent().hide();
29
				end_field_template.parent().hide();
30
			}
22 31
		}).change();
23 32
	});
24 33
</script>
combo/context_processors.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17

  
18 17
from django.conf import settings
19 18

  
20 19
from combo.apps.pwa.models import PwaSettings
tests/test_dataviz.py
1098 1098
    assert 'time_range' not in resp.form.fields
1099 1099
    assert 'time_range_start' not in resp.form.fields
1100 1100
    assert 'time_range_end' not in resp.form.fields
1101
    assert 'time_range_start_template' not in resp.form.fields
1102
    assert 'time_range_end_template_end' not in resp.form.fields
1101 1103

  
1102 1104
    cell.statistic = Statistic.objects.get(slug='example')
1103 1105
    cell.save()
......
1111 1113
    assert 'time_range' not in resp.form.fields
1112 1114
    assert 'time_range_start' not in resp.form.fields
1113 1115
    assert 'time_range_end' not in resp.form.fields
1116
    assert 'time_range_start_template' not in resp.form.fields
1117
    assert 'time_range_end_template_end' not in resp.form.fields
1114 1118

  
1115 1119
    cell.statistic = Statistic.objects.get(slug='unavailable-stat')
1116 1120
    cell.save()
......
1200 1204
    assert cell.time_range == ''
1201 1205

  
1202 1206

  
1207
@with_httmock(new_api_mock)
1208
@pytest.mark.freeze_time('2021-10-06')
1209
def test_chartng_cell_manager_new_api_time_range_templates(app, admin_user, new_api_statistics):
1210
    page = Page.objects.create(title='One', slug='index')
1211
    cell = ChartNgCell(page=page, order=1, placeholder='content')
1212
    cell.statistic = Statistic.objects.get(slug='one-serie')
1213
    cell.save()
1214

  
1215
    app = login(app)
1216
    resp = app.get('/manage/pages/%s/' % page.id)
1217
    field_prefix = 'cdataviz_chartngcell-%s-' % cell.id
1218

  
1219
    resp.form[field_prefix + 'time_range'] = 'range-template'
1220
    resp.form[field_prefix + 'time_range_start_template'] = 'today|add_days:"7"|adjust_to_week_monday'
1221
    resp.form[field_prefix + 'time_range_end_template'] = 'now|add_days:"14"|adjust_to_week_monday'
1222
    resp = resp.form.submit().follow()
1223
    cell.refresh_from_db()
1224
    assert cell.time_range == 'range-template'
1225
    assert cell.time_range_start_template == 'today|add_days:"7"|adjust_to_week_monday'
1226
    assert cell.time_range_end_template == 'now|add_days:"14"|adjust_to_week_monday'
1227

  
1228
    resp.form[field_prefix + 'time_range_start_template'] = ''
1229
    resp.form[field_prefix + 'time_range_end_template'] = ''
1230
    resp = resp.form.submit().follow()
1231
    cell.refresh_from_db()
1232
    assert cell.time_range_start_template == ''
1233
    assert cell.time_range_end_template == ''
1234

  
1235
    resp.form[field_prefix + 'time_range_start_template'] = 'xxx'
1236
    resp = resp.form.submit()
1237
    assert 'Template does not evaluate to a valid date.' in resp.text
1238

  
1239
    resp = app.get('/manage/pages/%s/' % page.id)
1240
    resp.form[field_prefix + 'time_range_start_template'] = 'today|xxx'
1241
    resp = resp.form.submit()
1242
    assert 'Invalid filter' in resp.text
1243

  
1244
    resp = app.get('/manage/pages/%s/' % page.id)
1245
    resp.form[field_prefix + 'time_range_start_template'] = 'today|date:xxx'
1246
    resp = resp.form.submit()
1247
    assert 'Failed lookup for key [xxx]' in resp.text
1248

  
1249

  
1203 1250
@with_httmock(new_api_mock)
1204 1251
def test_chartng_cell_manager_new_api_dynamic_fields(app, admin_user, new_api_statistics):
1205 1252
    page = Page.objects.create(title='One', slug='index')
......
1435 1482
    request = new_api_mock.call['requests'][-1]
1436 1483
    assert 'start=2020-10-01' in request.url and 'end=2020-11-03' in request.url
1437 1484

  
1485
    cell.time_range = 'range-template'
1486
    cell.save()
1487
    cell.get_chart()
1488
    request = new_api_mock.call['requests'][-1]
1489
    assert 'start' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1490
    assert 'end' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1491

  
1492
    cell.time_range_start_template = 'today|add_days:"7"|adjust_to_week_monday'
1493
    cell.save()
1494
    cell.get_chart()
1495
    request = new_api_mock.call['requests'][-1]
1496
    assert 'start=2020-03-09' in request.url
1497
    assert 'end' not in urllib.parse.parse_qs(urllib.parse.urlparse(request.url).query)
1498

  
1499
    cell.time_range_end_template = 'today|add_days:"14"|adjust_to_week_monday'
1500
    cell.save()
1501
    cell.get_chart()
1502
    request = new_api_mock.call['requests'][-1]
1503
    assert 'start=2020-03-09' in request.url and 'end=2020-03-16' in request.url
1504

  
1438 1505

  
1439 1506
@with_httmock(new_api_mock)
1440 1507
def test_chartng_cell_new_api_filter_params_month(new_api_statistics, nocache, freezer):
1441
-