diff -r 058bb3dc685f -r 0b59724cb3f2 web/views/plots.py
--- a/web/views/plots.py Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,228 +0,0 @@
-# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
-# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
-#
-# This file is part of CubicWeb.
-#
-# CubicWeb is free software: you can redistribute it and/or modify it under the
-# terms of the GNU Lesser General Public License as published by the Free
-# Software Foundation, either version 2.1 of the License, or (at your option)
-# any later version.
-#
-# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
-# details.
-#
-# You should have received a copy of the GNU Lesser General Public License along
-# with CubicWeb. If not, see .
-"""basic plot views"""
-
-__docformat__ = "restructuredtext en"
-from cubicweb import _
-
-from six import add_metaclass
-from six.moves import range
-
-from logilab.common.date import datetime2ticks
-from logilab.common.deprecation import class_deprecated
-from logilab.common.registry import objectify_predicate
-from logilab.mtconverter import xml_escape
-
-from cubicweb.utils import UStringIO, json_dumps
-from cubicweb.predicates import multi_columns_rset
-from cubicweb.web.views import baseviews
-
-@objectify_predicate
-def all_columns_are_numbers(cls, req, rset=None, *args, **kwargs):
- """accept result set with at least one line and two columns of result
- all columns after second must be of numerical types"""
- for etype in rset.description[0]:
- if etype not in ('Int', 'BigInt', 'Float'):
- return 0
- return 1
-
-@objectify_predicate
-def second_column_is_number(cls, req, rset=None, *args, **kwargs):
- etype = rset.description[0][1]
- if etype not in ('Int', 'BigInt', 'Float'):
- return 0
- return 1
-
-@objectify_predicate
-def columns_are_date_then_numbers(cls, req, rset=None, *args, **kwargs):
- etypes = rset.description[0]
- if etypes[0] not in ('Date', 'Datetime', 'TZDatetime'):
- return 0
- for etype in etypes[1:]:
- if etype not in ('Int', 'BigInt', 'Float'):
- return 0
- return 1
-
-
-def filterout_nulls(abscissa, plot):
- filtered = []
- for x, y in zip(abscissa, plot):
- if x is None or y is None:
- continue
- filtered.append( (x, y) )
- return sorted(filtered)
-
-class PlotWidget(object):
- # XXX refactor with cubicweb.web.views.htmlwidgets.HtmlWidget
- def _initialize_stream(self, w=None):
- if w:
- self.w = w
- else:
- self._stream = UStringIO()
- self.w = self._stream.write
-
- def render(self, *args, **kwargs):
- w = kwargs.pop('w', None)
- self._initialize_stream(w)
- self._render(*args, **kwargs)
- if w is None:
- return self._stream.getvalue()
-
- def _render(self, *args, **kwargs):
- raise NotImplementedError
-
-
-@add_metaclass(class_deprecated)
-class FlotPlotWidget(PlotWidget):
- """PlotRenderer widget using Flot"""
- __deprecation_warning__ = '[3.14] cubicweb.web.views.plots module is deprecated, use the jqplot cube instead'
- onload = u"""
-var fig = jQuery('#%(figid)s');
-if (fig.attr('cubicweb:type') != 'prepared-plot') {
- %(plotdefs)s
- jQuery.plot(jQuery('#%(figid)s'), [%(plotdata)s],
- {points: {show: true},
- lines: {show: true},
- grid: {hoverable: true},
- /*yaxis : {tickFormatter : suffixFormatter},*/
- xaxis: {mode: %(mode)s}});
- jQuery('#%(figid)s').data({mode: %(mode)s, dateformat: %(dateformat)s});
- jQuery('#%(figid)s').bind('plothover', onPlotHover);
- fig.attr('cubicweb:type','prepared-plot');
-}
-"""
-
- def __init__(self, labels, plots, timemode=False):
- self.labels = labels
- self.plots = plots # list of list of couples
- self.timemode = timemode
-
- def dump_plot(self, plot):
- if self.timemode:
- plot = [(datetime2ticks(x), y) for x, y in plot]
- return json_dumps(plot)
-
- def _render(self, req, width=500, height=400):
- if req.ie_browser():
- req.add_js('excanvas.js')
- req.add_js(('jquery.flot.js', 'cubicweb.flot.js'))
- figid = u'figure%s' % next(req.varmaker)
- plotdefs = []
- plotdata = []
- self.w(u'
' %
- (figid, width, height))
- for idx, (label, plot) in enumerate(zip(self.labels, self.plots)):
- plotid = '%s_%s' % (figid, idx)
- plotdefs.append('var %s = %s;' % (plotid, self.dump_plot(plot)))
- # XXX ugly but required in order to not crash my demo
- plotdata.append("{label: '%s', data: %s}" % (label.replace(u'&', u''), plotid))
- fmt = req.property_value('ui.date-format') # XXX datetime-format
- # XXX TODO make plot options customizable
- req.html_headers.add_onload(self.onload %
- {'plotdefs': '\n'.join(plotdefs),
- 'figid': figid,
- 'plotdata': ','.join(plotdata),
- 'mode': self.timemode and "'time'" or 'null',
- 'dateformat': '"%s"' % fmt})
-
-
-@add_metaclass(class_deprecated)
-class PlotView(baseviews.AnyRsetView):
- __deprecation_warning__ = '[3.14] cubicweb.web.views.plots module is deprecated, use the jqplot cube instead'
- __regid__ = 'plot'
- title = _('generic plot')
- __select__ = multi_columns_rset() & all_columns_are_numbers()
- timemode = False
- paginable = False
-
- def call(self, width=500, height=400):
- # prepare data
- rqlst = self.cw_rset.syntax_tree()
- # XXX try to make it work with unions
- varnames = [var.name for var in rqlst.children[0].get_selected_variables()][1:]
- abscissa = [row[0] for row in self.cw_rset]
- plots = []
- nbcols = len(self.cw_rset.rows[0])
- for col in range(1, nbcols):
- data = [row[col] for row in self.cw_rset]
- plots.append(filterout_nulls(abscissa, data))
- plotwidget = FlotPlotWidget(varnames, plots, timemode=self.timemode)
- plotwidget.render(self._cw, width, height, w=self.w)
-
-
-class TimeSeriePlotView(PlotView):
- __select__ = multi_columns_rset() & columns_are_date_then_numbers()
- timemode = True
-
-
-try:
- from GChartWrapper import Pie, Pie3D
-except ImportError:
- pass
-else:
-
- class PieChartWidget(PlotWidget):
- def __init__(self, labels, values, pieclass=Pie, title=None):
- self.labels = labels
- self.values = values
- self.pieclass = pieclass
- self.title = title
-
- def _render(self, width=None, height=None):
- piechart = self.pieclass(self.values)
- piechart.label(*self.labels)
- if width is not None:
- height = height or width
- piechart.size(width, height)
- if self.title:
- piechart.title(self.title)
- self.w(u'' % xml_escape(piechart.url))
-
- class PieChartView(baseviews.AnyRsetView):
- __regid__ = 'piechart'
- pieclass = Pie
- paginable = False
-
- __select__ = multi_columns_rset() & second_column_is_number()
-
- def _guess_vid(self, row):
- etype = self.cw_rset.description[row][0]
- if self._cw.vreg.schema.eschema(etype).final:
- return 'final'
- return 'textincontext'
-
- def call(self, title=None, width=None, height=None):
- labels = []
- values = []
- for rowidx, (_, value) in enumerate(self.cw_rset):
- if value is not None:
- vid = self._guess_vid(rowidx)
- label = '%s: %s' % (self._cw.view(vid, self.cw_rset, row=rowidx, col=0),
- value)
- labels.append(label.encode(self._cw.encoding))
- values.append(value)
- pie = PieChartWidget(labels, values, pieclass=self.pieclass,
- title=title)
- if width is not None:
- height = height or width
- pie.render(width, height, w=self.w)
-
-
- class PieChart3DView(PieChartView):
- __regid__ = 'piechart3D'
- pieclass = Pie3D