Projet

Général

Profil

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

Valentin Deniaud, 19 mai 2022 15:44

Télécharger (11,3 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        | 17 ++++++++++
 .../dataviz/templates/combo/chartngcell.html  | 16 +++++-----
 combo/apps/dataviz/views.py                   | 21 +++++++++++-
 combo/public/static/js/combo.public.js        |  2 ++
 tests/test_dataviz.py                         | 32 +++++++++++++++++++
 7 files changed, 97 insertions(+), 10 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
......
275 276

  
276 277
    def __init__(self, *args, **kwargs):
277 278
        page = kwargs.pop('page')
279
        filters_cell_id = kwargs.pop('filters_cell_id', None)
278 280
        super().__init__(*args, **kwargs)
279 281

  
280 282
        chart_cells = list(ChartNgCell.objects.filter(page=page, statistic__isnull=False).order_by('order'))
......
282 284
            self.fields.clear()
283 285
            return
284 286

  
287
        if filters_cell_id:
288
            for cell in chart_cells:
289
                cell.subfilters = cache.get(cell.get_cache_key(filters_cell_id), cell.subfilters)
290

  
285 291
        first_cell = chart_cells[0]
286 292
        for field in self._meta.fields:
287 293
            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
23 23

  
24 24
<script>
25 25
  $(function () {
26
    if (!$('body').data('filters-cell-id')) {
27
      $('body').data('filters-cell-id', 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('filters-cell-id') + '&' + $(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
5 5
<script>
6 6
$(function() {
7 7
  var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context');
8
  var chart_filters_form = $('#chart-filters');
9 8
  $(window).on('combo:refresh-graphs', function() {
10 9
    qs = [];
11
    if(chart_filters_form)
12
        qs.push(chart_filters_form.serialize());
10
    if($('#chart-filters')) {
11
      qs.push($('#chart-filters').serialize());
12
      qs.push('filters_cell_id=' + $('body').data('filters-cell-id'));
13
    }
13 14
    if(extra_context)
14 15
        qs.push('ctx=' + extra_context);
15 16
    $.ajax({
......
30 31
$(function() {
31 32
  var last_width = 1;
32 33
  var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context');
33
  var chart_filters_form = $('#chart-filters');
34 34
  $(window).on('load resize gadjo:sidepage-toggled combo:resize-graphs', function() {
35 35
    var chart_cell = $('#chart-{{cell.id}}').parent();
36 36
    var new_width = Math.floor($(chart_cell).width());
37 37
    var ratio = new_width / last_width;
38 38
    var qs = '?width=' + new_width
39
    if(chart_filters_form)
40
        qs += '&' + chart_filters_form.serialize()
39
    if($('#chart-filters'))
40
        qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('filters-cell-id')
41 41
    if(extra_context)
42 42
        qs += '&ctx=' + extra_context
43 43
    if (ratio > 1.2 || ratio < 0.8) {
......
47 47
  }).trigger('combo:resize-graphs');
48 48
  $(window).on('combo:refresh-graphs', function() {
49 49
    var qs = '?width=' + last_width
50
    if(chart_filters_form)
51
        qs += '&' + chart_filters_form.serialize()
50
    if($('#chart-filters'))
51
        qs += '&' + $('#chart-filters').serialize() + '&filters_cell_id=' + $('body').data('filters-cell-id')
52 52
    if(extra_context)
53 53
        qs += '&ctx=' + extra_context
54 54
    $('#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(
126
                cell.get_cache_key(self.filters_cell_id), data.json()['data'].get('subfilters', []), 300
127
            )
128

  
110 129

  
111 130
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
2566 2566

  
2567 2567
    refresh_statistics_data(cell.pk)
2568 2568
    assert len(bijoe_mock.call['requests']) == 1
2569

  
2570

  
2571
@with_httmock(new_api_mock)
2572
def test_chart_filters_cell_dynamic_subfilters(new_api_statistics, app, admin_user):
2573
    page = Page.objects.create(title='One', slug='index')
2574
    ChartFiltersCell.objects.create(page=page, order=1, placeholder='content')
2575
    cell = ChartNgCell.objects.create(page=page, order=2, placeholder='content')
2576
    cell.statistic = Statistic.objects.get(slug='with-subfilter')
2577
    cell.save()
2578

  
2579
    app = login(app)
2580
    resp = app.get('/')
2581
    assert 'form' in resp.form.fields
2582
    assert 'menu' not in resp.form.fields
2583

  
2584
    # ensure choice exist
2585
    resp.form['form'] = 'food-request'
2586

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

  
2590
    # simulate filters cell ajax refresh after cell refresh
2591
    location = resp.pyquery('.chartfilterscell').attr('data-ajax-cell-url')
2592
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2593
    assert resp.form['form'].value == 'food-request'
2594
    assert 'menu' in resp.form.fields
2595

  
2596
    # check isolation between pages by modifying filters_cell_id
2597
    app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=contact&filters_cell_id=yyy')
2598
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2599
    assert 'form' in resp.form.fields
2600
    assert 'menu' in resp.form.fields
2569
-