diff -r 000000000000 -r b97547f5f1fa web/views/plots.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/views/plots.py Wed Nov 05 15:52:50 2008 +0100 @@ -0,0 +1,103 @@ +import os + +from logilab.common import flatten + +from cubicweb.web.views import baseviews + +def plot_selector(cls, req, rset, *args, **kwargs): + """accept result set with at least one line and two columns of result + all columns after second must be of numerical types""" + if rset is None: + return 0 + if not len(rset): + return 0 + if len(rset.rows[0]) < 2: + return 0 + for etype in rset.description[0]: + if etype not in ('Int', 'Float'): + return 0 + return 1 + +try: + import matplotlib + import sys + if 'matplotlib.backends' not in sys.modules: + matplotlib.use('Agg') + from matplotlib.ticker import FormatStrFormatter + from pylab import figure, show +except ImportError: + pass +else: + class PlotView(baseviews.AnyRsetView): + id = 'plot' + title = _('generic plot') + binary = True + content_type = 'image/png' + _plot_count = 0 + __selectors__ = (plot_selector,) + + def call(self, width=None, height=None): + # compute dimensions + if width is None: + if 'width' in self.req.form: + width = int(self.req.form['width']) + else: + width = 500 + + if height is None: + if 'height' in self.req.form: + height = int(self.req.form['height']) + else: + height = 400 + dpi = 100. + + # compute data + abscisses = [row[0] for row in self.rset] + courbes = [] + nbcols = len(self.rset.rows[0]) + for col in range(1,nbcols): + courbe = [row[col] for row in self.rset] + courbes.append(courbe) + if not courbes: + raise Exception('no data') + # plot data + fig = figure(figsize=(width/dpi, height/dpi), dpi=dpi) + ax = fig.add_subplot(111) + colors = 'brgybrgy' + try: + float(abscisses[0]) + xlabels = None + except ValueError: + xlabels = abscisses + abscisses = range(len(xlabels)) + for idx,courbe in enumerate(courbes): + ax.plot(abscisses, courbe, '%sv-' % colors[idx], label=self.rset.description[0][idx+1]) + ax.autoscale_view() + alldata = flatten(courbes) + m, M = min(alldata or [0]), max(alldata or [1]) + if m is None: m = 0 + if M is None: M = 0 + margin = float(M-m)/10 + ax.set_ylim(m-margin, M+margin) + ax.grid(True) + ax.legend(loc='best') + if xlabels is not None: + ax.set_xticks(abscisses) + ax.set_xticklabels(xlabels) + try: + fig.autofmt_xdate() + except AttributeError: + # XXX too old version of matplotlib. Ignore safely. + pass + + # save plot + filename = self.build_figname() + fig.savefig(filename, dpi=100) + img = open(filename, 'rb') + self.w(img.read()) + img.close() + os.remove(filename) + + def build_figname(self): + self.__class__._plot_count += 1 + return '/tmp/burndown_chart_%s_%d.png' % (self.config.appid, self.__class__._plot_count)