[visualisation] graph of time taken by tests in "test reports" tabs (closes #2176598)
using clickable charts by testconfig/branch in the "test results" tab view
(with filter when not enough results)
--- a/__pkginfo__.py Wed Feb 22 09:19:25 2012 +0100
+++ b/__pkginfo__.py Mon Feb 20 12:47:14 2012 +0100
@@ -24,6 +24,7 @@
'cubicweb-vcsfile': '>= 1.6.1',
'cubicweb-file': None,
'cubicweb-narval': '>= 3.0.2',
+ 'cubicweb-jqplot': '>= 0.2.0',
}
__recommends__ = {'cubicweb-tracker': None,
'cubicweb-nosylist': '>= 0.5.0'}
--- a/data/cubes.apycot.css Wed Feb 22 09:19:25 2012 +0100
+++ b/data/cubes.apycot.css Mon Feb 20 12:47:14 2012 +0100
@@ -214,3 +214,7 @@
background-color : transparent ;
border: none;
}
+
+table.plotlegend td{
+ padding : 0 5px 0 10px
+}
\ No newline at end of file
--- a/i18n/en.po Wed Feb 22 09:19:25 2012 +0100
+++ b/i18n/en.po Mon Feb 20 12:47:14 2012 +0100
@@ -873,6 +873,9 @@
msgid "when this test config should be started"
msgstr ""
+msgid "Time taken by Test Executions"
+msgstr ""
+
#~ msgid "boxes_apycot.te.download_box"
#~ msgstr "download box"
--- a/i18n/fr.po Wed Feb 22 09:19:25 2012 +0100
+++ b/i18n/fr.po Mon Feb 20 12:47:14 2012 +0100
@@ -894,4 +894,8 @@
msgstr "chaque semaine"
msgid "when this test config should be started"
-msgstr "quand cette configuration doit-ellle être lancée"
+msgstr "quand cette configuration doit-elle être lancée"
+
+msgid "Time taken by Test Executions"
+msgstr "Temps d'exécution des tests"
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/views/plots.py Mon Feb 20 12:47:14 2012 +0100
@@ -0,0 +1,58 @@
+"""this module contains some plot report views for test execution results
+
+:organization: Logilab
+:copyright: 2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+:license: General Public License version 2 - http://www.gnu.org/licenses
+"""
+__docformat__ = "restructuredtext en"
+_ = unicode
+
+from cubes.jqplot.views import JQPlotSimpleView
+from cubicweb.selectors import multi_columns_rset
+
+ERROR_CODES = {
+ 'error': "#FF5555",
+ 'failure': "#FF8844",
+ 'partial': "#FFFF55",
+ 'success': "#33FF33",
+ 'other': "#AAAAAA",
+ }
+
+class JQPlotTestExecutionView(JQPlotSimpleView):
+ __regid__ = 'jqplot.testexecution'
+ __select__ = multi_columns_rset(3)
+ default_renderer = 'bar'
+
+ onload = '''%(id)s = $.jqplot("%(id)s", %(data)s, %(options)s);
+
+ $(document).ready(function(){
+ $("#%(id)s").bind('jqplotDataClick',
+ function (ev, seriesIndex, pointIndex, data) {
+ /* To open in a NEW window use: */
+ if ( ev.which == 2 ) {
+ window.open(data[2]);
+ } else {
+ /* To open in the same window use: */
+ window.location.href=data[2];
+ };
+ }
+ );
+ });
+ '''
+ default_options = {
+ 'varyBarColor': True,
+ 'barMargin':0,
+ }
+ default_legend = {
+ 'show': False,
+ }
+
+ def get_data(self):
+ return [[i+1,x[1],self._cw.build_url(x[0])] for i,x in enumerate(self.cw_rset.rows)]
+
+ def set_custom_options(self, options):
+ other = ERROR_CODES.get('other')
+ options['seriesColors'] = [ERROR_CODES.get(x[2], other) for x in self.cw_rset.rows]
+ #FIXME hack to approximate width since it doesn't work in js
+ options['series'][0]['rendererOptions']['barWidth'] = 350/len(self.cw_rset.rows)
--- a/views/tracker.py Wed Feb 22 09:19:25 2012 +0100
+++ b/views/tracker.py Mon Feb 20 12:47:14 2012 +0100
@@ -1,7 +1,7 @@
"""this module contains some stuff to integrate the apycot cube into jpl
:organization: Logilab
-:copyright: 2009-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2009-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
:license: General Public License version 2 - http://www.gnu.org/licenses
"""
@@ -12,15 +12,45 @@
from cubicweb.view import EntityView
from cubicweb.web import uicfg
+from cubes.apycot.views.plots import ERROR_CODES
+
_pvs = uicfg.primaryview_section
_pvs.tag_subject_of(('Project', 'has_apycot_environment', '*'), 'attributes')
+# in graphs ignore branches that have less MIN_NB_RUNS_IN_GRAPH
+MIN_NB_RUNS_IN_GRAPH = 3
+
class ProjectTestResultsTab(EntityView):
- """display project's documentation"""
+ """display project's test execution results"""
__regid__ = title = _('apycottestresults_tab')
__select__ = is_instance('Project')
def entity_call(self, entity):
+ self.w(u'<h3>%s</h3>' % _('Time taken by Test Executions'))
+ self.w(u'''<table class="plotlegend"><tr>%s</tr></table>
+ ''' % ''.join(['<td>%s</td><td style="background:%s"> </td>' % (x,y) for x,y in ERROR_CODES.items()]))
+ testconfig_rset = self._cw.execute('Any TC,NAME WHERE P has_apycot_environment TENV, '
+ 'TC use_environment TENV, P eid %(p)s, TC name NAME', {'p': entity.eid})
+ branches_rset = self._cw.execute('Any B GROUPBY B WHERE TE branch B, '
+ 'TE is TestExecution, TE using_environment TENV, '
+ 'P has_apycot_environment TENV, P eid %(p)s HAVING COUNT(TE) > %(limit)s',
+ {'p':entity.eid,
+ 'limit':MIN_NB_RUNS_IN_GRAPH})
+ for testconfig in testconfig_rset:
+ for branch in branches_rset:
+ label = '%s : %s - %s' % (_(u'Test run time'), testconfig[1], branch[0])
+ rset = self._cw.execute(
+ 'Any TE, ET - ST, S ORDERBY ST LIMIT 50 WHERE '
+ 'TE is TestExecution, TE using_environment TENV, '
+ 'P has_apycot_environment TENV, TE starttime ST, '
+ 'TE endtime ET, TE eid E, TE status S, P eid %(p)s, '
+ 'TE using_config TC, TC eid %(tc)s, TE branch %(branch)s',
+ {'p': entity.eid,
+ 'tc': testconfig[0],
+ 'branch':branch[0]})
+ if rset and len(rset) > MIN_NB_RUNS_IN_GRAPH:
+ self.w(u'<h4>%s</h4>' % label)
+ self.wview('jqplot.testexecution', rset, 'noresult')
rset = self._cw.execute(
'Any T,TC,T,TB,TST,TET,TF, TS ORDERBY TST DESC WHERE '
'T status TS, T using_config TC, T branch TB, '
@@ -49,10 +79,12 @@
# self.wview('summary', configsrset, title=self._cw._(self.title))
-try:
- from cubes.tracker.views.project import ProjectPrimaryView
-except ImportError:
- pass
-else:
- if 'apycottestresults_tab' not in ProjectPrimaryView.tabs:
- ProjectPrimaryView.tabs.append('apycottestresults_tab')
+def registration_callback(vreg):
+ try:
+ from cubes.tracker.views.project import ProjectPrimaryView
+ except ImportError:
+ pass
+ else:
+ vreg.register_all(globals().values(), __name__)
+ if 'apycottestresults_tab' not in ProjectPrimaryView.tabs:
+ ProjectPrimaryView.tabs.append('apycottestresults_tab')