Merge lp:~rvb/launchpad/dds-fix-localpackagediffs-745776 into lp:launchpad

Proposed by Raphaël Badin
Status: Merged
Approved by: Julian Edwards
Approved revision: no longer in the source branch.
Merged at revision: 12727
Proposed branch: lp:~rvb/launchpad/dds-fix-localpackagediffs-745776
Merge into: lp:launchpad
Diff against target: 311 lines (+163/-28)
5 files modified
lib/lp/registry/browser/distroseriesdifference.py (+36/-3)
lib/lp/registry/browser/tests/test_series_views.py (+99/-14)
lib/lp/registry/interfaces/distroseries.py (+2/-0)
lib/lp/registry/model/distroseries.py (+2/-0)
lib/lp/registry/templates/distroseries-localdifferences.pt (+24/-11)
To merge this branch: bzr merge lp:~rvb/launchpad/dds-fix-localpackagediffs-745776
Reviewer Review Type Date Requested Status
Julian Edwards (community) Approve
Gavin Panella (community) Approve
Review via email: mp+55704@code.launchpad.net

Commit message

[r=allenap,julian-edwards][bug=745776] Fix the display of the versions on the localpackagediffs page.

Description of the change

This branch fixes the display of the versions in the localpackagediffs page and the link to the sourcepackagerelease.

= Test =

{{{
 ./bin/test -cvv test_series_views test_diff_row_shows_version_attached
}}}

= QA =
Check that the versions displayed in the localpackagediff page's table are consistent with the version displayed in the details of the difference.
(https://dogfood.launchpad.net/deribuntu/dangerous/+localpackagediffs)

To post a comment you must log in.
Revision history for this message
Gavin Panella (allenap) wrote :

Lovely. A few minor comments.

[1]

+ @property
+ def parent_source_package_url(self):
+ return self._package_url(parent=True)
+
+ @property
+ def source_package_url(self):
+ return self._package_url()
+
+ def _package_url(self, parent=False):
+ if parent:
+ distro_series = self.context.derived_series.parent_series
+ version = self.context.parent_source_version
+ else:
+ distro_series = self.context.derived_series
+ version = self.context.source_version

You could get one less level of indirection if you pass distro_series
and version into _package_url():

    @property
    def parent_source_package_url(self):
        return self._package_url(
            self.context.derived_series.parent_series,
            self.context.parent_source_version)

    @property
    def source_package_url(self):
        return self._package_url(
            self.context.derived_series,
            self.context.source_version)

    def _package_url(self, distro_series, version):
        ...

But this is entirely up to you; it's fine as it is.

[2]

+ try:
+ url = canonical_url(
+ DistroSeriesSourcePackageRelease(
+ distro_series, pubs[0].sourcepackagerelease))
+ return url
+ except IndexError:
+ return ''

I assume you're attempting to catch any potential IndexErrors raised
by pubs[0], but it will also catch IndexError in any of the called
code.

Now pubs will be an SQLObjectResultSet, so you could do the following:

        from storm.zope.interfaces import IResultSet

        pub = IResultSet(pubs).first()
        if pub is None:
            return ''
        else:
            return canonical_url(
                DistroSeriesSourcePackageRelease(
                    distro_series, pub.sourcepackagerelease))

Out of interest, why do you only consider the first publication here?

[3]

+ row = diff_table.tbody.findAll('tr')[0]

This could simplified to:

        row = diff_table.tbody.tr

[4]

+ self.assertTrue(link.endswith(new_version))
...
+ self.assertTrue(
+ links[0].get('href').endswith(difference.source_version))

In the event of a failure, you could make the report more informative
by using the EndsWith matcher:

    from testtools.matchers import EndsWith
    self.assertTrue(link, EndsWith(new_version))

review: Approve
Revision history for this message
Julian Edwards (julian-edwards) wrote :

6 + def _package_url(self, distro_series, version):
57 + pubs = distro_series.getPublishedSources(
58 + self.context.source_package_name, include_pending=True,
59 + version=version)

This is going to break for 2 reasons:

 1. include_pending=True will make it potentially return more than one version
 2. the archive is not specified so it'll pick up publications from PPAs

I want to deprecate IDistroSeries.getPublishedSources(), it's a little evil. Let's use IArchive.getPublishedSources() instead, you can do the exact same thing by copying this code:

pubs = distro_series.main_archive.getPublishedSources(
    name=self.context.source_package_name,
    version=version,
    status=PackagePublishingStatus.PUBLISHED,
    exact_match=True)

and profit!

review: Needs Fixing
Revision history for this message
Julian Edwards (julian-edwards) wrote :

When you make the fix, can you put a comment in the interface and the model code for the IDistroSeries.getPublishedSources() call to say that it's deprecated in favour of the IArchive version. It's far too easy to do the wrong thing with it, plus it's not Storm-ified.

Revision history for this message
Gavin Panella (allenap) wrote :

> pubs = distro_series.main_archive.getPublishedSources(
> name=self.context.source_package_name,
> version=version,
> status=PackagePublishingStatus.PUBLISHED,
> exact_match=True)

How many records can pubs contain? Will it always be 1, 0 or 1, 0 or
more, or 1 or more?

Revision history for this message
Julian Edwards (julian-edwards) wrote :

I missed distroseries= in that example, oops :)

With that, it'll always be none or one.

Revision history for this message
Raphaël Badin (rvb) wrote :

[1], [2], [3], [4] Fixed.

[2] Only one or zero publication should be there. I've reworked the code to better take this fact into account.

Revision history for this message
Julian Edwards (julian-edwards) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/distroseriesdifference.py'
2--- lib/lp/registry/browser/distroseriesdifference.py 2011-03-31 16:21:15 +0000
3+++ lib/lp/registry/browser/distroseriesdifference.py 2011-04-01 10:04:32 +0000
4@@ -10,6 +10,7 @@
5 ]
6
7 from lazr.restful.interfaces import IWebServiceClientRequest
8+from storm.zope.interfaces import IResultSet
9 from z3c.ptcompat import ViewPageTemplateFile
10 from zope.app.form.browser.itemswidgets import RadioWidget
11 from zope.component import (
12@@ -27,15 +28,14 @@
13 )
14
15 from canonical.launchpad.webapp import (
16+ canonical_url,
17 LaunchpadView,
18 Navigation,
19 stepthrough,
20 )
21 from canonical.launchpad.webapp.authorization import check_permission
22 from canonical.launchpad.webapp.launchpadform import custom_widget
23-from lp.app.browser.launchpadform import (
24- LaunchpadFormView,
25- )
26+from lp.app.browser.launchpadform import LaunchpadFormView
27 from lp.registry.enum import DistroSeriesDifferenceStatus
28 from lp.registry.interfaces.distroseriesdifference import (
29 IDistroSeriesDifference,
30@@ -51,6 +51,10 @@
31 IComment,
32 IConversation,
33 )
34+from lp.soyuz.enums import PackagePublishingStatus
35+from lp.soyuz.model.distroseriessourcepackagerelease import (
36+ DistroSeriesSourcePackageRelease,
37+ )
38
39
40 class DistroSeriesDifferenceNavigation(Navigation):
41@@ -67,6 +71,35 @@
42 IDistroSeriesDifferenceCommentSource).getForDifference(
43 self.context, id)
44
45+ @property
46+ def parent_source_package_url(self):
47+ return self._package_url(
48+ self.context.derived_series.parent_series,
49+ self.context.parent_source_version)
50+
51+ @property
52+ def source_package_url(self):
53+ return self._package_url(
54+ self.context.derived_series,
55+ self.context.source_version)
56+
57+ def _package_url(self, distro_series, version):
58+ pubs = distro_series.main_archive.getPublishedSources(
59+ name=self.context.source_package_name.name,
60+ version=version,
61+ status=PackagePublishingStatus.PUBLISHED,
62+ distroseries=distro_series,
63+ exact_match=True)
64+
65+ # There is only one or zero published package.
66+ pub = IResultSet(pubs).one()
67+ if pub is None:
68+ return None
69+ else:
70+ return canonical_url(
71+ DistroSeriesSourcePackageRelease(
72+ distro_series, pub.sourcepackagerelease))
73+
74
75 class IDistroSeriesDifferenceForm(Interface):
76 """An interface used in the browser only for displaying form elements."""
77
78=== modified file 'lib/lp/registry/browser/tests/test_series_views.py'
79--- lib/lp/registry/browser/tests/test_series_views.py 2011-03-29 18:09:43 +0000
80+++ lib/lp/registry/browser/tests/test_series_views.py 2011-04-01 10:04:32 +0000
81@@ -5,11 +5,10 @@
82
83 from BeautifulSoup import BeautifulSoup
84 from storm.zope.interfaces import IResultSet
85+from testtools.matchers import EndsWith
86 from zope.component import getUtility
87 from zope.security.proxy import removeSecurityProxy
88
89-import unittest
90-
91 from canonical.config import config
92 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
93 from canonical.launchpad.testing.pages import find_tag_by_id
94@@ -17,32 +16,33 @@
95 from canonical.launchpad.webapp.publisher import canonical_url
96 from canonical.testing.layers import (
97 DatabaseFunctionalLayer,
98+ LaunchpadFunctionalLayer,
99 LaunchpadZopelessLayer,
100- LaunchpadFunctionalLayer,
101 )
102 from lp.registry.enum import (
103 DistroSeriesDifferenceStatus,
104 DistroSeriesDifferenceType,
105 )
106+from lp.services.features import (
107+ getFeatureFlag,
108+ install_feature_controller,
109+ )
110 from lp.services.features.flags import FeatureController
111 from lp.services.features.model import (
112 FeatureFlag,
113 getFeatureStore,
114 )
115-from lp.services.features import (
116- getFeatureFlag,
117- install_feature_controller,
118+from lp.soyuz.enums import (
119+ PackagePublishingStatus,
120+ SourcePackageFormat,
121 )
122 from lp.soyuz.interfaces.sourcepackageformat import (
123 ISourcePackageFormatSelectionSet,
124 )
125-from lp.soyuz.enums import (
126- SourcePackageFormat,
127- )
128 from lp.testing import (
129- TestCaseWithFactory,
130 login_person,
131 person_logged_in,
132+ TestCaseWithFactory,
133 )
134 from lp.testing.views import create_initialized_view
135
136@@ -268,6 +268,95 @@
137 self.assertEqual(1, len(links))
138 self.assertEqual(difference.source_package_name.name, links[0].string)
139
140+ def test_diff_row_shows_version_attached(self):
141+ # The +localpackagediffs page shows the version attached to the
142+ # DSD and not the last published version (bug=745776).
143+ package_name = 'package-1'
144+ derived_series = self.factory.makeDistroSeries(
145+ name='derilucid', parent_series=self.factory.makeDistroSeries(
146+ name='lucid'))
147+ versions = {
148+ 'base': u'1.0',
149+ 'derived': u'1.0derived1',
150+ 'parent': u'1.0-1',
151+ }
152+ new_version = u'1.2'
153+
154+ difference = self.factory.makeDistroSeriesDifference(
155+ versions=versions,
156+ source_package_name_str=package_name,
157+ derived_series=derived_series)
158+
159+ # Create a more recent source package publishing history.
160+ sourcepackagename = self.factory.getOrMakeSourcePackageName(
161+ package_name)
162+ self.factory.makeSourcePackagePublishingHistory(
163+ sourcepackagename=sourcepackagename,
164+ distroseries=derived_series,
165+ version=new_version)
166+
167+ set_derived_series_ui_feature_flag(self)
168+ view = create_initialized_view(
169+ derived_series, '+localpackagediffs')
170+ soup = BeautifulSoup(view())
171+ diff_table = soup.find('table', {'class': 'listing'})
172+ row = diff_table.tbody.tr
173+ links = row.findAll('a', {'class': 'derived-version'})
174+
175+ # The version displayed is the version attached to the
176+ # difference.
177+ self.assertEqual(1, len(links))
178+ self.assertEqual(versions['derived'], links[0].string.strip())
179+
180+ link = canonical_url(difference.source_pub.sourcepackagerelease)
181+ self.assertTrue(link, EndsWith(new_version))
182+ # The link points to the sourcepackagerelease referenced in the
183+ # difference.
184+ self.assertTrue(
185+ links[0].get('href'), EndsWith(difference.source_version))
186+
187+ def test_diff_row_no_published_version(self):
188+ # The +localpackagediffs page shows only the version (no link)
189+ # if we fail to fetch the published version.
190+ package_name = 'package-1'
191+ derived_series = self.factory.makeDistroSeries(
192+ name='derilucid', parent_series=self.factory.makeDistroSeries(
193+ name='lucid'))
194+ versions = {
195+ 'base': u'1.0',
196+ 'derived': u'1.0derived1',
197+ 'parent': u'1.0-1',
198+ }
199+ new_version = u'1.2'
200+
201+ difference = self.factory.makeDistroSeriesDifference(
202+ versions=versions,
203+ source_package_name_str=package_name,
204+ derived_series=derived_series)
205+
206+ # Delete the publications.
207+ difference.source_pub.status = PackagePublishingStatus.DELETED
208+ difference.parent_source_pub.status = PackagePublishingStatus.DELETED
209+
210+ set_derived_series_ui_feature_flag(self)
211+ view = create_initialized_view(
212+ derived_series, '+localpackagediffs')
213+ soup = BeautifulSoup(view())
214+ diff_table = soup.find('table', {'class': 'listing'})
215+ row = diff_table.tbody.tr
216+
217+ # The table feature a simple span since we were unable to fetch a
218+ # published sourcepackage.
219+ derived_span = row.findAll('span', {'class': 'derived-version'})
220+ parent_span = row.findAll('span', {'class': 'parent-version'})
221+ self.assertEqual(1, len(derived_span))
222+ self.assertEqual(1, len(parent_span))
223+
224+ # The versions displayed are the versions attached to the
225+ # difference.
226+ self.assertEqual(versions['derived'], derived_span[0].string.strip())
227+ self.assertEqual(versions['parent'], parent_span[0].string.strip())
228+
229
230 class DistroSeriesLocalPackageDiffsFunctionalTestCase(TestCaseWithFactory):
231
232@@ -504,7 +593,3 @@
233 for item in view.milestone_batch_navigator.currentBatch()]
234 self.assertEqual(expected, milestone_names)
235 config.pop('default-batch-size')
236-
237-
238-def test_suite():
239- return unittest.TestLoader().loadTestsFromName(__name__)
240
241=== modified file 'lib/lp/registry/interfaces/distroseries.py'
242--- lib/lp/registry/interfaces/distroseries.py 2011-03-30 11:28:04 +0000
243+++ lib/lp/registry/interfaces/distroseries.py 2011-04-01 10:04:32 +0000
244@@ -559,6 +559,8 @@
245 archive=None):
246 """Return the SourcePackagePublishingHistory(s)
247
248+ Deprecated. Use IArchive.getPublishedSources instead.
249+
250 Given a ISourcePackageName or name.
251
252 If pocket is not specified, we look in all pockets.
253
254=== modified file 'lib/lp/registry/model/distroseries.py'
255--- lib/lp/registry/model/distroseries.py 2011-03-30 11:32:49 +0000
256+++ lib/lp/registry/model/distroseries.py 2011-04-01 10:04:32 +0000
257@@ -1069,6 +1069,8 @@
258 pocket=None, include_pending=False,
259 exclude_pocket=None, archive=None):
260 """See `IDistroSeries`."""
261+ # Deprecated. Use IArchive.getPublishedSources instead.
262+
263 # XXX cprov 2006-02-13 bug 31317:
264 # We need a standard and easy API, no need
265 # to support multiple type arguments, only string name should be
266
267=== modified file 'lib/lp/registry/templates/distroseries-localdifferences.pt'
268--- lib/lp/registry/templates/distroseries-localdifferences.pt 2011-03-23 16:28:51 +0000
269+++ lib/lp/registry/templates/distroseries-localdifferences.pt 2011-04-01 10:04:32 +0000
270@@ -64,17 +64,30 @@
271 id string:field.selected_differences.${src_name}"/>
272
273 <a tal:attributes="href difference/fmt:url" class="toggle-extra"
274- tal:content="parent_source_pub/source_package_name">Foo</a>
275- </td>
276- <td><a tal:attributes="href parent_source_pub/sourcepackagerelease/fmt:url">
277- <tal:replace
278- replace="parent_source_pub/sourcepackagerelease/version"/></a>
279- </td>
280- <td><a tal:attributes="href source_pub/sourcepackagerelease/fmt:url"
281- class="derived-version">
282- <tal:replace
283- replace="source_pub/sourcepackagerelease/version"/></a>
284- </td>
285+ tal:content="parent_source_pub/source_package_name">Foo</a>
286+ </td>
287+ <td tal:define="parent_source_pck_url difference/@@/parent_source_package_url">
288+ <a tal:condition="parent_source_pck_url"
289+ tal:attributes="href difference/@@/parent_source_package_url"
290+ class="parent-version">
291+ <tal:replace
292+ replace="difference/parent_source_version"/></a>
293+ <span tal:condition="not: parent_source_pck_url"
294+ class="parent-version"
295+ tal:content="difference/parent_source_version">
296+ </span>
297+ </td>
298+ <td tal:define="source_pck_url difference/@@/source_package_url">
299+ <a tal:condition="source_pck_url"
300+ tal:attributes="href difference/@@/source_package_url"
301+ class="derived-version">
302+ <tal:replace
303+ replace="difference/source_version"/></a>
304+ <span tal:condition="not: source_pck_url"
305+ class="derived-version"
306+ tal:content="difference/source_version">
307+ </span>
308+ </td>
309 <td>
310 <span tal:attributes="title difference/source_pub/datepublished/fmt:datetime"
311 tal:content="difference/source_pub/datepublished/fmt:approximatedate">2005-09-16</span>