backport stable
authorSylvain Thénault <sylvain.thenault@logilab.fr>
Thu, 22 Dec 2011 17:43:34 +0100
changeset 808 2118c22e12d6
parent 780 82e5bc11b293 (current diff)
parent 807 109c2fe8dd82 (diff)
child 811 b46fb7e23870
backport stable
--- a/.hgtags	Fri Oct 07 16:56:17 2011 +0200
+++ b/.hgtags	Thu Dec 22 17:43:34 2011 +0100
@@ -70,3 +70,7 @@
 29da7647fd8d7c59e40829bcecdfc01b94b523e2 apycot-debian-version-2.1.0-1
 bd5595577b1b419dd1376fdf127dee617e74481a apycot-version-2.1.1
 6f767ca1279bdde91a09fad1e5bfa8ee6a54f4c8 apycot-debian-version-2.1.1-1
+d2e8558a7e91f6bd655c260ce2f768977fc3b8f7 apycot-version-2.2.0
+5b157776204a3d8afcbb3443a0c5925c72b8e854 apycot-debian-version-2.2.0-1
+35bdc8c657d6d85611e934b080a0e69a174851ae apycot-version-2.2.1
+74f6596c6246308afc32f6a08d390fddfb7fb635 apycot-debian-version-2.2.1-1
--- a/__pkginfo__.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/__pkginfo__.py	Thu Dec 22 17:43:34 2011 +0100
@@ -4,7 +4,7 @@
 modname = 'apycot'
 distname = 'apycot'
 
-numversion = (2, 1, 1)
+numversion = (2, 2, 1)
 version = '.'.join(str(num) for num in numversion)
 
 license = 'GPL'
@@ -20,7 +20,7 @@
     ]
 
 __depends__ = {'pyro': None,
-               'cubicweb': '>= 3.10.0',
+               'cubicweb': '>= 3.14.0',
                'cubicweb-vcsfile': '>= 1.6.1',
                'cubicweb-file': None,
                'cubicweb-narval': '>= 3.0.2',
--- a/_apycotlib/atest.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/_apycotlib/atest.py	Thu Dec 22 17:43:34 2011 +0100
@@ -81,8 +81,8 @@
         self.environ.update(self.environment.apycot_process_environment())
         self.environ.setdefault('LC_ALL', 'fr_FR.UTF-8') # XXX force utf-8
         self._substitute(self.environment, self.environ)
-        # track environment change to be able to restore it latter
-        # note sys.path is synchronized with the PYTHONPATH environment variable
+        # Track environment change to be able to restore it later.
+        # Notice sys.path is synchronized with the PYTHONPATH environment variable
         self._tracks = {}
         # flag indicating whether to clean test environment after test execution
         # or if an archive containing it should be uploaded
--- a/_apycotlib/checkers/python.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/_apycotlib/checkers/python.py	Thu Dec 22 17:43:34 2011 +0100
@@ -1,7 +1,7 @@
 """checkers for python source files
 
 :organization: Logilab
-:copyright: 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:copyright: 2003-2011 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
 """
@@ -56,9 +56,15 @@
         from logilab.devtools.lib.pkginfo import PackageInfo
         pkginfo = PackageInfo(directory=test.project_path())
         modname = getattr(pkginfo, 'modname', None)
+        distname = getattr(pkginfo, 'distname', None)
         package = getattr(pkginfo, 'subpackage_of', None)
         if modname and package:
             modname = '%s.%s' % (package, modname)
+        elif distname.startswith('cubicweb-'):
+            cubespdir = join(os.environ['APYCOT_ROOT'], 'local', 'share', 'cubicweb')
+            pypath = cubespdir + os.pathsep + os.environ.get('PYTHONPATH', '')
+            test.update_env(test.tconfig.name, 'PYTHONPATH', pypath, os.pathsep)
+            return join(cubespdir, 'cubes', modname)
     if modname:
         try:
             path = join(INSTALL_PREFIX[test.project_path()], *modname.split('.'))
--- a/_apycotlib/repositories.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/_apycotlib/repositories.py	Thu Dec 22 17:43:34 2011 +0100
@@ -150,8 +150,7 @@
     def _ref_repo(self):
         if self.repository.local_cache:
             cnx = self.repository._cw.cnx
-            cacheroot = cnx.get_option_value('local-repo-cache-root',
-                                             self.repository.eid)
+            cacheroot = cnx.get_option_value('local-repo-cache-root')
             path = osp.join(cacheroot, self.repository.local_cache)
             if osp.exists(path):
                 return path
--- a/data/cubes.apycot.css	Fri Oct 07 16:56:17 2011 +0200
+++ b/data/cubes.apycot.css	Thu Dec 22 17:43:34 2011 +0100
@@ -78,13 +78,13 @@
 
 .status_error, .status_error a, .status_error em,  a.status_error{
     text-align : justify;
-    color : blue !important;
+    color : purple !important;
     font-weight: bold;
 }
 
 .status_killed, .status_killed a, .status_killed em, a.status_killed{
     text-align : justify;
-    color : blue !important;
+    color : purple !important;
     font-weight: bold;
 }
 
--- a/debian/changelog	Fri Oct 07 16:56:17 2011 +0200
+++ b/debian/changelog	Thu Dec 22 17:43:34 2011 +0100
@@ -1,3 +1,15 @@
+apycot (2.2.1-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Thu, 22 Dec 2011 12:21:54 +0100
+
+apycot (2.2.0-1) unstable; urgency=low
+
+  * new upstream release
+
+ -- Sylvain Thénault <sylvain.thenault@logilab.fr>  Wed, 09 Nov 2011 18:02:28 +0100
+
 apycot (2.1.1-1) unstable; urgency=low
 
   * new upstream release
--- a/debian/control	Fri Oct 07 16:56:17 2011 +0200
+++ b/debian/control	Thu Dec 22 17:43:34 2011 +0100
@@ -13,7 +13,7 @@
 Package: cubicweb-apycot
 Architecture: all
 XB-Python-Version: ${python:Versions}
-Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (>= 3.10.0), cubicweb-vcsfile (>= 1.6.1), cubicweb-file (>= 1.8.2), cubicweb-narval (>= 3.0.2), pyro
+Depends: ${misc:Depends}, ${python:Depends}, cubicweb-common (>= 3.14.0), cubicweb-vcsfile (>= 1.6.1), cubicweb-file (>= 1.8.2), cubicweb-narval (>= 3.0.2), pyro
 Suggests: cubicweb-tracker, cubicweb-nosylist (>= 0.5.0)
 Description: apycot component for the CubicWeb framework
  This CubicWeb component store data from the Apycot testing framework
--- a/entities.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/entities.py	Thu Dec 22 17:43:34 2011 +0100
@@ -108,7 +108,7 @@
 class ProjectEnvironment(RefinementMixIn, ExecutionRSSMixin, AnyEntity):
     __regid__ = 'ProjectEnvironment'
 
-    fetch_attrs, fetch_order = fetch_config(['name', 'check_config', 'check_environment'])
+    fetch_attrs, cw_fetch_order = fetch_config(['name', 'check_config', 'check_environment'])
 
     # rss related methods #####################################################
 
@@ -194,7 +194,7 @@
 class TestConfig(RefinementMixIn, ExecutionRSSMixin, AnyEntity):
     __regid__ = 'TestConfig'
 
-    fetch_attrs, fetch_order = fetch_config(['name', 'label', 'check_config',
+    fetch_attrs, cw_fetch_order = fetch_config(['name', 'label', 'check_config',
                                              'check_environment'])
 
     def dc_title(self):
@@ -299,7 +299,7 @@
             #                 if not dup_arch:
             #                     duplicate.set_attributes(archive=False)
         else:
-            options = u'archive=%s' % archive
+            options = u'archive=%s' % archive if archive else u''
             texec = self._cw.create_entity(
                 'TestExecution', priority=priority, arguments=arguments,
                 options=options, execution_of=self.recipe,
@@ -427,7 +427,7 @@
 
 class CheckResult(AnyEntity):
     __regid__ = 'CheckResult'
-    fetch_attrs, fetch_order = fetch_config(['starttime', 'endtime',
+    fetch_attrs, cw_fetch_order = fetch_config(['starttime', 'endtime',
                                              'name', 'status'])
 
     def absolute_url(self, *args, **kwargs):
@@ -441,7 +441,7 @@
 
 class CheckResultInfo(AnyEntity):
     __regid__ = 'CheckResultInfo'
-    fetch_attrs, fetch_order = fetch_config(['type', 'label', 'value'])
+    fetch_attrs, cw_fetch_order = fetch_config(['type', 'label', 'value'])
 
     @property
     def check_result(self):
--- a/hooks.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/hooks.py	Thu Dec 22 17:43:34 2011 +0100
@@ -14,7 +14,7 @@
 from cubicweb import ValidationError
 from cubicweb.selectors import is_instance
 from cubicweb.uilib import text_cut
-from cubicweb.server import hook
+from cubicweb.server import hook, session
 from cubicweb.hooks import notification as notifhooks
 from cubicweb.sobjects import notification as notifviews
 
@@ -124,7 +124,7 @@
 
 class StartTestAfterAddRevision(hook.Hook):
     __regid__ = 'apycot.start_test_on_new_rev'
-    __select__ = hook.Hook.__select__ & is_instance('Revision')
+    __select__ = hook.Hook.__select__ & is_instance('Revision') & ~session.repairing()
     events = ('after_add_entity',)
 
     def __call__(self):
--- a/i18n/en.po	Fri Oct 07 16:56:17 2011 +0200
+++ b/i18n/en.po	Thu Dec 22 17:43:34 2011 +0100
@@ -25,7 +25,7 @@
 msgstr ""
 
 #, python-format
-msgid "%(pe)s/%(config)s <%(status)s>"
+msgid "%(pe)s/%(config)s &lt;%(status)s&gt;"
 msgstr ""
 
 #, python-format
@@ -556,9 +556,6 @@
 msgid "local_repository_object"
 msgstr "used by test config"
 
-msgid "log"
-msgstr ""
-
 msgctxt "CheckResult"
 msgid "log"
 msgstr ""
--- a/i18n/fr.po	Fri Oct 07 16:56:17 2011 +0200
+++ b/i18n/fr.po	Thu Dec 22 17:43:34 2011 +0100
@@ -32,7 +32,7 @@
 msgstr "%(nb)s vérifications exécutées en %(dur)s"
 
 #, python-format
-msgid "%(pe)s/%(config)s <%(status)s>"
+msgid "%(pe)s/%(config)s &lt;%(status)s&gt;"
 msgstr ""
 
 #, python-format
@@ -577,9 +577,6 @@
 msgid "local_repository_object"
 msgstr "utilisé par"
 
-msgid "log"
-msgstr "log"
-
 msgctxt "CheckResult"
 msgid "log"
 msgstr "log"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/migration/2.1.2_Any.py	Thu Dec 22 17:43:34 2011 +0100
@@ -0,0 +1,1 @@
+sync_schema_props_perms('File', syncprops=False)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/migration/2.2.1_Any.py	Thu Dec 22 17:43:34 2011 +0100
@@ -0,0 +1,7 @@
+
+for step in rql('RecipeStep RS WHERE RS target %(target)s', {'target': u'apycot.get_dependancies'}).entities():
+    step.set_attributes(target=u'apycot.get_dependencies')
+
+rql('DELETE Recipe R WHERE R name IN ("apycot.recipe.debian", "apycot.recipe.experimental")')
+
+commit()
--- a/migration/create_recipes.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/migration/create_recipes.py	Thu Dec 22 17:43:34 2011 +0100
@@ -9,7 +9,6 @@
 
 # define new recipes for current test config
 rql('SET X use_recipe Y WHERE X name "quick", Y name "apycot.recipe.quick"')
-rql('SET X use_recipe Y WHERE X name "package", Y name "apycot.recipe.debian"')
 rql('SET X use_recipe Y WHERE X name "full", Y name "apycot.recipe.full"')
 
 commit()
--- a/narval/apycot.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/narval/apycot.py	Thu Dec 22 17:43:34 2011 +0100
@@ -60,8 +60,8 @@
 
 @input('apycot', 'isinstance(elmt, Test)')
 @output('projectenvs', list=True)
-@action('apycot.get_dependancies')
-def act_get_dependancies(inputs):
+@action('apycot.get_dependencies')
+def act_get_dependencies(inputs):
     """Checkout repository for a test configuration"""
     tconfig = inputs['apycot'].tconfig
     environment = inputs['apycot'].environment
--- a/recipes.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/recipes.py	Thu Dec 22 17:43:34 2011 +0100
@@ -1,7 +1,7 @@
 def create_quick_recipe(session):
     recipe = session.create_entity('Recipe', name=u'apycot.recipe.quick')
     init = recipe.add_step(u'action', u'apycot.init', initial=True)
-    getdeps = init.add_next_step(u'action', u'apycot.get_dependancies')
+    getdeps = init.add_next_step(u'action', u'apycot.get_dependencies')
     checkout = getdeps.add_next_step(u'action', u'apycot.checkout', for_each=u'projectenv')
     install = checkout.add_next_step(u'action', u'apycot.install', for_each=u'projectenv')
     pyunit = install.add_next_step(u'action', u'apycot.pyunit', final=True)
@@ -9,7 +9,7 @@
 def create_full_recipe(session):
     recipe = session.create_entity('Recipe', name=u'apycot.recipe.full')
     init = recipe.add_step(u'action', u'apycot.init', initial=True)
-    getdeps = init.add_next_step(u'action', u'apycot.get_dependancies')
+    getdeps = init.add_next_step(u'action', u'apycot.get_dependencies')
     checkout = getdeps.add_next_step(u'action', u'apycot.checkout', for_each=u'projectenv')
     install = checkout.add_next_step(u'action', u'apycot.install', for_each=u'projectenv')
     pylint = recipe.add_step(u'action', u'apycot.pylint')
@@ -20,33 +20,3 @@
     recipe.add_transition((pylint, pycoverage),
                           recipe.add_step(u'action', u'basic.noop', final=True))
     return recipe
-
-def create_debian_recipe(session):
-    recipe = session.create_entity('Recipe', name=u'apycot.recipe.debian')
-    step1 = recipe.add_step(u'action', u'apycot.init', initial=True)
-    step2 = recipe.add_step(u'action', u'apycot.checkout')
-    step3 = recipe.add_step(u'action', u'apycot.lgp.check')
-    step3bis = recipe.add_step(u'action', u'apycot.lgp.build')
-    step4 = recipe.add_step(u'action', u'apycot.lintian')
-    step5 = recipe.add_step(u'action', u'basic.noop', final=True)
-    recipe.add_transition(step1, step2)
-    recipe.add_transition(step2, (step3, step3bis))
-    recipe.add_transition(step3bis, step4)
-    recipe.add_transition((step3, step4), step5)
-    return recipe
-
-def create_experimental_recipe(session):
-    recipe = session.create_entity('Recipe', name=u'apycot.recipe.experimental')
-    step1 = recipe.add_step(u'recipe', u'apycot.recipe.debian', initial=True)
-    step2 = recipe.add_step(u'action', u'apycot.ldi.upload')
-    step3 = recipe.add_step(u'action', u'apycot.ldi.publish', final=True)
-    recipe.add_transition(step1, step2)
-    recipe.add_transition(step2, step3)
-    return recipe
-
-# def create_publish_recipe(session):
-#     # XXX
-#     # copy/upload from logilab-experimental to logilab-public
-#     # example: ldi upload logilab-public /path/to/experimental/repo/dists/*/*.changes
-#     recipe = session.create_entity('Recipe', name=u'apycot.recipe.publish')
-#     return recipe
--- a/test/unittest_apycot.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/test/unittest_apycot.py	Thu Dec 22 17:43:34 2011 +0100
@@ -63,7 +63,8 @@
         stream = []
         log_to_html(self.request(), '', self.checks.get_entity(0, 0).log, stream.append)
         log_html = '\n'.join(stream)
-        self.assertXMLStringWellFormed(CW_NAMESPACE_DIV % log_html)
+        self.assertWellFormed(self.get_validator(content_type='application/xml'),
+                              CW_NAMESPACE_DIV % log_html)
         for pattern, count in (
                 ('<table class="listing" id="">', 1),
                 ('<tr class="logError"', 1),
@@ -84,7 +85,8 @@
         stream = []
         log_to_html(self.request(), '', self.checks.get_entity(1, 0).log, stream.append)
         log_html = '\n'.join(stream)
-        self.assertXMLStringWellFormed(CW_NAMESPACE_DIV % log_html)
+        self.assertWellFormed(self.get_validator(content_type='application/xml'),
+                              CW_NAMESPACE_DIV % log_html)
         for pattern, count in (
                 ('<table class="listing" id="">', 1),
                 ('<tr class="logDebug"', 1),
--- a/test/unittest_hooks.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/test/unittest_hooks.py	Thu Dec 22 17:43:34 2011 +0100
@@ -89,7 +89,7 @@
                      {'pe': self.lgce.eid})
         self.commit()
         # do not propagate to existing test execution
-        self.failIf('anon' in [u.login for u in plan.nosy_list])
+        self.assertFalse('anon' in [u.login for u in plan.nosy_list])
 
 
 HGREPO = os.path.join(INPUTS_DIR, u'hgrepo')
--- a/test/unittest_writer.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/test/unittest_writer.py	Thu Dec 22 17:43:34 2011 +0100
@@ -31,8 +31,8 @@
             path, line, msg = self.writer._msg_info('oops %s', 'badaboum', tb=True)
         self.assertEqual(path, None)
         self.assertEqual(line, None)
-        self.failUnless(msg.startswith('oops badaboum'))
-        self.failUnless('Traceback' in msg)
+        self.assertTrue(msg.startswith('oops badaboum'))
+        self.assertTrue('Traceback' in msg)
 
 
 if __name__ == '__main__':
--- a/test/utils.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/test/utils.py	Thu Dec 22 17:43:34 2011 +0100
@@ -138,7 +138,8 @@
             check_environment=u'SETUPTOOLS=1\nDISPLAY=:2.0'
             )
         self.vcsrepo = req.create_entity('Repository', type=u'mercurial',
-                                         source_url=u'http://www.logilab.org/src/logilab/common',
+                                         # use path to avoid clone attempt when using url
+                                         path=u'/src/logilab/common',
                                          reverse_local_repository=self.lgce)
         self.pyp = req.create_entity('TestConfig', name=u'PYTHONPACKAGE',
                                      check_config=u'python_lint_treshold=7\n'
--- a/views/primary.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/views/primary.py	Thu Dec 22 17:43:34 2011 +0100
@@ -135,14 +135,14 @@
 
     html_headers = no_robot_index
 
-    def cell_call(self, row, col):
+    def entity_call(self, entity):
         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, '
             'T starttime TST, T endtime TET, T log_file TF?, '
             'T using_environment PE, PE eid %(pe)s',
-            {'pe': self.cw_rset[row][col]})
-        self.wview('apycot.te.summarytable', rset, 'noresult', showpe=False)
+            {'pe': entity.eid})
+        self.wview('apycot.te.nopetable', rset, 'noresult')
 
 
 class PETestConfigView(EntityView):
@@ -246,14 +246,14 @@
 
     html_headers = no_robot_index
 
-    def cell_call(self, row, col):
+    def entity_call(self, entity):
         rset = self._cw.execute(
             'Any T,PE,T,TB,TST,TET,TF, TS,PEN ORDERBY 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?, '
             'PE name PEN, TC eid %(tc)s',
-            {'tc': self.cw_rset[row][col]})
-        self.wview('apycot.tc.te.summarytable', rset, 'noresult')
+            {'tc': entity.eid})
+        self.wview('apycot.te.notctable', rset, 'noresult')
 
 
 class TCStartModeView(EntityView):
@@ -268,25 +268,6 @@
             self.w(self._cw._(' (inherited)'))
 
 
-class TCTESummaryTable(tableview.TableView):
-    __select__ = is_instance('TestExecution')
-    __regid__ = 'apycot.tc.te.summarytable'
-
-    html_headers = no_robot_index
-
-    def call(self):
-        self._cw.add_css('cubes.apycot.css')
-        _ = self._cw._
-        super(TCTESummaryTable, self).call(
-            displayfilter=True, paginate=True,
-            headers=[_('TestExecution'), _('ProjectEnvironment'),
-                     _('checks'), _('branch'),
-                     _('starttime'), _('endtime'), _('archive')],
-            cellvids={0: 'apycot.te.statuscell',
-                      2: 'apycot.te.summarycell',
-                      6: 'icon'})
-
-
 def _available_branches(form, field):
     tc = form.edited_entity
     environment = form.cw_extra_kwargs['environment']
--- a/views/testexecution.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/views/testexecution.py	Thu Dec 22 17:43:34 2011 +0100
@@ -51,7 +51,10 @@
         if not changes_only:
             if exc.endtime is not None:
                 nb_checkers = len(exc.checkers)
-                duration = exc.endtime - exc.starttime
+                if exc.starttime:
+                    duration = exc.endtime - exc.starttime
+                else:
+                    duration = self._cw._('unknown duration')
                 if nb_checkers > 1:
                     w(u'<p>')
                     w(_(u'%(nb)s checkers run in %(dur)s')
@@ -64,8 +67,11 @@
                     w(u'</p>')
             else:
                 w(u'<p>')
-                w(_(u'running for %(dur)s')
-                       % {'dur': datetime.now() - exc.starttime})
+                if exc.starttime:
+                    duration = datetime.now() - exc.starttime
+                else:
+                    duration = self._cw._('unknown duration')
+                w(_(u'running for %(dur)s') % {'dur': duration})
                 w(u'</p>')
         changes = exc.status_changes()
         if changes_only:
@@ -92,42 +98,59 @@
 
 # TestExecution ################################################################
 
-class TESummaryTable(tableview.TableView):
+class TESummaryTable(EntityView):
     __regid__ = 'apycot.te.summarytable'
     __select__ = is_instance('TestExecution') | none_rset()
-
-    html_headers = no_robot_index
     title = _('Apycot executions')
     category = 'startupview'
 
-    def call(self, showpe=True):
-        self._cw.add_css('cubes.apycot.css')
-        _ = self._cw._
+    html_headers = no_robot_index
+    default_rql = ('Any T,PE,TC,T,TB,TST,TET,TF, TS ORDERBY 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?')
+
+    def call(self):
+        w = self.w
+        req = self._cw
         if self.cw_rset is None:
-            assert showpe
-            self.cw_rset = self._cw.execute(
-                'Any T,PE,TC,T,TB,TST,TET,TF, TS ORDERBY 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?')
-            self.w('<h1>%s</h1>' % _(self.title))
-        if showpe:
-            headers = [_('TestExecution'), _('ProjectEnvironment'), _('TestConfig'),
-                       _('checks'), _('branch'),
-                       _('starttime'), _('endtime'), _('archive')]
-            cellvids = {0: 'apycot.te.statuscell',
-                        3: 'apycot.te.summarycell',
-                        7: 'icon'}
-        else:
-            headers = [_('TestExecution'), _('TestConfig'),
-                       _('checks'), _('branch'),
-                       _('starttime'), _('endtime'), _('archive')]
-            cellvids = {0: 'apycot.te.statuscell',
-                        2: 'apycot.te.summarycell',
-                        6: 'icon'}
-        displayfilter = self._cw.form.get('displayfilter', True)
-        super(TESummaryTable, self).call(displayfilter=displayfilter,
-                                         paginate=True,
-                                         headers=headers, cellvids=cellvids)
+            self.cw_rset = req.execute(self.default_rql)
+        self.wview('apycot.te.table', self.cw_rset, 'null')
+
+
+class TETable(tableview.EntityTableView):
+    __regid__ = 'apycot.te.table'
+    __select__ = is_instance('TestExecution')
+
+    columns = ['testexecution', 'projectenvironment', 'using_config', 'checks', 'branch', 'starttime', 'endtime', 'log_file']
+    column_renderers = {
+            'testexecution': tableview.MainEntityColRenderer(
+                vid='apycot.te.statuscell'),
+            'using_config': tableview.RelatedEntityColRenderer(
+                header=_('TestConfig'), getrelated=lambda x: x.configuration),
+            'projectenvironment': tableview.RelatedEntityColRenderer(
+                header=_('ProjectEnvironment'), getrelated=lambda x: x.environment),
+            'checks': tableview.MainEntityColRenderer(
+                header=_('checks'), addcount=False, vid='apycot.te.summarycell'),
+            'log_file': tableview.RelationColRenderer(
+                subvid='icon')
+            }
+    layout_args = {
+            'display_filter': 'top'
+            }
+
+    def call(self, columns=None):
+        self._cw.add_css('cubes.apycot.css')
+        super(TETable, self).call(columns)
+
+
+class TENoPETable(TETable):
+    __regid__ = 'apycot.te.nopetable'
+    columns = ['testexecution', 'using_config', 'checks', 'branch', 'starttime', 'endtime', 'log_file']
+
+
+class TENoTCTable(TETable):
+    __regid__ = 'apycot.te.notctable'
+    columns = ['testexecution', 'projectenvironment', 'checks', 'branch', 'starttime', 'endtime', 'log_file']
 
 
 _pvdc.tag_attribute(('TestExecution', 'priority',), {'vid': 'tasksqueue.priority'}) # XXX rtag inheritance bug
@@ -294,17 +317,15 @@
             status = self._cw._('%(pe)s/%(config)s on %(date)s: %(status)s') % {
                 'config': xml_escape(entity.configuration.dc_title()),
                 'pe': xml_escape(entity.environment.dc_title()),
-                'status': xml_escape(status),
+                'status': status,
                 'date': entity.printable_value('starttime')}
         else:
-            status = self._cw._('%(pe)s/%(config)s <%(status)s>') % {
+            status = self._cw._('%(pe)s/%(config)s &lt;%(status)s&gt;') % {
                 'config': xml_escape(entity.configuration.dc_title()),
                 'pe': xml_escape(entity.environment.dc_title()),
-                'status': xml_escape(status)}
+                'status': status}
         self.w('<a href="%s" title="%s">%s</a>' % (
-               entity.absolute_url(),
-               self._cw._('view details'),
-               xml_escape(status)))
+               entity.absolute_url(), self._cw._('view details'), status))
 
 
 class TEDownloadBox(box.EntityBoxTemplate):
--- a/views/tracker.py	Fri Oct 07 16:56:17 2011 +0200
+++ b/views/tracker.py	Thu Dec 22 17:43:34 2011 +0100
@@ -20,14 +20,14 @@
     __regid__ = title = _('apycottestresults_tab')
     __select__ = is_instance('Project')
 
-    def cell_call(self, row, col):
+    def entity_call(self, entity):
         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, '
             'T starttime TST, T endtime TET, T log_file TF?, '
             'T using_environment PE, P has_apycot_environment PE, '
-            'P eid %(p)s', {'p': self.cw_rset[row][col]})
-        self.wview('apycot.te.summarytable', rset, 'noresult', showpe=False)
+            'P eid %(p)s', {'p': entity.eid})
+        self.wview('apycot.te.nopetable', rset, 'noresult')
 
 
 # class VersionTestResultsVComponent(component.EntityVComponent):