dataviz: add sort and hide empty data options on chart cell (#45503)

This commit is contained in:
Nicolas Roche 2020-08-12 19:09:01 +02:00
parent 05f16b523a
commit ee8c7b2306
4 changed files with 345 additions and 1 deletions

View File

@ -43,7 +43,8 @@ class ChartForm(forms.ModelForm):
class ChartNgForm(forms.ModelForm):
class Meta:
model = ChartNgCell
fields = ('title', 'data_reference', 'chart_type', 'height')
fields = ('title', 'data_reference', 'chart_type', 'height', 'sort_order',
'hide_null_values')
def __init__(self, *args, **kwargs):
super(ChartNgForm, self).__init__(*args, **kwargs)

View File

@ -0,0 +1,35 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.18 on 2020-08-13 09:00
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0010_auto_20190328_1111'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='hide_null_values',
field=models.BooleanField(default=False, help_text='This setting only applies for one-dimensional charts.', verbose_name='Hide null values'),
),
migrations.AddField(
model_name='chartngcell',
name='sort_order',
field=models.CharField(choices=[('none', 'None'), ('alpha', 'Alphabetically'), ('asc', 'Increasing values'), ('desc', 'Decreasing values')], default='none', help_text='This setting only applies for one-dimensional charts.', max_length=5, verbose_name='Sort data'),
),
migrations.AlterField(
model_name='chartngcell',
name='chart_type',
field=models.CharField(choices=[('bar', 'Bar'), ('horizontal-bar', 'Horizontal Bar'), ('stacked-bar', 'Stacked Bar'), ('line', 'Line'), ('pie', 'Pie'), ('dot', 'Dot'), ('table', 'Table')], default='bar', max_length=20, verbose_name='Chart Type'),
),
migrations.AlterField(
model_name='chartngcell',
name='height',
field=models.CharField(choices=[('150', 'Short (150px)'), ('250', 'Average (250px)'), ('350', 'Tall (350px)')], default='250', max_length=20, verbose_name='Height'),
),
]

View File

@ -126,6 +126,18 @@ class ChartNgCell(CellBase):
('350', _('Tall (350px)')),
))
sort_order = models.CharField(_('Sort data'), max_length=5, default='none',
help_text=_('This setting only applies for one-dimensional charts.'),
choices=(
('none', _('None')),
('alpha', _('Alphabetically')),
('asc', _('Increasing values')),
('desc', _('Decreasing values')),
))
hide_null_values = models.BooleanField(default=False, verbose_name=_('Hide null values'),
help_text=_('This setting only applies for one-dimensional charts.'))
manager_form_template = 'combo/chartngcell_form.html'
class Meta:
@ -233,6 +245,26 @@ class ChartNgCell(CellBase):
else:
chart.axis_count = 2
# hide/sort values
if chart.axis_count == 1 and (self.sort_order != 'none' or self.hide_null_values):
if self.sort_order == 'alpha':
tmp_items = sorted(zip(x_labels, data), key=lambda x: x[0])
elif self.sort_order == 'asc':
tmp_items = sorted(zip(x_labels, data), key=lambda x: x[1])
elif self.sort_order == 'desc':
tmp_items = sorted(zip(x_labels, data), key=lambda x: x[1], reverse=True)
else:
tmp_items = zip(x_labels, data)
tmp_x_labels = []
tmp_data = []
for label, value in tmp_items:
if self.hide_null_values and not value:
continue
tmp_x_labels.append(label)
tmp_data.append(value)
x_labels = tmp_x_labels
data = tmp_data
chart.config.margin = 0
if width:
chart.config.width = width

View File

@ -361,6 +361,282 @@ def test_chartng_cell(app):
chart = cell.get_chart()
def test_chartng_cell_hide_null_values(app):
page = Page(title='One', slug='index')
page.save()
with override_settings(KNOWN_SERVICES={
'bijoe': {'plop': {'title': 'test', 'url': 'https://bijoe.example.com',
'secret': 'combo', 'orig': 'combo'}}}):
with HTTMock(bijoe_mock):
cell = ChartNgCell(page=page, order=1)
cell.data_reference = 'plop:example'
cell.hide_null_values = True
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[0]
# bar
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Bar'
assert chart.x_labels == ['web', 'mail', 'email']
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
# horizontal bar
cell.chart_type = 'horizontal-bar'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'HorizontalBar'
assert chart.x_labels == ['web', 'mail', 'email']
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
# pie
cell.chart_type = 'pie'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Pie'
assert chart.x_labels == ['web', 'mail', 'email']
assert chart.raw_series == [
([222], {'title': u'web'}),
([134], {'title': u'mail'}),
([53], {'title': u'email'})
]
# data in Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:second'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[1]
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'email']
assert chart.raw_series == [([222, 134, 53], {'title': ''})]
# data in X/Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:third'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[2]
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# single data point
cell.chart_type = 'bar'
cell.data_reference = 'plop:fourth'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[3]
chart = cell.get_chart()
assert chart.x_labels == ['']
assert chart.raw_series == [([222], {'title': ''})]
# loop/X
cell.data_reference = 'plop:fifth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# loop/Y
cell.data_reference = 'plop:sixth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
def test_chartng_cell_sort_order_alpha(app):
page = Page(title='One', slug='index')
page.save()
with override_settings(KNOWN_SERVICES={
'bijoe': {'plop': {'title': 'test', 'url': 'https://bijoe.example.com',
'secret': 'combo', 'orig': 'combo'}}}):
with HTTMock(bijoe_mock):
cell = ChartNgCell(page=page, order=1)
cell.data_reference = 'plop:example'
cell.sort_order = 'alpha'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[0]
# bar
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Bar'
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
# horizontal bar
cell.chart_type = 'horizontal-bar'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'HorizontalBar'
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
# pie
cell.chart_type = 'pie'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Pie'
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
assert chart.raw_series == [
([53], {'title': u'email'}),
([134], {'title': u'mail'}),
([222], {'title': u'web'})
]
# data in Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:second'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[1]
chart = cell.get_chart()
assert chart.x_labels == ['email', 'mail', 'phone', 'web']
assert chart.raw_series == [([53, 134, 0, 222], {'title': ''})]
# data in X/Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:third'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[2]
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# single data point
cell.chart_type = 'bar'
cell.data_reference = 'plop:fourth'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[3]
chart = cell.get_chart()
assert chart.x_labels == ['']
assert chart.raw_series == [([222], {'title': ''})]
# loop/X
cell.data_reference = 'plop:fifth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# loop/Y
cell.data_reference = 'plop:sixth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
def test_chartng_cell_sort_order_desc(app):
page = Page(title='One', slug='index')
page.save()
with override_settings(KNOWN_SERVICES={
'bijoe': {'plop': {'title': 'test', 'url': 'https://bijoe.example.com',
'secret': 'combo', 'orig': 'combo'}}}):
with HTTMock(bijoe_mock):
cell = ChartNgCell(page=page, order=1)
cell.data_reference = 'plop:example'
cell.sort_order = 'desc'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[0]
# bar
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Bar'
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
# horizontal bar
cell.chart_type = 'horizontal-bar'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'HorizontalBar'
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
# pie
cell.chart_type = 'pie'
chart = cell.get_chart()
assert chart.__class__.__name__ == 'Pie'
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
assert chart.raw_series == [
([222], {'title': u'web'}),
([134], {'title': u'mail'}),
([53], {'title': u'email'})
]
# data in Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:second'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[1]
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'email', 'phone']
assert chart.raw_series == [([222, 134, 53, 0], {'title': ''})]
# data in X/Y
cell.chart_type = 'bar'
cell.data_reference = 'plop:third'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[2]
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# single data point
cell.chart_type = 'bar'
cell.data_reference = 'plop:fourth'
cell.save()
assert cell.cached_json == VISUALIZATION_JSON[3]
chart = cell.get_chart()
assert chart.x_labels == ['']
assert chart.raw_series == [([222], {'title': ''})]
# loop/X
cell.data_reference = 'plop:fifth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
# loop/Y
cell.data_reference = 'plop:sixth'
cell.save()
chart = cell.get_chart()
assert chart.x_labels == ['web', 'mail', 'phone', 'email']
assert chart.raw_series == [
([222, 134, 0, 53], {'title': u'foo'}),
([122, 114, 2, 33], {'title': u'bar'}),
]
def test_chartng_cell_view(app, normal_user):
page = Page(title='One', slug='index')
page.save()