Projet

Général

Profil

0001-dataviz-reload-chart-filters-cell-to-reflect-subfilt.patch

Valentin Deniaud, 10 mai 2022 17:58

Télécharger (10,8 ko)

Voir les différences:

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(-)
combo/apps/dataviz/forms.py
19 19

  
20 20
from django import forms
21 21
from django.conf import settings
22
from django.core.cache import cache
22 23
from django.db import transaction
23 24
from django.db.models import Q
24 25
from django.db.models.fields import BLANK_CHOICE_DASH
......
254 255

  
255 256
    def __init__(self, *args, **kwargs):
256 257
        page = kwargs.pop('page')
258
        filters_cell_id = kwargs.pop('filters_cell_id', None)
257 259
        super().__init__(*args, **kwargs)
258 260

  
259 261
        chart_cells = list(ChartNgCell.objects.filter(page=page, statistic__isnull=False).order_by('order'))
......
261 263
            self.fields.clear()
262 264
            return
263 265

  
266
        if filters_cell_id:
267
            for cell in chart_cells:
268
                cell.subfilters = cache.get(cell.get_cache_key(filters_cell_id), cell.subfilters)
269

  
264 270
        first_cell = chart_cells[0]
265 271
        for field in self._meta.fields:
266 272
            self.fields[field].initial = getattr(first_cell, field)
combo/apps/dataviz/models.py
727 727
            self.filter_params = {k: v for k, v in self.filter_params.items() if k in subfilter_ids}
728 728
            self.save()
729 729

  
730
    def get_cache_key(self, filters_cell_id):
731
        return 'dataviz:%s:%s' % (filters_cell_id, self.pk)
732

  
730 733

  
731 734
@register_cell_class
732 735
class ChartFiltersCell(CellBase):
......
745 748
        from .forms import ChartFiltersForm
746 749

  
747 750
        ctx = super().get_cell_extra_context(context)
748
        ctx['form'] = ChartFiltersForm(page=self.page)
751
        if 'filters_cell_id' in context['request'].GET:  # detect refresh on submit
752
            ctx['form'] = ChartFiltersForm(
753
                data=context['request'].GET,
754
                page=self.page,
755
                filters_cell_id=context['request'].GET['filters_cell_id'],
756
            )
757
        else:
758
            ctx['form'] = ChartFiltersForm(page=self.page)
759

  
749 760
        return ctx
combo/apps/dataviz/templates/combo/chart-filters.html
1
{% load i18n %}
1
{% load i18n gadjo %}
2 2

  
3 3
{% block cell-content %}
4 4
<h2>{{ cell.title }}</h2>
......
23 23

  
24 24
<script>
25 25
  $(function () {
26
    if(!$('body').data('dataviz-uuid')) {
27
      $('body').data('dataviz-uuid', Math.random().toString(36).slice(2, 7));
28

  
29
      var loaded_cell_count = 0;
30
      document.querySelectorAll('div.chartngcell embed').forEach(graph => {
31
        graph.addEventListener('load', function() {
32
            if(++loaded_cell_count == $('div.chartngcell embed').length) {
33
              combo_load_cell($('.chart-filters-cell'));
34
              loaded_cell_count = 0;
35
          }
36
        });
37
      });
38
    }
39

  
26 40
    start_field = $('#id_time_range_start');
27 41
    end_field = $('#id_time_range_end');
28 42
    $('#id_time_range').change(function() {
......
37 51
    $('#chart-filters').submit(function(e) {
38 52
      e.preventDefault();
39 53
      $(window).trigger('combo:refresh-graphs');
54
      chart_cell = $(this).parents('.cell');
55
      new_url = chart_cell.data('ajax-cell-url')  + '?filters_cell_id=' + $('body').data('dataviz-uuid') + '&' + $(this).serialize()
56
      chart_cell.data('ajax-cell-url', new_url);
40 57
    });
41 58
  });
42 59
</script>
combo/apps/dataviz/templates/combo/chartngcell.html
30 30
$(function() {
31 31
  var last_width = 1;
32 32
  var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context');
33
  var chart_filters_form = $('#chart-filters');
34 33
  $(window).on('load resize gadjo:sidepage-toggled combo:resize-graphs', function() {
35 34
    var chart_cell = $('#chart-{{cell.id}}').parent();
36 35
    var new_width = Math.floor($(chart_cell).width());
37 36
    var ratio = new_width / last_width;
38 37
    var qs = '?width=' + new_width
39
    if(chart_filters_form)
40
        qs += '&' + chart_filters_form.serialize()
38
    if($('#chart-filters'))
39
        qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('dataviz-uuid')
41 40
    if(extra_context)
42 41
        qs += '&ctx=' + extra_context
43 42
    if (ratio > 1.2 || ratio < 0.8) {
......
47 46
  }).trigger('combo:resize-graphs');
48 47
  $(window).on('combo:refresh-graphs', function() {
49 48
    var qs = '?width=' + last_width
50
    if(chart_filters_form)
51
        qs += '&' + chart_filters_form.serialize()
49
    if($('#chart-filters'))
50
        qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('dataviz-uuid')
52 51
    if(extra_context)
53 52
        qs += '&ctx=' + extra_context
54 53
    $('#chart-{{cell.id}}').attr('src', "{% url 'combo-dataviz-graph' cell=cell.id %}" + qs);
combo/apps/dataviz/views.py
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17 17
from django.core import signing
18
from django.core.cache import cache
18 19
from django.core.exceptions import PermissionDenied
19 20
from django.http import Http404, HttpResponse, HttpResponseBadRequest
20 21
from django.shortcuts import render
......
24 25
from django.views.generic import DetailView
25 26
from requests.exceptions import HTTPError
26 27

  
27
from combo.utils import get_templated_url, requests
28
from combo.utils import NothingInCacheException, get_templated_url, requests
28 29

  
29 30
from .forms import ChartNgPartialForm
30 31
from .models import ChartNgCell, Gauge, MissingVariable, UnsupportedDataSet
......
42 43

  
43 44
    def dispatch(self, request, *args, **kwargs):
44 45
        self.cell = self.get_object()
46
        self.filters_cell_id = request.GET.get('filters_cell_id')
45 47

  
46 48
        if not self.cell.page.is_visible(request.user):
47 49
            raise PermissionDenied()
......
50 52
        if not self.cell.statistic or not self.cell.statistic.url:
51 53
            raise Http404('misconfigured cell')
52 54

  
55
        if self.filters_cell_id:
56
            self.cell.subfilters = cache.get(
57
                self.cell.get_cache_key(self.filters_cell_id), self.cell.subfilters
58
            )
53 59
        return super().dispatch(request, *args, **kwargs)
54 60

  
55 61
    def get(self, request, *args, **kwargs):
......
84 90
            else:
85 91
                return self.error(_('Unknown HTTP error: %s' % e))
86 92

  
93
        if self.filters_cell_id:
94
            self.update_subfilters_cache(form.instance)
95

  
87 96
        if self.cell.chart_type == 'table':
88 97
            if not chart.raw_series:
89 98
                return self.error(_('No data'))
......
107 116
        }
108 117
        return render(self.request, 'combo/dataviz-error.svg', context=context, content_type='image/svg+xml')
109 118

  
119
    def update_subfilters_cache(self, cell):
120
        try:
121
            data = cell.get_statistic_data(raise_if_not_cached=True)
122
        except NothingInCacheException:
123
            pass  # should not happen
124
        else:
125
            cache.set(cell.get_cache_key(self.filters_cell_id), data.json()['data'].get('subfilters', []))
126

  
110 127

  
111 128
dataviz_graph = xframe_options_sameorigin(DatavizGraphView.as_view())
combo/public/static/js/combo.public.js
6 6
  var qs;
7 7
  if (window.location.search) {
8 8
    qs = window.location.search + '&';
9
  } else if(url.includes('?')) {
10
    qs = '&';
9 11
  } else {
10 12
    qs = '?';
11 13
  }
tests/test_dataviz.py
2495 2495

  
2496 2496
    refresh_statistics_data(cell.pk)
2497 2497
    assert len(bijoe_mock.call['requests']) == 1
2498

  
2499

  
2500
@with_httmock(new_api_mock)
2501
def test_chart_filters_cell_dynamic_subfilters(new_api_statistics, app, admin_user):
2502
    page = Page.objects.create(title='One', slug='index')
2503
    ChartFiltersCell.objects.create(page=page, order=1, placeholder='content')
2504
    cell = ChartNgCell.objects.create(page=page, order=2, placeholder='content')
2505
    cell.statistic = Statistic.objects.get(slug='with-subfilter')
2506
    cell.save()
2507

  
2508
    app = login(app)
2509
    resp = app.get('/')
2510
    assert 'form' in resp.form.fields
2511
    assert 'menu' not in resp.form.fields
2512

  
2513
    # ensure choice exist
2514
    resp.form['form'] = 'food-request'
2515

  
2516
    # simulate chart cell ajax refresh on form submission
2517
    app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=food-request&filters_cell_id=xxx')
2518

  
2519
    # simulate filters cell ajax refresh after cell refresh
2520
    location = resp.pyquery('.chartfilterscell').attr('data-ajax-cell-url')
2521
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2522
    assert resp.form['form'].value == 'food-request'
2523
    assert 'menu' in resp.form.fields
2524

  
2525
    # check isolation between pages by modifying filters_cell_id
2526
    app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=contact&filters_cell_id=yyy')
2527
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2528
    assert 'form' in resp.form.fields
2529
    assert 'menu' in resp.form.fields
2498
-