launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03258
[Merge] lp:~rvb/launchpad/dds-link-to-derivedseries into lp:launchpad
Raphaël Victor Badin has proposed merging lp:~rvb/launchpad/dds-link-to-derivedseries into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #747502 in Launchpad itself: "Derived distributions must be discoverable from the "parent" distribution"
https://bugs.launchpad.net/launchpad/+bug/747502
For more details, see:
https://code.launchpad.net/~rvb/launchpad/dds-link-to-derivedseries/+merge/56931
This branch fixes the UI so that distribution/distroseries index pages feature references to derivatives.
== Details ==
For distro index page:
- Rename the "Series and milestones" to "Active series and milestones".
- Add a new slot displaying the 5 most recent derivatives (the total number of derivatives might be huge, hence the limitation on the index page) with a link to the complete list.
- Add a new 'Derivatives' page displaying all the derivatives (re-using the already existing template 'distribution-series.pt').
For the distroseries index page:
- Add a new slot "Derived series:" listing all the derivatives of this series.
== Tests ==
./bin/test -cvv -t xx-distribution.txt
./bin/test -cvv -t distribution-views.txt
./bin/test -cvv -t xx-distribution-overview.txt
./bin/test -cvv -t xx-distroseries-index.txt
== QA ==
The new slots will be seen:
On a local instance:
https://launchpad.dev/ubuntu
https://launchpad.dev/ubuntu/+derivatives
https://launchpad.dev/ubuntu/warty
On dogfood:
https://dogfood.launchpad.net/ubuntu
https://dogfood.launchpad.net/ubuntu/+derivatives
https://dogfood.launchpad.net/ubuntu/maverick
--
https://code.launchpad.net/~rvb/launchpad/dds-link-to-derivedseries/+merge/56931
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~rvb/launchpad/dds-link-to-derivedseries into lp:launchpad.
=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-03-30 11:27:18 +0000
+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2011-04-08 13:40:53 +0000
@@ -437,6 +437,7 @@
# IDistribution
IDistribution['series'].value_type.schema = IDistroSeries
+IDistribution['derivatives'].value_type.schema = IDistroSeries
patch_reference_property(
IDistribution, 'currentseries', IDistroSeries)
patch_entry_return_type(
=== modified file 'lib/lp/registry/browser/configure.zcml'
--- lib/lp/registry/browser/configure.zcml 2011-04-06 22:51:53 +0000
+++ lib/lp/registry/browser/configure.zcml 2011-04-08 13:40:53 +0000
@@ -1948,6 +1948,20 @@
template="../templates/distribution-series-and-milestones.pt"/>
<browser:page
for="lp.registry.interfaces.distribution.IDistribution"
+ class="lp.registry.browser.distribution.DistributionDerivativesView"
+ permission="zope.Public"
+ name="+derivatives"
+ facet="overview"
+ template="../templates/distribution-series.pt"/>
+ <browser:page
+ for="lp.registry.interfaces.distribution.IDistribution"
+ class="lp.registry.browser.distribution.DistributionView"
+ permission="zope.Public"
+ name="+derivatives-slot"
+ facet="overview"
+ template="../templates/distribution-derivatives.pt"/>
+ <browser:page
+ for="lp.registry.interfaces.distribution.IDistribution"
class="lp.registry.browser.distribution.DistributionView"
permission="zope.Public"
name="+details"
=== modified file 'lib/lp/registry/browser/distribution.py'
--- lib/lp/registry/browser/distribution.py 2011-04-06 16:07:23 +0000
+++ lib/lp/registry/browser/distribution.py 2011-04-08 13:40:53 +0000
@@ -24,6 +24,7 @@
'DistributionPublisherConfigView',
'DistributionReassignmentView',
'DistributionSeriesView',
+ 'DistributionDerivativesView',
'DistributionSeriesMirrorsRSSView',
'DistributionSeriesMirrorsView',
'DistributionSetActionNavigationMenu',
@@ -323,6 +324,7 @@
'reassign',
'addseries',
'series',
+ 'derivatives',
'milestones',
'top_contributors',
'builds',
@@ -420,6 +422,10 @@
text = 'All series'
return Link('+series', text, icon='info')
+ def derivatives(self):
+ text = 'All derivatives'
+ return Link('+derivatives', text, icon='info')
+
def milestones(self):
text = 'All milestones'
return Link('+milestones', text, icon='info')
@@ -646,6 +652,11 @@
return english_list(linked_milestones)
+ @cachedproperty
+ def latest_derivatives(self):
+ """The 5 most recent derivatives."""
+ return self.context.derivatives[:5]
+
class DistributionArchivesView(LaunchpadView):
@@ -853,16 +864,13 @@
data['enable_bug_expiration'] = False
-class DistributionSeriesView(LaunchpadView):
- """A view to list the distribution series"""
-
- label = 'Timeline'
-
+class DistributionSeriesBaseView(LaunchpadView):
+ """A base view to list distroseries."""
@cachedproperty
def styled_series(self):
"""A list of dicts; keys: series, css_class, is_development_focus"""
all_series = []
- for series in self.context.series:
+ for series in self._displayed_series:
all_series.append({
'series': series,
'css_class': self.getCssClass(series),
@@ -880,6 +888,28 @@
return ''
+class DistributionSeriesView(DistributionSeriesBaseView):
+ """A view to list the distribution series."""
+ label = 'Timeline'
+ show_add_series_link = True
+ show_milestones_link = True
+
+ @property
+ def _displayed_series(self):
+ return self.context.series
+
+
+class DistributionDerivativesView(DistributionSeriesBaseView):
+ """A view to list the distribution derivatives."""
+ label = 'Derivatives'
+ show_add_series_link = False
+ show_milestones_link = False
+
+ @property
+ def _displayed_series(self):
+ return self.context.derivatives
+
+
class DistributionChangeMirrorAdminView(RegistryEditFormView):
"""A view to change the mirror administrator."""
schema = IDistribution
=== modified file 'lib/lp/registry/browser/tests/distribution-views.txt'
--- lib/lp/registry/browser/tests/distribution-views.txt 2011-01-17 22:27:35 +0000
+++ lib/lp/registry/browser/tests/distribution-views.txt 2011-04-08 13:40:53 +0000
@@ -314,7 +314,7 @@
--------------------
The +series view provides a page title and list of dicts that represent
-the series and the CSS class to present them wth.
+the series and the CSS class to present them with.
>>> view = create_view(ubuntu, name='+series')
>>> print view.label
@@ -328,6 +328,24 @@
warty
+Distribution +derivatives
+-------------------------
+
+The +derivatives view provides a page title and list of dicts that represent
+the derivatives and the CSS class to present them with.
+
+ >>> view = create_view(ubuntu, name='+derivatives')
+ >>> print view.label
+ Derivatives
+
+ >>> for styled_series in view.styled_series:
+ ... print styled_series['series'].name, styled_series['css_class']
+ hoary-test
+ krunch
+ breezy-autotest
+ 2k5 highlight
+
+
Distribution +ppas
------------------
=== modified file 'lib/lp/registry/browser/tests/test_distribution.py'
--- lib/lp/registry/browser/tests/test_distribution.py 2011-03-14 14:28:52 +0000
+++ lib/lp/registry/browser/tests/test_distribution.py 2011-04-08 13:40:53 +0000
@@ -44,8 +44,8 @@
canonical_url(self.distro, view_name='+addseries')},
text='Add series'),
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'),
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'),
)
self.assertThat(view.render(), series_matches)
@@ -63,8 +63,8 @@
text='Add series'))
series_header_match = soupmatchers.HTMLContains(
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'))
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'))
self.assertThat(
view.render(),
Not(MatchesAny(add_series_match, series_header_match)))
@@ -84,7 +84,7 @@
text='Add series'))
series_header_match = soupmatchers.HTMLContains(
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'))
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'))
self.assertThat(view.render(), series_header_match)
self.assertThat(view.render(), Not(add_series_match))
=== modified file 'lib/lp/registry/interfaces/distribution.py'
--- lib/lp/registry/interfaces/distribution.py 2011-03-25 03:26:29 +0000
+++ lib/lp/registry/interfaces/distribution.py 2011-04-08 13:40:53 +0000
@@ -268,6 +268,11 @@
title=_("DistroSeries inside this Distribution"),
# Really IDistroSeries, see _schema_circular_imports.py.
value_type=Reference(schema=Interface))))
+ derivatives = exported(doNotSnapshot(
+ CollectionField(
+ title=_("This Distribution's derivatives"),
+ # Really IDistroSeries, see _schema_circular_imports.py.
+ value_type=Reference(schema=Interface))))
architectures = List(
title=_("DistroArchSeries inside this Distribution"))
uploaders = Attribute(_(
=== modified file 'lib/lp/registry/model/distribution.py'
--- lib/lp/registry/model/distribution.py 2011-03-29 01:28:52 +0000
+++ lib/lp/registry/model/distribution.py 2011-04-08 13:40:53 +0000
@@ -19,6 +19,7 @@
StringCol,
)
from sqlobject.sqlbuilder import SQLConstant
+from storm.info import ClassAlias
from storm.locals import (
And,
Desc,
@@ -597,6 +598,18 @@
distribution=self)
return sorted(ret, key=lambda a: Version(a.version), reverse=True)
+ @cachedproperty
+ def derivatives(self):
+ """See `IDistribution`."""
+ ParentDistroSeries = ClassAlias(DistroSeries)
+ ret = Store.of(self).find(
+ DistroSeries,
+ ParentDistroSeries.id==DistroSeries.parent_seriesID,
+ ParentDistroSeries.distributionID==self.id,
+ DistroSeries.distributionID!=self.id)
+ return ret.config(
+ distinct=True).order_by(Desc(DistroSeries.date_created))
+
@property
def architectures(self):
"""See `IDistribution`."""
=== modified file 'lib/lp/registry/model/distroseries.py'
--- lib/lp/registry/model/distroseries.py 2011-04-06 15:35:05 +0000
+++ lib/lp/registry/model/distroseries.py 2011-04-08 13:40:53 +0000
@@ -1989,8 +1989,11 @@
def getDerivedSeries(self):
"""See `IDistroSeriesPublic`."""
- return Store.of(self).find(
- DistroSeries, DistroSeries.parent_series == self)
+ results = Store.of(self).find(
+ DistroSeries,
+ DistroSeries.parent_series==self.id,
+ DistroSeries.distributionID!=self.distributionID)
+ return results.order_by(Desc(DistroSeries.date_created))
def getBugTaskWeightFunction(self):
"""Provide a weight function to determine optimal bug task.
=== modified file 'lib/lp/registry/stories/distribution/xx-distribution-overview.txt'
--- lib/lp/registry/stories/distribution/xx-distribution-overview.txt 2011-03-10 22:36:25 +0000
+++ lib/lp/registry/stories/distribution/xx-distribution-overview.txt 2011-04-08 13:40:53 +0000
@@ -18,13 +18,14 @@
== Distribution home pages ==
Each distribution has a home page in the system. The page will show a
-consolidated view of series and milestones of that series.
+consolidated view of active series, milestones and derivatives of that
+series.
Some distributions have listings of major versions, for example Debian:
>>> anon_browser.open('http://launchpad.dev/debian')
>>> print extract_text(find_tag_by_id(anon_browser.contents, 'sandm'))
- Series and milestones
+ Active series and milestones
3.1 “Sarge” series - frozen
3.0 “Woody” series - current
Milestones: 3.1 and 3.1-rc1
@@ -44,7 +45,7 @@
>>> MemcachedLayer.purge()
>>> anon_browser.open('http://launchpad.dev/debian')
>>> print extract_text(find_tag_by_id(anon_browser.contents, 'sandm'))
- Series and milestones
+ Active series and milestones
...
Milestones: testmilestone, 3.1, and 3.1-rc1
...
@@ -79,8 +80,44 @@
>>> print find_tag_by_id(anon_browser.contents, 'sandm')
None
-
-== Registration information ==
+The 5 latest derivatives are displayed on the home page
+along with a link to list all of them.
+
+ >>> anon_browser.open('http://launchpad.dev/ubuntu')
+ >>> print extract_text(find_tag_by_id(anon_browser.contents,
+ ... 'derivatives'))
+ Latest derivatives
+ 9.9.9
+ “Hoary Mock” series
+ (from Warty)
+ 8.06
+ “Krunch” series
+ (from Hoary)
+ 6.6.6
+ “Breezy Badger Autotest” series
+ (from Warty)
+ 2005
+ “Guada2005” series
+ (from Hoary)
+ All derivatives
+
+
+The "All derivatives" link takes you to the derivatives page.
+
+ >>> anon_browser.getLink('All derivatives').url
+ 'http://launchpad.dev/ubuntu/+derivatives'
+
+If there is no derivatives, the link to the derivatives page is
+not there.
+
+ >>> anon_browser.open('http://launchpad.dev/ubuntutest')
+ >>> print extract_text(find_tag_by_id(anon_browser.contents,
+ ... 'derivatives'))
+ Latest derivatives
+ No derivatives.
+
+
+ == Registration information ==
The distroseries pages presents the registeration information besides
its main 'heading'.
=== modified file 'lib/lp/registry/stories/distroseries/xx-distroseries-index.txt'
--- lib/lp/registry/stories/distroseries/xx-distroseries-index.txt 2011-03-13 20:58:59 +0000
+++ lib/lp/registry/stories/distroseries/xx-distroseries-index.txt 2011-04-08 13:40:53 +0000
@@ -47,6 +47,7 @@
Release manager: None
Status: Current Stable Release
Derives from: Warty (4.10) is not derived from another series.
+ Derived series: Hoary Mock (9.9.9), Breezy Badger Autotest (6.6.6)
Source packages: 3
Binary packages: 4
@@ -63,6 +64,7 @@
Release manager: Jeff Waugh
Status: Pre-release Freeze
Derives from: Woody (3.0)
+ Derived series: No derived series.
Source packages: No sources imported or published.
Binary packages: No binaries imported or published.
=== modified file 'lib/lp/registry/stories/webservice/xx-distribution.txt'
--- lib/lp/registry/stories/webservice/xx-distribution.txt 2011-03-10 22:36:25 +0000
+++ lib/lp/registry/stories/webservice/xx-distribution.txt 2011-04-08 13:40:53 +0000
@@ -30,6 +30,7 @@
cdimage_mirrors_collection_link: u'http://.../ubuntu/cdimage_mirrors'
current_series_link: u'http://.../ubuntu/hoary'
date_created: u'2006-10-16T18:31:43.415195+00:00'
+ derivatives_collection_link: u'http://.../ubuntu/derivatives'
description: u'Ubuntu is a new approach...'
display_name: u'Ubuntu'
domain_name: u'ubuntulinux.org'
=== added file 'lib/lp/registry/templates/distribution-derivatives.pt'
--- lib/lp/registry/templates/distribution-derivatives.pt 1970-01-01 00:00:00 +0000
+++ lib/lp/registry/templates/distribution-derivatives.pt 2011-04-08 13:40:53 +0000
@@ -0,0 +1,32 @@
+<div
+ xmlns:tal="http://xml.zope.org/namespaces/tal"
+ xmlns:metal="http://xml.zope.org/namespaces/metal"
+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
+ tal:define="addserieslink context/menu:overview/addseries"
+ tal:condition="view/latest_derivatives"
+ class="portlet" id="derivatives">
+ <h2>Latest derivatives</h2>
+ <ul tal:condition="not: view/latest_derivatives/is_empty">
+ <tal:per_series repeat="derivative view/latest_derivatives">
+ <li>
+ <strong>
+ <a tal:attributes="href derivative/fmt:url" class="sprite distribution">
+ <tal:version replace="derivative/version">3.0</tal:version>
+ “<tal:codename replace="derivative/displayname" />” series
+ </a>
+ </strong>
+ (from <a tal:attributes="href derivative/parent_series/fmt:url"
+ tal:content="derivative/parent_series/displayname" />)
+ <br/>
+ </li>
+ </tal:per_series>
+ </ul>
+ <span tal:condition="view/latest_derivatives/is_empty">
+ No derivatives.
+ </span>
+ <ul class="horizontal">
+ <li tal:condition="not: view/latest_derivatives/is_empty">
+ <a tal:replace="structure context/menu:overview/derivatives/fmt:link" />
+ </li>
+ </ul>
+</div>
=== modified file 'lib/lp/registry/templates/distribution-index.pt'
--- lib/lp/registry/templates/distribution-index.pt 2011-03-30 21:38:03 +0000
+++ lib/lp/registry/templates/distribution-index.pt 2011-04-08 13:40:53 +0000
@@ -63,7 +63,10 @@
<div class="yui-u">
<tal:series replace="structure context/@@+series-and-milestones" />
</div>
- </div>
+ <div class="yui-u">
+ <tal:series replace="structure context/@@+derivatives-slot" />
+ </div>
+ </div>
<div class="yui-g">
<div class="yui-u first">
=== modified file 'lib/lp/registry/templates/distribution-series-and-milestones.pt'
--- lib/lp/registry/templates/distribution-series-and-milestones.pt 2011-03-02 16:19:50 +0000
+++ lib/lp/registry/templates/distribution-series-and-milestones.pt 2011-04-08 13:40:53 +0000
@@ -5,7 +5,7 @@
tal:define="addserieslink context/menu:overview/addseries"
tal:condition="python:context.series or addserieslink.enabled"
class="portlet" id="sandm">
- <h2>Series and milestones</h2>
+ <h2>Active series and milestones</h2>
<ul tal:condition="context/series"
tal:content="cache:public, 1 hour">
<tal:per_series repeat="series context/series">
=== modified file 'lib/lp/registry/templates/distribution-series.pt'
--- lib/lp/registry/templates/distribution-series.pt 2009-11-07 16:14:22 +0000
+++ lib/lp/registry/templates/distribution-series.pt 2011-04-08 13:40:53 +0000
@@ -11,10 +11,10 @@
<ul class="horizontal">
<li
tal:define="link context/menu:overview/addseries"
- tal:condition="link/enabled">
+ tal:condition="python: link.enabled and view.show_add_series_link">
<a tal:replace="structure link/fmt:link" />
</li>
- <li>
+ <li tal:condition="view/show_milestones_link">
<a tal:replace="structure context/menu:overview/milestones/fmt:link" />
</li>
</ul>
=== modified file 'lib/lp/registry/templates/distroseries-details.pt'
--- lib/lp/registry/templates/distroseries-details.pt 2009-11-07 07:46:54 +0000
+++ lib/lp/registry/templates/distroseries-details.pt 2011-04-08 13:40:53 +0000
@@ -60,6 +60,21 @@
</dd>
</dl>
+ <dl tal:define="all_child_series context/getDerivedSeries">
+ <dt>Derived series:</dt>
+ <dd>
+ <tal:per_child_series repeat="child_series all_child_series">
+ <a
+ tal:attributes="href child_series/fmt:url"
+ tal:content="child_series/named_version" /><tal:comma
+ condition="not: repeat/child_series/end">,</tal:comma>
+ </tal:per_child_series>
+ <tal:none condition="all_child_series/is_empty">
+ No derived series.
+ </tal:none>
+ </dd>
+ </dl>
+
<dl tal:define="sourcecount context/sourcecount">
<dt>Source packages:</dt>
<dd
=== modified file 'lib/lp/registry/tests/test_distribution.py'
--- lib/lp/registry/tests/test_distribution.py 2011-03-18 15:28:46 +0000
+++ lib/lp/registry/tests/test_distribution.py 2011-04-08 13:40:53 +0000
@@ -213,8 +213,8 @@
canonical_url(self.distro, view_name='+addseries')},
text='Add series'),
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'),
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'),
)
self.assertThat(view.render(), series_matches)
@@ -233,8 +233,8 @@
text='Add series'))
series_header_match = soupmatchers.HTMLContains(
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'))
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'))
self.assertThat(
view.render(),
Not(MatchesAny(add_series_match, series_header_match)))
@@ -256,8 +256,8 @@
text='Add series'))
series_header_match = soupmatchers.HTMLContains(
soupmatchers.Tag(
- 'Series and milestones widget', 'h2',
- text='Series and milestones'))
+ 'Active series and milestones widget', 'h2',
+ text='Active series and milestones'))
self.assertThat(view.render(), series_header_match)
self.assertThat(view.render(), Not(add_series_match))