launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #21919
[Merge] lp:~cjwatson/launchpad/batch-snap-listing into lp:launchpad
Colin Watson has proposed merging lp:~cjwatson/launchpad/batch-snap-listing into lp:launchpad.
Commit message:
Batch snap listing views.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1722562 in Launchpad itself: "Timeout when looking at b.s.i snaps page"
https://bugs.launchpad.net/launchpad/+bug/1722562
For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/batch-snap-listing/+merge/332085
--
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~cjwatson/launchpad/batch-snap-listing into lp:launchpad.
=== 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 @@
-# Copyright 2015 Canonical Ltd. This software is licensed under the
+# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Base class view for snap listings."""
@@ -18,7 +18,9 @@
from lp.code.browser.decorations import DecoratedBranch
from lp.services.database.decoratedresultset import DecoratedResultSet
from lp.services.feeds.browser import FeedsMixin
+from lp.services.propertycache import cachedproperty
from lp.services.webapp import LaunchpadView
+from lp.services.webapp.batching import BatchNavigator
from lp.snappy.interfaces.snap import ISnapSet
@@ -46,6 +48,10 @@
getUtility(ISnapSet).preloadDataForSnaps, user=self.user)
self.snaps = DecoratedResultSet(snaps, pre_iter_hook=loader)
+ @cachedproperty
+ def batchnav(self):
+ return BatchNavigator(self.snaps, self.request)
+
class BranchSnapListingView(SnapListingView):
=== 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 @@
-# Copyright 2015-2016 Canonical Ltd. This software is licensed under the
+# Copyright 2015-2017 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Test snap package listings."""
__metaclass__ = type
+from datetime import (
+ datetime,
+ timedelta,
+ )
+from functools import partial
+
+import pytz
import soupmatchers
-from testtools.matchers import Not
+from testtools.matchers import (
+ MatchesAll,
+ Not,
+ )
+from zope.security.proxy import removeSecurityProxy
from lp.code.tests.helpers import GitHostingFixture
from lp.services.database.constants import (
@@ -24,6 +35,7 @@
)
from lp.testing.layers import LaunchpadFunctionalLayer
from lp.testing.matchers import HasQueryCount
+from lp.testing.views import create_initialized_view
class TestSnapListing(BrowserTestCase):
@@ -141,6 +153,7 @@
snap-name.* Team Name.* lp:.* .*""", text)
def assertSnapsQueryCount(self, context, item_creator):
+ self.pushConfig("launchpad", default_batch_size=10)
recorder1, recorder2 = record_two_runs(
lambda: self.getMainText(context, "+snaps"), item_creator, 5)
self.assertThat(recorder2, HasQueryCount.byEquality(recorder1))
@@ -224,3 +237,87 @@
self.factory.makeSnap(git_ref=ref)
self.assertSnapsQueryCount(project, create_snap)
+
+ def makeSnapsAndMatchers(self, create_snap, count, start_time):
+ snaps = [create_snap() for i in range(count)]
+ for i, snap in enumerate(snaps):
+ removeSecurityProxy(snap).date_last_modified = (
+ start_time - timedelta(seconds=i))
+ return [
+ soupmatchers.Tag(
+ "snap link", "a", text=snap.name,
+ attrs={
+ "href": canonical_url(snap, path_only_if_possible=True)})
+ for snap in snaps]
+
+ def assertBatches(self, context, link_matchers, batched, start, size):
+ view = create_initialized_view(context, "+snaps")
+ listing_tag = soupmatchers.Tag(
+ "snap listing", "table", attrs={"class": "listing sortable"})
+ batch_nav_tag = soupmatchers.Tag(
+ "batch nav links", "td",
+ attrs={"class": "batch-navigation-links"})
+ present_links = ([batch_nav_tag] if batched else []) + [
+ matcher for i, matcher in enumerate(link_matchers)
+ if i in range(start, start + size)]
+ absent_links = ([] if batched else [batch_nav_tag]) + [
+ matcher for i, matcher in enumerate(link_matchers)
+ if i not in range(start, start + size)]
+ self.assertThat(
+ view.render(),
+ MatchesAll(
+ soupmatchers.HTMLContains(listing_tag, *present_links),
+ Not(soupmatchers.HTMLContains(*absent_links))))
+
+ def test_branch_batches_snaps(self):
+ branch = self.factory.makeAnyBranch()
+ create_snap = partial(self.factory.makeSnap, branch=branch)
+ now = datetime.now(pytz.UTC)
+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
+ self.assertBatches(branch, link_matchers, False, 0, 3)
+ link_matchers.extend(self.makeSnapsAndMatchers(
+ create_snap, 7, now - timedelta(seconds=3)))
+ self.assertBatches(branch, link_matchers, True, 0, 5)
+
+ def test_git_repository_batches_snaps(self):
+ repository = self.factory.makeGitRepository()
+ [ref] = self.factory.makeGitRefs(repository=repository)
+ create_snap = partial(self.factory.makeSnap, git_ref=ref)
+ now = datetime.now(pytz.UTC)
+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
+ self.assertBatches(repository, link_matchers, False, 0, 3)
+ link_matchers.extend(self.makeSnapsAndMatchers(
+ create_snap, 7, now - timedelta(seconds=3)))
+ self.assertBatches(repository, link_matchers, True, 0, 5)
+
+ def test_git_ref_batches_snaps(self):
+ [ref] = self.factory.makeGitRefs()
+ create_snap = partial(self.factory.makeSnap, git_ref=ref)
+ now = datetime.now(pytz.UTC)
+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
+ self.assertBatches(ref, link_matchers, False, 0, 3)
+ link_matchers.extend(self.makeSnapsAndMatchers(
+ create_snap, 7, now - timedelta(seconds=3)))
+ self.assertBatches(ref, link_matchers, True, 0, 5)
+
+ def test_person_batches_snaps(self):
+ owner = self.factory.makePerson()
+ create_snap = partial(
+ self.factory.makeSnap, registrant=owner, owner=owner)
+ now = datetime.now(pytz.UTC)
+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
+ self.assertBatches(owner, link_matchers, False, 0, 3)
+ link_matchers.extend(self.makeSnapsAndMatchers(
+ create_snap, 7, now - timedelta(seconds=3)))
+ self.assertBatches(owner, link_matchers, True, 0, 5)
+
+ def test_project_batches_snaps(self):
+ project = self.factory.makeProduct()
+ branch = self.factory.makeProductBranch(product=project)
+ create_snap = partial(self.factory.makeSnap, branch=branch)
+ now = datetime.now(pytz.UTC)
+ link_matchers = self.makeSnapsAndMatchers(create_snap, 3, now)
+ self.assertBatches(project, link_matchers, False, 0, 3)
+ link_matchers.extend(self.makeSnapsAndMatchers(
+ create_snap, 7, now - timedelta(seconds=3)))
+ self.assertBatches(project, link_matchers, True, 0, 5)
=== 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 @@
<div metal:fill-slot="main">
+ <tal:navigation
+ condition="view/batchnav/has_multiple_pages"
+ replace="structure view/batchnav/@@+navigation-links-upper" />
<table id="snaptable" class="listing sortable">
<thead>
<tr>
@@ -20,7 +23,7 @@
</tr>
</thead>
<tbody>
- <tal:snaps repeat="snap view/snaps">
+ <tal:snaps repeat="snap view/batchnav/currentBatch">
<tr>
<td colspan="2">
<a tal:attributes="href snap/fmt:url" tal:content="snap/name" />
@@ -34,6 +37,9 @@
</tal:snaps>
</tbody>
</table>
+ <tal:navigation
+ condition="view/batchnav/has_multiple_pages"
+ replace="structure view/batchnav/@@+navigation-links-lower" />
</div>
</body>
Follow ups