15 # |
15 # |
16 # You should have received a copy of the GNU Lesser General Public License along |
16 # You should have received a copy of the GNU Lesser General Public License along |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
18 """basic plot views""" |
18 """basic plot views""" |
19 |
19 |
20 |
|
21 from cubicweb import _ |
|
22 |
|
23 from six import add_metaclass |
|
24 from six.moves import range |
|
25 |
|
26 from logilab.common.date import datetime2ticks |
|
27 from logilab.common.deprecation import class_deprecated |
|
28 from logilab.common.registry import objectify_predicate |
20 from logilab.common.registry import objectify_predicate |
29 from logilab.mtconverter import xml_escape |
21 from logilab.mtconverter import xml_escape |
30 |
22 |
31 from cubicweb.utils import UStringIO, json_dumps |
23 from cubicweb.utils import UStringIO |
32 from cubicweb.predicates import multi_columns_rset |
24 from cubicweb.predicates import multi_columns_rset |
33 from cubicweb.web.views import baseviews |
25 from cubicweb.web.views import baseviews |
34 |
26 |
35 @objectify_predicate |
27 @objectify_predicate |
36 def all_columns_are_numbers(cls, req, rset=None, *args, **kwargs): |
28 def all_columns_are_numbers(cls, req, rset=None, *args, **kwargs): |
85 |
77 |
86 def _render(self, *args, **kwargs): |
78 def _render(self, *args, **kwargs): |
87 raise NotImplementedError |
79 raise NotImplementedError |
88 |
80 |
89 |
81 |
90 @add_metaclass(class_deprecated) |
|
91 class FlotPlotWidget(PlotWidget): |
|
92 """PlotRenderer widget using Flot""" |
|
93 __deprecation_warning__ = '[3.14] cubicweb.web.views.plots module is deprecated, use the jqplot cube instead' |
|
94 onload = u""" |
|
95 var fig = jQuery('#%(figid)s'); |
|
96 if (fig.attr('cubicweb:type') != 'prepared-plot') { |
|
97 %(plotdefs)s |
|
98 jQuery.plot(jQuery('#%(figid)s'), [%(plotdata)s], |
|
99 {points: {show: true}, |
|
100 lines: {show: true}, |
|
101 grid: {hoverable: true}, |
|
102 /*yaxis : {tickFormatter : suffixFormatter},*/ |
|
103 xaxis: {mode: %(mode)s}}); |
|
104 jQuery('#%(figid)s').data({mode: %(mode)s, dateformat: %(dateformat)s}); |
|
105 jQuery('#%(figid)s').bind('plothover', onPlotHover); |
|
106 fig.attr('cubicweb:type','prepared-plot'); |
|
107 } |
|
108 """ |
|
109 |
|
110 def __init__(self, labels, plots, timemode=False): |
|
111 self.labels = labels |
|
112 self.plots = plots # list of list of couples |
|
113 self.timemode = timemode |
|
114 |
|
115 def dump_plot(self, plot): |
|
116 if self.timemode: |
|
117 plot = [(datetime2ticks(x), y) for x, y in plot] |
|
118 return json_dumps(plot) |
|
119 |
|
120 def _render(self, req, width=500, height=400): |
|
121 if req.ie_browser(): |
|
122 req.add_js('excanvas.js') |
|
123 req.add_js(('jquery.flot.js', 'cubicweb.flot.js')) |
|
124 figid = u'figure%s' % next(req.varmaker) |
|
125 plotdefs = [] |
|
126 plotdata = [] |
|
127 self.w(u'<div id="%s" style="width: %spx; height: %spx;"></div>' % |
|
128 (figid, width, height)) |
|
129 for idx, (label, plot) in enumerate(zip(self.labels, self.plots)): |
|
130 plotid = '%s_%s' % (figid, idx) |
|
131 plotdefs.append('var %s = %s;' % (plotid, self.dump_plot(plot))) |
|
132 # XXX ugly but required in order to not crash my demo |
|
133 plotdata.append("{label: '%s', data: %s}" % (label.replace(u'&', u''), plotid)) |
|
134 fmt = req.property_value('ui.date-format') # XXX datetime-format |
|
135 # XXX TODO make plot options customizable |
|
136 req.html_headers.add_onload(self.onload % |
|
137 {'plotdefs': '\n'.join(plotdefs), |
|
138 'figid': figid, |
|
139 'plotdata': ','.join(plotdata), |
|
140 'mode': self.timemode and "'time'" or 'null', |
|
141 'dateformat': '"%s"' % fmt}) |
|
142 |
|
143 |
|
144 @add_metaclass(class_deprecated) |
|
145 class PlotView(baseviews.AnyRsetView): |
|
146 __deprecation_warning__ = '[3.14] cubicweb.web.views.plots module is deprecated, use the jqplot cube instead' |
|
147 __regid__ = 'plot' |
|
148 title = _('generic plot') |
|
149 __select__ = multi_columns_rset() & all_columns_are_numbers() |
|
150 timemode = False |
|
151 paginable = False |
|
152 |
|
153 def call(self, width=500, height=400): |
|
154 # prepare data |
|
155 rqlst = self.cw_rset.syntax_tree() |
|
156 # XXX try to make it work with unions |
|
157 varnames = [var.name for var in rqlst.children[0].get_selected_variables()][1:] |
|
158 abscissa = [row[0] for row in self.cw_rset] |
|
159 plots = [] |
|
160 nbcols = len(self.cw_rset.rows[0]) |
|
161 for col in range(1, nbcols): |
|
162 data = [row[col] for row in self.cw_rset] |
|
163 plots.append(filterout_nulls(abscissa, data)) |
|
164 plotwidget = FlotPlotWidget(varnames, plots, timemode=self.timemode) |
|
165 plotwidget.render(self._cw, width, height, w=self.w) |
|
166 |
|
167 |
|
168 class TimeSeriePlotView(PlotView): |
|
169 __select__ = multi_columns_rset() & columns_are_date_then_numbers() |
|
170 timemode = True |
|
171 |
|
172 |
|
173 try: |
82 try: |
174 from GChartWrapper import Pie, Pie3D |
83 from GChartWrapper import Pie, Pie3D |
175 except ImportError: |
84 except ImportError: |
176 pass |
85 pass |
177 else: |
86 else: |