← Back to team overview

launchpad-reviewers team mailing list archive

[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 &#8220;Sarge&#8221; series - frozen
     3.0 &#8220;Woody&#8221; 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
+   &#8220;Hoary Mock&#8221; series
+   (from Warty)
+   8.06
+   &#8220;Krunch&#8221; series
+   (from Hoary)
+   6.6.6
+   &#8220;Breezy Badger Autotest&#8221; series
+   (from Warty)
+   2005
+   &#8220;Guada2005&#8221; 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>
+            &#8220;<tal:codename replace="derivative/displayname" />&#8221; 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))