From bd847d85f32203ebab99ef1e9630e7baf8586707 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Wed, 27 Apr 2022 18:03:08 +0200 Subject: [PATCH] dataviz: reload chart filters cell to reflect subfilters (#62533) --- combo/apps/dataviz/forms.py | 6 ++++ combo/apps/dataviz/models.py | 13 +++++++- .../templates/combo/chart-filters.html | 19 ++++++++++- .../dataviz/templates/combo/chartngcell.html | 9 +++--- combo/apps/dataviz/views.py | 19 ++++++++++- combo/public/static/js/combo.public.js | 2 ++ tests/test_dataviz.py | 32 +++++++++++++++++++ 7 files changed, 92 insertions(+), 8 deletions(-) diff --git a/combo/apps/dataviz/forms.py b/combo/apps/dataviz/forms.py index 84bf9a22..46d00e17 100644 --- a/combo/apps/dataviz/forms.py +++ b/combo/apps/dataviz/forms.py @@ -19,6 +19,7 @@ from collections import OrderedDict from django import forms from django.conf import settings +from django.core.cache import cache from django.db import transaction from django.db.models import Q from django.db.models.fields import BLANK_CHOICE_DASH @@ -254,6 +255,7 @@ class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm): def __init__(self, *args, **kwargs): page = kwargs.pop('page') + filters_cell_id = kwargs.pop('filters_cell_id', None) super().__init__(*args, **kwargs) chart_cells = list(ChartNgCell.objects.filter(page=page, statistic__isnull=False).order_by('order')) @@ -261,6 +263,10 @@ class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm): self.fields.clear() return + if filters_cell_id: + for cell in chart_cells: + cell.subfilters = cache.get(cell.get_cache_key(filters_cell_id), cell.subfilters) + first_cell = chart_cells[0] for field in self._meta.fields: self.fields[field].initial = getattr(first_cell, field) diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py index df295b6e..108281f8 100644 --- a/combo/apps/dataviz/models.py +++ b/combo/apps/dataviz/models.py @@ -727,6 +727,9 @@ class ChartNgCell(CellBase): self.filter_params = {k: v for k, v in self.filter_params.items() if k in subfilter_ids} self.save() + def get_cache_key(self, filters_cell_id): + return 'dataviz:%s:%s' % (filters_cell_id, self.pk) + @register_cell_class class ChartFiltersCell(CellBase): @@ -745,5 +748,13 @@ class ChartFiltersCell(CellBase): from .forms import ChartFiltersForm ctx = super().get_cell_extra_context(context) - ctx['form'] = ChartFiltersForm(page=self.page) + if 'filters_cell_id' in context['request'].GET: # detect refresh on submit + ctx['form'] = ChartFiltersForm( + data=context['request'].GET, + page=self.page, + filters_cell_id=context['request'].GET['filters_cell_id'], + ) + else: + ctx['form'] = ChartFiltersForm(page=self.page) + return ctx diff --git a/combo/apps/dataviz/templates/combo/chart-filters.html b/combo/apps/dataviz/templates/combo/chart-filters.html index 496dbb1b..ad7eb3d4 100644 --- a/combo/apps/dataviz/templates/combo/chart-filters.html +++ b/combo/apps/dataviz/templates/combo/chart-filters.html @@ -1,4 +1,4 @@ -{% load i18n %} +{% load i18n gadjo %} {% block cell-content %}

{{ cell.title }}

@@ -23,6 +23,20 @@ diff --git a/combo/apps/dataviz/templates/combo/chartngcell.html b/combo/apps/dataviz/templates/combo/chartngcell.html index 489ad98c..9154f8b2 100644 --- a/combo/apps/dataviz/templates/combo/chartngcell.html +++ b/combo/apps/dataviz/templates/combo/chartngcell.html @@ -30,14 +30,13 @@ $(function() { $(function() { var last_width = 1; var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context'); - var chart_filters_form = $('#chart-filters'); $(window).on('load resize gadjo:sidepage-toggled combo:resize-graphs', function() { var chart_cell = $('#chart-{{cell.id}}').parent(); var new_width = Math.floor($(chart_cell).width()); var ratio = new_width / last_width; var qs = '?width=' + new_width - if(chart_filters_form) - qs += '&' + chart_filters_form.serialize() + if($('#chart-filters')) + qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('dataviz-uuid') if(extra_context) qs += '&ctx=' + extra_context if (ratio > 1.2 || ratio < 0.8) { @@ -47,8 +46,8 @@ $(function() { }).trigger('combo:resize-graphs'); $(window).on('combo:refresh-graphs', function() { var qs = '?width=' + last_width - if(chart_filters_form) - qs += '&' + chart_filters_form.serialize() + if($('#chart-filters')) + qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('dataviz-uuid') if(extra_context) qs += '&ctx=' + extra_context $('#chart-{{cell.id}}').attr('src', "{% url 'combo-dataviz-graph' cell=cell.id %}" + qs); diff --git a/combo/apps/dataviz/views.py b/combo/apps/dataviz/views.py index 6bdfc2af..5347a718 100644 --- a/combo/apps/dataviz/views.py +++ b/combo/apps/dataviz/views.py @@ -15,6 +15,7 @@ # along with this program. If not, see . from django.core import signing +from django.core.cache import cache from django.core.exceptions import PermissionDenied from django.http import Http404, HttpResponse, HttpResponseBadRequest from django.shortcuts import render @@ -24,7 +25,7 @@ from django.views.decorators.clickjacking import xframe_options_sameorigin from django.views.generic import DetailView from requests.exceptions import HTTPError -from combo.utils import get_templated_url, requests +from combo.utils import NothingInCacheException, get_templated_url, requests from .forms import ChartNgPartialForm from .models import ChartNgCell, Gauge, MissingVariable, UnsupportedDataSet @@ -42,6 +43,7 @@ class DatavizGraphView(DetailView): def dispatch(self, request, *args, **kwargs): self.cell = self.get_object() + self.filters_cell_id = request.GET.get('filters_cell_id') if not self.cell.page.is_visible(request.user): raise PermissionDenied() @@ -50,6 +52,10 @@ class DatavizGraphView(DetailView): if not self.cell.statistic or not self.cell.statistic.url: raise Http404('misconfigured cell') + if self.filters_cell_id: + self.cell.subfilters = cache.get( + self.cell.get_cache_key(self.filters_cell_id), self.cell.subfilters + ) return super().dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): @@ -84,6 +90,9 @@ class DatavizGraphView(DetailView): else: return self.error(_('Unknown HTTP error: %s' % e)) + if self.filters_cell_id: + self.update_subfilters_cache(form.instance) + if self.cell.chart_type == 'table': if not chart.raw_series: return self.error(_('No data')) @@ -107,5 +116,13 @@ class DatavizGraphView(DetailView): } return render(self.request, 'combo/dataviz-error.svg', context=context, content_type='image/svg+xml') + def update_subfilters_cache(self, cell): + try: + data = cell.get_statistic_data(raise_if_not_cached=True) + except NothingInCacheException: + pass # should not happen + else: + cache.set(cell.get_cache_key(self.filters_cell_id), data.json()['data'].get('subfilters', [])) + dataviz_graph = xframe_options_sameorigin(DatavizGraphView.as_view()) diff --git a/combo/public/static/js/combo.public.js b/combo/public/static/js/combo.public.js index eb3ec073..d929a18f 100644 --- a/combo/public/static/js/combo.public.js +++ b/combo/public/static/js/combo.public.js @@ -6,6 +6,8 @@ function combo_load_cell(elem) { var qs; if (window.location.search) { qs = window.location.search + '&'; + } else if(url.includes('?')) { + qs = '&'; } else { qs = '?'; } diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py index d66f2dba..961a157f 100644 --- a/tests/test_dataviz.py +++ b/tests/test_dataviz.py @@ -2495,3 +2495,35 @@ def test_spooler_refresh_statistics_data_bijoe(statistics): refresh_statistics_data(cell.pk) assert len(bijoe_mock.call['requests']) == 1 + + +@with_httmock(new_api_mock) +def test_chart_filters_cell_dynamic_subfilters(new_api_statistics, app, admin_user): + page = Page.objects.create(title='One', slug='index') + ChartFiltersCell.objects.create(page=page, order=1, placeholder='content') + cell = ChartNgCell.objects.create(page=page, order=2, placeholder='content') + cell.statistic = Statistic.objects.get(slug='with-subfilter') + cell.save() + + app = login(app) + resp = app.get('/') + assert 'form' in resp.form.fields + assert 'menu' not in resp.form.fields + + # ensure choice exist + resp.form['form'] = 'food-request' + + # simulate chart cell ajax refresh on form submission + app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=food-request&filters_cell_id=xxx') + + # simulate filters cell ajax refresh after cell refresh + location = resp.pyquery('.chartfilterscell').attr('data-ajax-cell-url') + resp = app.get(location + '?form=food-request&filters_cell_id=xxx') + assert resp.form['form'].value == 'food-request' + assert 'menu' in resp.form.fields + + # check isolation between pages by modifying filters_cell_id + app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=contact&filters_cell_id=yyy') + resp = app.get(location + '?form=food-request&filters_cell_id=xxx') + assert 'form' in resp.form.fields + assert 'menu' in resp.form.fields -- 2.30.2