From 287bd0156e7d7ff63132106c6c910718db8857f9 Mon Sep 17 00:00:00 2001 From: Valentin Deniaud Date: Mon, 12 Apr 2021 15:36:38 +0200 Subject: [PATCH] dataviz: add stacked bar percent chart type (#52845) --- .../migrations/0011_auto_20200813_1100.py | 1 + combo/apps/dataviz/models.py | 20 ++++++++++++++++ tests/test_dataviz.py | 24 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py b/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py index 0d823b02..5528e3fa 100644 --- a/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py +++ b/combo/apps/dataviz/migrations/0011_auto_20200813_1100.py @@ -45,6 +45,7 @@ class Migration(migrations.Migration): ('bar', 'Bar'), ('horizontal-bar', 'Horizontal Bar'), ('stacked-bar', 'Stacked Bar'), + ('stacked-bar-percent', 'Stacked Bar (%)'), ('line', 'Line'), ('pie', 'Pie'), ('dot', 'Dot'), diff --git a/combo/apps/dataviz/models.py b/combo/apps/dataviz/models.py index d6f7322d..13524584 100644 --- a/combo/apps/dataviz/models.py +++ b/combo/apps/dataviz/models.py @@ -190,6 +190,7 @@ class ChartNgCell(CellBase): ('bar', _('Bar')), ('horizontal-bar', _('Horizontal Bar')), ('stacked-bar', _('Stacked Bar')), + ('stacked-bar-percent', _('Stacked Bar (%)')), ('line', _('Line')), ('pie', _('Pie')), ('dot', _('Dot')), @@ -306,6 +307,7 @@ class ChartNgCell(CellBase): 'bar': pygal.Bar, 'horizontal-bar': pygal.HorizontalBar, 'stacked-bar': pygal.StackedBar, + 'stacked-bar-percent': pygal.StackedBar, 'line': pygal.Line, 'pie': pygal.Pie, 'dot': pygal.Dot, @@ -337,6 +339,9 @@ class ChartNgCell(CellBase): if data ] + if self.chart_type == 'stacked-bar-percent': + self.make_percent([serie['data'] for serie in data['series']]) + for serie in data['series']: chart.add(serie['label'], serie['data']) @@ -485,12 +490,16 @@ class ChartNgCell(CellBase): def add_data_to_chart(self, chart, data, y_labels): if self.chart_type != 'pie': + series_data = [] for i, serie_label in enumerate(y_labels): if chart.axis_count < 2: values = data else: values = [data[i][j] for j in range(len(chart.x_labels))] + series_data.append(values) chart.add(serie_label, values) + if self.chart_type == 'stacked-bar-percent': + self.make_percent(series_data) else: # pie, create a serie by data, to get different colours values = data @@ -529,3 +538,14 @@ class ChartNgCell(CellBase): elif measure == 'percent': percent_formatter = lambda x: '{:.1f}%'.format(x) return percent_formatter + + def make_percent(self, series_data): + for i, values in enumerate(zip(*series_data)): + sum_values = sum([v for v in values if v is not None]) + if sum_values == 0: + continue + + factor = 100 / sum_values + for values in series_data: + if values[i] is not None: + values[i] = round(values[i] * factor, 1) diff --git a/tests/test_dataviz.py b/tests/test_dataviz.py index f8794f6d..9b5ffab1 100644 --- a/tests/test_dataviz.py +++ b/tests/test_dataviz.py @@ -441,6 +441,18 @@ def test_chartng_cell(app, statistics): ([122, 114, 2, 33], {'title': u'bar'}), ] + # stacked bar with percent + cell.chart_type = 'stacked-bar-percent' + cell.save() + + chart = cell.get_chart() + assert chart.x_labels == ['web', 'mail', 'phone', 'email'] + assert chart.raw_series == [ + ([64.5, 54, 0, 61.6], {'title': u'foo'}), + ([35.5, 46, 100, 38.4], {'title': u'bar'}), + ] + assert all(x + y == 100 for x, y in zip(chart.raw_series[0][0], chart.raw_series[1][0])) + # single data point cell.chart_type = 'bar' cell.statistic = Statistic.objects.get(slug='fourth') @@ -533,6 +545,18 @@ def test_chartng_cell_new_api(app, new_api_statistics): assert chart.x_labels == ['2020-10', '2020-11', '2020-12'] assert chart.raw_series == [([None, 16, 2], {'title': 'Serie 1'}), ([2, 1, None], {'title': 'Serie 2'})] + # stacked bar with percent + cell.chart_type = 'stacked-bar-percent' + cell.save() + + chart = cell.get_chart() + assert chart.x_labels == ['2020-10', '2020-11', '2020-12'] + assert chart.raw_series == [ + ([None, 94.1, 100], {'title': 'Serie 1'}), + ([100, 5.9, None], {'title': 'Serie 2'}), + ] + assert all(x + y == 100 for x, y in zip(chart.raw_series[0][0], chart.raw_series[1][0]) if x and y) + cell.statistic = Statistic.objects.get(slug='no-data') cell.save() -- 2.20.1