Projet

Général

Profil

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

Valentin Deniaud, 02 juin 2022 14:44

Télécharger (10 ko)

Voir les différences:

Subject: [PATCH 2/2] dataviz: reload chart filters cell to reflect subfilters
 (#62533)

 combo/apps/dataviz/forms.py                   |  6 ++++
 combo/apps/dataviz/models.py                  | 13 +++++++-
 combo/apps/dataviz/static/js/chartngcell.js   |  4 ++-
 .../templates/combo/chart-filters.html        | 17 ++++++++++
 combo/apps/dataviz/views.py                   | 21 +++++++++++-
 combo/public/static/js/combo.public.js        |  7 +++-
 tests/test_dataviz.py                         | 32 +++++++++++++++++++
 7 files changed, 96 insertions(+), 4 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
......
278 279

  
279 280
    def __init__(self, *args, **kwargs):
280 281
        page = kwargs.pop('page')
282
        filters_cell_id = kwargs.pop('filters_cell_id', None)
281 283
        super().__init__(*args, **kwargs)
282 284

  
283 285
        chart_cells = list(ChartNgCell.objects.filter(page=page, statistic__isnull=False).order_by('order'))
......
285 287
            self.fields.clear()
286 288
            return
287 289

  
290
        if filters_cell_id:
291
            for cell in chart_cells:
292
                cell.subfilters = cache.get(cell.get_cache_key(filters_cell_id), cell.subfilters)
293

  
288 294
        first_cell = chart_cells[0]
289 295
        for field in self._meta.fields:
290 296
            self.fields[field].initial = getattr(first_cell, field)
combo/apps/dataviz/models.py
734 734
            self.filter_params = {k: v for k, v in self.filter_params.items() if k in subfilter_ids}
735 735
            self.save()
736 736

  
737
    def get_cache_key(self, filters_cell_id):
738
        return 'dataviz:%s:%s' % (filters_cell_id, self.pk)
739

  
737 740

  
738 741
@register_cell_class
739 742
class ChartFiltersCell(CellBase):
......
752 755
        from .forms import ChartFiltersForm
753 756

  
754 757
        ctx = super().get_cell_extra_context(context)
755
        ctx['form'] = ChartFiltersForm(page=self.page)
758
        if 'filters_cell_id' in context['request'].GET:  # detect refresh on submit
759
            ctx['form'] = ChartFiltersForm(
760
                data=context['request'].GET,
761
                page=self.page,
762
                filters_cell_id=context['request'].GET['filters_cell_id'],
763
            )
764
        else:
765
            ctx['form'] = ChartFiltersForm(page=self.page)
766

  
756 767
        return ctx
combo/apps/dataviz/static/js/chartngcell.js
1 1
function get_graph_querystring(extra_context, width=undefined) {
2 2
  qs = [];
3
  if($('#chart-filters'))
3
  if($('#chart-filters')) {
4 4
    qs.push($('#chart-filters').serialize());
5
    qs.push('filters_cell_id=' + $('body').data('filters-cell-id'));
6
  }
5 7
  if(extra_context)
6 8
      qs.push('ctx=' + extra_context);
7 9
  if (window.location.search)
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/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):
......
83 89
            else:
84 90
                return self.error(_('Unknown HTTP error: %s' % e))
85 91

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

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

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

  
109 128

  
110 129
dataviz_graph = xframe_options_sameorigin(DatavizGraphView.as_view())
combo/public/static/js/combo.public.js
4 4
  var extra_context = $elem.data('extra-context');
5 5
  $.support.cors = true; /* IE9 */
6 6
  var qs;
7
  if (window.location.search) {
7
  if (url.includes('?')) {
8
    qs = '&';
9
    if (window.location.search) {
10
      qs += window.location.search.slice(1) + '&';
11
    }
12
  } else if (window.location.search) {
8 13
    qs = window.location.search + '&';
9 14
  } else {
10 15
    qs = '?';
tests/test_dataviz.py
2617 2617
    resp.form[field_prefix + 'form'] = 'food-request'
2618 2618
    manager_submit_cell(resp.form)
2619 2619
    assert field_prefix + 'menu' in resp.form.fields
2620

  
2621

  
2622
@with_httmock(new_api_mock)
2623
def test_chart_filters_cell_dynamic_subfilters(new_api_statistics, app, admin_user):
2624
    page = Page.objects.create(title='One', slug='index')
2625
    ChartFiltersCell.objects.create(page=page, order=1, placeholder='content')
2626
    cell = ChartNgCell.objects.create(page=page, order=2, placeholder='content')
2627
    cell.statistic = Statistic.objects.get(slug='with-subfilter')
2628
    cell.save()
2629

  
2630
    app = login(app)
2631
    resp = app.get('/')
2632
    assert 'form' in resp.form.fields
2633
    assert 'menu' not in resp.form.fields
2634

  
2635
    # ensure choice exist
2636
    resp.form['form'] = 'food-request'
2637

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

  
2641
    # simulate filters cell ajax refresh after cell refresh
2642
    location = resp.pyquery('.chartfilterscell').attr('data-ajax-cell-url')
2643
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2644
    assert resp.form['form'].value == 'food-request'
2645
    assert 'menu' in resp.form.fields
2646

  
2647
    # check isolation between pages by modifying filters_cell_id
2648
    app.get('/api/dataviz/graph/%s/' % cell.pk + '?form=contact&filters_cell_id=yyy')
2649
    resp = app.get(location + '?form=food-request&filters_cell_id=xxx')
2650
    assert 'form' in resp.form.fields
2651
    assert 'menu' in resp.form.fields
2620
-