From 51c7263e4f82f2afa6b048bd542dae1cc487990b Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Thu, 15 May 2014 14:36:43 +0200 Subject: [PATCH] Cleanup a bit and prevent crash with log --- pygal/graph/base.py | 7 ++--- pygal/graph/dot.py | 2 +- pygal/graph/graph.py | 6 ++-- pygal/graph/stackedbar.py | 13 +++++---- pygal/graph/verticalpyramid.py | 51 +++++++++------------------------- pygal/test/test_config.py | 3 +- pygal/view.py | 23 ++++++--------- 7 files changed, 37 insertions(+), 68 deletions(-) diff --git a/pygal/graph/base.py b/pygal/graph/base.py index bf77066..741b61a 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -55,9 +55,8 @@ class BaseGraph(object): self.view = None if self.logarithmic and self.zero == 0: # Explicit min to avoid interpolation dependency - from pygal.graph.xy import XY - if isinstance(self, XY): - get = lambda x: x[1] + if self._dual: + get = lambda x: x[1] or 1 else: get = lambda x: x @@ -66,7 +65,7 @@ class BaseGraph(object): [get(val) for serie in self.series for val in serie.safe_values])) - self.zero = min(positive_values) if positive_values else 0 + self.zero = min(positive_values or 1,) or 1 self._draw() self.svg.pre_render() diff --git a/pygal/graph/dot.py b/pygal/graph/dot.py index 7a29ab9..6dc3f5d 100644 --- a/pygal/graph/dot.py +++ b/pygal/graph/dot.py @@ -76,6 +76,6 @@ class Dot(Graph): def _plot(self): r_max = min( self.view.x(1) - self.view.x(0), - self.view.y(0) - self.view.y(1)) / (2 * (self._max or 1) * 1.05) + (self.view.y(0) or 0) - self.view.y(1)) / (2 * (self._max or 1) * 1.05) for index, serie in enumerate(self.series): self.dot(self._serie(index), serie, r_max) diff --git a/pygal/graph/graph.py b/pygal/graph/graph.py index 3e025cc..3b100ec 100644 --- a/pygal/graph/graph.py +++ b/pygal/graph/graph.py @@ -128,8 +128,8 @@ class Graph(BaseGraph): if self.x_label_rotation or len(self._x_labels) <= 1: truncation = 25 else: - first_label_position = self.view.x(self._x_labels[0][1]) - last_label_position = self.view.x(self._x_labels[-1][1]) + first_label_position = self.view.x(self._x_labels[0][1]) or 0 + last_label_position = self.view.x(self._x_labels[-1][1]) or 0 available_space = ( last_label_position - first_label_position) / ( len(self._x_labels) - 1) @@ -169,7 +169,7 @@ class Graph(BaseGraph): last_guide = (self._y_2nd_labels and label == lastlabel) self.svg.node( guides, 'path', - d='M%f %f v%f' % (x, 0, self.view.height), + d='M%f %f v%f' % (x or 0, 0, self.view.height), class_='%s%sline' % ( 'major ' if major else '', 'guide ' if position != 0 and not last_guide else '')) diff --git a/pygal/graph/stackedbar.py b/pygal/graph/stackedbar.py index 2b22e65..55ead91 100644 --- a/pygal/graph/stackedbar.py +++ b/pygal/graph/stackedbar.py @@ -82,13 +82,13 @@ class StackedBar(Bar): positive_vals, negative_vals = self._get_separated_values(True) self.secondary_negative_cumulation = [0] * self._len self.secondary_positive_cumulation = [0] * self._len + self._pre_compute_secondary(positive_vals, negative_vals) - # In case of pyramids - sum_ = lambda x: sum(x) if isinstance(x, tuple) else x - self._secondary_min = (negative_vals and min( - sum_(min(negative_vals)), self.zero)) or self.zero - self._secondary_max = (positive_vals and max( - sum_(max(positive_vals)), self.zero)) or self.zero + def _pre_compute_secondary(self, positive_vals, negative_vals): + self._secondary_min = (negative_vals and min( + min(negative_vals), self.zero)) or self.zero + self._secondary_max = (positive_vals and max( + max(positive_vals), self.zero)) or self.zero def _bar(self, parent, x, y, index, i, zero, shift=False, secondary=False, rounded=False): @@ -109,6 +109,7 @@ class StackedBar(Bar): width = (self.view.x(1) - self.view.x(0)) / self._len x, y = self.view((x, y)) + y = y or 0 series_margin = width * self._series_margin x += series_margin width -= 2 * series_margin diff --git a/pygal/graph/verticalpyramid.py b/pygal/graph/verticalpyramid.py index 6db4860..074713a 100644 --- a/pygal/graph/verticalpyramid.py +++ b/pygal/graph/verticalpyramid.py @@ -37,49 +37,24 @@ class VerticalPyramid(StackedBar): def _get_separated_values(self, secondary=False): series = self.secondary_series if secondary else self.series - positive_vals = zip(*[serie.safe_values - for index, serie in enumerate(series) - if index % 2]) - negative_vals = zip(*[serie.safe_values - for index, serie in enumerate(series) - if not index % 2]) + positive_vals = map(sum, zip( + *[serie.safe_values + for index, serie in enumerate(series) + if index % 2])) + negative_vals = map(sum, zip( + *[serie.safe_values + for index, serie in enumerate(series) + if not index % 2])) return list(positive_vals), list(negative_vals) def _compute_box(self, positive_vals, negative_vals): - positive_sum = list(map(sum, positive_vals)) or [self.zero] - negative_sum = list(map(sum, negative_vals)) or [self.zero] - self._box.ymax = max(max(positive_sum), max(negative_sum)) + self._box.ymax = max(max(positive_vals or [self.zero]), + max(negative_vals or [self.zero])) self._box.ymin = - self._box.ymax - def _compute_secondary(self): - # Need refactoring - if self.secondary_series: - y_pos = list(zip(*self._y_labels))[1] - positive_vals, negative_vals = self._get_separated_values(True) - positive_sum = map(sum, positive_vals) or [self.zero] - negative_sum = map(sum, negative_vals) or [self.zero] - ymax = max(max(positive_sum), max(negative_sum)) - ymin = -ymax - - min_0_ratio = (self.zero - self._box.ymin) / self._box.height - max_0_ratio = (self._box.ymax - self.zero) / self._box.height - - new_ymax = (self.zero - ymin) * (1 / min_0_ratio - 1) - new_ymin = -(ymax - self.zero) * (1 / max_0_ratio - 1) - if ymax > self._box.ymax: - ymin = new_ymin - else: - ymax = new_ymax - - left_range = abs(self._box.ymax - self._box.ymin) - right_range = abs(ymax - ymin) - self._scale = left_range / right_range - self._scale_diff = self._box.ymin - self._scale_min_2nd = ymin - self._y_2nd_labels = [ - (self._format(self._box.xmin + y * right_range / left_range), - y) - for y in y_pos] + def _pre_compute_secondary(self, positive_vals, negative_vals): + self._secondary_max = max(max(positive_vals), max(negative_vals)) + self._secondary_min = - self._secondary_max def _bar(self, parent, x, y, index, i, zero, shift=True, secondary=False, rounded=False): diff --git a/pygal/test/test_config.py b/pygal/test/test_config.py index d40bc74..df53d8e 100644 --- a/pygal/test/test_config.py +++ b/pygal/test/test_config.py @@ -23,9 +23,8 @@ from pygal import ( FrenchMap_Regions, FrenchMap_Departments) from pygal._compat import u from pygal.test.utils import texts -from pygal.test import pytest_generate_tests, make_data +from pygal.test import pytest_generate_tests from tempfile import NamedTemporaryFile -from uuid import uuid4 def test_config_behaviours(): diff --git a/pygal/view.py b/pygal/view.py index 44be9cf..8a93c3a 100644 --- a/pygal/view.py +++ b/pygal/view.py @@ -288,7 +288,6 @@ class PolarThetaLogView(View): class LogView(View): """Logarithmic projection """ # Do not want to call the parent here - # pylint: disable-msg=W0231 def __init__(self, width, height, box): self.width = width self.height = height @@ -297,11 +296,10 @@ class LogView(View): self.log10_ymin = log10(self.box.ymin) if self.box.ymin > 0 else 0 self.box.fix(False) - # pylint: enable-msg=W0231 def y(self, y): """Project y""" if y is None or y <= 0 or self.log10_ymax - self.log10_ymin == 0: - return None + return 0 return (self.height - self.height * (log10(y) - self.log10_ymin) / (self.log10_ymax - self.log10_ymin)) @@ -310,16 +308,14 @@ class LogView(View): class XLogView(View): """Logarithmic projection """ # Do not want to call the parent here - # pylint: disable-msg=W0231 def __init__(self, width, height, box): self.width = width self.height = height self.box = box - self.log10_xmax = log10(self.box.xmax) - self.log10_xmin = log10(self.box.xmin) + self.log10_xmax = log10(self.box.xmax) if self.box.xmax > 0 else 0 + self.log10_xmin = log10(self.box.xmin) if self.box.xmin > 0 else 0 self.box.fix(False) - # pylint: enable-msg=W0231 def x(self, x): """Project x""" if x is None or x <= 0 or self.log10_xmax - self.log10_xmin == 0: @@ -334,24 +330,23 @@ class XYLogView(XLogView, LogView): self.width = width self.height = height self.box = box - self.log10_ymax = log10(self.box.ymax) - self.log10_ymin = log10(self.box.ymin) - self.log10_xmax = log10(self.box.xmax) - self.log10_xmin = log10(self.box.xmin) + self.log10_ymax = log10(self.box.ymax) if self.box.ymax > 0 else 0 + self.log10_ymin = log10(self.box.ymin) if self.box.ymin > 0 else 0 + self.log10_xmax = log10(self.box.xmax) if self.box.xmax > 0 else 0 + self.log10_xmin = log10(self.box.xmin) if self.box.xmin > 0 else 0 self.box.fix(False) class HorizontalLogView(XLogView): """Logarithmic projection """ # Do not want to call the parent here - # pylint: disable-msg=W0231 def __init__(self, width, height, box): self._force_vertical = None self.width = width self.height = height self.box = box - self.log10_xmax = log10(self.box.ymax) - self.log10_xmin = log10(self.box.ymin) + self.log10_xmax = log10(self.box.ymax) if self.box.ymax > 0 else 0 + self.log10_xmin = log10(self.box.ymin) if self.box.ymin > 0 else 0 self.box.fix(False) self.box.swap()