Merge lp:~jcsackett/launchpad/dont-show-obsolete-junk into lp:launchpad

Proposed by j.c.sackett
Status: Merged
Approved by: Brad Crittenden
Approved revision: no longer in the source branch.
Merged at revision: 11373
Proposed branch: lp:~jcsackett/launchpad/dont-show-obsolete-junk
Merge into: lp:launchpad
Diff against target: 217 lines (+53/-25)
6 files modified
lib/lp/registry/browser/person.py (+1/-1)
lib/lp/registry/doc/person-account.txt (+1/-1)
lib/lp/registry/model/person.py (+28/-16)
lib/lp/registry/stories/person/xx-person-projects.txt (+2/-2)
lib/lp/registry/templates/person-portlet-related-projects.pt (+1/-3)
lib/lp/registry/tests/test_person.py (+20/-2)
To merge this branch: bzr merge lp:~jcsackett/launchpad/dont-show-obsolete-junk
Reviewer Review Type Date Requested Status
Brad Crittenden (community) code Approve
Review via email: mp+32585@code.launchpad.net

Commit message

Fixes the related-projects for a team so that only active projects &c are shown. Incidentally fixes the branding issues with how those projects are shown (i.e. branding wasn't shown before).

Description of the change

Summary

Fixes an issue with related projects show obsolete/inactive/invalid projects. Incidentally also fixes an issue with related projects not showing branding.

Proposed fix

If possible, change the model to not return inactive projects. Otherwise, change the view to iterate over the returned set and remove anything we don't want to show.

Pre-implementation notes

Spoke with Curtis Hovey (sinzui) about the problem and investigated the view and model with him.

Implementation details

The person model's getOwnedOrDrivenPillars now filters out inactive pillars where possible, and returns a DecoratedResultSet rather than a running a list comprehension before returning.

The template now users pillarname/pillar/fmt:link rather than a hand crafted url.

Demo and Q/A

Go to a team page (https://launchpad.dev/~ubuntu-team); you should see only active entities, and should see their branding.

lint
= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/browser/person.py
  lib/lp/registry/model/person.py
  lib/lp/registry/stories/person/xx-person-projects.txt
  lib/lp/registry/templates/person-portlet-related-projects.pt
  lib/lp/registry/tests/test_person.py

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

Hi Jonathan,

This branch looks good. I think the following:

return len(list(self._related_projects()))

could be:

return self._related_projects().count()

Please give that a whirl.

I'm marking this Approved with the understanding you'll make that change.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/browser/person.py'
2--- lib/lp/registry/browser/person.py 2010-08-13 07:48:54 +0000
3+++ lib/lp/registry/browser/person.py 2010-08-17 14:54:45 +0000
4@@ -5168,7 +5168,7 @@
5 @cachedproperty
6 def related_projects_count(self):
7 """The number of project owned or driven by this person."""
8- return len(self._related_projects())
9+ return self._related_projects().count()
10
11 @cachedproperty
12 def has_more_related_projects(self):
13
14=== modified file 'lib/lp/registry/doc/person-account.txt'
15--- lib/lp/registry/doc/person-account.txt 2010-07-14 21:19:42 +0000
16+++ lib/lp/registry/doc/person-account.txt 2010-08-17 14:54:45 +0000
17@@ -185,7 +185,7 @@
18
19 ...no owned or driven pillars...
20
21- >>> len(foobar.getOwnedOrDrivenPillars())
22+ >>> foobar.getOwnedOrDrivenPillars().count()
23 0
24
25 ...and, finally, to not be considered a valid person in Launchpad.
26
27=== modified file 'lib/lp/registry/model/person.py'
28--- lib/lp/registry/model/person.py 2010-08-16 07:27:07 +0000
29+++ lib/lp/registry/model/person.py 2010-08-17 14:54:45 +0000
30@@ -61,11 +61,13 @@
31 from canonical.database.sqlbase import (
32 cursor, quote, quote_like, sqlvalues, SQLBase)
33
34-from canonical.cachedproperty import cachedproperty, cache_property, clear_property
35-
36-from canonical.lazr.utils import get_current_browser_request, safe_hasattr
37-
38-from canonical.launchpad.components.decoratedresultset import DecoratedResultSet
39+from canonical.cachedproperty import (cachedproperty, cache_property,
40+ clear_property)
41+
42+from canonical.lazr.utils import get_current_browser_request
43+
44+from canonical.launchpad.components.decoratedresultset import (
45+ DecoratedResultSet)
46 from canonical.launchpad.database.account import Account, AccountPassword
47 from canonical.launchpad.interfaces.account import AccountSuspendedError
48 from lp.bugs.model.bugtarget import HasBugsBase
49@@ -890,14 +892,16 @@
50 SELECT name, 3 as kind, displayname
51 FROM product
52 WHERE
53- driver = %(person)s
54- OR owner = %(person)s
55+ active = True AND
56+ (driver = %(person)s
57+ OR owner = %(person)s)
58 UNION
59 SELECT name, 2 as kind, displayname
60 FROM project
61 WHERE
62- driver = %(person)s
63- OR owner = %(person)s
64+ active = True AND
65+ (driver = %(person)s
66+ OR owner = %(person)s)
67 UNION
68 SELECT name, 1 as kind, displayname
69 FROM distribution
70@@ -909,7 +913,12 @@
71 """ % sqlvalues(person=self))
72 results = IStore(self).using(origin).find(find_spec)
73 results = results.order_by('kind', 'displayname')
74- return [pillar_name for pillar_name, kind, displayname in results]
75+
76+ def get_pillar_name(result):
77+ pillar_name, kind, displayname = result
78+ return pillar_name
79+
80+ return DecoratedResultSet(results, get_pillar_name)
81
82 def getOwnedProjects(self, match_name=None):
83 """See `IPerson`."""
84@@ -1447,7 +1456,7 @@
85 need_location=False, need_archive=False, need_preferred_email=False,
86 need_validity=False):
87 """Lookup all members of the team with optional precaching.
88-
89+
90 :param need_karma: The karma attribute will be cached.
91 :param need_ubuntu_coc: The is_ubuntu_coc_signer attribute will be
92 cached.
93@@ -1475,7 +1484,8 @@
94 if need_karma:
95 # New people have no karmatotalcache rows.
96 origin.append(
97- LeftJoin(KarmaTotalCache, KarmaTotalCache.person == Person.id))
98+ LeftJoin(KarmaTotalCache,
99+ KarmaTotalCache.person == Person.id))
100 columns.append(KarmaTotalCache)
101 if need_ubuntu_coc:
102 columns.append(Alias(Exists(Select(SignedCodeOfConduct,
103@@ -1488,9 +1498,9 @@
104 LeftJoin(PersonLocation, PersonLocation.person == Person.id))
105 columns.append(PersonLocation)
106 if need_archive:
107- # Not everyone has PPA's
108- # It would be nice to cleanly expose the soyuz rules for this to avoid
109- # duplicating the relationships.
110+ # Not everyone has PPA's
111+ # It would be nice to cleanly expose the soyuz rules for this to
112+ # avoid duplicating the relationships.
113 origin.append(
114 LeftJoin(Archive, Archive.owner == Person.id))
115 columns.append(Archive)
116@@ -1519,6 +1529,7 @@
117 # Adapt the result into a cached Person.
118 columns = tuple(columns)
119 raw_result = store.using(*origin).find(columns, conditions)
120+
121 def prepopulate_person(row):
122 result = row[0]
123 index = 1
124@@ -1556,7 +1567,8 @@
125 index += 1
126 cache_property(result, '_is_valid_person_cached', valid)
127 return result
128- return DecoratedResultSet(raw_result, result_decorator=prepopulate_person)
129+ return DecoratedResultSet(raw_result,
130+ result_decorator=prepopulate_person)
131
132 def _getMembersWithPreferredEmails(self):
133 """Helper method for public getMembersWithPreferredEmails.
134
135=== modified file 'lib/lp/registry/stories/person/xx-person-projects.txt'
136--- lib/lp/registry/stories/person/xx-person-projects.txt 2010-07-14 16:27:33 +0000
137+++ lib/lp/registry/stories/person/xx-person-projects.txt 2010-08-17 14:54:45 +0000
138@@ -9,8 +9,8 @@
139 ... anon_browser.contents, 'portlet-related-projects')
140 >>> for tr in related_projects.findAll('tr'):
141 ... print extract_text(tr)
142- Ubuntu Linux
143- Ubuntu Test
144+ Ubuntu
145+ ubuntutest
146 Tomcat
147
148 The +related-software page displays a table with links to open bugs,
149
150=== modified file 'lib/lp/registry/templates/person-portlet-related-projects.pt'
151--- lib/lp/registry/templates/person-portlet-related-projects.pt 2009-08-25 02:01:55 +0000
152+++ lib/lp/registry/templates/person-portlet-related-projects.pt 2010-08-17 14:54:45 +0000
153@@ -25,9 +25,7 @@
154 <tbody>
155 <tr tal:repeat="pillarname pillarnames">
156 <td>
157- <a tal:attributes="href string:/${pillarname/pillar/name};
158- class pillarname/pillar/image:sprite_css"
159- tal:content="pillarname/pillar/title">Pillar title</a>
160+ <a tal:replace="structure pillarname/pillar/fmt:link" />
161 </td>
162 </tr>
163 </tbody>
164
165=== modified file 'lib/lp/registry/tests/test_person.py'
166--- lib/lp/registry/tests/test_person.py 2010-08-17 02:13:54 +0000
167+++ lib/lp/registry/tests/test_person.py 2010-08-17 14:54:45 +0000
168@@ -44,6 +44,7 @@
169 )
170 from lp.testing.matchers import HasQueryCount
171 from lp.testing.views import create_initialized_view
172+from lp.testing import celebrity_logged_in
173 from lp.testing._webservice import QueryCollector
174 from lp.registry.interfaces.person import PrivatePersonLinkageError
175 from canonical.testing.layers import DatabaseFunctionalLayer, reconnect_stores
176@@ -71,6 +72,22 @@
177
178 layer = DatabaseFunctionalLayer
179
180+ def test_getOwnedOrDrivenPillars(self):
181+ user = self.factory.makePerson()
182+ active_project = self.factory.makeProject(owner=user)
183+ inactive_project = self.factory.makeProject(owner=user)
184+ with celebrity_logged_in('admin'):
185+ inactive_project.active = False
186+ expected_pillars = [active_project.name]
187+ received_pillars = [pillar.name for pillar in
188+ user.getOwnedOrDrivenPillars()]
189+ self.assertEqual(expected_pillars, received_pillars)
190+
191+
192+class TestPersonStates(TestCaseWithFactory):
193+
194+ layer = DatabaseFunctionalLayer
195+
196 def setUp(self):
197 TestCaseWithFactory.setUp(self, 'foo.bar@canonical.com')
198 person_set = getUtility(IPersonSet)
199@@ -201,7 +218,7 @@
200 def test_person_snapshot(self):
201 omitted = (
202 'activemembers', 'adminmembers', 'allmembers',
203- 'all_members_prepopulated', 'approvedmembers',
204+ 'all_members_prepopulated', 'approvedmembers',
205 'deactivatedmembers', 'expiredmembers', 'inactivemembers',
206 'invited_members', 'member_memberships', 'pendingmembers',
207 'proposedmembers', 'unmapped_participants',
208@@ -583,7 +600,8 @@
209 self.addCleanup(collector.unregister)
210 url = "/~%s/participants" % team.name
211 logout()
212- response = webservice.get(url, headers={'User-Agent':'AnonNeedsThis'})
213+ response = webservice.get(url,
214+ headers={'User-Agent': 'AnonNeedsThis'})
215 self.assertEqual(response.status, 200,
216 "Got %d for url %r with response %r" % (
217 response.status, url, response.body))