From a52491ea5ed34b5258dd1c35e7fefc80bf5d71ee Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Wed, 20 Apr 2022 16:00:32 +0200 Subject: [PATCH] dataviz: load table charts asynchronously (#64315) --- combo/apps/dataviz/models.py | 33 +----------- .../dataviz/templates/combo/chartngcell.html | 22 +++++++- combo/apps/dataviz/views.py | 30 ++++++++--- tests/test_dataviz.py | 52 +++++++------------ 4 files changed, 63 insertions(+), 74 deletions(-) diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py index 488b334b..df295b6e 100644 --- a/combo/apps/dataviz/models.py +++ b/combo/apps/dataviz/models.py @@ -36,7 +36,7 @@ from django.utils.functional import cached_property from django.utils.translation import gettext from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext -from requests.exceptions import HTTPError, RequestException +from requests.exceptions import RequestException from combo.data.library import register_cell_class from combo.data.models import CellBase, django_template_validator @@ -308,34 +308,6 @@ class ChartNgCell(CellBase): resp, not_found_code='statistic_data_not_found', invalid_code='statistic_url_invalid' ) - def get_cell_extra_context(self, context): - ctx = super().get_cell_extra_context(context) - if self.chart_type == 'table' and self.statistic and self.statistic.url: - self._context = context - try: - chart = self.get_chart(raise_if_not_cached=not (context.get('synchronous'))) - except UnsupportedDataSet: - ctx['table'] = '

%s

' % _('Unsupported dataset.') - except MissingVariable: - ctx['table'] = '

%s

' % _('Page variable not found.') - except TemplateSyntaxError: - ctx['table'] = '

%s

' % _('Syntax error in page variable.') - except VariableDoesNotExist: - ctx['table'] = '

%s

' % _('Cannot evaluate page variable.') - except HTTPError as e: - if e.response.status_code == 404: - ctx['table'] = '

%s

' % _('Visualization not found.') - else: - if not chart.raw_series: - ctx['table'] = '

%s

' % _('No data.') - else: - ctx['table'] = chart.render_table( - transpose=bool(chart.axis_count == 2), - total=getattr(chart, 'compute_sum', True), - ) - ctx['table'] = ctx['table'].replace('', '
') - return ctx - def get_statistic_data(self, raise_if_not_cached=False, invalidate_cache=False): return requests.get( self.statistic.url, @@ -485,9 +457,6 @@ class ChartNgCell(CellBase): @cached_property def request_context(self): - if hasattr(self, '_context'): - return Context(self._context) - if not hasattr(self, '_request'): raise MissingRequest diff --git a/combo/apps/dataviz/templates/combo/chartngcell.html b/combo/apps/dataviz/templates/combo/chartngcell.html index fb6b31f8..489ad98c 100644 --- a/combo/apps/dataviz/templates/combo/chartngcell.html +++ b/combo/apps/dataviz/templates/combo/chartngcell.html @@ -1,7 +1,27 @@ {% load i18n %} {% if cell.title %}

{{cell.title}}

{% endif %} {% if cell.chart_type == "table" %} -{{table|safe}} +
+ {% else %}
diff --git a/combo/apps/dataviz/views.py b/combo/apps/dataviz/views.py index 5720f780..0d53ef23 100644 --- a/combo/apps/dataviz/views.py +++ b/combo/apps/dataviz/views.py @@ -54,7 +54,7 @@ class DatavizGraphView(DetailView): def get(self, request, *args, **kwargs): form = ChartNgPartialForm(request.GET, instance=self.cell) if not form.is_valid(): - return self.svg_error(_('Wrong parameters.')) + return self.error(_('Wrong parameters.')) request.extra_context = {} if request.GET.get('ctx'): @@ -70,22 +70,36 @@ class DatavizGraphView(DetailView): height=int(request.GET['height']) if request.GET.get('height') else int(self.cell.height), ) except UnsupportedDataSet: - return self.svg_error(_('Unsupported dataset.')) + return self.error(_('Unsupported dataset.')) except MissingVariable: - return self.svg_error(_('Page variable not found.')) + return self.error(_('Page variable not found.')) except TemplateSyntaxError: - return self.svg_error(_('Syntax error in page variable.')) + return self.error(_('Syntax error in page variable.')) except VariableDoesNotExist: - return self.svg_error(_('Cannot evaluate page variable.')) + return self.error(_('Cannot evaluate page variable.')) except HTTPError as e: if e.response.status_code == 404: - return self.svg_error(_('Visualization not found.')) + return self.error(_('Visualization not found.')) else: - return self.svg_error(_('Unknown HTTP error: %s' % e)) + return self.error(_('Unknown HTTP error: %s' % e)) + + if self.cell.chart_type == 'table': + if not chart.raw_series: + return self.error(_('No data')) + + rendered = chart.render_table( + transpose=bool(chart.axis_count == 2), + total=getattr(chart, 'compute_sum', True), + ) + rendered = rendered.replace('
', '
') + return HttpResponse(rendered) return HttpResponse(chart.render(), content_type='image/svg+xml') - def svg_error(self, error_text): + def error(self, error_text): + if self.cell.chart_type == 'table': + return HttpResponse('

%s

' % error_text) + context = { 'width': self.request.GET.get('width', 200), 'text': error_text, diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py index 29ed6814..d66f2dba 100644 --- a/tests/test_dataviz.py +++ b/tests/test_dataviz.py @@ -1142,15 +1142,14 @@ def test_chartng_cell_view(app, normal_user, statistics): # table visualization cell.chart_type = 'table' cell.save() - resp = app.get('/') + resp = app.get(location, status=200) assert '' in resp.text # unsupported dataset cell.statistic = Statistic.objects.get(slug='seventh') cell.save() - resp = app.get(location) # get data in cache - resp = app.get('/') - assert 'Unsupported dataset' in resp.text + resp = app.get(location, status=200) + assert '

Unsupported dataset.

' in resp.text cell.chart_type = 'bar' cell.save() @@ -1161,8 +1160,7 @@ def test_chartng_cell_view(app, normal_user, statistics): cell.statistic = Statistic.objects.get(slug='eighth') cell.chart_type = 'table' cell.save() - resp = app.get(location) # get data in cache - resp = app.get('/') + resp = app.get(location, status=200) assert '' in resp.text assert '' in resp.text assert '' in resp.text @@ -1180,8 +1178,7 @@ def test_chartng_cell_view(app, normal_user, statistics): cell.statistic = Statistic.objects.get(slug='tenth') cell.chart_type = 'table' cell.save() - resp = app.get(location) # get data in cache - resp = app.get('/') + resp = app.get(location, status=200) assert '' in resp.text cell.chart_type = 'bar' @@ -1195,13 +1192,6 @@ def test_chartng_cell_view(app, normal_user, statistics): resp = app.get(location) assert 'not found' in resp.text - # cell with no statistic chosen - cell.chart_type = 'table' - cell.statistic = None - cell.save() - resp = app.get('/') - assert not 'cell' in resp.text - @with_httmock(new_api_mock) def test_chartng_cell_view_new_api(app, normal_user, new_api_statistics): @@ -1218,8 +1208,7 @@ def test_chartng_cell_view_new_api(app, normal_user, new_api_statistics): # table visualization cell.chart_type = 'table' cell.save() - resp = app.get(location) # populate cache - resp = app.get('/') + resp = app.get(location, status=200) assert '' in resp.text # deleted visualization @@ -1643,33 +1632,28 @@ def test_table_cell(app, admin_user, statistics): cell.save() location = '/api/dataviz/graph/%s/' % cell.id resp = app.get(location) - resp = app.get('/') assert resp.text.count('Total') == 1 cell.statistic = Statistic.objects.get(slug='second') cell.save() resp = app.get(location) - resp = app.get('/') assert resp.text.count('Total') == 1 cell.statistic = Statistic.objects.get(slug='third') cell.save() resp = app.get(location) - resp = app.get('/') assert '114' in resp.text assert resp.text.count('Total') == 2 cell.statistic = Statistic.objects.get(slug='fourth') cell.save() resp = app.get(location) - resp = app.get('/') assert resp.text.count('Total') == 0 # total of durations is not computed cell.statistic = Statistic.objects.get(slug='eighth') cell.save() resp = app.get(location) - resp = app.get('/') assert resp.text.count('Total') == 0 @@ -1682,18 +1666,19 @@ def test_table_cell_new_api(app, admin_user, new_api_statistics): cell.save() app = login(app) - resp = app.get('/') + location = '/api/dataviz/graph/%s/' % cell.id + resp = app.get(location) assert resp.text.count('Total') == 1 cell.statistic = Statistic.objects.get(slug='two-series') cell.save() - resp = app.get('/') + resp = app.get(location) assert '21' in resp.text assert resp.text.count('Total') == 2 cell.statistic = Statistic.objects.get(slug='no-data') cell.save() - resp = app.get('/') + resp = app.get(location) assert resp.text.count('Total') == 0 @@ -2009,7 +1994,7 @@ def test_chartng_cell_new_api_filter_params_page_variables(app, admin_user, new_ @with_httmock(new_api_mock) -def test_chartng_cell_new_api_filter_params_page_variables_table(new_api_statistics, nocache): +def test_chartng_cell_new_api_filter_params_page_variables_table(new_api_statistics, app, nocache): Page.objects.create(title='One', slug='index') page = Page.objects.create( title='One', @@ -2026,7 +2011,8 @@ def test_chartng_cell_new_api_filter_params_page_variables_table(new_api_statist cell.filter_params = {'service': 'chrono', 'ou': 'variable:foo'} cell.save() - cell.render({**page.extra_variables, 'synchronous': True}) + location = '/api/dataviz/graph/%s/' % cell.pk + app.get(location) request = new_api_mock.call['requests'][0] assert 'service=chrono' in request.url assert 'ou=bar' in request.url @@ -2035,25 +2021,25 @@ def test_chartng_cell_new_api_filter_params_page_variables_table(new_api_statist cell.filter_params = {'service': 'chrono', 'ou': 'variable:unknown'} cell.save() - content = cell.render({'synchronous': True}) + resp = app.get(location) assert len(new_api_mock.call['requests']) == 1 - assert 'Page variable not found.' in content + assert 'Page variable not found.' in resp.text # variable with invalid syntax cell.filter_params = {'service': 'chrono', 'ou': 'variable:syntax_error'} cell.save() - content = cell.render({**page.extra_variables, 'synchronous': True}) + resp = app.get(location) assert len(new_api_mock.call['requests']) == 1 - assert 'Syntax error in page variable.' in content + assert 'Syntax error in page variable.' in resp.text # variable with missing context cell.filter_params = {'service': 'chrono', 'ou': 'variable:subslug_dependant'} cell.save() - content = cell.render({**page.extra_variables, 'synchronous': True}) + resp = app.get(location) assert len(new_api_mock.call['requests']) == 1 - assert 'Cannot evaluate page variable.' in content + assert 'Cannot evaluate page variable.' in resp.text def test_dataviz_check_validity(nocache): -- 2.30.2
222Less than an hour1 day and 10 hours2 hours10.0%18