[explore] Start jqplot view
authorVincent Michel <vincent.michel@logilab.fr>
Wed, 17 Jul 2013 13:30:18 +0200
changeset 309 4312d239670e
parent 308 0d353676690c
child 310 678a4fe01f99
[explore] Start jqplot view
__pkginfo__.py
views/jqplot.py
views/tabs.py
--- a/__pkginfo__.py	Wed Jul 17 10:44:56 2013 +0200
+++ b/__pkginfo__.py	Wed Jul 17 13:30:18 2013 +0200
@@ -22,6 +22,7 @@
                 'cubicweb-cwbootstrap': None,
                 'cubicweb-comment': None,
                 'cubicweb-registration': None,
+                'cubicweb-jqplot': None,
                 }
 __recommends__ = {}
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/views/jqplot.py	Wed Jul 17 13:30:18 2013 +0200
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# copyright 2013 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2013 CEA (Saclay, FRANCE), all rights reserved.
+# contact http://www.logilab.fr -- mailto:contact@logilab.fr
+#
+# This program 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.
+#
+# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+
+from cubicweb.utils import json_dumps, js_dumps, JSString
+from cubicweb.predicates import multi_columns_rset
+
+from cubes.jqplot.views import JQPlotSimpleView
+
+
+################################################################################
+### JQPLOT RSET VIEW ###########################################################
+################################################################################
+class BrainomicsRsetJqplot(JQPlotSimpleView):
+    __regid__ = 'jqplot-2col'
+    __select__ = multi_columns_rset(2)
+
+    def call(self, rset=None, tab_id=None, jsbind=None, renderer=None, options=None,
+             divid=None, legend=None, colors=None,
+             width=450, height=300, displayfilter=False, mainvar=None, title=None,
+             displayactions=False, actions=None):
+        # Refefine the call() function to allow to pass an rset
+        if self._cw.ie_browser():
+            self._cw.add_js('excanvas.js')
+        self._cw.add_js(('jquery.jqplot.js', 'cubes.jqplot.js', 'cubes.jqplot.ext.js'))
+        self._cw.add_css('jquery.jqplot.min.css')
+        data, ticks = self.get_data(rset)
+        if legend is None:
+            legend = self.default_legend
+        if divid is None:
+            divid = u'figure%s' % self._cw.varmaker.next()
+        if renderer is None:
+            renderer = self.default_renderer
+        serie_options = {'renderer': self.renderer(self.renderers, renderer)}
+        if options is None:
+            options = self.default_options
+        serie_options['rendererOptions']= options
+        options = {'series': [serie_options],
+                   'legend': legend,
+                   'title': title,
+                   }
+        if ticks:
+            self._cw.html_headers.add_onload('ticks = %s' % json_dumps(ticks))
+            options['axes'] = {'xaxis': {'ticks': JSString('ticks'),
+                                         'renderer': JSString('$.jqplot.CategoryAxisRenderer')}}
+            self._cw.add_js('plugins/jqplot.categoryAxisRenderer.min.js')
+        if colors is not None:
+            options['seriesColors'] = colors
+        self.set_custom_options(options)
+        # Allow an onload on tab/pill show
+        js_onload  = self.onload % {'id': divid, 'data': json_dumps([data]),
+                                    'options': js_dumps(options)}
+        if not tab_id:
+            self._cw.html_headers.add_onload(js_onload)
+        else:
+            self._cw.html_headers.add_onload("""$('a[data-toggle="%s"]').on('shown',
+            function (e) {%s})""" % (tab_id, js_onload))
+        self.div_holder(divid, width, height)
+        if displayfilter and not 'fromformfilter' in self._cw.form:
+            self.form_filter(divid, mainvar)
+        if displayactions and not 'fromformfilter' in self._cw.form:
+            self.display_actions(divid, actions)
+        # Add a js bind function
+        if jsbind:
+            self._cw.html_headers.add_onload("""$('#%s').bind('jqplotDataClick',%s)"""
+                                             % (divid, jsbind))
+
+    def get_data(self, rset=None):
+        rset = rset or self.cw_rset
+        if isinstance(rset[0][0], basestring):
+            ticks = list(set([r[0] for r in rset if r[0] is not None and r[1] is not None]))
+            data = [[ticks.index(r[0]), r[1]] for r in rset if r[0] is not None and r[1] is not None]
+            return [d[1] for d in sorted(data, key=lambda x: x[0])], ticks
+        else:
+            return [[r[0], r[1]] for r in rset if r[0] is not None and r[1] is not None], None
--- a/views/tabs.py	Wed Jul 17 10:44:56 2013 +0200
+++ b/views/tabs.py	Wed Jul 17 13:30:18 2013 +0200
@@ -25,7 +25,7 @@
 
 
 ###############################################################################
-### TABBLABLE COMPONENTS ######################################################
+### TABBLABLE SELECTION COMPONENT #############################################
 ###############################################################################
 class BrainomicsTabblableSelectionComponent(component.CtxComponent):
     """ Component of selection of the different tabs
@@ -69,6 +69,9 @@
         w(u'</div>')
 
 
+###############################################################################
+### TABBLABLE COMPONENTS - COMMENT ############################################
+###############################################################################
 class BrainomicsTabblableComment(AbstractBrainomicsTabblable):
     """ Component used to display a tab with comment. """
     __select__ = AbstractBrainomicsTabblable.__select__ & is_instance(*COMMENTED_ENTITIES)
@@ -86,6 +89,9 @@
             comp.render(w=w)
 
 
+###############################################################################
+### TABBLABLE COMPONENTS - WIKI ###############################################
+###############################################################################
 class BrainomicsTabblableWiki(AbstractBrainomicsTabblable):
     """ Component used to display a tab with wiki. """
     __select__ = AbstractBrainomicsTabblable.__select__ & is_instance(*WIKI_ENTITIES)
@@ -102,3 +108,90 @@
             w(self._cw.view('documentation_card', rset=card_rset))
         else:
             w(u'<div>%s</div>' % self._cw._('No documentation available'))
+
+
+###############################################################################
+### TABBLABLE COMPONENTS - JQPLOT #############################################
+###############################################################################
+class BrainomicsTabblableJqplotQuestion(AbstractBrainomicsTabblable):
+    __select__ = AbstractBrainomicsTabblable.__select__ & is_instance('Question')
+    __regid__ = 'entity-jqplot-tab'
+
+    def get_title(self):
+        return self._cw._('Plot')
+
+    def get_values_rset(self):
+        # XXX Adapter ?
+        entity = self.cw_rset.get_entity(0, 0)
+        return self._cw.execute('Any V, COUNT(A) GROUPBY V WHERE A is Answer, '
+                                'A question Q, Q eid %(e)s, A value V', {'e': entity.eid})
+
+    def render_content(self, w, **kwargs):
+        entity = self.cw_rset.get_entity(0, 0)
+        rset = self.get_values_rset()
+        jsbind = self.add_jsbind(w)
+        if rset:
+            w(self._cw.view('jqplot-2col', tab_id='pill', rset=rset, jsbind=jsbind,
+                            title=xml_escape(entity.dc_title()),
+                            displayactions=True, actions=('print',),
+                            legend={'show': False}, width='100%'))
+        else:
+            w(u'<div>%s</div>' % self._cw._('No values available'))
+
+    def get_rql(self):
+        entity = self.cw_rset.get_entity(0, 0)
+        return 'Any A WHERE A is Answer, A question Q, Q eid %s, A value' % entity.eid
+
+    def add_jsbind(self, w):
+        w(u'<div id="jqplot-info"></div>')
+        jsbind = """function (ev, seriesIndex, pointIndex, data) {
+        var htmlstr = data[0] + ' : ' + data[1] + ' entries';
+        var url = '%s?rql=' + '%s ' + data[0];
+        var htmlstr = htmlstr + '   <a href="' + url +'">See all</a>';
+        $('#jqplot-info').html(htmlstr);
+        }""" % (self._cw.base_url(), self.get_rql())
+        return jsbind
+
+
+class BrainomicsTabblableJqplotScoreDef(BrainomicsTabblableJqplotQuestion):
+    __select__ = AbstractBrainomicsTabblable.__select__ & is_instance('ScoreDefinition')
+
+    def get_values_rset(self):
+        # XXX Adapter ?
+        entity = self.cw_rset.get_entity(0, 0)
+        if entity.type == 'numerical':
+            rset = self._cw.execute('Any V, COUNT(S) GROUPBY V WHERE S value V, '
+                                    'S is ScoreValue, S definition D, D eid %(e)s',
+                                    {'e': entity.eid})
+        else: # Text
+            rset = self._cw.execute('Any V, COUNT(S) GROUPBY V WHERE S text V, '
+                                    'S is ScoreValue, S definition D, D eid %(e)s',
+                                    {'e': entity.eid})
+        return rset
+
+    def get_rql(self):
+        entity = self.cw_rset.get_entity(0, 0)
+        if entity.type == 'numerical':
+            return 'Any S WHERE S is ScoreValue, S definition D, D eid %s, S value' % entity.eid
+        else: # Text
+            return 'Any S WHERE S is ScoreValue, S definition D, D eid %s, S text' % entity.eid
+
+    def add_jsbind(self, w):
+        w(u'<div id="jqplot-info"></div>')
+        entity = self.cw_rset.get_entity(0, 0)
+        if entity.type == 'numerical':
+            jsbind = """function (ev, seriesIndex, pointIndex, data) {
+            var htmlstr = data[0] + ' : ' + data[1] + ' entries';
+            var url = '%s?rql=' + '%s ' + data[0];
+            var htmlstr = htmlstr + '   <a href="' + url +'">See all</a>';
+            $('#jqplot-info').html(htmlstr);
+            }""" % (self._cw.base_url(), self.get_rql())
+        else: # Text
+            jsbind = """function (ev, seriesIndex, pointIndex, data) {
+            var htmlstr = ticks[pointIndex] + ' : ' + data[1] + ' entries';
+            var url = '%s?rql=' + '%s "' + ticks[pointIndex] + '"';
+            console.log(url)
+            var htmlstr = htmlstr + "   <a href='" + url +"'>See all</a>";
+            $('#jqplot-info').html(htmlstr);
+            }""" % (self._cw.base_url(), self.get_rql())
+        return jsbind