dataviz: split get_chart into several methods (#48865)
This commit is contained in:
parent
356bb767f5
commit
4f878ac96a
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue