From 64501f61c02648d1c80e1b1175ec5fa980eed52c Mon Sep 17 00:00:00 2001 From: Florian Mounier Date: Fri, 12 Apr 2013 14:55:59 +0200 Subject: [PATCH] Make tests pass for DateY + Pep8 --- demo/moulinrouge/tests.py | 15 +++++++++++- pygal/adapters.py | 13 +++++++++++ pygal/graph/base.py | 10 +++++++- pygal/graph/datey.py | 49 ++++++++++++++++++++++----------------- pygal/test/__init__.py | 11 ++++++++- pygal/util.py | 10 ++++++-- 6 files changed, 82 insertions(+), 26 deletions(-) diff --git a/demo/moulinrouge/tests.py b/demo/moulinrouge/tests.py index 437fb4e..166071c 100644 --- a/demo/moulinrouge/tests.py +++ b/demo/moulinrouge/tests.py @@ -2,7 +2,7 @@ # This file is part of pygal from pygal import ( Bar, Gauge, Pyramid, Funnel, Dot, StackedBar, XY, - CHARTS_BY_NAME, Config, Line) + CHARTS_BY_NAME, Config, Line, DateY) from pygal.style import styles @@ -248,4 +248,17 @@ def get_test_routes(app): stacked.add('2', [4, 5, 6]) return stacked.render_response() + @app.route('/test/datey') + def test_datey(): + from datetime import datetime + datey = DateY(show_dots=False) + datey.add('1', [ + (datetime(2011, 12, 21), 10), + (datetime(2013, 4, 8), 12), + (datetime(2010, 2, 28), 2) + ]) + datey.add('2', [(12, 4), (219, 8), (928, 6)]) + datey.x_label_rotation = 25 + return datey.render_response() + return filter(lambda x: x.startswith('test'), locals()) diff --git a/pygal/adapters.py b/pygal/adapters.py index c7e2f46..9e98a4d 100644 --- a/pygal/adapters.py +++ b/pygal/adapters.py @@ -20,6 +20,8 @@ Value adapters to use when a chart doesn't accept all value types """ +import datetime +from numbers import Number def positive(x): @@ -38,3 +40,14 @@ def not_zero(x): def none_to_zero(x): return x or 0 + + +def date(x): + # Make int work for date graphs by counting days number from now + if isinstance(x, Number): + try: + d = datetime.date.today() + datetime.timedelta(days=x) + return datetime.datetime.combine(d, datetime.time(0, 0, 0)) + except OverflowError: + return None + return x diff --git a/pygal/graph/base.py b/pygal/graph/base.py index bbc7e14..8f92db3 100644 --- a/pygal/graph/base.py +++ b/pygal/graph/base.py @@ -52,9 +52,17 @@ 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] + else: + get = lambda x: x + positive_values = filter( lambda x: x > 0, - [val for serie in self.series for val in serie.safe_values]) + [get(val) + for serie in self.series for val in serie.safe_values]) + self.zero = min(positive_values) if positive_values else 0 self._draw() self.svg.pre_render() diff --git a/pygal/graph/datey.py b/pygal/graph/datey.py index 19e8e20..1039954 100644 --- a/pygal/graph/datey.py +++ b/pygal/graph/datey.py @@ -27,46 +27,52 @@ from datetime import datetime,timedelta def jour(n) : return datetime(year=2013,month=1,day=1)+timedelta(days=n) - + x=(1,20,35,54,345,898) x=tuple(map(jour,x)) y=(1,3,4,2,3,1) graph=pygal.DateY(x_label_rotation=20) graph.add("graph1",list(zip(x,y))+[None,None]) -graph.render_in_browser() +graph.render_in_browser() """ - +from pygal.adapters import date from pygal.util import compute_scale -from pygal.graph.line import Line -from pygal.graph.xy import XY +from pygal.graph.xy import XY import datetime + class DateY(XY): """ DateY Graph """ - _offset=datetime.datetime(year=2000,month=1,day=1) - def _todate(self, d) : - """ Converts a number to a date """ - return str(self._offset+datetime.timedelta(seconds=d)) - def _tonumber(self,d) : + _offset = datetime.datetime(year=2000, month=1, day=1) + _adapters = [date] + + def _todate(self, d): + """ Converts a number to a date """ + return str(self._offset + datetime.timedelta(seconds=d)) + + def _tonumber(self, d): """ Converts a date to a number """ - if d==None: return None - return (d-self._offset).total_seconds() + if d is None: + return None + return (d - self._offset).total_seconds() def _get_value(self, values, i): - return 'x=%s, y=%s' % (self._todate(values[i][0]),self._format(values[i][1])) + return 'x=%s, y=%s' % ( + self._todate(values[i][0]), self._format(values[i][1])) def _compute(self): # Approximatively the same code as in XY. # The only difference is the transformation of dates to numbers - # (beginning) and the reversed transforamtion to dates (end) + # (beginning) and the reversed transformation to dates (end) self._offset = min([val[0] - for serie in self.series - for val in serie.values - if val[0] is not None]) - for serie in self.series : - serie.values=[(self._tonumber(v[0]),v[1]) for v in serie.values] - + for serie in self.series + for val in serie.values + if val[0] is not None] + or [datetime.datetime.fromtimestamp(0)]) + for serie in self.series: + serie.values = [(self._tonumber(v[0]), v[1]) for v in serie.values] + xvals = [val[0] for serie in self.series for val in serie.values @@ -86,7 +92,8 @@ class DateY(XY): serie.points = serie.values if self.interpolate and rng: vals = list(zip(*sorted( - [t for t in serie.points if None not in t], key=lambda x: x[0]))) + [t for t in serie.points if None not in t], + key=lambda x: x[0]))) serie.interpolated = self._interpolate( vals[1], vals[0], xy=True, xy_xmin=xmin, xy_rng=rng) diff --git a/pygal/test/__init__.py b/pygal/test/__init__.py index 556573e..8fb231b 100644 --- a/pygal/test/__init__.py +++ b/pygal/test/__init__.py @@ -19,6 +19,7 @@ import pygal from pygal.util import cut +from datetime import datetime def get_data(i): @@ -43,8 +44,16 @@ def pytest_generate_tests(metafunc): def make_data(chart, datas, secondary=False): + def get(data): + if isinstance(chart, pygal.XY): + if isinstance(chart, pygal.DateY): + # Convert to a credible datetime + return datetime.fromtimestamp(1360000000 + data * 987654) + return data + return cut(data) + for data in datas: chart.add(data[0], - data[1] if chart.__class__ == pygal.XY else cut(data[1]), + get(data[1]), secondary=secondary) return chart diff --git a/pygal/util.py b/pygal/util.py index 3eb0019..631894b 100644 --- a/pygal/util.py +++ b/pygal/util.py @@ -297,6 +297,9 @@ from pygal.serie import Serie def prepare_values(raw, config, cls): """Prepare the values to start with sane values""" + from pygal.graph.xy import XY + from pygal.graph.datey import DateY + if not raw: return @@ -333,12 +336,15 @@ def prepare_values(raw, config, cls): else: value = raw_value - if cls.__name__ == 'XY' or cls.__name__ == 'DateY': + if issubclass(cls, XY): if value is None: value = (None, None) elif not hasattr(value, '__iter__'): value = (value, config.zero) - value = map(adapter, value) + if issubclass(cls, DateY): + value = (adapter(value[0]), value[1]) + else: + value = map(adapter, value) else: value = adapter(value)