From 7a723a061f04d7ae2a3a8d434d3ef54d5fbb3cac Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Mon, 12 Dec 2022 15:12:53 +0100 Subject: [PATCH] dataviz: allow disabling filters in filters cell (#71655) --- combo/apps/dataviz/forms.py | 51 +++++++++++++++- .../0026_chartfilterscell_filters.py | 19 ++++++ combo/apps/dataviz/models.py | 11 +++- .../combo/chartfilterscell_form.html | 6 ++ tests/test_dataviz.py | 58 +++++++++++++++++++ 5 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 combo/apps/dataviz/migrations/0026_chartfilterscell_filters.py create mode 100644 combo/apps/dataviz/templates/combo/chartfilterscell_form.html diff --git a/combo/apps/dataviz/forms.py b/combo/apps/dataviz/forms.py index f16a2cdc..477579c4 100644 --- a/combo/apps/dataviz/forms.py +++ b/combo/apps/dataviz/forms.py @@ -28,7 +28,7 @@ from django.utils.translation import gettext_lazy as _ from combo.utils import cache_during_request, requests, spooler -from .models import ChartCell, ChartNgCell +from .models import ChartCell, ChartFiltersCell, ChartNgCell class ChartForm(forms.ModelForm): @@ -286,6 +286,7 @@ class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm): def __init__(self, *args, **kwargs): page = kwargs.pop('page') + filters_cell = kwargs.pop('filters_cell') filters_cell_id = kwargs.pop('filters_cell_id', None) super().__init__(*args, **kwargs) @@ -337,4 +338,52 @@ class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm): if 'time_range' in self.fields: self.update_time_range_choices(cell.statistic) + self.update_backoffice_filter_choices(filters_cell, dynamic_fields) + dynamic_fields = { + name: field for name, field in dynamic_fields.items() if filters_cell.filters[name]['enabled'] + } self.fields.update(dynamic_fields) + + @staticmethod + def update_backoffice_filter_choices(filters_cell, dynamic_fields): + # remove absent filters from cell configuration, except if it was disabled + filters_cell.filters = { + k: v for k, v in filters_cell.filters.items() if k in dynamic_fields or not v['enabled'] + } + + # add filters to cell configuration + for field_name, field in dynamic_fields.items(): + if not field_name in filters_cell.filters: + filters_cell.filters[field_name] = {'label': str(field.label), 'enabled': True} + continue + + filters_cell.save() + + +class ChartFiltersConfigForm(forms.ModelForm): + filters = forms.MultipleChoiceField( + label=_('Filters'), widget=forms.CheckboxSelectMultiple, required=False + ) + + class Meta: + model = ChartFiltersCell + fields = [] + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if not self.instance.filters: + del self.fields['filters'] + return + + self.initial['filters'] = [] + self.fields['filters'].choices = [] + for filter_id, config in self.instance.filters.items(): + self.fields['filters'].choices.append((filter_id, config['label'])) + + if config['enabled']: + self.initial['filters'].append(filter_id) + + def save(self, *args, **kwargs): + for filter_id in self.instance.filters: + self.instance.filters[filter_id]['enabled'] = bool(filter_id in self.cleaned_data['filters']) + return super().save(*args, **kwargs) diff --git a/combo/apps/dataviz/migrations/0026_chartfilterscell_filters.py b/combo/apps/dataviz/migrations/0026_chartfilterscell_filters.py new file mode 100644 index 00000000..aa0645c3 --- /dev/null +++ b/combo/apps/dataviz/migrations/0026_chartfilterscell_filters.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.26 on 2022-12-12 13:28 + +import django.contrib.postgres.fields.jsonb +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('dataviz', '0025_statistic_data_type'), + ] + + operations = [ + migrations.AddField( + model_name='chartfilterscell', + name='filters', + field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, verbose_name='Filters'), + ), + ] diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py index 4a347c99..156d8939 100644 --- a/combo/apps/dataviz/models.py +++ b/combo/apps/dataviz/models.py @@ -758,8 +758,11 @@ class ChartNgCell(CellBase): @register_cell_class class ChartFiltersCell(CellBase): + filters = JSONField(_('Filters'), default=dict) + title = _('Filters') default_template_name = 'combo/chart-filters.html' + manager_form_template = 'combo/chartfilterscell_form.html' max_one_by_page = True class Meta: @@ -777,9 +780,15 @@ class ChartFiltersCell(CellBase): ctx['form'] = ChartFiltersForm( data=context['request'].GET, page=self.page, + filters_cell=self, filters_cell_id=context['request'].GET['filters_cell_id'], ) else: - ctx['form'] = ChartFiltersForm(page=self.page) + ctx['form'] = ChartFiltersForm(page=self.page, filters_cell=self) return ctx + + def get_default_form_class(self): + from .forms import ChartFiltersConfigForm + + return ChartFiltersConfigForm diff --git a/combo/apps/dataviz/templates/combo/chartfilterscell_form.html b/combo/apps/dataviz/templates/combo/chartfilterscell_form.html new file mode 100644 index 00000000..0c92961c --- /dev/null +++ b/combo/apps/dataviz/templates/combo/chartfilterscell_form.html @@ -0,0 +1,6 @@ +{% load gadjo %} + +{% block cell-form %} + {{form|with_template}} +{% endblock %} + diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py index 4cee2506..541f968b 100644 --- a/tests/test_dataviz.py +++ b/tests/test_dataviz.py @@ -2705,6 +2705,64 @@ def test_chart_filters_cell_with_subfilters(new_api_statistics, app, admin_user, assert 'filter-menu' in resp.form.fields +@with_httmock(new_api_mock) +def test_chart_filters_cell_select_filters(new_api_statistics, app, admin_user, nocache): + page = Page.objects.create(title='One', slug='index') + filters_cell = ChartFiltersCell.objects.create(page=page, order=1, placeholder='content') + app = login(app) + + # no chart cell, filters cell configuration is empty + resp = app.get('/manage/pages/%s/' % page.id) + field_prefix = 'cdataviz_chartfilterscell-%s-' % filters_cell.id + assert field_prefix + 'filters' not in resp.form.fields + + # add chart cell + cell = ChartNgCell.objects.create(page=page, order=2, placeholder='content') + cell.statistic = Statistic.objects.get(slug='one-serie') + cell.save() + + resp = app.get('/') + assert len(resp.form.fields) == 7 + assert 'filter-ou' in resp.form.fields + assert 'filter-service' in resp.form.fields + assert 'filter-time_range' in resp.form.fields + + # filters cell configuration shows filters + resp = app.get('/manage/pages/%s/' % page.id) + assert field_prefix + 'filters' in resp.forms[0].fields + + # all filters are active + assert all(resp.forms[0].get(field_prefix + 'filters', index=i).checked for i in range(3)) + assert [resp.forms[0].get(field_prefix + 'filters', index=i).value for i in range(3)] == [ + 'ou', + 'service', + 'time_interval', + ] + + # disable OU filter + resp.forms[0].get(field_prefix + 'filters', index=0).checked = False + manager_submit_cell(resp.forms[0]) + + resp = app.get('/') + assert len(resp.form.fields) == 6 + assert 'filter-ou' not in resp.form.fields + assert 'filter-service' in resp.form.fields + assert 'filter-time_range' in resp.form.fields + + # choose other statistic with different filters + cell.statistic = Statistic.objects.get(slug='filter-multiple') + cell.save() + + # OU filter has been kept as it is disabled, but other disappeared + resp = app.get('/manage/pages/%s/' % page.id) + assert [resp.forms[0].get(field_prefix + 'filters', index=i).value for i in range(2)] == [ + None, + 'color', + ] + resp.forms[0].get(field_prefix + 'filters', index=0).checked = True + assert resp.forms[0].get(field_prefix + 'filters', index=0).value == 'ou' + + @with_httmock(new_api_mock) @pytest.mark.freeze_time('2021-10-06') def test_chartng_cell_api_view_get_parameters(app, normal_user, new_api_statistics, nocache): -- 2.35.1