diff --git a/combo/apps/dataviz/forms.py b/combo/apps/dataviz/forms.py
index 50f86f59..1701de0e 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
@@ -278,6 +279,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'))
@@ -285,6 +287,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 05c53689..a4f36dcc 100644
--- a/combo/apps/dataviz/models.py
+++ b/combo/apps/dataviz/models.py
@@ -738,6 +738,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):
@@ -756,5 +759,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/static/js/chartngcell.js b/combo/apps/dataviz/static/js/chartngcell.js
index 73ae1c36..71cf3b4f 100644
--- a/combo/apps/dataviz/static/js/chartngcell.js
+++ b/combo/apps/dataviz/static/js/chartngcell.js
@@ -1,7 +1,9 @@
function get_graph_querystring(extra_context, width=undefined) {
qs = [];
- if ($('#chart-filters'))
+ if ($('#chart-filters')) {
qs.push($('#chart-filters').serialize());
+ qs.push('filters_cell_id=' + $('body').data('filters-cell-id'));
+ }
if (extra_context)
qs.push('ctx=' + extra_context);
if (window.location.search)
diff --git a/combo/apps/dataviz/templates/combo/chart-filters.html b/combo/apps/dataviz/templates/combo/chart-filters.html
index 496dbb1b..3d47d893 100644
--- a/combo/apps/dataviz/templates/combo/chart-filters.html
+++ b/combo/apps/dataviz/templates/combo/chart-filters.html
@@ -23,6 +23,20 @@
diff --git a/combo/apps/dataviz/views.py b/combo/apps/dataviz/views.py
index 74bc2bea..5e1c3a88 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):
@@ -83,6 +89,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'))
@@ -106,5 +115,15 @@ 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', []), 300
+ )
+
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 f41e9d61..7291ee1d 100644
--- a/combo/public/static/js/combo.public.js
+++ b/combo/public/static/js/combo.public.js
@@ -4,7 +4,12 @@ function combo_load_cell(elem) {
var extra_context = $elem.data('extra-context');
$.support.cors = true; /* IE9 */
var qs;
- if (window.location.search) {
+ if (url.includes('?')) {
+ qs = '&';
+ if (window.location.search) {
+ qs += window.location.search.slice(1) + '&';
+ }
+ } else if (window.location.search) {
qs = window.location.search + '&';
} else {
qs = '?';
diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py
index d358149e..292402c5 100644
--- a/tests/test_dataviz.py
+++ b/tests/test_dataviz.py
@@ -2655,3 +2655,35 @@ def test_chartng_cell_subfilter_page_variable(new_api_statistics, app, admin_use
resp.form[field_prefix + 'form'] = 'contact'
manager_submit_cell(resp.form)
assert field_prefix + 'menu' in resp.form.fields
+
+
+@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 'filter-form' in resp.form.fields
+ assert 'filter-menu' not in resp.form.fields
+
+ # ensure choice exist
+ resp.form['filter-form'] = 'food-request'
+
+ # simulate chart cell ajax refresh on form submission
+ app.get('/api/dataviz/graph/%s/' % cell.pk + '?filter-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 + '?filter-form=food-request&filters_cell_id=xxx')
+ assert resp.form['filter-form'].value == 'food-request'
+ assert 'filter-menu' in resp.form.fields
+
+ # check isolation between pages by modifying filters_cell_id
+ app.get('/api/dataviz/graph/%s/' % cell.pk + '?filter-form=contact&filters_cell_id=yyy')
+ resp = app.get(location + '?filter-form=food-request&filters_cell_id=xxx')
+ assert 'filter-form' in resp.form.fields
+ assert 'filter-menu' in resp.form.fields