Compare commits

..

10 Commits

3 changed files with 180 additions and 22 deletions

View File

@ -360,7 +360,6 @@ class ChartNgCell(CellBase):
if self.statistic.service_slug == 'bijoe':
x_labels, y_labels, data = self.parse_response(response, chart)
chart.x_labels = x_labels
self.prepare_chart(chart, width, height)
if chart.axis_count == 1:
data = self.process_one_dimensional_data(chart, data)
@ -379,7 +378,6 @@ class ChartNgCell(CellBase):
chart.x_labels = data['x_labels']
chart.axis_count = min(len(data['series']), 2)
self.prepare_chart(chart, width, height)
if self.statistic.data_type:
chart.config.value_formatter = self.get_value_formatter(self.statistic.data_type)
@ -401,7 +399,7 @@ class ChartNgCell(CellBase):
for serie in data['series']:
chart.add(serie['label'], serie['data'])
self.configure_chart(chart, width, height)
return chart
def get_filter_params(self):
@ -531,14 +529,37 @@ class ChartNgCell(CellBase):
return x_labels, y_labels, data
def prepare_chart(self, chart, width, height):
def configure_dot_chart(self, chart, width, height):
chart.show_legend = False
# use a single colour for dots
chart.config.style.colors = ('#1f77b4',) * max(len(chart.x_labels), 1)
def configure_horizontal_bar_chart(self, chart, width, height):
if width and width < 500:
# truncate labels
chart.x_labels = [pygal.util.truncate(x, 15) for x in chart.x_labels]
def configure_pie_chart(self, chart, width, height):
chart.show_legend = True
if width and height:
# pies are as tall as wide, reserve the appropriate space and distribute
# the rest for the legend.
chart.truncate_legend = (width - height) // 10
elif width:
chart.truncate_legend = width // 20
def configure_chart(self, chart, width, height):
auto_height_scale = pygal.style.DefaultStyle.legend_font_size * 1.75
chart.config.margin = 0
if width:
chart.config.width = width
if height:
chart.config.height = height
height = height or int(self.height)
# adapt chart's height to legend length
chart.config.height = max(height, auto_height_scale * len(chart.raw_series))
if width or height:
chart.config.explicit_size = True
chart.config.js = [os.path.join(settings.STATIC_URL, 'js/pygal-tooltips.js')]
chart.show_legend = bool(chart.axis_count > 1)
@ -557,25 +578,15 @@ class ChartNgCell(CellBase):
'#17becf',
)
if self.chart_type == 'dot':
chart.show_legend = False
# use a single colour for dots
chart.config.style.colors = ('#1f77b4',) * max(len(chart.x_labels), 1)
custom_configure_method_name = 'configure_%s_chart' % self.chart_type.replace('-', '_')
if hasattr(self, custom_configure_method_name):
getattr(self, custom_configure_method_name)(chart, width, height)
if self.chart_type != 'pie':
if width and width < 500:
chart.legend_at_bottom = True
if self.chart_type == 'horizontal-bar':
# truncate labels
chart.x_labels = [pygal.util.truncate(x, 15) for x in chart.x_labels]
else:
chart.show_legend = True
if width and height:
# pies are as tall as wide, reserve the appropriate space and distribute
# the rest for the legend.
chart.truncate_legend = (width - height) // 10
elif width:
chart.truncate_legend = width // 20
# restore demanded chart's height
chart.config.height = height
def process_one_dimensional_data(self, chart, data):
if self.hide_null_values:

View File

@ -73,7 +73,7 @@ class DatavizGraphView(DetailView):
try:
chart = form.instance.get_chart(
width=int(request.GET['width']) if request.GET.get('width') else None,
height=int(request.GET['height']) if request.GET.get('height') else int(self.cell.height),
height=int(request.GET['height']) if request.GET.get('height') else None,
)
except UnsupportedDataSet:
return self.error(_('Unsupported dataset.'))

View File

@ -4,6 +4,7 @@ import json
import urllib.parse
from unittest import mock
import lxml
import pytest
from django.apps import apps
from django.contrib.auth.models import Group
@ -149,6 +150,12 @@ VISUALIZATION_JSON = [
'name': 'sixteenth visualization (numerical labels)',
'slug': 'sixteenth',
},
{
'data-url': 'https://bijoe.example.com/visualization/17/json/',
'path': 'https://bijoe.example.com/visualization/17/iframe/?signature=123',
'name': 'seventeenth visualization (string labels)',
'slug': 'seventeenth',
},
]
@ -324,6 +331,83 @@ def bijoe_mock(url, request):
'measure': 'integer',
}
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
if url.path == '/visualization/17/json/':
response = {
'data': [
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
],
'axis': {
'x_labels': [
'Mois de %s' % m
for m in [
'janvier',
'février',
'mars',
'avril',
'mai',
'juin',
'juillet',
'août',
'septembre',
'octobre',
'novembre',
'décembre',
]
],
'y_labels': ['Label %s' % i for i in range(0, 9)],
},
'format': '1',
'unit': None,
'measure': 'integer',
}
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
if url.path == '/visualization/17/json/':
response = {
'data': [
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2],
],
'axis': {
'x_labels': [
'Mois de %s' % m
for m in [
'janvier',
'février',
'mars',
'avril',
'mai',
'juin',
'juillet',
'août',
'septembre',
'octobre',
'novembre',
'décembre',
]
],
'y_labels': ['Label %s' % i for i in range(0, 9)],
},
'format': '1',
'unit': None,
'measure': 'integer',
}
return {'content': json.dumps(response), 'request': request, 'status_code': 200}
STATISTICS_LIST = {
@ -1345,6 +1429,69 @@ def test_chartng_cell_view(app, normal_user, statistics):
assert 'not found' in resp.text
@with_httmock(bijoe_mock)
def test_chartng_cell_with_truncated_labels(app, normal_user, statistics):
page = Page(title='One', slug='index')
page.save()
cell = ChartNgCell(page=page, order=1, placeholder='content', chart_type='horizontal-bar')
cell.statistic = Statistic.objects.get(slug='seventeenth')
cell.save()
location = '/api/dataviz/graph/%s/' % cell.id
resp = app.get(location + '?width=499')
svg = lxml.etree.fromstring(resp.text.encode())
for label in svg.xpath(
"//ns:g[@class='legends']//ns:text", namespaces={'ns': 'http://www.w3.org/2000/svg'}
):
assert len(label) <= 15
@with_httmock(bijoe_mock)
def test_chartng_cell_viewbox(app, normal_user, statistics):
page = Page(title='One', slug='index')
page.save()
cell = ChartNgCell(page=page, order=1, placeholder='content')
cell.statistic = Statistic.objects.get(slug='eighth')
cell.save()
location = '/api/dataviz/graph/%s/' % cell.id
resp = app.get(location + '?width=600')
svg = lxml.etree.fromstring(resp.text.encode())
assert svg.attrib['width'] == '600'
assert svg.attrib['height'] == '250'
cell.statistic = Statistic.objects.get(slug='seventeenth')
cell.save()
resp = app.get(location + '?width=800&height=600')
svg = lxml.etree.fromstring(resp.text.encode())
assert svg.attrib['width'] == '800'
assert svg.attrib['height'] == '600'
# simulate chart preview from backoffice
resp = app.get(location + '?width=300&height=150')
svg = lxml.etree.fromstring(resp.text.encode())
assert svg.attrib['width'] == '300'
assert svg.attrib['height'] == '150'
# reduce cell's height
cell.height = '150'
cell.save()
resp = app.get(location + '?width=600&height=200')
svg = lxml.etree.fromstring(resp.text.encode())
# rendered cell should be higher than defined height
assert svg.attrib['height'] == '220.5'
# test pie chart rendering
cell.chart_type = 'pie'
cell.save()
resp = app.get(location + '?width=600&height=200')
svg = lxml.etree.fromstring(resp.text.encode())
# rendered cell should be higher than defined height
assert svg.attrib['height'] == '220.5'
@with_httmock(new_api_mock)
def test_chartng_cell_view_new_api(app, normal_user, new_api_statistics):
page = Page.objects.create(title='One', slug='index')