Merge lp:~cjwatson/launchpad/batch-snap-listing into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18482
Proposed branch: lp:~cjwatson/launchpad/batch-snap-listing
Merge into: lp:launchpad
Diff against target: 197 lines (+113/-4)
3 files modified
lib/lp/snappy/browser/snaplisting.py (+7/-1)
lib/lp/snappy/browser/tests/test_snaplisting.py (+99/-2)
lib/lp/snappy/templates/snap-listing.pt (+7/-1)
To merge this branch: bzr merge lp:~cjwatson/launchpad/batch-snap-listing
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+332085@code.launchpad.net

Commit message

Batch snap listing views.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
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/snappy/browser/snaplisting.py'
2--- lib/lp/snappy/browser/snaplisting.py 2015-10-05 13:36:06 +0000
3+++ lib/lp/snappy/browser/snaplisting.py 2017-10-10 23:06:51 +0000
4@@ -1,4 +1,4 @@
5-# Copyright 2015 Canonical Ltd. This software is licensed under the
6+# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
7 # GNU Affero General Public License version 3 (see the file LICENSE).
8
9 """Base class view for snap listings."""
10@@ -18,7 +18,9 @@
11 from lp.code.browser.decorations import DecoratedBranch
12 from lp.services.database.decoratedresultset import DecoratedResultSet
13 from lp.services.feeds.browser import FeedsMixin
14+from lp.services.propertycache import cachedproperty
15 from lp.services.webapp import LaunchpadView
16+from lp.services.webapp.batching import BatchNavigator
17 from lp.snappy.interfaces.snap import ISnapSet
18
19
20@@ -46,6 +48,10 @@
21 getUtility(ISnapSet).preloadDataForSnaps, user=self.user)
22 self.snaps = DecoratedResultSet(snaps, pre_iter_hook=loader)
23
24+ @cachedproperty
25+ def batchnav(self):
26+ return BatchNavigator(self.snaps, self.request)
27+
28
29 class BranchSnapListingView(SnapListingView):
30
31
32=== modified file 'lib/lp/snappy/browser/tests/test_snaplisting.py'
33--- lib/lp/snappy/browser/tests/test_snaplisting.py 2016-12-02 12:53:41 +0000
34+++ lib/lp/snappy/browser/tests/test_snaplisting.py 2017-10-10 23:06:51 +0000
35@@ -1,12 +1,23 @@
36-# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
37+# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
38 # GNU Affero General Public License version 3 (see the file LICENSE).
39
40 """Test snap package listings."""
41
42 __metaclass__ = type
43
44+from datetime import (
45+ datetime,
46+ timedelta,
47+ )
48+from functools import partial
49+
50+import pytz
51 import soupmatchers
52-from testtools.matchers import Not
53+from testtools.matchers import (
54+ MatchesAll,
55+ Not,
56+ )
57+from zope.security.proxy import removeSecurityProxy
58
59 from lp.code.tests.helpers import GitHostingFixture
60 from lp.services.database.constants import (
61@@ -24,6 +35,7 @@
62 )
63 from lp.testing.layers import LaunchpadFunctionalLayer
64 from lp.testing.matchers import HasQueryCount
65+from lp.testing.views import create_initialized_view
66
67
68 class TestSnapListing(BrowserTestCase):
69@@ -141,6 +153,7 @@
70 snap-name.* Team Name.* lp:.* .*""", text)
71
72 def assertSnapsQueryCount(self, context, item_creator):
73+ self.pushConfig("launchpad", default_batch_size=10)
74 recorder1, recorder2 = record_two_runs(
75 lambda: self.getMainText(context, "+snaps"), item_creator, 5)
76 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
77@@ -224,3 +237,87 @@
78 self.factory.makeSnap(git_ref=ref)
79
80 self.assertSnapsQueryCount(project, create_snap)
81+
82+ def makeSnapsAndMatchers(self, create_snap, count, start_time):
83+ snaps = [create_snap() for i in range(count)]
84+ for i, snap in enumerate(snaps):
85+ removeSecurityProxy(snap).date_last_modified = (
86+ start_time - timedelta(seconds=i))
87+ return [
88+ soupmatchers.Tag(
89+ "snap link", "a", text=snap.name,
90+ attrs={
91+ "href": canonical_url(snap, path_only_if_possible=True)})
92+ for snap in snaps]
93+
94+ def assertBatches(self, context, link_matchers, batched, start, size):
95+ view = create_initialized_view(context, "+snaps")
96+ listing_tag = soupmatchers.Tag(
97+ "snap listing", "table", attrs={"class": "listing sortable"})
98+ batch_nav_tag = soupmatchers.Tag(
99+ "batch nav links", "td",
100+ attrs={"class": "batch-navigation-links"})
101+ present_links = ([batch_nav_tag] if batched else []) + [
102+ matcher for i, matcher in enumerate(link_matchers)
103+ if i in range(start, start + size)]
104+ absent_links = ([] if batched else [batch_nav_tag]) + [
105+ matcher for i, matcher in enumerate(link_matchers)
106+ if i not in range(start, start + size)]
107+ self.assertThat(
108+ view.render(),
109+ MatchesAll(
110+ soupmatchers.HTMLContains(listing_tag, *present_links),
111+ Not(soupmatchers.HTMLContains(*absent_links))))
112+
113+ def test_branch_batches_snaps(self):
114+ branch = self.factory.makeAnyBranch()
115+ create_snap = partial(self.factory.makeSnap, branch=branch)
116+ now = datetime.now(pytz.UTC)
117+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
118+ self.assertBatches(branch, link_matchers, False, 0, 3)
119+ link_matchers.extend(self.makeSnapsAndMatchers(
120+ create_snap, 7, now - timedelta(seconds=3)))
121+ self.assertBatches(branch, link_matchers, True, 0, 5)
122+
123+ def test_git_repository_batches_snaps(self):
124+ repository = self.factory.makeGitRepository()
125+ [ref] = self.factory.makeGitRefs(repository=repository)
126+ create_snap = partial(self.factory.makeSnap, git_ref=ref)
127+ now = datetime.now(pytz.UTC)
128+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
129+ self.assertBatches(repository, link_matchers, False, 0, 3)
130+ link_matchers.extend(self.makeSnapsAndMatchers(
131+ create_snap, 7, now - timedelta(seconds=3)))
132+ self.assertBatches(repository, link_matchers, True, 0, 5)
133+
134+ def test_git_ref_batches_snaps(self):
135+ [ref] = self.factory.makeGitRefs()
136+ create_snap = partial(self.factory.makeSnap, git_ref=ref)
137+ now = datetime.now(pytz.UTC)
138+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
139+ self.assertBatches(ref, link_matchers, False, 0, 3)
140+ link_matchers.extend(self.makeSnapsAndMatchers(
141+ create_snap, 7, now - timedelta(seconds=3)))
142+ self.assertBatches(ref, link_matchers, True, 0, 5)
143+
144+ def test_person_batches_snaps(self):
145+ owner = self.factory.makePerson()
146+ create_snap = partial(
147+ self.factory.makeSnap, registrant=owner, owner=owner)
148+ now = datetime.now(pytz.UTC)
149+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
150+ self.assertBatches(owner, link_matchers, False, 0, 3)
151+ link_matchers.extend(self.makeSnapsAndMatchers(
152+ create_snap, 7, now - timedelta(seconds=3)))
153+ self.assertBatches(owner, link_matchers, True, 0, 5)
154+
155+ def test_project_batches_snaps(self):
156+ project = self.factory.makeProduct()
157+ branch = self.factory.makeProductBranch(product=project)
158+ create_snap = partial(self.factory.makeSnap, branch=branch)
159+ now = datetime.now(pytz.UTC)
160+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
161+ self.assertBatches(project, link_matchers, False, 0, 3)
162+ link_matchers.extend(self.makeSnapsAndMatchers(
163+ create_snap, 7, now - timedelta(seconds=3)))
164+ self.assertBatches(project, link_matchers, True, 0, 5)
165
166=== modified file 'lib/lp/snappy/templates/snap-listing.pt'
167--- lib/lp/snappy/templates/snap-listing.pt 2015-09-16 13:30:33 +0000
168+++ lib/lp/snappy/templates/snap-listing.pt 2017-10-10 23:06:51 +0000
169@@ -10,6 +10,9 @@
170
171 <div metal:fill-slot="main">
172
173+ <tal:navigation
174+ condition="view/batchnav/has_multiple_pages"
175+ replace="structure view/batchnav/@@+navigation-links-upper" />
176 <table id="snaptable" class="listing sortable">
177 <thead>
178 <tr>
179@@ -20,7 +23,7 @@
180 </tr>
181 </thead>
182 <tbody>
183- <tal:snaps repeat="snap view/snaps">
184+ <tal:snaps repeat="snap view/batchnav/currentBatch">
185 <tr>
186 <td colspan="2">
187 <a tal:attributes="href snap/fmt:url" tal:content="snap/name" />
188@@ -34,6 +37,9 @@
189 </tal:snaps>
190 </tbody>
191 </table>
192+ <tal:navigation
193+ condition="view/batchnav/has_multiple_pages"
194+ replace="structure view/batchnav/@@+navigation-links-lower" />
195
196 </div>
197 </body>