dataviz: split get_chart into several methods (#48865)

This commit is contained in:
Valentin Deniaud 2020-12-03 15:08:17 +01:00
parent 356bb767f5
commit 4f878ac96a
1 changed files with 77 additions and 48 deletions

View File

@ -229,6 +229,22 @@ class ChartNgCell(CellBase):
'table': pygal.Bar,
}[self.chart_type](config=pygal.Config(style=copy.copy(style)))
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:
if self.hide_null_values:
data = self.hide_values(chart, data)
if self.sort_order != 'none':
data = self.sort_values(chart, data)
if chart.compute_sum and self.chart_type == 'table':
data = self.add_total_to_line_table(chart, data)
self.add_data_to_chart(chart, data, y_labels)
return chart
def parse_response(self, response, chart):
# normalize axis to have a fake axis when there are no dimensions and
# always a x axis when there is a single dimension.
data = response['data']
@ -261,26 +277,16 @@ 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] or 0))
elif self.sort_order == 'desc':
tmp_items = sorted(zip(x_labels, data), key=lambda x: (x[1] or 0), 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.show_legend = bool(len(response['axis']) > 1)
chart.compute_sum = bool(response.get('measure') == 'integer' and chart.axis_count > 0)
formatter = self.get_value_formatter(response.get('unit'), response.get('measure'))
if formatter:
chart.config.value_formatter = formatter
return x_labels, y_labels, data
def prepare_chart(self, chart, width, height):
chart.config.margin = 0
if width:
chart.config.width = width
@ -289,9 +295,7 @@ class ChartNgCell(CellBase):
if width or height:
chart.config.explicit_size = True
chart.config.js = [os.path.join(settings.STATIC_URL, 'js/pygal-tooltips.js')]
chart.x_labels = x_labels
chart.show_legend = bool(len(response['axis']) > 1)
chart.truncate_legend = 30
# matplotlib tab10 palette
chart.config.style.colors = (
@ -302,40 +306,67 @@ class ChartNgCell(CellBase):
if self.chart_type == 'dot':
chart.show_legend = False
# use a single colour for dots
chart.config.style.colors = ('#1f77b4',) * len(x_labels)
chart.compute_sum = bool(response.get('measure') == 'integer')
if chart.compute_sum and self.chart_type == 'table':
if chart.axis_count < 2: # workaround pygal
chart.compute_sum = False
if chart.axis_count == 1:
data.append(sum(data))
x_labels.append(gettext('Total'))
chart.config.style.colors = ('#1f77b4',) * len(chart.x_labels)
if self.chart_type != 'pie':
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(x_labels))]
chart.add(serie_label, values)
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:
# pie, create a serie by data, to get different colours
values = data
for label, value in zip(x_labels, values):
if not value:
continue
chart.add(label, value)
chart.show_legend = True
if width and width < 500:
chart.truncate_legend = 15
if response.get('unit') == 'seconds' or response.get('measure') == 'duration':
@staticmethod
def hide_values(chart, data):
x_labels, new_data = [], []
for label, value in zip(chart.x_labels, data):
if value:
x_labels.append(label)
new_data.append(value)
chart.x_labels = x_labels
return new_data
def sort_values(self, chart, data):
if self.sort_order == 'alpha':
tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: x[0])
elif self.sort_order == 'asc':
tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0))
elif self.sort_order == 'desc':
tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0), reverse=True)
x_labels, sorted_data = zip(*[(label, value) for label, value in tmp_items])
chart.x_labels = list(x_labels)
return list(sorted_data)
@staticmethod
def add_total_to_line_table(chart, data):
# workaround pygal
chart.compute_sum = False
data.append(sum(data))
chart.x_labels.append(gettext('Total'))
return data
def add_data_to_chart(self, chart, data, y_labels):
if self.chart_type != 'pie':
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))]
chart.add(serie_label, values)
else:
# pie, create a serie by data, to get different colours
values = data
for label, value in zip(chart.x_labels, values):
if not value:
continue
chart.add(label, value)
@staticmethod
def get_value_formatter(unit, measure):
if unit == 'seconds' or measure == 'duration':
def format_duration(value):
if value is None:
return '-'
@ -357,9 +388,7 @@ class ChartNgCell(CellBase):
else:
value = _('Less than an hour')
return force_text(value)
chart.config.value_formatter = format_duration
elif response.get('measure') == 'percent':
return format_duration
elif measure == 'percent':
percent_formatter = lambda x: '{:.1f}%'.format(x)
chart.config.value_formatter = percent_formatter
return chart
return percent_formatter