launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #00954
[Merge] lp:~jtv/launchpad/536819-display into lp:launchpad
Jeroen T. Vermeulen has proposed merging lp:~jtv/launchpad/536819-display into lp:launchpad with lp:~jtv/launchpad/buildfarmjob-getspecificjob-translationtemplatesbuild as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers): code
Related bugs:
#536819 Translation template jobs should keep history
https://bugs.launchpad.net/bugs/536819
= Bug 536819: Display =
I hope this will be the final part in our efforts to move the Translations part of the build-farm work over to the latest data model. It produces a simple UI page for TranslationTemplatesBuild.
This is still a work in progress. More details as they develop.
Jeroen
--
https://code.launchpad.net/~jtv/launchpad/536819-display/+merge/35085
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jtv/launchpad/536819-display into lp:launchpad.
=== modified file 'lib/lp/code/browser/branch.py'
--- lib/lp/code/browser/branch.py 2010-08-24 10:45:57 +0000
+++ lib/lp/code/browser/branch.py 2010-09-10 11:41:18 +0000
@@ -104,6 +104,7 @@
from canonical.widgets.branch import TargetBranchWidget
from canonical.widgets.itemswidgets import LaunchpadRadioWidgetWithDescription
from canonical.widgets.lazrjs import vocabulary_to_choice_edit_items
+from lp.app.errors import NotFoundError
from lp.blueprints.interfaces.specificationbranch import ISpecificationBranch
from lp.bugs.interfaces.bug import IBugSet
from lp.bugs.interfaces.bugbranch import IBugBranch
@@ -145,6 +146,9 @@
from lp.registry.interfaces.productseries import IProductSeries
from lp.registry.vocabularies import UserTeamsParticipationPlusSelfVocabulary
from lp.services.propertycache import cachedproperty
+from lp.translations.interfaces.translationtemplatesbuild import (
+ ITranslationTemplatesBuildSource,
+ )
def quote(text):
@@ -237,6 +241,16 @@
"""Traverses to the `ICodeImport` for the branch."""
return self.context.code_import
+ @stepthrough("+translation-templates-build")
+ def traverse_translation_templates_build(self, id_string):
+ """Traverses to a `TranslationTemplatesBuild`."""
+ try:
+ buildfarmjob_id = int(id_string)
+ except ValueError:
+ raise NotFoundError(id_string)
+ source = getUtility(ITranslationTemplatesBuildSource)
+ return source.getByBuildFarmJob(buildfarmjob_id)
+
class BranchEditMenu(NavigationMenu):
"""Edit menu for IBranch."""
=== modified file 'lib/lp/translations/browser/configure.zcml'
--- lib/lp/translations/browser/configure.zcml 2010-07-23 09:41:07 +0000
+++ lib/lp/translations/browser/configure.zcml 2010-09-10 11:41:18 +0000
@@ -1,4 +1,4 @@
-<!-- Copyright 2009 Canonical Ltd. This software is licensed under the
+<!-- Copyright 2009-2010 Canonical Ltd. This software is licensed under the
GNU Affero General Public License version 3 (see the file LICENSE).
-->
@@ -1024,4 +1024,21 @@
permission="launchpad.Admin"/>
</facet>
+
+<!-- TranslationTemplateBuild -->
+ <browser:defaultView
+ for="lp.translations.interfaces.translationtemplatesbuild.ITranslationTemplatesBuild"
+ name="+index"/>
+ <browser:url
+ for="lp.translations.interfaces.translationtemplatesbuild.ITranslationTemplatesBuild"
+ path_expression="string:+translation-templates-build/${build_farm_job/id}"
+ attribute_to_parent="branch"/>
+ <browser:page
+ for="lp.translations.interfaces.translationtemplatesbuild.ITranslationTemplatesBuild"
+ class="lp.translations.browser.translationtemplatesbuild.TranslationTemplatesBuildView"
+ permission="launchpad.View"
+ name="+index"
+ facet="overview"
+ template="../templates/translationtemplatesbuild-index.pt"/>
+
</configure>
=== added file 'lib/lp/translations/browser/translationtemplatesbuild.py'
--- lib/lp/translations/browser/translationtemplatesbuild.py 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/browser/translationtemplatesbuild.py 2010-09-10 11:41:18 +0000
@@ -0,0 +1,72 @@
+# Copyright 2010 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Display `TranslationTemplateBuild`s."""
+
+__metaclass__ = type
+__all__ = [
+ 'TranslationTemplatesBuildNavigation',
+ 'TranslationTemplatesBuildUrl',
+ 'TranslationTemplatesBuildView',
+ ]
+
+from zope.component import getUtility
+
+from canonical.launchpad.webapp.publisher import LaunchpadView
+from canonical.launchpad.webapp.tales import DateTimeFormatterAPI
+from lp.buildmaster.enums import BuildStatus
+from lp.registry.interfaces.productseries import IProductSeriesSet
+from lp.services.propertycache import cachedproperty
+
+
+class TranslationTemplatesBuildView(LaunchpadView):
+
+ def getTargets(self):
+ utility = getUtility(IProductSeriesSet)
+ return utility.findByTranslationsImportBranch(self.context.branch)
+
+ @property
+ def status(self):
+ return self.context.status
+
+ def isBuilding(self):
+ return self.status == BuildStatus.BUILDING
+
+ @cachedproperty
+ def current_builder(self):
+ if self.isBuilding():
+ return self.context.buildqueue.builder
+ else:
+ return self.context.builder
+
+ def getDispatchTime(self):
+ if self.context.was_built:
+ return self.context.buildqueue_record.job.date_started
+ elif self.context.date_started is not None:
+ return self.context.date_started
+ elif self.context.buildqueue_record is not None:
+ return self.context.buildqueue_record.getEstimatedJobStartTime()
+ else:
+ return None
+
+ def _composeTimeText(self, time, preamble=''):
+ if time is None:
+ return None
+ formatter = DateTimeFormatterAPI(time)
+ return '%s <span title="%s">%s</span>' % (
+ preamble, formatter.datetime(), formatter.approximatedate())
+
+ def composeDispatchTimeText(self):
+ if self.context.date_started is None:
+ preamble = "Start"
+ else:
+ preamble = "Started"
+
+ return self._composeTimeText(self.getDispatchTime(), preamble)
+
+ def composeFinishTimeText(self):
+ return self._composeTimeText(self.context.date_finished, "Finished")
+
+ @cachedproperty
+ def last_score(self):
+ return self.context.buildqueue.lastscore
=== modified file 'lib/lp/translations/interfaces/translationtemplatesbuild.py'
--- lib/lp/translations/interfaces/translationtemplatesbuild.py 2010-09-10 11:41:16 +0000
+++ lib/lp/translations/interfaces/translationtemplatesbuild.py 2010-09-10 11:41:18 +0000
@@ -35,5 +35,15 @@
def create(build_farm_job, branch):
"""Create a new `ITranslationTemplatesBuild`."""
- def findByBranch(branch):
+ def findByBranch(branch, store=None):
"""Find `ITranslationTemplatesBuild`s for `branch`."""
+
+ def get(build_id, store=None):
+ """Find `ITranslationTemplatesBuild`s by id.
+
+ :param build_id: Numerical id to look for.
+ :param store: Optional database store to look in.
+ """
+
+ def getByBuildFarmJob(buildfarmjob_id, store=None):
+ """Find `ITranslationTemplatesBuild`s by `BuildFarmJob` id."""
=== modified file 'lib/lp/translations/model/translationtemplatesbuild.py'
--- lib/lp/translations/model/translationtemplatesbuild.py 2010-09-10 11:41:16 +0000
+++ lib/lp/translations/model/translationtemplatesbuild.py 2010-09-10 11:41:18 +0000
@@ -20,7 +20,7 @@
)
from zope.security.proxy import ProxyFactory
-from canonical.launchpad.interfaces.lpstorm import IMasterStore
+from canonical.launchpad.interfaces.lpstorm import IStore
from lp.buildmaster.model.buildfarmjob import BuildFarmJobDerived
from lp.code.model.branchjob import (
BranchJob,
@@ -43,7 +43,7 @@
__storm_table__ = 'TranslationTemplatesBuild'
- id = Int(primary=True)
+ id = Int(name='id', primary=True)
build_farm_job_id = Int(name='build_farm_job', allow_none=False)
build_farm_job = Reference(build_farm_job_id, 'BuildFarmJob.id')
branch_id = Int(name='branch', allow_none=False)
@@ -54,16 +54,9 @@
self.build_farm_job = build_farm_job
self.branch = branch
- @classmethod
- def create(self, build_farm_job, branch):
- """See `ITranslationTemplatesBuildSource`."""
- build = TranslationTemplatesBuild(build_farm_job, branch)
- IMasterStore(TranslationTemplatesBuild).add(build)
- return build
-
def makeJob(self):
"""See `IBuildFarmJobOld`."""
- store = IMasterStore(BranchJob)
+ store = IStore(BranchJob)
# Pass public HTTP URL for the branch.
metadata = {'branch_url': self.branch.composePublicURL()}
@@ -72,6 +65,49 @@
store.add(branch_job)
return TranslationTemplatesBuildJob(branch_job)
+ @classmethod
+ def _getStore(cls, store=None):
+ """Return `store` if given, or the default."""
+ if store is None:
+ return IStore(cls)
+ else:
+ return store
+
+ @classmethod
+ def create(cls, build_farm_job, branch):
+ """See `ITranslationTemplatesBuildSource`."""
+ build = TranslationTemplatesBuild(build_farm_job, branch)
+ store = cls._getStore()
+ store.add(build)
+ store.flush()
+ return build
+
+ @classmethod
+ def get(cls, build_id, store=None):
+ """See `ITranslationTemplatesBuildSource`."""
+ store = cls._getStore(store)
+ match = store.find(
+ TranslationTemplatesBuild,
+ TranslationTemplatesBuild.id == build_id)
+ return match.one()
+
+ @classmethod
+ def getByBuildFarmJob(cls, buildfarmjob_id, store=None):
+ """See `ITranslationTemplatesBuildSource`."""
+ store = cls._getStore(store)
+ match = store.find(
+ TranslationTemplatesBuild,
+ TranslationTemplatesBuild.build_farm_job == buildfarmjob_id)
+ return match.one()
+
+ @classmethod
+ def findByBranch(cls, branch, store=None):
+ """See `ITranslationTemplatesBuildSource`."""
+ store = cls._getStore(store)
+ return store.find(
+ TranslationTemplatesBuild,
+ TranslationTemplatesBuild.branch == branch)
+
def get_translation_templates_build_for_build_farm_job(build_farm_job):
"""Return a `TranslationTemplatesBuild` from its `BuildFarmJob`."""
=== modified file 'lib/lp/translations/stories/buildfarm/xx-build-summary.txt'
--- lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-08-31 21:30:43 +0000
+++ lib/lp/translations/stories/buildfarm/xx-build-summary.txt 2010-09-10 11:41:18 +0000
@@ -1,13 +1,18 @@
+<<<<<<< TREE
TranslationTemplatesBuildJob Build Summary
==========================================
+=======
+TranslationTemplatesBuild Build Summary
+=======================================
+>>>>>>> MERGE-SOURCE
-The builders UI can show TranslationTemplateBuildJobs, although they
+The builders UI can show TranslationTemplateBuild records, although they
look a little different from Soyuz-style jobs.
Setup
-----
-Create a builder working on a TranslationTemplatesBuildJob for a branch.
+Create a builder working on a TranslationTemplatesBuild for a branch.
>>> from zope.component import getUtility
>>> from canonical.launchpad.interfaces.launchpad import (
@@ -76,3 +81,18 @@
Working on TranslationTemplatesBuildJob for branch ...
>>> user_browser.getLink(branch_url).click()
+
+
+Show build record
+-----------------
+
+ >>> login(ANONYMOUS)
+ >>> from lp.translations.interfaces.translationtemplatesbuild import (
+ ... ITranslationTemplatesBuildSource,
+ ... )
+ >>> build = getUtility(ITranslationTemplatesBuildSource).findByBranch(
+ ... branch).one()
+ >>> build_url = canonical_url(build)
+ >>> logout()
+
+ >>> user_browser.open(build_url)
=== added file 'lib/lp/translations/templates/translationtemplatesbuild-index.pt'
--- lib/lp/translations/templates/translationtemplatesbuild-index.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/translations/templates/translationtemplatesbuild-index.pt 2010-09-10 11:41:18 +0000
@@ -0,0 +1,133 @@
+<html
+ xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ metal:use-macro="view/macro:page/main_only"
+ i18n:domain="launchpad"
+>
+
+ <body>
+
+ <tal:registering metal:fill-slot="registering">
+ created
+ <span tal:content="context/date_created/fmt:displaydate"
+ tal:attributes="title context/date_created/fmt:datetime"
+ >on 2005-01-01</span>
+ </tal:registering>
+
+ <div metal:fill-slot="main">
+
+ <div class="yui-g">
+
+ <div id="status" class="yui-u first">
+ <div class="portlet">
+ <div metal:use-macro="template/macros/status" />
+ </div>
+ </div>
+
+ <div id="details" class="yui-u">
+ <div class="portlet">
+ <div metal:use-macro="template/macros/details" />
+ </div>
+ </div>
+
+ </div> <!-- yui-g -->
+
+ <div id="buildlog" class="portlet"
+ tal:condition="context/status/enumvalue:BUILDING">
+ <div metal:use-macro="template/macros/buildlog" />
+ </div>
+
+ </div> <!-- main -->
+
+
+<metal:macros fill-slot="bogus">
+
+ <metal:macro define-macro="details">
+ <tal:comment replace="nothing">
+ Details section.
+ </tal:comment>
+ <h2>Build details</h2>
+ <p>Branch:
+ <tal:branch replace="structure context/branch/fmt:link">
+ lp:foo/trunk
+ </tal:branch>
+ </p>
+ <tal:targets tal:define="targets view/getTargets">
+ <div tal:condition="targets">
+ For import into:
+ <ul>
+ <li tal:repeat="target targets">
+ <a tal:replace="target/fmt:link">gawk trunk series</a>
+ </li>
+ </ul>
+ </div>
+ <div tal:condition="not:targets">
+ <em>Not imported anywhere.</em>
+ </div>
+ </tal:targets>
+ </metal:macro>
+
+ <metal:macro define-macro="status">
+ <tal:comment replace="nothing">
+ Status section.
+ </tal:comment>
+ <h2>Build status</h2>
+ <p>
+ <span tal:replace="structure context/image:icon" />
+ <span tal:attributes="
+ class string:buildstatus${context/status/name};"
+ tal:content="view/status/title">Fully built</span>
+ <tal:building define="builder view/current_builder"
+ condition="builder">
+ on <a tal:content="builder/title"
+ tal:attributes="href builder/fmt:url"/>
+ </tal:building>
+ </p>
+
+ <ul>
+ <li tal:define="time view/composeDispatchTimeText"
+ tal:condition="time"
+ tal:content="structure time">
+ Started 5 minutes ago
+ </li>
+ <li tal:define="time view/composeFinishTimeText"
+ tal:condition="time"
+ tal:content="structure time">
+ Finished 30 seconds ago
+ <tal:duration define="duration context/duration" condition="duration">
+ (took <span tal:replace="duration/fmt:exactduration" />)
+ </tal:duration>
+ </li>
+
+ <li tal:define="file context/log"
+ tal:condition="file">
+ <a class="sprite download"
+ tal:attributes="href context/log_url"
+ tal:content="string: buildlog">BUILDLOG</a>
+ (<span tal:replace="file/content/filesize/fmt:bytes" />)
+ </li>
+ </ul>
+ </metal:macro>
+
+ <metal:macro define-macro="buildlog">
+ <tal:comment replace="nothing">
+ Buildlog section.
+ </tal:comment>
+ <h2>Buildlog</h2>
+ <div id="buildlog-tail" class="logtail"
+ tal:define="logtail context/buildqueue_record/logtail"
+ tal:content="structure logtail/fmt:text-to-html">
+ <p>Things are crashing and burning all over the place.</p>
+ </div>
+ <p class="discreet" tal:condition="view/user">
+ Updated on <span tal:replace="structure view/user/fmt:local-time"/>
+ </p>
+ </metal:macro>
+
+</metal:macros>
+
+
+ </body>
+</html>
=== modified file 'lib/lp/translations/tests/test_translationtemplatesbuild.py'
--- lib/lp/translations/tests/test_translationtemplatesbuild.py 2010-09-10 11:41:16 +0000
+++ lib/lp/translations/tests/test_translationtemplatesbuild.py 2010-09-10 11:41:18 +0000
@@ -5,7 +5,6 @@
__metaclass__ = type
-from storm.store import Store
from zope.component import getUtility
from zope.interface.verify import verifyObject
@@ -14,7 +13,6 @@
from lp.buildmaster.interfaces.buildfarmjob import (
IBuildFarmJob,
IBuildFarmJobSource,
- ISpecificBuildFarmJob,
)
from lp.testing import TestCaseWithFactory
from lp.translations.interfaces.translationtemplatesbuild import (
@@ -33,12 +31,6 @@
layer = DatabaseFunctionalLayer
- def _findBuildForBranch(self, branch):
- """Find the `TranslationTemplatesBuild` for `branch`, if any."""
- return Store.of(branch).find(
- TranslationTemplatesBuild,
- TranslationTemplatesBuild.branch == branch).one()
-
def _makeBuildFarmJob(self):
"""Create a `BuildFarmJob` for testing."""
source = getUtility(IBuildFarmJobSource)
@@ -53,7 +45,6 @@
self.assertTrue(verifyObject(ITranslationTemplatesBuild, build))
self.assertTrue(verifyObject(IBuildFarmJob, build))
- self.assertTrue(verifyObject(ISpecificBuildFarmJob, build))
self.assertEqual(build_farm_job, build.build_farm_job)
self.assertEqual(branch, build.branch)
@@ -62,10 +53,14 @@
# TranslationTemplatesBuild. This utility will become obsolete
# later.
jobset = getUtility(ITranslationTemplatesBuildJobSource)
+ source = getUtility(ITranslationTemplatesBuildSource)
branch = self.factory.makeBranch()
translationtemplatesbuildjob = jobset.create(branch)
- self.assertNotEqual(None, self._findBuildForBranch(branch))
+
+ builds = list(source.findByBranch(branch))
+ self.assertEqual(1, len(builds))
+ self.assertIsInstance(builds[0], TranslationTemplatesBuild)
def test_getSpecificJob(self):
source = getUtility(ITranslationTemplatesBuildSource)
@@ -74,3 +69,49 @@
build = source.create(build_farm_job, branch)
self.assertEqual(build, build_farm_job.getSpecificJob())
+
+ def test_findByBranch(self):
+ source = getUtility(ITranslationTemplatesBuildSource)
+ build_farm_job = self._makeBuildFarmJob()
+ branch = self.factory.makeBranch()
+
+ self.assertContentEqual([], source.findByBranch(branch))
+
+ build = source.create(build_farm_job, branch)
+
+ by_branch = list(source.findByBranch(branch))
+ self.assertEqual([build], by_branch)
+
+ def test_get(self):
+ source = getUtility(ITranslationTemplatesBuildSource)
+ build_farm_job = self._makeBuildFarmJob()
+ branch = self.factory.makeBranch()
+ build = source.create(build_farm_job, branch)
+
+ self.assertEqual(build, source.get(build.id))
+
+ def test_get_returns_none_if_not_found(self):
+ source = getUtility(ITranslationTemplatesBuildSource)
+ build_farm_job = self._makeBuildFarmJob()
+ branch = self.factory.makeBranch()
+ build = source.create(build_farm_job, branch)
+
+ self.assertIs(None, source.get(build.id + 999))
+
+ def test_getByBuildFarmJob(self):
+ source = getUtility(ITranslationTemplatesBuildSource)
+ build_farm_job = self._makeBuildFarmJob()
+ branch = self.factory.makeBranch()
+ build = source.create(build_farm_job, branch)
+
+ self.assertEqual(build, source.getByBuildFarmJob(build_farm_job.id))
+
+ def test_getByBuildFarmJob_returns_none_if_not_found(self):
+ source = getUtility(ITranslationTemplatesBuildSource)
+ build_farm_job = self._makeBuildFarmJob()
+ branch = self.factory.makeBranch()
+ build = source.create(build_farm_job, branch)
+
+ self.assertIs(
+ None,
+ source.getByBuildFarmJob(build_farm_job.id + 999))
Follow ups