Merge lp:~bac/launchpad/bug-487793 into lp:launchpad

Proposed by Brad Crittenden
Status: Merged
Approved by: Curtis Hovey
Approved revision: not available
Merged at revision: not available
Proposed branch: lp:~bac/launchpad/bug-487793
Merge into: lp:launchpad
Diff against target: 646 lines (+234/-51)
18 files modified
lib/canonical/launchpad/tour/bugs (+1/-1)
lib/lp/answers/interfaces/questionenums.py (+2/-2)
lib/lp/blueprints/browser/specificationtarget.py (+5/-4)
lib/lp/blueprints/interfaces/specification.py (+2/-2)
lib/lp/blueprints/stories/sprints/01-sprint-overview.txt (+1/-1)
lib/lp/bugs/doc/externalbugtracker.txt (+1/-1)
lib/lp/bugs/scripts/checkwatches.py (+1/-1)
lib/lp/registry/browser/configure.zcml (+4/-0)
lib/lp/registry/browser/distroseries.py (+29/-4)
lib/lp/registry/doc/distroseries.txt (+35/-4)
lib/lp/registry/interfaces/distroseries.py (+10/-3)
lib/lp/registry/model/distroseries.py (+27/-9)
lib/lp/registry/stories/distroseries/xx-distroseries-index.txt (+21/-0)
lib/lp/registry/templates/distroseries-index.pt (+5/-4)
lib/lp/registry/templates/distroseries-portlet-packaging.pt (+78/-0)
lib/lp/registry/tests/test_distroseries.py (+10/-10)
lib/lp/soyuz/templates/distroseries-portlet-latestuploads.pt (+0/-3)
lib/lp/translations/help/imported-upload.html (+2/-2)
To merge this branch: bzr merge lp:~bac/launchpad/bug-487793
Reviewer Review Type Date Requested Status
Curtis Hovey (community) ui Approve
Abel Deuring (community) Approve
Guilherme Salgado Pending
Review via email: mp+19700@code.launchpad.net

Commit message

Add a portlet on the distroseries index page showing the packages most recently linked to an upstream and those that need linking.

To post a comment you must log in.
Revision history for this message
Brad Crittenden (bac) wrote :

= Summary =

A new portlet to show information about a distroseries' upstream packaging
associations is shown on the distroseries-index page.

== Proposed fix ==

Create a new portlet showing the most recently linked packages and those that most
need attention, either linking or providing additional information like bug tracker
location, etc.

== Pre-implementation notes ==

Talks with Curtis.

== Implementation details ==

As above.

== Tests ==

bin/test -vvm lp.registry -t distroseries.txt -t xx-distroseries-index.txt

== Demo and Q/A ==

https://launchpad.dev/ubuntu/warty
https://launchpad.dev/ubuntu/warty
https://launchpad.net/ubuntu/lucid

= Launchpad lint =

Checking for conflicts. and issues in doctests and templates.
Running jslint, xmllint, pyflakes, and pylint.
Using normal rules.

Linting changed files:
  lib/lp/registry/browser/configure.zcml
  lib/lp/registry/templates/distroseries-portlet-packaging.pt
  lib/lp/registry/templates/distroseries-index.pt
  lib/lp/blueprints/browser/specificationtarget.py
  lib/lp/registry/doc/distroseries.txt
  lib/lp/registry/interfaces/distroseries.py
  BRANCH.TODO
  lib/lp/registry/stories/distroseries/xx-distroseries-index.txt
  lib/lp/registry/model/distroseries.py
  lib/lp/registry/browser/distroseries.py
  lib/lp/soyuz/templates/distroseries-portlet-latestuploads.pt

== Pylint notices ==

lib/lp/registry/interfaces/distroseries.py
    23: [F0401] Unable to import 'lazr.enum' (No module named enum)
    50: [F0401] Unable to import 'lazr.restful.fields' (No module named restful)
    51: [F0401] Unable to import 'lazr.restful.declarations' (No module named restful)
    116: [E1002, DistroSeriesVersionField._validate] Use super on an old style class
    452: [C0322, IDistroSeriesPublic.getPackageUploads] Operator not preceded by a space
    description=_("Return items that are more recent than this "
    ^
    "timestamp."),
    required=False),
    status=Choice(

    vocabulary=DBEnumeratedType,
    title=_("Package Upload Status"),
    description=_("Return only items that have this status."),
    required=False),
    archive=Reference(

    schema=Interface,
    title=_("Archive"),
    description=_("Return only items for this archive."),
    required=False),
    pocket=Choice(

    vocabulary=DBEnumeratedType,
    title=_("Pocket"),
    description=_("Return only items targeted to this pocket"),
    required=False),
    custom_type=Choice(

    vocabulary=DBEnumeratedType,
    title=_("Custom Type"),
    description=_("Return only items with custom files of this "
    "type."),
    required=False),
    )

    @operation_returns_collection_of(Interface)
    @export_read_operation()
    def getPackageUploads(created_since_date, status, archive, pocket,
    custom_type):

lib/lp/registry/model/distroseries.py
    478: [W0105, DistroSeries.getMostRecentlyLinkedPackagings] String statement has
no effect

The last lint issue will be fixed before landing.

Also a number of drive-by typos will be fixed before landing. The diff is at:
http://pastebin.ubuntu.com/379696/

Priorized -> Prioritized (in two method names)
prioritised -> prioritzed (use en_US)
pakage -> package

Revision history for this message
Abel Deuring (adeuring) wrote :

Hi Brad,

a nice branch! Thanks for all the drve-by cleanups!

I have just one minor suggestion, see below.

Abel

> === modified file 'lib/lp/registry/model/distroseries.py'
> --- lib/lp/registry/model/distroseries.py 2010-01-21 18:09:05 +0000
> +++ lib/lp/registry/model/distroseries.py 2010-02-19 12:22:16 +0000
> @@ -315,9 +315,9 @@
> result += self.distribution.name + self.name
> return result
>
> - @property
> - def packagings(self):
> - """See `IDistroSeries`."""
> + @cachedproperty
> + def _all_packagings(self):
> + """Get an unordered list of all packagings."""
> # We join to SourcePackageName, ProductSeries, and Product to cache
> # the objects that are implicitly needed to work with a
> # Packaging object.
> @@ -338,6 +338,12 @@
> ProductSeries.product == Product.id)]
> condition = [Packaging.distroseries == self.id]
> results = IStore(self).using(*origin).find(find_spec, *condition)
> + return results

Since the list 'condition' will never change in this method, I think this can be simplified to

          condition = Packaging.distroseries == self.id
          results = IStore(self).using(*origin).find(find_spec, condition)

review: Approve
Revision history for this message
Brad Crittenden (bac) wrote :

Abel thanks for your suggestion. Unfortunately the condition has to be a sequence. Since it is immutable I could make it a tuple instead of a list but that would look like:

condition = (Packaging.distroseries == self.id, )

which I personally to be more distracting than just using a list. So unless you have strong objections I'd prefer to leave the code as is.

Revision history for this message
Abel Deuring (adeuring) wrote :

On 19.02.2010 16:35, Brad Crittenden wrote:
> Abel thanks for your suggestion. Unfortunately the condition has to be a sequence. Since it is immutable I could make it a tuple instead of a list but that would look like:
>
> condition = (Packaging.distroseries == self.id, )
>
> which I personally to be more distracting than just using a list. So unless you have strong objections I'd prefer to leave the code as is.

Sure, having a tuple there would be more distracting. But, well, perhaps
I am a bit thick today, and out of curiosity -- what is wrong with

        condition = Packaging.distroseries == self.id
        results = IStore(self).using(*origin).find(find_spec, condition)
        return results

I just tried the tests with this variant -- it passed just fine.

Abel

Revision history for this message
Brad Crittenden (bac) wrote :

Abel thanks for the follow up. It was me being dense, not you!

When I'd attempted to make the change earlier I failed to remove the * from *condition when passing the parameter.

I've made the improvement now as you suggest.

Revision history for this message
Curtis Hovey (sinzui) wrote :

Hi Brad.

Thanks for working on this. Thanks for finalising my mistmatched suggestions.

There is a link problem. I see (i) Show uploads in the new portlet. It should have remained with the Latest uploads portlet that was moved below the milestones. We expect the user to see the latest uploads, then use the (i) Show uploads link to see the uploads that are not listed.

The new portlet is showing linked and unlinked packagings, and each has its own report. We want the new portlet to link to these reports: (i) Needs upstream links (i) All upstream links. Note that each report links to its counterpart so that the user does not need to return to the portlet.

This is fine to land after to reconcile the links.

Oh wow, I just used Edwin's link source package to upstream project multi-step form. That was nice. That is much faster than the series picker. I see that users are shown when they set the package...sample data sucks.

review: Approve (ui)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/tour/bugs'
2--- lib/canonical/launchpad/tour/bugs 2010-01-31 15:25:37 +0000
3+++ lib/canonical/launchpad/tour/bugs 2010-02-22 13:15:31 +0000
4@@ -96,7 +96,7 @@
5 <p>
6 Manage bug reports entirely through your mail client, using the Launchpad Bugs email interface.
7 <br /><br />
8- Report, subscribe, comment on, assign, prioritise and make just about any other update to bugs tracked in Launchpad using email. Launchpad will also mail you with updates on any bug you're interested in, whether an individual bug or all the bugs associated with a particular project or package. Read more about <a href="https://help.launchpad.net/Bugs/EmailInterface">the bug tracker's e-mail interface &gt;</a><br /><br />
9+ Report, subscribe, comment on, assign, prioritize and make just about any other update to bugs tracked in Launchpad using email. Launchpad will also mail you with updates on any bug you're interested in, whether an individual bug or all the bugs associated with a particular project or package. Read more about <a href="https://help.launchpad.net/Bugs/EmailInterface">the bug tracker's e-mail interface &gt;</a><br /><br />
10 And, if you prefer, you can also subscribe to bug information in your feed reader with our Atom feeds. <img src="https://edge.launchpad.net/@@/rss" alt="Feed logo" />
11 </p>
12 </div>
13
14=== modified file 'lib/lp/answers/interfaces/questionenums.py'
15--- lib/lp/answers/interfaces/questionenums.py 2009-06-24 23:10:46 +0000
16+++ lib/lp/answers/interfaces/questionenums.py 2010-02-22 13:15:31 +0000
17@@ -127,7 +127,7 @@
18 class QuestionPriority(DBEnumeratedType):
19 """The Priority with a Question must be handled.
20
21- This enum is used to prioritise work done in the Launchpad Answert Tracker
22+ This enum is used to prioritize work done in the Launchpad Answert Tracker
23 management system.
24 """
25
26@@ -150,7 +150,7 @@
27 High
28
29 This question has been flagged as being of higher than normal
30- priority. It should always be prioritised over a "normal" question.
31+ priority. It should always be prioritized over a "normal" question.
32 """)
33
34 EMERGENCY = DBItem(90, """
35
36=== modified file 'lib/lp/blueprints/browser/specificationtarget.py'
37--- lib/lp/blueprints/browser/specificationtarget.py 2010-02-17 12:13:47 +0000
38+++ lib/lp/blueprints/browser/specificationtarget.py 2010-02-22 13:15:31 +0000
39@@ -349,7 +349,7 @@
40 """
41 categories = {}
42 for spec in self.specs:
43- if categories.has_key(spec.definition_status):
44+ if spec.definition_status in categories:
45 category = categories[spec.definition_status]
46 else:
47 category = {}
48@@ -361,8 +361,10 @@
49 return sorted(categories, key=itemgetter('definition_status'))
50
51 def getLatestSpecifications(self, quantity=5):
52- """Return <quantity> latest specs created for this target. This
53- is used by the +portlet-latestspecs view.
54+ """Return <quantity> latest specs created for this target.
55+
56+ Only ACCEPTED specifications are returned. This list is used by the
57+ +portlet-latestspecs view.
58 """
59 return self.context.specifications(sort=SpecificationSort.DATE,
60 quantity=quantity, prejoin_people=False)
61@@ -425,4 +427,3 @@
62 class BlueprintsVHostBreadcrumb(Breadcrumb):
63 rootsite = 'blueprints'
64 text = 'Blueprints'
65-
66
67=== modified file 'lib/lp/blueprints/interfaces/specification.py'
68--- lib/lp/blueprints/interfaces/specification.py 2010-02-17 11:19:42 +0000
69+++ lib/lp/blueprints/interfaces/specification.py 2010-02-22 13:15:31 +0000
70@@ -199,7 +199,7 @@
71 class SpecificationPriority(DBEnumeratedType):
72 """The Priority with a Specification must be implemented.
73
74- This enum is used to prioritise work.
75+ This enum is used to prioritize work.
76 """
77
78 NOTFORUS = DBItem(0, """
79@@ -218,7 +218,7 @@
80 Undefined
81
82 This feature has recently been proposed and has not yet been
83- evaluated and prioritised by the project leaders.
84+ evaluated and prioritized by the project leaders.
85 """)
86
87 LOW = DBItem(10, """
88
89=== modified file 'lib/lp/blueprints/stories/sprints/01-sprint-overview.txt'
90--- lib/lp/blueprints/stories/sprints/01-sprint-overview.txt 2009-08-21 21:43:42 +0000
91+++ lib/lp/blueprints/stories/sprints/01-sprint-overview.txt 2010-02-22 13:15:31 +0000
92@@ -3,7 +3,7 @@
93
94 Launchpad lets us register a meeting, and then keep track of which specs are
95 due to be discussed at that meeting. As a result we can schedule and
96-prioritise BOF's at the meeting, using an as-yet-undeveloped
97+prioritize BOF's at the meeting, using an as-yet-undeveloped
98 schedul-o-matic.
99
100 Let's start by viewing the list of sprints registered.
101
102=== modified file 'lib/lp/bugs/doc/externalbugtracker.txt'
103--- lib/lp/bugs/doc/externalbugtracker.txt 2010-01-20 15:58:04 +0000
104+++ lib/lp/bugs/doc/externalbugtracker.txt 2010-02-22 13:15:31 +0000
105@@ -955,7 +955,7 @@
106
107 == Prioritisation of watches ==
108
109-_getRemoteIdsToCheck() prioritises the IDs it returns. Bug watches which have
110+_getRemoteIdsToCheck() prioritizes the IDs it returns. Bug watches which have
111 comments to push or which have never been checked will always be returned in
112 the remote_ids_to_check list, limited only by the batch_size of the bug
113 tracker (see "Batched BugWatch Updating" in doc/checkwatches.txt).
114
115=== modified file 'lib/lp/bugs/scripts/checkwatches.py'
116--- lib/lp/bugs/scripts/checkwatches.py 2010-02-12 15:48:39 +0000
117+++ lib/lp/bugs/scripts/checkwatches.py 2010-02-22 13:15:31 +0000
118@@ -637,7 +637,7 @@
119
120 if batch_size is not None:
121 # We'll recreate our remote_ids_to_check list so that it's
122- # prioritised. We always include remote ids with comments.
123+ # prioritized. We always include remote ids with comments.
124 actual_remote_ids_to_check = sorted(
125 remote_ids_with_comments[:batch_size])
126
127
128=== modified file 'lib/lp/registry/browser/configure.zcml'
129--- lib/lp/registry/browser/configure.zcml 2010-02-17 11:19:42 +0000
130+++ lib/lp/registry/browser/configure.zcml 2010-02-22 13:15:31 +0000
131@@ -91,6 +91,10 @@
132 name="+portlet-details"
133 facet="overview"
134 template="../templates/distroseries-details.pt"/>
135+ <browser:page
136+ name="+portlet-package-summary"
137+ facet="overview"
138+ template="../templates/distroseries-portlet-packaging.pt"/>
139 </browser:pages>
140 <browser:page
141 for="lp.registry.interfaces.distroseries.IDistroSeries"
142
143=== modified file 'lib/lp/registry/browser/distroseries.py'
144--- lib/lp/registry/browser/distroseries.py 2010-02-18 20:44:30 +0000
145+++ lib/lp/registry/browser/distroseries.py 2010-02-22 13:15:31 +0000
146@@ -179,12 +179,12 @@
147
148 def packaging(self):
149 text = 'All upstream links'
150- summary = 'A listing of source pakages and their upstream projects'
151+ summary = 'A listing of source packages and their upstream projects'
152 return Link('+packaging', text, summary=summary, icon='info')
153
154 def needs_packaging(self):
155 text = 'Needs upstream links'
156- summary = 'A listing of source pakages without upstream projects'
157+ summary = 'A listing of source packages without upstream projects'
158 return Link('+needs-packaging', text, summary=summary, icon='info')
159
160 # A search link isn't needed because the distro series overview
161@@ -332,6 +332,31 @@
162 See `BuildRecordsView` for further details."""
163 return True
164
165+ @cachedproperty
166+ def num_linked_packages(self):
167+ """The number of linked packagings for this distroseries."""
168+ return len(self.context.packagings)
169+
170+ @cachedproperty
171+ def _unlinked_packages(self):
172+ """Get a prioritized list of unlinked source packages."""
173+ return self.context.getPrioritizedUnlinkedSourcePackages()
174+
175+ @property
176+ def num_unlinked_packages(self):
177+ """The number of unlinked packagings for this distroseries."""
178+ return len(self._unlinked_packages)
179+
180+ @cachedproperty
181+ def recently_linked(self):
182+ """Return the packages that were most recently linked upstream."""
183+ return self.context.getMostRecentlyLinkedPackagings()
184+
185+ @property
186+ def needs_linking(self):
187+ """Return a list of 10 packages most in need of upstream linking."""
188+ return self._unlinked_packages[:10]
189+
190 milestone_can_release = False
191
192
193@@ -474,7 +499,7 @@
194 @cachedproperty
195 def cached_packagings(self):
196 """The batched upstream packaging links."""
197- packagings = self.context.getPriorizedlPackagings()
198+ packagings = self.context.getPrioritizedlPackagings()
199 navigator = BatchNavigator(packagings, self.request, size=20)
200 navigator.setHeadings('packaging', 'packagings')
201 return navigator
202@@ -489,7 +514,7 @@
203 @cachedproperty
204 def cached_unlinked_packages(self):
205 """The batched `ISourcePackage`s that needs packaging links."""
206- packages = self.context.getPriorizedUnlinkedSourcePackages()
207+ packages = self.context.getPrioritizedUnlinkedSourcePackages()
208 navigator = BatchNavigator(packages, self.request, size=20)
209 navigator.setHeadings('package', 'packages')
210 return navigator
211
212=== modified file 'lib/lp/registry/doc/distroseries.txt'
213--- lib/lp/registry/doc/distroseries.txt 2010-01-21 14:53:29 +0000
214+++ lib/lp/registry/doc/distroseries.txt 2010-02-22 13:15:31 +0000
215@@ -503,13 +503,13 @@
216 Packages that need linking and packagings that need upstream information
217 -----------------------------------------------------------------------
218
219-The distroseries getPriorizedUnlinkedSourcePackages() method that returns
220+The distroseries getPrioritizedUnlinkedSourcePackages() method returns
221 a prioritized list of `ISourcePackage` objects that need a packaging link to
222 an `IProductSeries` to provide the the upstream information to share bugs,
223 translations, and code. Each item in the list is a dict with the 'package',
224 total_bugs, and total_messages (translatable messages).
225
226- >>> for summary in hoary.getPriorizedUnlinkedSourcePackages():
227+ >>> for summary in hoary.getPrioritizedUnlinkedSourcePackages():
228 ... print summary['package'].name
229 ... print '%(total_bugs)s %(total_messages)s' % summary
230 pmount 0 64
231@@ -519,16 +519,47 @@
232 linux-source-2.6.15 1 0
233
234
235-The distroseries getPriorizedlPackagings() method that returns a prioritized
236+The distroseries getPrioritizedlPackagings() method that returns a prioritized
237 list of `IPackaging` that need more information about the upstream project to
238 share bugs, translations, and code.
239
240- >>> for packaging in hoary.getPriorizedlPackagings():
241+ >>> for packaging in hoary.getPrioritizedlPackagings():
242 ... print packaging.sourcepackagename.name
243 netapplet
244 evolution
245
246
247+Most recently linked packagings
248+-------------------------------
249+
250+The distroseries getMostRecentlyLinkedPackagings() method returns a
251+list of up to five packages that are the most recently linked to an
252+upstream.
253+
254+ >>> distribution = factory.makeDistribution()
255+ >>> distroseries = factory.makeDistroRelease(distribution=distribution)
256+ >>> pkgs = distroseries.getMostRecentlyLinkedPackagings()
257+ >>> print len(pkgs)
258+ 0
259+
260+ >>> for name in ['aaron', 'bjorn', 'chex', 'deryck', 'edwin', 'francis']:
261+ ... product = factory.makeProduct(name=name)
262+ ... productseries = factory.makeProductSeries(product=product)
263+ ... spn = factory.makeSourcePackageName(name=name)
264+ ... package = factory.makeSourcePackage(sourcepackagename=spn,
265+ ... distroseries=distroseries)
266+ ... package.setPackaging(productseries, product.owner)
267+
268+
269+ >>> pkgs = distroseries.getMostRecentlyLinkedPackagings()
270+ >>> for packaging in pkgs:
271+ ... print packaging.sourcepackagename.name
272+ francis
273+ edwin
274+ deryck
275+ chex
276+ bjorn
277+
278 DistroSeries can build meta objects for packages
279 ------------------------------------------------
280
281
282=== modified file 'lib/lp/registry/interfaces/distroseries.py'
283--- lib/lp/registry/interfaces/distroseries.py 2010-01-21 17:49:38 +0000
284+++ lib/lp/registry/interfaces/distroseries.py 2010-02-22 13:15:31 +0000
285@@ -389,7 +389,7 @@
286 def getDistroArchSeriesByProcessor(processor):
287 """Return the distroarchseries for this distroseries with the
288 given architecturetag from a `IProcessor`.
289-
290+
291 :param processor: An `IProcessor`
292 :return: An `IDistroArchSeries` or None when none was found.
293 """
294@@ -429,16 +429,23 @@
295 that can be translated.
296 """
297
298- def getPriorizedUnlinkedSourcePackages():
299+ def getPrioritizedUnlinkedSourcePackages():
300 """Return a list of package summaries that need packaging links.
301
302 A summary is a dict of package (`ISourcePackage`), total_bugs,
303 and total_messages (translatable messages).
304 """
305
306- def getPriorizedlPackagings():
307+ def getPrioritizedlPackagings():
308 """Return a list of packagings that need more upstream information."""
309
310+ def getMostRecentlyLinkedPackagings():
311+ """Return a list of packagings that are the most recently linked.
312+
313+ At most five packages are returned of those most recently linked to an
314+ upstream.
315+ """
316+
317 @operation_parameters(
318 created_since_date=Datetime(
319 title=_("Created Since Timestamp"),
320
321=== modified file 'lib/lp/registry/model/distroseries.py'
322--- lib/lp/registry/model/distroseries.py 2010-01-21 18:09:05 +0000
323+++ lib/lp/registry/model/distroseries.py 2010-02-22 13:15:31 +0000
324@@ -315,9 +315,9 @@
325 result += self.distribution.name + self.name
326 return result
327
328- @property
329- def packagings(self):
330- """See `IDistroSeries`."""
331+ @cachedproperty
332+ def _all_packagings(self):
333+ """Get an unordered list of all packagings."""
334 # We join to SourcePackageName, ProductSeries, and Product to cache
335 # the objects that are implicitly needed to work with a
336 # Packaging object.
337@@ -336,14 +336,20 @@
338 Join(
339 Product,
340 ProductSeries.product == Product.id)]
341- condition = [Packaging.distroseries == self.id]
342- results = IStore(self).using(*origin).find(find_spec, *condition)
343+ condition = Packaging.distroseries == self.id
344+ results = IStore(self).using(*origin).find(find_spec, condition)
345+ return results
346+
347+ @property
348+ def packagings(self):
349+ """See `IDistroSeries`."""
350+ results = self._all_packagings
351 results = results.order_by(SourcePackageName.name)
352 return [
353 packaging
354 for (packaging, spn, product_series, product) in results]
355
356- def getPriorizedUnlinkedSourcePackages(self):
357+ def getPrioritizedUnlinkedSourcePackages(self):
358 """See `IDistroSeries`.
359
360 The prioritization is a heuristic rule using bug heat,
361@@ -368,7 +374,7 @@
362 'total_messages': total_messages}
363 for (spn, score, total_bugs, total_messages) in results]
364
365- def getPriorizedlPackagings(self):
366+ def getPrioritizedlPackagings(self):
367 """See `IDistroSeries`.
368
369 The prioritization is a heuristic rule using the branch, bug heat,
370@@ -405,7 +411,7 @@
371
372 @property
373 def _current_sourcepackage_joins_and_conditions(self):
374- """The SQL joins and conditions to prioritise source packages."""
375+ """The SQL joins and conditions to prioritize source packages."""
376 heat_score = ("""
377 LEFT JOIN (
378 SELECT
379@@ -467,11 +473,23 @@
380 primary=ArchivePurpose.PRIMARY))
381 return (joins, conditions)
382
383+ def getMostRecentlyLinkedPackagings(self):
384+ """See `IDistroSeries`."""
385+ results = self._all_packagings
386+ # Order by creation date with a secondary ordering by sourcepackage
387+ # name to ensure the ordering for test data where many packagings have
388+ # identical creation dates.
389+ results = results.order_by(Desc(Packaging.datecreated),
390+ SourcePackageName.name)[:5]
391+ return [
392+ packaging
393+ for (packaging, spn, product_series, product) in results]
394+
395 @property
396 def supported(self):
397 return self.status in [
398 SeriesStatus.CURRENT,
399- SeriesStatus.SUPPORTED
400+ SeriesStatus.SUPPORTED,
401 ]
402
403 @property
404
405=== modified file 'lib/lp/registry/stories/distroseries/xx-distroseries-index.txt'
406--- lib/lp/registry/stories/distroseries/xx-distroseries-index.txt 2009-11-07 07:46:54 +0000
407+++ lib/lp/registry/stories/distroseries/xx-distroseries-index.txt 2010-02-22 13:15:31 +0000
408@@ -80,3 +80,24 @@
409 Subscribe to Bugs in The Warty Warthog Release
410
411
412+Upstream packaging portlet
413+--------------------------
414+
415+The distroseries page contains a portlet with information on the
416+upstream packaging.
417+
418+ >>> anon_browser.open('http://launchpad.dev/ubuntu/warty')
419+ >>> print extract_text(
420+ ... find_portlet(anon_browser.contents, 'Upstream packaging'))
421+ Upstream packaging
422+ 5 source packages are linked to registered upstream projects.
423+ 3 need linking.
424+ Recently linked to upstream:
425+ alsa-utils linked...
426+ a52dec linked...
427+ evolution linked...
428+ mozilla-firefox linked...
429+ netapplet linked...
430+ Needs more information or linking to upstream:
431+ linux-source-2.6.15 cdrkit iceweasel
432+ Needs upstream links All upstream links
433
434=== modified file 'lib/lp/registry/templates/distroseries-index.pt'
435--- lib/lp/registry/templates/distroseries-index.pt 2010-02-11 20:59:29 +0000
436+++ lib/lp/registry/templates/distroseries-index.pt 2010-02-22 13:15:31 +0000
437@@ -55,7 +55,7 @@
438 </div>
439
440 <div class="yui-u">
441- <div tal:replace="structure context/@@+portlet-latestuploads" />
442+ <div tal:replace="structure context/@@+portlet-package-summary" />
443 </div>
444 </div>
445
446@@ -74,14 +74,15 @@
447 <div class="yui-g">
448 <div class="yui-u first">
449 <div
450+ tal:replace="structure context/@@+portlet-architectures" />
451+ <div
452 tal:content="structure context/@@+portlet-latestbugs"
453 tal:condition="context/@@+get-involved/official_malone" />
454-
455- <div
456- tal:replace="structure context/@@+portlet-architectures" />
457 </div>
458
459 <div class="yui-u">
460+ <div tal:replace="structure context/@@+portlet-latestuploads" />
461+
462 <div
463 tal:content="structure context/@@+portlet-latestspecs"
464 tal:condition="context/@@+get-involved/official_blueprints" />
465
466=== added file 'lib/lp/registry/templates/distroseries-portlet-packaging.pt'
467--- lib/lp/registry/templates/distroseries-portlet-packaging.pt 1970-01-01 00:00:00 +0000
468+++ lib/lp/registry/templates/distroseries-portlet-packaging.pt 2010-02-22 13:15:31 +0000
469@@ -0,0 +1,78 @@
470+<div
471+ xmlns:tal="http://xml.zope.org/namespaces/tal"
472+ xmlns:metal="http://xml.zope.org/namespaces/metal"
473+ xmlns:i18n="http://xml.zope.org/namespaces/i18n"
474+ id="series-packaging" class="portlet"
475+ tal:define="overview_menu context/menu:overview">
476+ <h2>Upstream packaging</h2>
477+
478+ <p id="packaging-summary"
479+ tal:define="count view/num_linked_packages;
480+ singular string:source package is;
481+ plural string:source packages are">
482+ <strong tal:content="view/num_linked_packages"/>&nbsp;
483+ <tal:plural
484+ metal:use-macro="context/@@+base-layout-macros/plural-message"/>
485+ linked to
486+ registered upstream projects.
487+ <strong tal:content="view/num_unlinked_packages"/>&nbsp;
488+ <tal:needs define="singular string:needs;
489+ plural string:need">
490+ <tal:plural
491+ metal:use-macro="context/@@+base-layout-macros/plural-message"/>
492+ </tal:needs>
493+ linking.
494+ </p>
495+
496+ <dl>
497+ <dt id="recently-linked"
498+ tal:condition="view/recently_linked">
499+ Recently linked to upstream:
500+ </dt>
501+ <dd>
502+ <tal:package repeat="package view/recently_linked">
503+ <p>
504+ <a class="sprite package-source"
505+ tal:attributes="href package/sourcepackage/fmt:url"
506+ tal:content="package/sourcepackage/name">evolution</a>
507+ <tal:owner condition="package/owner">
508+ <span class="discreet greyed-out">linked by</span>
509+ <a tal:replace="structure package/owner/fmt:link" />
510+ </tal:owner>
511+ <tal:no_owner condition="not:package/owner">
512+ <span class="discreet greyed-out">linked</span>
513+ </tal:no_owner>
514+ <span class="discreet greyed-out"
515+ tal:attributes="title package/datecreated/fmt:datetime"
516+ tal:content="package/datecreated/fmt:approximatedate" />
517+ </p>
518+ </tal:package>
519+ </dd>
520+
521+ <dt id="needs-linking"
522+ tal:condition="view/needs_linking">
523+ Needs more information or linking to upstream:
524+ </dt>
525+ <dd>
526+ <ul class="horizontal">
527+ <tal:package repeat="package view/needs_linking">
528+ <li>
529+ <a class="sprite package-source"
530+ tal:attributes="href package/package/fmt:url"
531+ tal:content="package/package/name">evolution</a>
532+ </li>
533+ </tal:package>
534+ </ul>
535+ </dd>
536+ </dl>
537+
538+ <ul class="horizontal">
539+ <li>
540+ <a tal:replace="structure context/menu:overview/needs_packaging/fmt:link" />
541+ </li>
542+ <li>
543+ <a tal:replace="structure context/menu:overview/packaging/fmt:icon-link" />
544+ </li>
545+ </ul>
546+
547+</div>
548
549=== modified file 'lib/lp/registry/tests/test_distroseries.py'
550--- lib/lp/registry/tests/test_distroseries.py 2010-01-21 17:49:38 +0000
551+++ lib/lp/registry/tests/test_distroseries.py 2010-02-22 13:15:31 +0000
552@@ -253,47 +253,47 @@
553 self.series, self.packages[name].sourcepackagename, self.user)
554 return product_series
555
556- def test_getPriorizedUnlinkedSourcePackages(self):
557+ def test_getPrioritizedUnlinkedSourcePackages(self):
558 # Verify the ordering of source packages that need linking.
559- package_summaries = self.series.getPriorizedUnlinkedSourcePackages()
560+ package_summaries = self.series.getPrioritizedUnlinkedSourcePackages()
561 names = [summary['package'].name for summary in package_summaries]
562 expected = [
563 u'main', u'hot-translatable', u'hot', u'translatable', u'normal']
564 self.assertEqual(expected, names)
565
566- def test_getPriorizedlPackagings(self):
567+ def test_getPrioritizedlPackagings(self):
568 # Verify the ordering of packagings that need more upstream info.
569 for name in ['main', 'hot-translatable', 'hot', 'translatable']:
570 self.linkPackage(name)
571- packagings = self.series.getPriorizedlPackagings()
572+ packagings = self.series.getPrioritizedlPackagings()
573 names = [packaging.sourcepackagename.name for packaging in packagings]
574 expected = [
575 u'main', u'hot-translatable', u'hot', u'translatable', u'linked']
576 self.assertEqual(expected, names)
577
578- def test_getPriorizedlPackagings_bug_tracker(self):
579+ def test_getPrioritizedlPackagings_bug_tracker(self):
580 # Verify the ordering of packagings with and without a bug tracker.
581 self.linkPackage('hot')
582 self.makeSeriesPackage('cold')
583 product_series = self.linkPackage('cold')
584 product_series.product.bugtraker = self.factory.makeBugTracker()
585- packagings = self.series.getPriorizedlPackagings()
586+ packagings = self.series.getPrioritizedlPackagings()
587 names = [packaging.sourcepackagename.name for packaging in packagings]
588 expected = [u'hot', u'linked', u'cold']
589 self.assertEqual(expected, names)
590
591- def test_getPriorizedlPackagings_branch(self):
592+ def test_getPrioritizedlPackagings_branch(self):
593 # Verify the ordering of packagings with and without a branch.
594 self.linkPackage('translatable')
595 self.makeSeriesPackage('withbranch')
596 product_series = self.linkPackage('withbranch')
597 product_series.branch = self.factory.makeBranch()
598- packagings = self.series.getPriorizedlPackagings()
599+ packagings = self.series.getPrioritizedlPackagings()
600 names = [packaging.sourcepackagename.name for packaging in packagings]
601 expected = [u'translatable', u'linked', u'withbranch']
602 self.assertEqual(expected, names)
603
604- def test_getPriorizedlPackagings_translation(self):
605+ def test_getPrioritizedlPackagings_translation(self):
606 # Verify the ordering of translatable packagings that are and are not
607 # configured to import.
608 self.linkPackage('translatable')
609@@ -302,7 +302,7 @@
610 product_series.branch = self.factory.makeBranch()
611 product_series.translations_autoimport_mode = (
612 TranslationsBranchImportMode.IMPORT_TEMPLATES)
613- packagings = self.series.getPriorizedlPackagings()
614+ packagings = self.series.getPrioritizedlPackagings()
615 names = [packaging.sourcepackagename.name for packaging in packagings]
616 expected = [u'translatable', u'linked', u'importabletranslatable']
617 self.assertEqual(expected, names)
618
619=== modified file 'lib/lp/soyuz/templates/distroseries-portlet-latestuploads.pt'
620--- lib/lp/soyuz/templates/distroseries-portlet-latestuploads.pt 2009-12-05 03:14:51 +0000
621+++ lib/lp/soyuz/templates/distroseries-portlet-latestuploads.pt 2010-02-22 13:15:31 +0000
622@@ -32,8 +32,5 @@
623 <li>
624 <a tal:replace="structure context/menu:overview/queue/fmt:link" />
625 </li>
626- <li>
627- <a tal:replace="structure context/menu:overview/packaging/fmt:icon-link" />
628- </li>
629 </ul>
630 </div>
631
632=== modified file 'lib/lp/translations/help/imported-upload.html'
633--- lib/lp/translations/help/imported-upload.html 2010-01-12 13:48:24 +0000
634+++ lib/lp/translations/help/imported-upload.html 2010-02-22 13:15:31 +0000
635@@ -19,10 +19,10 @@
636 the externally generated translations from time to time.
637 </p>
638
639- <h2>How Launchpad prioritises imported translation strings</h2>
640+ <h2>How Launchpad prioritizes imported translation strings</h2>
641
642 <p>To help ensure the best quality translations, Launchpad
643- prioritises translation strings differently depending on where
644+ prioritizes translation strings differently depending on where
645 they were made.
646 </p>
647