dataviz: add time range fields (#49248)

This commit is contained in:
Valentin Deniaud 2020-12-14 15:45:38 +01:00
parent 6fe79041bf
commit c492cf4d02
5 changed files with 156 additions and 5 deletions

View File

@ -47,8 +47,12 @@ class ChartNgForm(forms.ModelForm):
class Meta:
model = ChartNgCell
fields = ('title', 'statistic', 'chart_type', 'height', 'sort_order',
'hide_null_values')
fields = ('title', 'statistic', 'time_range','time_range_start', 'time_range_end', 'chart_type',
'height', 'sort_order', 'hide_null_values')
widgets = {
'time_range_start': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'time_range_end': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@ -61,6 +65,10 @@ class ChartNgForm(forms.ModelForm):
stat_field.queryset = stat_field.queryset.filter(Q(available=True) | Q(pk=self.instance.statistic.pk))
field_ids = list(self._meta.fields)
if self.instance.statistic.service_slug == 'bijoe':
field_ids = [
x for x in field_ids if x not in ('time_range', 'time_range_start', 'time_range_end')
]
field_insert_index = field_ids.index('statistic') + 1
for filter_ in reversed(self.instance.statistic.filters):
filter_id = filter_['id']
@ -82,6 +90,7 @@ class ChartNgForm(forms.ModelForm):
def save(self, *args, **kwargs):
if 'statistic' in self.changed_data:
self.instance.filter_params.clear()
self.instance.time_range = ''
else:
for filter_ in self.instance.statistic.filters:
field = filter_['id']

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-12-15 15:24
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0015_auto_20201202_1424'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='time_range',
field=models.CharField(blank=True, choices=[('current-year', 'Current year'), ('previous-year', 'Previous year'), ('current-month', 'Current month'), ('previous-month', 'Previous month'), ('range', 'Free range')], max_length=20, verbose_name='Filtering (time)'),
),
migrations.AddField(
model_name='chartngcell',
name='time_range_end',
field=models.DateField(blank=True, null=True, verbose_name='To'),
),
migrations.AddField(
model_name='chartngcell',
name='time_range_start',
field=models.DateField(blank=True, null=True, verbose_name='From'),
),
]

View File

@ -16,9 +16,11 @@
import copy
import os
from datetime import date
from django.urls import reverse
from django.db import models
from django.utils import timezone
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _, ungettext, gettext
from django.conf import settings
@ -137,11 +139,33 @@ class Statistic(models.Model):
@register_cell_class
class ChartNgCell(CellBase):
TIME_FILTERS = (
('current-year', _('Current year')),
('previous-year', _('Previous year')),
('current-month', _('Current month')),
('previous-month', _('Previous month')),
('range', _('Free range')),
)
statistic = models.ForeignKey(
verbose_name=_('Data'), to=Statistic, blank=False, null=True, on_delete=models.SET_NULL, related_name='cells'
)
filter_params = JSONField(default=dict)
title = models.CharField(_('Title'), max_length=150, blank=True)
time_range = models.CharField(
_('Filtering (time)'),
max_length=20,
blank=True,
choices=(
('current-year', _('Current year')),
('previous-year', _('Previous year')),
('current-month', _('Current month')),
('previous-month', _('Previous month')),
('range', _('Free range')),
)
)
time_range_start = models.DateField(_('From'), null=True, blank=True)
time_range_end = models.DateField(_('To'), null=True, blank=True)
chart_type = models.CharField(_('Chart Type'), max_length=20, default='bar',
choices=(
('bar', _('Bar')),
@ -215,7 +239,7 @@ class ChartNgCell(CellBase):
def get_chart(self, width=None, height=None, raise_if_not_cached=False):
response = requests.get(
self.statistic.url,
params=self.filter_params,
params=self.get_filter_params(),
cache_duration=300,
remote_service='auto',
without_user=True,
@ -267,6 +291,26 @@ class ChartNgCell(CellBase):
return chart
def get_filter_params(self):
params = self.filter_params.copy()
now = timezone.now().date()
if self.time_range == 'current-year':
params['start'] = date(year=now.year, month=1, day=1)
elif self.time_range == 'previous-year':
params['start'] = date(year=now.year - 1, month=1, day=1)
params['end'] = date(year=now.year, month=1, day=1)
elif self.time_range == 'current-month':
params['start'] = date(year=now.year, month=now.month, day=1)
elif self.time_range == 'previous-month':
params['start'] = date(year=now.year, month=now.month - 1, day=1)
params['end'] = date(year=now.year, month=now.month, day=1)
elif self.time_range == 'range':
if self.time_range_start:
params['start'] = self.time_range_start
if self.time_range_end:
params['end'] = self.time_range_end
return params
def parse_response(self, response, chart):
# normalize axis to have a fake axis when there are no dimensions and
# always a x axis when there is a single dimension.

View File

@ -6,3 +6,19 @@
</div>
{% endif %}
</div>
<script>
$(function () {
start_field = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_start');
end_field = $('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range_end');
$('#id_cdataviz_chartngcell-{{ cell.pk }}-time_range').change(function() {
if(this.value == 'range') {
start_field.parent().show();
end_field.parent().show();
} else {
start_field.parent().hide();
end_field.parent().hide();
}
}).change();
});
</script>

View File

@ -2,7 +2,7 @@ import json
import mock
import pytest
from datetime import timedelta
from datetime import timedelta, date
from httmock import HTTMock, with_httmock, remember_called, urlmatch
from requests.exceptions import HTTPError
@ -931,6 +931,9 @@ def test_chartng_cell_manager(app, admin_user, statistics):
assert statistics_field.value == str(cell.statistic.pk)
assert statistics_field.options[1][2] == 'test: eighth visualization (duration)'
assert not 'Unavailable Stat' in resp.text
assert 'time_range' not in resp.form.fields
assert 'time_range_start' not in resp.form.fields
assert 'time_range_end' not in resp.form.fields
cell.statistic = Statistic.objects.get(slug='unavailable-stat')
cell.save()
@ -986,6 +989,27 @@ def test_chartng_cell_manager_new_api(app, admin_user, new_api_statistics):
cell.refresh_from_db()
assert cell.filter_params == {'time_interval': 'month'}
resp.form[field_prefix + 'time_range'] = 'previous-year'
resp = resp.form.submit().follow()
cell.refresh_from_db()
assert cell.time_range == 'previous-year'
resp.form[field_prefix + 'time_range'] = 'range'
resp.form[field_prefix + 'time_range_start'] = '2020-10-01'
resp.form[field_prefix + 'time_range_end'] = '2020-11-03'
resp = resp.form.submit().follow()
cell.refresh_from_db()
assert cell.time_range == 'range'
assert cell.time_range_start == date(year=2020, month=10, day=1)
assert cell.time_range_end == date(year=2020, month=11, day=3)
resp.form[field_prefix + 'time_range_start'] = ''
resp.form[field_prefix + 'time_range_end'] = ''
resp = resp.form.submit().follow()
cell.refresh_from_db()
assert cell.time_range_start is None
assert cell.time_range_end is None
no_filters_stat = Statistic.objects.get(slug='two-series')
resp.form[field_prefix + 'statistic'] = no_filters_stat.pk
resp = resp.form.submit().follow()
@ -994,6 +1018,7 @@ def test_chartng_cell_manager_new_api(app, admin_user, new_api_statistics):
assert field_prefix + 'ou' not in resp.form.fields
cell.refresh_from_db()
assert cell.filter_params == {}
assert cell.time_range == ''
@with_httmock(new_api_mock)
@ -1198,7 +1223,7 @@ def test_dataviz_api_list_statistics(new_api_statistics, settings):
@with_httmock(new_api_mock)
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache):
def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache, freezer):
page = Page.objects.create(title='One', slug='index')
cell = ChartNgCell(page=page, order=1, placeholder='content')
cell.statistic = Statistic.objects.get(slug='one-serie')
@ -1215,3 +1240,30 @@ def test_chartng_cell_new_api_filter_params(new_api_statistics, nocache):
request = new_api_mock.call['requests'][1]
assert 'time_interval=day' in request.url
assert 'ou=default' in request.url
freezer.move_to('2020-03-02 12:01')
cell.time_range = 'previous-year'
cell.save()
chart = cell.get_chart()
request = new_api_mock.call['requests'][2]
assert 'time_interval=day' in request.url
assert 'ou=default' in request.url
assert 'start=2019-01-01' in request.url and 'end=2020-01-01' in request.url
cell.time_range = 'range'
cell.save()
chart = cell.get_chart()
request = new_api_mock.call['requests'][3]
assert 'start' not in request.url and 'end' not in request.url
cell.time_range_start = '2020-10-01'
cell.save()
chart = cell.get_chart()
request = new_api_mock.call['requests'][4]
assert 'start=2020-10-01' in request.url
cell.time_range_end = '2020-11-03'
cell.save()
chart = cell.get_chart()
request = new_api_mock.call['requests'][5]
assert 'start=2020-10-01' in request.url and 'end=2020-11-03' in request.url