dataviz: reload chart filters cell to reflect subfilters (#62533)
This commit is contained in:
parent
7f7d75c509
commit
b5132df5f5
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -23,6 +23,20 @@
|
|||
|
||||
<script>
|
||||
$(function () {
|
||||
if (!$('body').data('filters-cell-id')) {
|
||||
$('body').data('filters-cell-id', Math.random().toString(36).slice(2, 7));
|
||||
|
||||
var loaded_cell_count = 0;
|
||||
document.querySelectorAll('div.chartngcell embed').forEach(graph => {
|
||||
graph.addEventListener('load', function() {
|
||||
if (++loaded_cell_count == $('div.chartngcell embed').length) {
|
||||
combo_load_cell($('.chart-filters-cell'));
|
||||
loaded_cell_count = 0;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
start_field = $('#id_time_range_start');
|
||||
end_field = $('#id_time_range_end');
|
||||
$('#id_time_range').change(function() {
|
||||
|
@ -37,6 +51,9 @@
|
|||
$('#chart-filters').submit(function(e) {
|
||||
e.preventDefault();
|
||||
$(window).trigger('combo:refresh-graphs');
|
||||
chart_cell = $(this).parents('.cell');
|
||||
new_url = chart_cell.data('ajax-cell-url') + '?filters_cell_id=' + $('body').data('filters-cell-id') + '&' + $(this).serialize();
|
||||
chart_cell.data('ajax-cell-url', new_url);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
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())
|
||||
|
|
|
@ -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 = '?';
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue