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
=== modified file 'lib/lp/snappy/browser/snaplisting.py'
--- lib/lp/snappy/browser/snaplisting.py 2015-10-05 13:36:06 +0000
+++ lib/lp/snappy/browser/snaplisting.py 2017-10-10 23:06:51 +0000
@@ -1,4 +1,4 @@
1# Copyright 2015 Canonical Ltd. This software is licensed under the1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Base class view for snap listings."""4"""Base class view for snap listings."""
@@ -18,7 +18,9 @@
18from lp.code.browser.decorations import DecoratedBranch18from lp.code.browser.decorations import DecoratedBranch
19from lp.services.database.decoratedresultset import DecoratedResultSet19from lp.services.database.decoratedresultset import DecoratedResultSet
20from lp.services.feeds.browser import FeedsMixin20from lp.services.feeds.browser import FeedsMixin
21from lp.services.propertycache import cachedproperty
21from lp.services.webapp import LaunchpadView22from lp.services.webapp import LaunchpadView
23from lp.services.webapp.batching import BatchNavigator
22from lp.snappy.interfaces.snap import ISnapSet24from lp.snappy.interfaces.snap import ISnapSet
2325
2426
@@ -46,6 +48,10 @@
46 getUtility(ISnapSet).preloadDataForSnaps, user=self.user)48 getUtility(ISnapSet).preloadDataForSnaps, user=self.user)
47 self.snaps = DecoratedResultSet(snaps, pre_iter_hook=loader)49 self.snaps = DecoratedResultSet(snaps, pre_iter_hook=loader)
4850
51 @cachedproperty
52 def batchnav(self):
53 return BatchNavigator(self.snaps, self.request)
54
4955
50class BranchSnapListingView(SnapListingView):56class BranchSnapListingView(SnapListingView):
5157
5258
=== modified file 'lib/lp/snappy/browser/tests/test_snaplisting.py'
--- lib/lp/snappy/browser/tests/test_snaplisting.py 2016-12-02 12:53:41 +0000
+++ lib/lp/snappy/browser/tests/test_snaplisting.py 2017-10-10 23:06:51 +0000
@@ -1,12 +1,23 @@
1# Copyright 2015-2016 Canonical Ltd. This software is licensed under the1# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).2# GNU Affero General Public License version 3 (see the file LICENSE).
33
4"""Test snap package listings."""4"""Test snap package listings."""
55
6__metaclass__ = type6__metaclass__ = type
77
8from datetime import (
9 datetime,
10 timedelta,
11 )
12from functools import partial
13
14import pytz
8import soupmatchers15import soupmatchers
9from testtools.matchers import Not16from testtools.matchers import (
17 MatchesAll,
18 Not,
19 )
20from zope.security.proxy import removeSecurityProxy
1021
11from lp.code.tests.helpers import GitHostingFixture22from lp.code.tests.helpers import GitHostingFixture
12from lp.services.database.constants import (23from lp.services.database.constants import (
@@ -24,6 +35,7 @@
24 )35 )
25from lp.testing.layers import LaunchpadFunctionalLayer36from lp.testing.layers import LaunchpadFunctionalLayer
26from lp.testing.matchers import HasQueryCount37from lp.testing.matchers import HasQueryCount
38from lp.testing.views import create_initialized_view
2739
2840
29class TestSnapListing(BrowserTestCase):41class TestSnapListing(BrowserTestCase):
@@ -141,6 +153,7 @@
141 snap-name.* Team Name.* lp:.* .*""", text)153 snap-name.* Team Name.* lp:.* .*""", text)
142154
143 def assertSnapsQueryCount(self, context, item_creator):155 def assertSnapsQueryCount(self, context, item_creator):
156 self.pushConfig("launchpad", default_batch_size=10)
144 recorder1, recorder2 = record_two_runs(157 recorder1, recorder2 = record_two_runs(
145 lambda: self.getMainText(context, "+snaps"), item_creator, 5)158 lambda: self.getMainText(context, "+snaps"), item_creator, 5)
146 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))159 self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
@@ -224,3 +237,87 @@
224 self.factory.makeSnap(git_ref=ref)237 self.factory.makeSnap(git_ref=ref)
225238
226 self.assertSnapsQueryCount(project, create_snap)239 self.assertSnapsQueryCount(project, create_snap)
240
241 def makeSnapsAndMatchers(self, create_snap, count, start_time):
242 snaps = [create_snap() for i in range(count)]
243 for i, snap in enumerate(snaps):
244 removeSecurityProxy(snap).date_last_modified = (
245 start_time - timedelta(seconds=i))
246 return [
247 soupmatchers.Tag(
248 "snap link", "a", text=snap.name,
249 attrs={
250 "href": canonical_url(snap, path_only_if_possible=True)})
251 for snap in snaps]
252
253 def assertBatches(self, context, link_matchers, batched, start, size):
254 view = create_initialized_view(context, "+snaps")
255 listing_tag = soupmatchers.Tag(
256 "snap listing", "table", attrs={"class": "listing sortable"})
257 batch_nav_tag = soupmatchers.Tag(
258 "batch nav links", "td",
259 attrs={"class": "batch-navigation-links"})
260 present_links = ([batch_nav_tag] if batched else []) + [
261 matcher for i, matcher in enumerate(link_matchers)
262 if i in range(start, start + size)]
263 absent_links = ([] if batched else [batch_nav_tag]) + [
264 matcher for i, matcher in enumerate(link_matchers)
265 if i not in range(start, start + size)]
266 self.assertThat(
267 view.render(),
268 MatchesAll(
269 soupmatchers.HTMLContains(listing_tag, *present_links),
270 Not(soupmatchers.HTMLContains(*absent_links))))
271
272 def test_branch_batches_snaps(self):
273 branch = self.factory.makeAnyBranch()
274 create_snap = partial(self.factory.makeSnap, branch=branch)
275 now = datetime.now(pytz.UTC)
276 link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
277 self.assertBatches(branch, link_matchers, False, 0, 3)
278 link_matchers.extend(self.makeSnapsAndMatchers(
279 create_snap, 7, now - timedelta(seconds=3)))
280 self.assertBatches(branch, link_matchers, True, 0, 5)
281
282 def test_git_repository_batches_snaps(self):
283 repository = self.factory.makeGitRepository()
284 [ref] = self.factory.makeGitRefs(repository=repository)
285 create_snap = partial(self.factory.makeSnap, git_ref=ref)
286 now = datetime.now(pytz.UTC)
287 link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
288 self.assertBatches(repository, link_matchers, False, 0, 3)
289 link_matchers.extend(self.makeSnapsAndMatchers(
290 create_snap, 7, now - timedelta(seconds=3)))
291 self.assertBatches(repository, link_matchers, True, 0, 5)
292
293 def test_git_ref_batches_snaps(self):
294 [ref] = self.factory.makeGitRefs()
295 create_snap = partial(self.factory.makeSnap, git_ref=ref)
296 now = datetime.now(pytz.UTC)
297 link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
298 self.assertBatches(ref, link_matchers, False, 0, 3)
299 link_matchers.extend(self.makeSnapsAndMatchers(
300 create_snap, 7, now - timedelta(seconds=3)))
301 self.assertBatches(ref, link_matchers, True, 0, 5)
302
303 def test_person_batches_snaps(self):
304 owner = self.factory.makePerson()
305 create_snap = partial(
306 self.factory.makeSnap, registrant=owner, owner=owner)
307 now = datetime.now(pytz.UTC)
308 link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
309 self.assertBatches(owner, link_matchers, False, 0, 3)
310 link_matchers.extend(self.makeSnapsAndMatchers(
311 create_snap, 7, now - timedelta(seconds=3)))
312 self.assertBatches(owner, link_matchers, True, 0, 5)
313
314 def test_project_batches_snaps(self):
315 project = self.factory.makeProduct()
316 branch = self.factory.makeProductBranch(product=project)
317 create_snap = partial(self.factory.makeSnap, branch=branch)
318 now = datetime.now(pytz.UTC)
319 link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
320 self.assertBatches(project, link_matchers, False, 0, 3)
321 link_matchers.extend(self.makeSnapsAndMatchers(
322 create_snap, 7, now - timedelta(seconds=3)))
323 self.assertBatches(project, link_matchers, True, 0, 5)
227324
=== modified file 'lib/lp/snappy/templates/snap-listing.pt'
--- lib/lp/snappy/templates/snap-listing.pt 2015-09-16 13:30:33 +0000
+++ lib/lp/snappy/templates/snap-listing.pt 2017-10-10 23:06:51 +0000
@@ -10,6 +10,9 @@
1010
11 <div metal:fill-slot="main">11 <div metal:fill-slot="main">
1212
13 <tal:navigation
14 condition="view/batchnav/has_multiple_pages"
15 replace="structure view/batchnav/@@+navigation-links-upper" />
13 <table id="snaptable" class="listing sortable">16 <table id="snaptable" class="listing sortable">
14 <thead>17 <thead>
15 <tr>18 <tr>
@@ -20,7 +23,7 @@
20 </tr>23 </tr>
21 </thead>24 </thead>
22 <tbody>25 <tbody>
23 <tal:snaps repeat="snap view/snaps">26 <tal:snaps repeat="snap view/batchnav/currentBatch">
24 <tr>27 <tr>
25 <td colspan="2">28 <td colspan="2">
26 <a tal:attributes="href snap/fmt:url" tal:content="snap/name" />29 <a tal:attributes="href snap/fmt:url" tal:content="snap/name" />
@@ -34,6 +37,9 @@
34 </tal:snaps>37 </tal:snaps>
35 </tbody>38 </tbody>
36 </table>39 </table>
40 <tal:navigation
41 condition="view/batchnav/has_multiple_pages"
42 replace="structure view/batchnav/@@+navigation-links-lower" />
3743
38 </div>44 </div>
39</body>45</body>