Deal with narval's workflow changes
authorJulien Cristau <julien.cristau@logilab.fr>
Thu, 02 May 2013 12:40:58 +0200
changeset 1007 0f8e2403d4d4
parent 1006 e50f134e41b7
child 1419 04b8e8af2e66
Deal with narval's workflow changes starttime and endtime are gone, replace them by looking at related TrInfo entities. The starttime facet is being removed though, hopefully temporarily.
__pkginfo__.py
entities.py
hooks.py
migration/3.0.0_Any.py
migration/postcreate.py
test/unittest_hooks.py
test/utils.py
views/__init__.py
views/facets.py
views/primary.py
views/testexecution.py
views/tracker.py
--- a/__pkginfo__.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/__pkginfo__.py	Thu May 02 12:40:58 2013 +0200
@@ -23,7 +23,7 @@
                'cubicweb': '>= 3.14.0',
                'cubicweb-vcsfile': '>= 1.12',
                'cubicweb-file': None,
-               'cubicweb-narval': '>= 3.0.2',
+               'cubicweb-narval': '>= 4',
                }
 __recommends__ = {'cubicweb-tracker': None,
                   'cubicweb-nosylist': '>= 0.5.0',
--- a/entities.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/entities.py	Thu May 02 12:40:58 2013 +0200
@@ -132,8 +132,14 @@
             assert False, 'unknow vid %s' % vid
 
     def rss_rql(self, vid='rss'):
-        return 'TestExecution TE ORDERBY SD DESC LIMIT %i WHERE TE using_config TC, TE starttime SD, TC use_environment PE, PE eid %i'\
-               % (self.RSS_LIMIT, self.eid)
+        return ('TestExecution TE ORDERBY is_null(SD) DESC, SD DESC LIMIT %i '
+                'WHERE TE using_config TC,'
+                '      TC use_environment PE,'
+                '      PE eid %i,'
+                '      TR? wf_info_for TE,'
+                '      TR creation_date SD,'
+                '      TR tr_count 0'
+                % (self.RSS_LIMIT, self.eid))
 
     # cube specific logic #####################################################
 
@@ -221,8 +227,13 @@
             assert False, 'unknow vid %s' % vid
 
     def rss_rql(self, vid='rss'):
-        return 'TestExecution TE ORDERBY SD DESC LIMIT %i WHERE TE using_config TC, TE starttime SD, TC eid %i'\
-               % (self.RSS_LIMIT, self.eid)
+        return ('TestExecution TE ORDERBY is_null(SD) DESC, SD DESC LIMIT %i '
+                'WHERE TE using_config TC,'
+                '      TR? wf_info_for TE,'
+                '      TR tr_count 0,'
+                '      TR creation_date SD,'
+                '      TC eid %i'
+               % (self.RSS_LIMIT, self.eid))
 
     # cube specific logic #####################################################
 
@@ -325,7 +336,7 @@
         if self.starttime:
             return self._cw._('%(date)s: %(status)s') % {
                 'status': self.printable_value('status'),
-                'date': self.printable_value('starttime')}
+                'date': self._cw.format_date(self.starttime, time=True)}
         else:
             return '<%(status)s>' % {
                 'status': self.printable_value('status'),
@@ -337,7 +348,7 @@
                 'status': self.printable_value('status'),
                 'config': self.configuration.dc_title(),
                 'pe': self.environment.dc_title(),
-                'date': self.printable_value('starttime')}
+                'date': self._cw.format_date(self.starttime, time=True)}
         else:
             return self._cw._('%(pe)s/%(config)s <%(status)s>') % {
                 'status': self.printable_value('status'),
@@ -363,10 +374,22 @@
 
     def rss_rql(self, vid='rss'):
         if self.branch is None:
-            return 'TestExecution TE ORDERBY SD DESC LIMIT %i WHERE TE using_config TC, TC eid %i, TE branch NULL, TE starttime SD'\
+            return 'TestExecution TE ORDERBY is_null(SD) DESC, SD DESC LIMIT %i '\
+                   'WHERE TE using_config TC,'\
+                   '      TR? wf_info_for TE,'\
+                   '      TR tr_count 0,'\
+                   '      TR creation_date SD,'\
+                   '      TC eid %i,'\
+                   '      TE branch NULL'\
                    % (self.RSS_LIMIT, self.configuration.eid)
         else:
-            return 'TestExecution TE ORDERBY SD DESC LIMIT %i WHERE TE using_config TC, TC eid %i, TE branch "%s", TE starttime SD'\
+            return 'TestExecution TE ORDERBY is_null(SD) DESC, SD DESC LIMIT %i '\
+                   'WHERE TE using_config TC,'\
+                   '      TR? wf_info_for TE,'\
+                   '      TR tr_count 0,'\
+                   '      TR creation_date SD,'\
+                   '      TC eid %i,'\
+                   '      TE branch "%s"'\
                    % (self.RSS_LIMIT, self.configuration.eid, self.branch)
 
     # cube specific logic #####################################################
@@ -432,8 +455,7 @@
 
 class CheckResult(AnyEntity):
     __regid__ = 'CheckResult'
-    fetch_attrs, cw_fetch_order = fetch_config(['starttime', 'endtime',
-                                             'name', 'status'])
+    fetch_attrs, cw_fetch_order = fetch_config(['name', 'status'])
 
     def absolute_url(self, *args, **kwargs):
         kwargs.setdefault('tab', domid(self.anchored_name))
--- a/hooks.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/hooks.py	Thu May 02 12:40:58 2013 +0200
@@ -12,6 +12,7 @@
 from datetime import datetime, timedelta
 
 from cubicweb import ValidationError
+from cubicweb.predicates import on_fire_transition
 from cubicweb.selectors import is_instance
 from cubicweb.uilib import text_cut
 from cubicweb.server import hook, session
@@ -233,8 +234,8 @@
         entity = self.cw_rset.get_entity(0, 0)
         prevexec = entity.previous_execution()
         ctx  = super(ExecStatusChangeView, self).context()
-        ctx['ex1time'] = prevexec.printable_value('starttime')
-        ctx['ex2time'] = entity.printable_value('starttime')
+        ctx['ex1time'] = self._cw.format_date(prevexec.starttime, time=True)
+        ctx['ex2time'] = self._cw.format_date(entity.starttime, time=True)
         ctx['branch'] = entity.branch
         chgs = []
         _ = self._cw._
@@ -278,23 +279,20 @@
 
 class TestExecutionUpdatedHook(hook.Hook):
     __regid__ = 'apycot.te.status_change'
-    __select__ = hook.Hook.__select__ & is_instance('TestExecution')
-    events = ('before_update_entity',)
+    __select__ = hook.Hook.__select__ & (
+            on_fire_transition('TestExecution', 'end') |
+            on_fire_transition('TestExecution', 'fail') |
+            on_fire_transition('TestExecution', 'kill'))
+    events = ('after_add_entity',)
 
     def __call__(self):
-        # end of test execution : set endtime
-        entity = self.entity
-        if 'endtime' in entity.cw_edited and entity.status_changes():
+        trinfo = self.entity
+        te = trinfo.for_entity
+        if te.status_changes():
             view = self._cw.vreg['views'].select(
-                'apycot.notif.exstchange', self._cw, rset=entity.cw_rset,
-                row=entity.cw_row, col=entity.cw_col)
+                'apycot.notif.exstchange', self._cw, rset=te.as_rset(),
+                row=0, col=0)
             notifhooks.RenderAndSendNotificationView(self._cw, view=view)
-        if 'execution_status' in entity.cw_edited and \
-               entity.status == 'waiting execution':
-            status = entity.execution_status
-            if status == 'failed':
-                status = 'error'
-            entity['status'] = entity.execution_status
 
 
 try:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/migration/3.0.0_Any.py	Thu May 02 12:40:58 2013 +0200
@@ -0,0 +1,2 @@
+rql('SET WF workflow_of TE, TE default_workflow WF WHERE WF workflow_of P, P name "Plan", TE name "TestExecution"')
+commit()
--- a/migration/postcreate.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/migration/postcreate.py	Thu May 02 12:40:58 2013 +0200
@@ -8,6 +8,10 @@
 wf.add_transition(_('activate'), deactivated, activated,
                   requiredgroups=('managers',))
 
+# workflows don't consider schema inheritance, so we need to set it explicitly
+rql('SET WF workflow_of TE, TE default_workflow WF WHERE WF workflow_of P, P name "Plan", TE name "TestExecution"')
+commit()
+
 from cubes.apycot import recipes
 recipes.create_quick_recipe(session)
 
--- a/test/unittest_hooks.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/test/unittest_hooks.py	Thu May 02 12:40:58 2013 +0200
@@ -29,19 +29,34 @@
     def test_exec_status_change(self):
         self.login('narval', password='narval0')
         plan = self.start_lgc_tests()
+        plan = self.request().entity_from_eid(plan.eid)
         self.dumb_execution(plan, [('unittest', 'success'), ('coverage', 'success')])
+        plan.cw_adapt_to('IWorkflowable').fire_transition('start')
+        self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('end')
+        self.commit()
+        self.assertEqual(MAILBOX, [])
+        self.commit()
+        self.assertEqual(len(MAILBOX), 0)
+        plan = self.start_lgc_tests()
+        plan = self.request().entity_from_eid(plan.eid)
+        self.dumb_execution(plan, [('unittest', 'success'), ('coverage', 'success')])
+        plan.cw_adapt_to('IWorkflowable').fire_transition('start')
+        self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('end')
+        self.commit()
         self.assertEqual(len(MAILBOX), 0)
         self.commit()
         self.assertEqual(len(MAILBOX), 0)
         plan = self.start_lgc_tests()
-        self.dumb_execution(plan, [('unittest', 'success'), ('coverage', 'success')])
-        self.assertEqual(len(MAILBOX), 0)
-        self.commit()
-        self.assertEqual(len(MAILBOX), 0)
-        plan = self.start_lgc_tests()
+        plan = self.request().entity_from_eid(plan.eid)
         self.dumb_execution(plan, [('unittest', 'failure'), ('coverage', 'failure')])
         self.assertEqual(len(MAILBOX), 0, MAILBOX)
         self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('start')
+        self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('end')
+        self.commit()
         self.assertEqual(len(MAILBOX), 1)
         self.assertEqual(MAILBOX[0].recipients, ['admin@cubicweb.org'])
         self.assertEqual(MAILBOX[0].message.get('Subject'),
@@ -61,11 +76,21 @@
     def test_exec_one_status_change(self):
         self.login('narval', password='narval0')
         plan = self.start_lgc_tests()
+        plan = self.request().entity_from_eid(plan.eid)
         self.dumb_execution(plan, [('unittest', 'success'), ('coverage', 'success')])
         self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('start')
+        self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('end')
+        self.commit()
         plan = self.start_lgc_tests()
+        plan = self.request().entity_from_eid(plan.eid)
         self.dumb_execution(plan, [('unittest', 'failure')])
         self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('start')
+        self.commit()
+        plan.cw_adapt_to('IWorkflowable').fire_transition('end')
+        self.commit()
         self.assertEqual(len(MAILBOX), 1)
         self.assertEqual(MAILBOX[0].recipients, ['admin@cubicweb.org'])
         self.assertEqual(MAILBOX[0].message.get('Subject'),
--- a/test/utils.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/test/utils.py	Thu May 02 12:40:58 2013 +0200
@@ -172,5 +172,5 @@
             req.execute('SET X during_execution Y WHERE X eid %(x)s, Y eid %(e)s',
                         {'x': cr.eid, 'e': ex.eid})
         if setend:
-            req.execute('SET X starttime %(et)s, X endtime %(et)s, X status "success" '
-                        'WHERE X eid %(x)s', {'et': datetime.now(), 'x': ex.eid})
+            req.execute('SET X status "success" '
+                        'WHERE X eid %(x)s', {'x': ex.eid})
--- a/views/__init__.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/views/__init__.py	Thu May 02 12:40:58 2013 +0200
@@ -52,9 +52,10 @@
         if cls.__regid__ == 'TestExecution':
             # XXX query duplicated from TESummaryTable
             rset = req.execute(
-                    'Any T,PE,TC,T,TB,TST,TET,TF, TS ORDERBY TST DESC WHERE '
+                    'Any T,PE,TC,T,TB,TF, TS ORDERBY is_null(TST) DESC, TST DESC WHERE '
                     'T status TS, T using_config TC, T using_environment PE, '
-                    'T branch TB, T starttime TST, T endtime TET, T log_file TF?')
+                    'TR? wf_info_for T, TR creation_date TST, TR tr_count 0, '
+                    'T branch TB, T log_file TF?')
             req.form['displayfilter'] = ''
             req.form['vid'] = 'apycot.te.summarytable'
             return None, rset
--- a/views/facets.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/views/facets.py	Thu May 02 12:40:58 2013 +0200
@@ -53,11 +53,13 @@
     i18nable = False
     order = 3
 
-class TestExecutionStarttimeFacet(facet.DateRangeFacet):
-    __regid__ = 'apycot.te.starttime'
-    __select__ = facet.DateRangeFacet.__select__ & is_instance('TestExecution')
-    rtype = 'starttime'
-    order = 4
+#TODO bring this back with a slider widget
+#class TestExecutionStarttimeFacet(facet.RQLPathFacet):
+#    __regid__ = 'apycot.te.starttime'
+#    __select__ = facet.RQLPathFacet.__select__ & is_instance('TestExecution')
+#    order = 4
+#    path = ['TI wf_info_for X', 'TI by_transition T', 'T name "start"', 'TI creation_date D']
+#    filter_variable = 'D'
 
 class TestExecutionLogFileFacet(facet.HasRelationFacet):
     __regid__ = 'apycot.te.logfile'
--- a/views/primary.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/views/primary.py	Thu May 02 12:40:58 2013 +0200
@@ -140,9 +140,10 @@
                                             {'e': entity.eid})
             self.wview('projectgraphtestresults', project_rset, 'null')
         rset = self._cw.execute(
-            'Any T,TC,T,TB,TST,TET,TF, TS ORDERBY TST DESC WHERE '
+            'Any T,TC,T,TB,TF, TS ORDERBY is_null(TST) DESC, TST DESC WHERE '
             'T status TS, T using_config TC, T branch TB, '
-            'T starttime TST, T endtime TET, T log_file TF?, '
+            'TR? wf_info_for T, TR creation_date TST, TR tr_count 0, '
+            'T log_file TF?, '
             'T using_environment PE, PE eid %(pe)s',
             {'pe': entity.eid})
         self.wview('apycot.te.nopetable', rset, 'noresult')
@@ -251,9 +252,10 @@
 
     def entity_call(self, entity):
         rset = self._cw.execute(
-            'Any T,PE,T,TB,TST,TET,TF, TS,PEN ORDERBY TST DESC WHERE '
+            'Any T,PE,T,TB,TF, TS,PEN ORDERBY is_null(TST) DESC, TST DESC WHERE '
             'T status TS, T using_config TC, T using_environment PE, '
-            'T branch TB, T starttime TST, T endtime TET, T log_file TF?, '
+            'T branch TB, T log_file TF?, '
+            'TR? wf_info_for T, TR creation_date TST, TR tr_count 0, '
             'PE name PEN, TC eid %(tc)s',
             {'tc': entity.eid})
         self.wview('apycot.te.notctable', rset, 'noresult')
--- a/views/testexecution.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/views/testexecution.py	Thu May 02 12:40:58 2013 +0200
@@ -50,10 +50,7 @@
         if not changes_only:
             if exc.endtime is not None:
                 nb_checkers = len(exc.checkers)
-                if exc.starttime:
-                    duration = exc.endtime - exc.starttime
-                else:
-                    duration = self._cw._('unknown duration')
+                duration = exc.duration or self._cw._('unknown duration')
                 if nb_checkers > 1:
                     w(u'<p>')
                     w(_(u'%(nb)s checkers run in %(dur)s')
@@ -104,9 +101,10 @@
     category = 'startupview'
 
     html_headers = no_robot_index
-    default_rql = ('Any T,PE,TC,T,TB,TST,TET,TF, TS ORDERBY TST DESC WHERE '
+    default_rql = ('Any T,PE,TC,T,TB,TF, TS ORDERBY is_null(TST) DESC, TST DESC WHERE '
                    'T status TS, T using_config TC, T using_environment PE, '
-                   'T branch TB, T starttime TST, T endtime TET, T log_file TF?')
+                   'TR? wf_info_for T, TR creation_date TST, TR tr_count 0, '
+                   'T branch TB, T log_file TF?')
 
     def call(self):
         w = self.w
@@ -128,6 +126,8 @@
                 header=_('TestConfig'), getrelated=lambda x: x.configuration),
             'projectenvironment': tableview.RelatedEntityColRenderer(
                 header=_('ProjectEnvironment'), getrelated=lambda x: x.environment),
+            'starttime': tableview.EntityTableColRenderer(lambda w, p: w(p._cw.format_date(p.starttime, time=True))),
+            'endtime': tableview.EntityTableColRenderer(lambda w, p: w(p._cw.format_date(p.endtime, time=True))),
             'checks': tableview.MainEntityColRenderer(
                 header=_('checks'), addcount=False, vid='apycot.te.summarycell'),
             'log_file': tableview.RelationColRenderer(
@@ -232,7 +232,7 @@
     __select__ = is_instance('TestExecution')
     def cell_call(self, row, col):
         entity = self.cw_rset.get_entity(row, col)
-        self.w(entity.printable_value('starttime'))
+        self.w(self._cw.format_date(entity.starttime, time=True))
 
 
 class TEStatusCell(tabs.PrimaryTab):
@@ -296,7 +296,7 @@
         if entity.starttime:
             status = self._cw._('%(date)s: %(status)s') % {
                 'status': status,
-                'date': entity.printable_value('starttime')}
+                'date': self._cw.format_date(entity.starttime, time=True)}
         else:
             status = '[%s]' % status
         self.w('<a href="%s" title="%s">%s</a>' % (
@@ -317,7 +317,7 @@
                 'config': xml_escape(entity.configuration.dc_title()),
                 'pe': xml_escape(entity.environment.dc_title()),
                 'status': status,
-                'date': entity.printable_value('starttime')}
+                'date': self._cw.format_date(entity.starttime, time=True)}
         else:
             status = self._cw._('%(pe)s/%(config)s &lt;%(status)s&gt;') % {
                 'config': xml_escape(entity.configuration.dc_title()),
--- a/views/tracker.py	Tue Jul 23 15:12:25 2013 +0200
+++ b/views/tracker.py	Thu May 02 12:40:58 2013 +0200
@@ -39,8 +39,10 @@
             label = '%s : %s - %s' % (_(u'Test run time'), testconfig[1], branch[0])
             rql = '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, ' \
+            'START wf_info_for TE, START tr_count 0, START creation_date ST, ' \
+            'END wf_info_for TE, END tr_count 1, END creation_date ET, ' \
+            'P has_apycot_environment TENV,' \
+            'TE eid E, TE status S, P eid %(p)s, ' \
             'TE using_config TC, TC eid %(tc)s, TE branch "%(branch)s"'
             var_dict['tc'] = testconfig[0]
             rset = form._cw.execute(rql % var_dict)
@@ -53,9 +55,10 @@
         for cri_label, label in (('pylint.evaluation', 'Pylint score'),
                                  ('cover-line-rate', 'Cover line rate')):
             label = '%s : %s' % (_(label), branch[0])
-            rql = 'Any V ORDERBY D LIMIT 50 WHERE X is CheckResultInfo, X label "%(cri_label)s", ' \
+            rql = 'Any V ORDERBY D DESC LIMIT 50 WHERE X is CheckResultInfo, X label "%(cri_label)s", ' \
                   'X value V, X for_check CR, CR during_execution TE, TE using_environment TENV, '\
-                  'P has_apycot_environment TENV, P eid %(p)s, TE starttime D, TE branch "%(branch)s" '
+                  'TR wf_info_for TE, TR creation_date D, ' \
+                  'P has_apycot_environment TENV, P eid %(p)s, TE branch "%(branch)s" '
             var_dict['cri_label'] = cri_label
             rset = form._cw.execute(rql % var_dict)
             if rset and len(rset) > MIN_NB_RUNS_IN_GRAPH:
@@ -102,9 +105,10 @@
         if 'jqplot' in self._cw.vreg.config.cubes():
             self.wview('projectgraphtestresults', self.cw_rset, 'null')
         rset = self._cw.execute(
-            'Any T,TC,T,TB,TST,TET,TF, TS ORDERBY TST DESC WHERE '
+            'Any T,TC,T,TB,TF, TS ORDERBY is_null(TST) DESC, TST DESC WHERE '
             'T status TS, T using_config TC, T branch TB, '
-            'T starttime TST, T endtime TET, T log_file TF?, '
+            'T log_file TF?, '
+            'TR? wf_info_for T, TR tr_count 0, TR creation_date TST, '
             'T using_environment PE, P has_apycot_environment PE, '
             'P eid %(p)s', {'p': entity.eid})
         self.wview('apycot.te.nopetable', rset, 'noresult')