launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #24764
[Merge] ~pappacena/launchpad:ui-ociproject-search-on-projects into launchpad:master
Thiago F. Pappacena has proposed merging ~pappacena/launchpad:ui-ociproject-search-on-projects into launchpad:master with ~pappacena/launchpad:gitrepo-ociproject-refactoring as a prerequisite.
Commit message:
OCIProject search on project page.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/launchpad/+git/launchpad/+merge/384514
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/launchpad:ui-ociproject-search-on-projects into launchpad:master.
diff --git a/lib/lp/registry/browser/configure.zcml b/lib/lp/registry/browser/configure.zcml
index b8dc1bb..a409eb1 100644
--- a/lib/lp/registry/browser/configure.zcml
+++ b/lib/lp/registry/browser/configure.zcml
@@ -2158,9 +2158,16 @@
<browser:page
name="+search-oci-project"
for="lp.registry.interfaces.distribution.IDistribution"
- class="lp.registry.browser.distribution.DistributionOCIProjectSearchView"
+ class="lp.registry.browser.ociproject.OCIProjectSearchView"
permission="zope.Public"
- template="../templates/distribution-search-oci-projects.pt"
+ template="../templates/search-oci-projects.pt"
+ />
+ <browser:page
+ name="+search-oci-project"
+ for="lp.registry.interfaces.product.IProduct"
+ class="lp.registry.browser.ociproject.OCIProjectSearchView"
+ permission="zope.Public"
+ template="../templates/search-oci-projects.pt"
/>
<browser:page
name="+search"
diff --git a/lib/lp/registry/browser/distribution.py b/lib/lp/registry/browser/distribution.py
index ffabedb..0585def 100644
--- a/lib/lp/registry/browser/distribution.py
+++ b/lib/lp/registry/browser/distribution.py
@@ -42,7 +42,6 @@ from collections import defaultdict
import datetime
from lazr.restful.utils import smartquote
-import six
from zope.component import getUtility
from zope.event import notify
from zope.formlib import form
@@ -106,7 +105,6 @@ from lp.registry.interfaces.distributionmirror import (
MirrorContent,
MirrorSpeed,
)
-from lp.registry.interfaces.ociproject import IOCIProjectSet
from lp.registry.interfaces.series import SeriesStatus
from lp.services.database.decoratedresultset import DecoratedResultSet
from lp.services.feeds.browser import FeedsMixin
@@ -1376,50 +1374,3 @@ class DistributionPublisherConfigView(LaunchpadFormView):
self.request.response.addInfoNotification(
'Your changes have been applied.')
self.next_url = canonical_url(self.context)
-
-
-class DistributionOCIProjectSearchView(LaunchpadView):
- """Page to search for OCI projects of a given distribution."""
- page_title = ''
-
- @property
- def label(self):
- return "Search OCI projects in %s" % self.context.title
-
- @property
- def text(self):
- text = self.request.get("text", None)
- if isinstance(text, list):
- # The user may have URL hacked a query string with more than one
- # "text" parameter. We'll take the last one.
- text = text[-1]
- return text
-
- @property
- def search_requested(self):
- return self.text is not None
-
- @property
- def title(self):
- return self.context.name
-
- @cachedproperty
- def count(self):
- """Return the number of matched search results."""
- return self.batchnav.batch.total()
-
- @cachedproperty
- def batchnav(self):
- """Return the batch navigator for the search results."""
- return BatchNavigator(self.search_results, self.request)
-
- @cachedproperty
- def preloaded_batch(self):
- projects = self.batchnav.batch
- getUtility(IOCIProjectSet).preloadDataForOCIProjects(projects)
- return projects
-
- @property
- def search_results(self):
- return getUtility(IOCIProjectSet).findByDistributionAndName(
- self.context, self.text or six.ensure_text(''))
diff --git a/lib/lp/registry/browser/ociproject.py b/lib/lp/registry/browser/ociproject.py
index df7ac85..c4b8fca 100644
--- a/lib/lp/registry/browser/ociproject.py
+++ b/lib/lp/registry/browser/ociproject.py
@@ -14,6 +14,7 @@ __all__ = [
'OCIProjectNavigationMenu',
]
+import six
from zope.component import getUtility
from zope.formlib import form
from zope.interface import implementer
@@ -39,16 +40,19 @@ from lp.registry.interfaces.ociprojectname import (
IOCIProjectNameSet,
)
from lp.services.features import getFeatureFlag
+from lp.services.propertycache import cachedproperty
from lp.services.webapp import (
canonical_url,
ContextMenu,
enabled_with_permission,
+ LaunchpadView,
Link,
Navigation,
NavigationMenu,
StandardLaunchpadFacets,
stepthrough,
)
+from lp.services.webapp.batching import BatchNavigator
from lp.services.webapp.breadcrumb import Breadcrumb
from lp.services.webapp.interfaces import IMultiFacetedBreadcrumb
@@ -213,3 +217,50 @@ class OCIProjectEditView(LaunchpadEditFormView):
return canonical_url(self.context)
cancel_url = next_url
+
+
+class OCIProjectSearchView(LaunchpadView):
+ """Page to search for OCI projects of a given pillar."""
+ page_title = ''
+
+ @property
+ def label(self):
+ return "Search OCI projects in %s" % self.context.title
+
+ @property
+ def text(self):
+ text = self.request.get("text", None)
+ if isinstance(text, list):
+ # The user may have URL hacked a query string with more than one
+ # "text" parameter. We'll take the last one.
+ text = text[-1]
+ return text
+
+ @property
+ def search_requested(self):
+ return self.text is not None
+
+ @property
+ def title(self):
+ return self.context.name
+
+ @cachedproperty
+ def count(self):
+ """Return the number of matched search results."""
+ return self.batchnav.batch.total()
+
+ @cachedproperty
+ def batchnav(self):
+ """Return the batch navigator for the search results."""
+ return BatchNavigator(self.search_results, self.request)
+
+ @cachedproperty
+ def preloaded_batch(self):
+ projects = self.batchnav.batch
+ getUtility(IOCIProjectSet).preloadDataForOCIProjects(projects)
+ return projects
+
+ @property
+ def search_results(self):
+ return getUtility(IOCIProjectSet).findByPillarAndName(
+ self.context, self.text or six.ensure_text(''))
diff --git a/lib/lp/registry/browser/tests/test_distribution.py b/lib/lp/registry/browser/tests/test_distribution.py
index 0979ec2..b271bfe 100644
--- a/lib/lp/registry/browser/tests/test_distribution.py
+++ b/lib/lp/registry/browser/tests/test_distribution.py
@@ -24,18 +24,11 @@ from lp.registry.interfaces.series import SeriesStatus
from lp.services.webapp import canonical_url
from lp.testing import (
admin_logged_in,
- BrowserTestCase,
login_celebrity,
login_person,
- record_two_runs,
TestCaseWithFactory,
)
from lp.testing.layers import DatabaseFunctionalLayer
-from lp.testing.pages import (
- extract_text,
- find_tag_by_id,
- find_tags_by_class,
- )
from lp.testing.views import create_initialized_view
@@ -163,133 +156,3 @@ class TestDistributionView(TestCaseWithFactory):
self.assertContentEqual(
team_membership_policy_data,
cache.objects['team_membership_policy_data'])
-
-
-class TestDistributionOCIProjectSearchView(BrowserTestCase):
-
- layer = DatabaseFunctionalLayer
-
- def assertPaginationIsPresent(
- self, browser, results_in_page, total_result):
- """Checks that pagination is shown at the browser."""
- nav_index = find_tags_by_class(
- browser.contents, "batch-navigation-index")[0]
- nav_index_text = extract_text(nav_index).replace('\n', ' ')
- self.assertIn(
- "1 → %s of %s results" % (results_in_page, total_result),
- nav_index_text)
-
- nav_links = find_tags_by_class(
- browser.contents, "batch-navigation-links")[0]
- nav_links_text = extract_text(nav_links).replace('\n', ' ')
- self.assertIn("First • Previous • Next • Last", nav_links_text)
-
- def assertOCIProjectsArePresent(self, browser, oci_projects):
- table = find_tag_by_id(browser.contents, "projects_list")
- with admin_logged_in():
- for oci_project in oci_projects:
- url = canonical_url(oci_project, force_local_path=True)
- self.assertIn(url, str(table))
- self.assertIn(oci_project.name, str(table))
-
- def assertOCIProjectsAreNotPresent(self, browser, oci_projects):
- table = find_tag_by_id(browser.contents, "projects_list")
- with admin_logged_in():
- for oci_project in oci_projects:
- url = canonical_url(oci_project, force_local_path=True)
- self.assertNotIn(url, str(table))
- self.assertNotIn(oci_project.name, str(table))
-
- def test_search_no_oci_projects(self):
- person = self.factory.makePerson()
- distribution = self.factory.makeDistribution()
- browser = self.getViewBrowser(
- distribution, user=person, view_name='+search-oci-project')
-
- main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
- self.assertIn(
- "There are no OCI projects registered for %s" % distribution.name,
- extract_text(main_portlet).replace("\n", " "))
-
- def test_oci_projects_no_search_keyword(self):
- person = self.factory.makePerson()
- distro = self.factory.makeDistribution(owner=person)
-
- # Creates 3 OCI Projects
- oci_projects = [
- self.factory.makeOCIProject(
- ociprojectname="test-project-%s" % i,
- registrant=person, pillar=distro) for i in range(3)]
-
- browser = self.getViewBrowser(
- distro, user=person, view_name='+search-oci-project')
-
- # Check top message.
- main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
- self.assertIn(
- "There are 3 OCI projects registered for %s" % distro.name,
- extract_text(main_portlet).replace("\n", " "))
-
- self.assertOCIProjectsArePresent(browser, oci_projects)
- self.assertPaginationIsPresent(browser, 3, 3)
-
- def test_oci_projects_with_search_keyword(self):
- person = self.factory.makePerson()
- distro = self.factory.makeDistribution(owner=person)
-
- # And 2 OCI projects that will match the name
- oci_projects = [
- self.factory.makeOCIProject(
- ociprojectname="find-me-%s" % i,
- registrant=person, pillar=distro) for i in range(2)]
-
- # Creates 2 OCI Projects that will not match search
- other_oci_projects = [
- self.factory.makeOCIProject(
- ociprojectname="something-%s" % i,
- registrant=person, pillar=distro) for i in range(2)]
-
- browser = self.getViewBrowser(
- distro, user=person, view_name='+search-oci-project')
- browser.getControl(name="text").value = "find-me"
- browser.getControl("Search").click()
-
- # Check top message.
- main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
- self.assertIn(
- 'There are 2 OCI projects registered for %s matching "%s"' %
- (distro.name, "find-me"),
- extract_text(main_portlet).replace("\n", " "))
-
- self.assertOCIProjectsArePresent(browser, oci_projects)
- self.assertOCIProjectsAreNotPresent(browser, other_oci_projects)
- self.assertPaginationIsPresent(browser, 2, 2)
-
- def test_query_count_is_constant(self):
- batch_size = 3
- self.pushConfig("launchpad", default_batch_size=batch_size)
-
- person = self.factory.makePerson()
- distro = self.factory.makeDistribution(owner=person)
- name_pattern = "find-me-"
-
- def createOCIProject():
- self.factory.makeOCIProject(
- ociprojectname=self.factory.getUniqueString(name_pattern),
- pillar=distro)
-
- viewer = self.factory.makePerson()
-
- def getView():
- browser = self.getViewBrowser(
- distro, user=viewer, view_name='+search-oci-project')
- browser.getControl(name="text").value = name_pattern
- browser.getControl("Search").click()
- return browser
-
- def do_login():
- login_person(person)
-
- recorder1, recorder2 = record_two_runs(
- getView, createOCIProject, 1, 10, login_method=do_login)
- self.assertEqual(recorder1.count, recorder2.count)
diff --git a/lib/lp/registry/browser/tests/test_ociproject.py b/lib/lp/registry/browser/tests/test_ociproject.py
index 5187cd6..70cfd71 100644
--- a/lib/lp/registry/browser/tests/test_ociproject.py
+++ b/lib/lp/registry/browser/tests/test_ociproject.py
@@ -12,6 +12,7 @@ __all__ = []
from datetime import datetime
import pytz
+from zope.security.proxy import removeSecurityProxy
from lp.oci.interfaces.ocirecipe import OCI_RECIPE_ALLOW_CREATE
from lp.registry.interfaces.ociproject import (
@@ -25,7 +26,9 @@ from lp.services.webapp.escaping import structured
from lp.testing import (
admin_logged_in,
BrowserTestCase,
+ login_person,
person_logged_in,
+ record_two_runs,
test_tales,
TestCaseWithFactory,
)
@@ -34,6 +37,7 @@ from lp.testing.matchers import MatchesTagText
from lp.testing.pages import (
extract_text,
find_main_content,
+ find_tag_by_id,
find_tags_by_class,
)
from lp.testing.publication import test_traverse
@@ -312,3 +316,162 @@ class TestOCIProjectAddView(BrowserTestCase):
new_distribution,
user=another_person,
view_name='+new-oci-project')
+
+
+class TestOCIProjectSearchView(BrowserTestCase):
+
+ layer = DatabaseFunctionalLayer
+
+ def assertPaginationIsPresent(
+ self, browser, results_in_page, total_result):
+ """Checks that pagination is shown at the browser."""
+ nav_index = find_tags_by_class(
+ browser.contents, "batch-navigation-index")[0]
+ nav_index_text = extract_text(nav_index).replace('\n', ' ')
+ self.assertIn(
+ "1 → %s of %s results" % (results_in_page, total_result),
+ nav_index_text)
+
+ nav_links = find_tags_by_class(
+ browser.contents, "batch-navigation-links")[0]
+ nav_links_text = extract_text(nav_links).replace('\n', ' ')
+ self.assertIn("First • Previous • Next • Last", nav_links_text)
+
+ def assertOCIProjectsArePresent(self, browser, oci_projects):
+ table = find_tag_by_id(browser.contents, "projects_list")
+ with admin_logged_in():
+ for oci_project in oci_projects:
+ url = canonical_url(oci_project, force_local_path=True)
+ self.assertIn(url, str(table))
+ self.assertIn(oci_project.name, str(table))
+
+ def assertOCIProjectsAreNotPresent(self, browser, oci_projects):
+ table = find_tag_by_id(browser.contents, "projects_list")
+ with admin_logged_in():
+ for oci_project in oci_projects:
+ url = canonical_url(oci_project, force_local_path=True)
+ self.assertNotIn(url, str(table))
+ self.assertNotIn(oci_project.name, str(table))
+
+ def check_search_no_oci_projects(self, pillar):
+ pillar = removeSecurityProxy(pillar)
+ person = self.factory.makePerson()
+
+ browser = self.getViewBrowser(
+ pillar, user=person, view_name='+search-oci-project')
+
+ main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
+ self.assertIn(
+ "There are no OCI projects registered for %s" % pillar.name,
+ extract_text(main_portlet).replace("\n", " "))
+
+ def test_search_no_oci_projects_distribution_pillar(self):
+ return self.check_search_no_oci_projects(
+ self.factory.makeDistribution())
+
+ def test_search_no_oci_projects_project_pillar(self):
+ return self.check_search_no_oci_projects(self.factory.makeProduct())
+
+ def check_oci_projects_no_search_keyword(self, pillar):
+ pillar = removeSecurityProxy(pillar)
+ person = pillar.owner
+
+ # Creates 3 OCI Projects
+ oci_projects = [
+ self.factory.makeOCIProject(
+ ociprojectname="test-project-%s" % i,
+ registrant=person, pillar=pillar) for i in range(3)]
+
+ browser = self.getViewBrowser(
+ pillar, user=person, view_name='+search-oci-project')
+
+ # Check top message.
+ main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
+ self.assertIn(
+ "There are 3 OCI projects registered for %s" % pillar.name,
+ extract_text(main_portlet).replace("\n", " "))
+
+ self.assertOCIProjectsArePresent(browser, oci_projects)
+ self.assertPaginationIsPresent(browser, 3, 3)
+
+ def test_oci_projects_no_search_keyword_for_distribution(self):
+ return self.check_oci_projects_no_search_keyword(
+ self.factory.makeDistribution())
+
+ def test_oci_projects_no_search_keyword_for_project(self):
+ return self.check_oci_projects_no_search_keyword(
+ self.factory.makeProduct())
+
+ def check_oci_projects_with_search_keyword(self, pillar):
+ pillar = removeSecurityProxy(pillar)
+ person = pillar.owner
+
+ # And 2 OCI projects that will match the name
+ oci_projects = [
+ self.factory.makeOCIProject(
+ ociprojectname="find-me-%s" % i,
+ registrant=person, pillar=pillar) for i in range(2)]
+
+ # Creates 2 OCI Projects that will not match search
+ other_oci_projects = [
+ self.factory.makeOCIProject(
+ ociprojectname="something-%s" % i,
+ registrant=person, pillar=pillar) for i in range(2)]
+
+ browser = self.getViewBrowser(
+ pillar, user=person, view_name='+search-oci-project')
+ browser.getControl(name="text").value = "find-me"
+ browser.getControl("Search").click()
+
+ # Check top message.
+ main_portlet = find_tags_by_class(browser.contents, "main-portlet")[0]
+ self.assertIn(
+ 'There are 2 OCI projects registered for %s matching "%s"' %
+ (pillar.name, "find-me"),
+ extract_text(main_portlet).replace("\n", " "))
+
+ self.assertOCIProjectsArePresent(browser, oci_projects)
+ self.assertOCIProjectsAreNotPresent(browser, other_oci_projects)
+ self.assertPaginationIsPresent(browser, 2, 2)
+
+ def test_oci_projects_with_search_keyword_for_distribution(self):
+ self.check_oci_projects_with_search_keyword(
+ self.factory.makeDistribution())
+
+ def test_oci_projects_with_search_keyword_for_project(self):
+ self.check_oci_projects_with_search_keyword(self.factory.makeProduct())
+
+ def check_query_count_is_constant(self, pillar):
+ batch_size = 3
+ self.pushConfig("launchpad", default_batch_size=batch_size)
+
+ person = self.factory.makePerson()
+ distro = self.factory.makeDistribution(owner=person)
+ name_pattern = "find-me-"
+
+ def createOCIProject():
+ self.factory.makeOCIProject(
+ ociprojectname=self.factory.getUniqueString(name_pattern),
+ pillar=distro)
+
+ viewer = self.factory.makePerson()
+
+ def getView():
+ browser = self.getViewBrowser(
+ distro, user=viewer, view_name='+search-oci-project')
+ browser.getControl(name="text").value = name_pattern
+ browser.getControl("Search").click()
+ return browser
+
+ def do_login():
+ login_person(person)
+
+ recorder1, recorder2 = record_two_runs(
+ getView, createOCIProject, 1, 10, login_method=do_login)
+ self.assertEqual(recorder1.count, recorder2.count)
+
+ def test_query_count_is_constant_for_distribution(self):
+ self.check_query_count_is_constant(self.factory.makeDistribution())
+
+ def test_query_count_is_constant_for_project(self):
+ self.check_query_count_is_constant(self.factory.makeProduct())
diff --git a/lib/lp/registry/interfaces/ociproject.py b/lib/lp/registry/interfaces/ociproject.py
index d6f8468..a99850d 100644
--- a/lib/lp/registry/interfaces/ociproject.py
+++ b/lib/lp/registry/interfaces/ociproject.py
@@ -203,9 +203,9 @@ class IOCIProjectSet(Interface):
:return: The OCIProject found.
"""
- def findByDistributionAndName(distribution, name_substring):
- """Find OCIProjects for a given distribution that contains the
- provided name."""
+ def findByPillarAndName(pillar, name_substring):
+ """Find OCIProjects for a given pillar that contains the provided
+ name."""
def preloadDataForOCIProjects(oci_projects):
"""Preload data for the given list of OCIProject objects."""
diff --git a/lib/lp/registry/model/ociproject.py b/lib/lp/registry/model/ociproject.py
index 387d9fe..2b72a98 100644
--- a/lib/lp/registry/model/ociproject.py
+++ b/lib/lp/registry/model/ociproject.py
@@ -32,8 +32,8 @@ from lp.registry.interfaces.ociproject import (
IOCIProjectSet,
)
from lp.registry.interfaces.ociprojectname import IOCIProjectNameSet
-from lp.registry.interfaces.product import IProduct
from lp.registry.interfaces.person import IPersonSet
+from lp.registry.interfaces.product import IProduct
from lp.registry.interfaces.series import SeriesStatus
from lp.registry.model.ociprojectname import OCIProjectName
from lp.registry.model.ociprojectseries import OCIProjectSeries
@@ -275,11 +275,11 @@ class OCIProjectSet:
OCIProjectName.name == name).one()
return target
- def findByDistributionAndName(self, distribution, name_substring):
+ def findByPillarAndName(self, pillar, name_substring):
"""See `IOCIProjectSet`."""
return IStore(OCIProject).find(
OCIProject,
- OCIProject.distribution == distribution,
+ self._get_pillar_attribute(pillar) == pillar,
OCIProject.ociprojectname == OCIProjectName.id,
OCIProjectName.name.contains_string(name_substring))
diff --git a/lib/lp/registry/templates/distribution-search-oci-projects.pt b/lib/lp/registry/templates/search-oci-projects.pt
similarity index 100%
rename from lib/lp/registry/templates/distribution-search-oci-projects.pt
rename to lib/lp/registry/templates/search-oci-projects.pt
References