← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~twom/launchpad:oci-policy-list-some-recipes-for-tasty-ui into launchpad:master

 

Tom Wardill has proposed merging ~twom/launchpad:oci-policy-list-some-recipes-for-tasty-ui into launchpad:master.

Commit message:
Add recipe view to OCI Project +index

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/396927

If a project has recipes, we should use the spare space on the OCI Project page to view them.

* List official recipes, put the others behind a 'View all recipes' link.
* Move Create OCI Recipe button to right hand side context menu

https://people.canonical.com/~tomwardill/recipe-view-on-project.png

-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~twom/launchpad:oci-policy-list-some-recipes-for-tasty-ui into launchpad:master.
diff --git a/lib/lp/registry/browser/ociproject.py b/lib/lp/registry/browser/ociproject.py
index 380b94a..8a032b6 100644
--- a/lib/lp/registry/browser/ociproject.py
+++ b/lib/lp/registry/browser/ociproject.py
@@ -187,12 +187,22 @@ class OCIProjectNavigationMenu(NavigationMenu):
 
     facet = 'overview'
 
-    links = ('edit',)
+    links = ('edit', 'create_recipe', 'view_recipes')
 
     @enabled_with_permission('launchpad.Edit')
     def edit(self):
         return Link('+edit', 'Edit OCI project', icon='edit')
 
+    @enabled_with_permission('launchpad.AnyLegitimatePerson')
+    def create_recipe(self):
+        return Link('+new-recipe', 'Create OCI recipe', icon='add')
+
+    def view_recipes(self):
+        enabled = not getUtility(IOCIRecipeSet).findByOCIProject(
+            self.context).is_empty()
+        return Link(
+            '+recipes', 'View all recipes', icon='info', enabled=enabled)
+
 
 class OCIProjectContextMenu(ContextMenu):
     """Context menu for OCI projects."""
@@ -211,7 +221,7 @@ class OCIProjectContextMenu(ContextMenu):
         enabled = not getUtility(IOCIRecipeSet).findByOCIProject(
             self.context).is_empty()
         return Link(
-            '+recipes', 'View OCI recipes', icon='info', enabled=enabled)
+            '+recipes', 'View all recipes', icon='info', enabled=enabled)
 
 
 class OCIProjectIndexView(LaunchpadView):
@@ -227,6 +237,16 @@ class OCIProjectIndexView(LaunchpadView):
     def git_ssh_hostname(self):
         return urlsplit(config.codehosting.git_ssh_root).hostname
 
+    @property
+    def official_recipe_count(self):
+        return self.context.getOfficialRecipes().count()
+
+    @property
+    def other_recipe_count(self):
+        all_count = self.context.getRecipes().count()
+        official_count = self.context.getOfficialRecipes().count()
+        return all_count - official_count
+
 
 class OCIProjectEditView(LaunchpadEditFormView):
     """Edit an OCI project."""
diff --git a/lib/lp/registry/browser/tests/test_ociproject.py b/lib/lp/registry/browser/tests/test_ociproject.py
index a8b4113..980957a 100644
--- a/lib/lp/registry/browser/tests/test_ociproject.py
+++ b/lib/lp/registry/browser/tests/test_ociproject.py
@@ -14,7 +14,7 @@ from datetime import datetime
 import pytz
 from zope.security.proxy import removeSecurityProxy
 
-from lp.oci.interfaces.ocirecipe import OCI_RECIPE_ALLOW_CREATE
+from lp.oci.tests.helpers import OCIConfigHelperMixin
 from lp.registry.interfaces.ociproject import (
     OCI_PROJECT_ALLOW_CREATE,
     OCIProjectCreateFeatureDisabled,
@@ -79,10 +79,14 @@ class TestOCIProjectNavigation(TestCaseWithFactory):
         self.assertEqual(oci_project, obj)
 
 
-class TestOCIProjectView(BrowserTestCase):
+class TestOCIProjectView(OCIConfigHelperMixin, BrowserTestCase):
 
     layer = DatabaseFunctionalLayer
 
+    def setUp(self):
+        super(TestOCIProjectView, self).setUp()
+        self.setConfig()
+
     def test_index_distribution_pillar(self):
         distribution = self.factory.makeDistribution(displayname="My Distro")
         oci_project = self.factory.makeOCIProject(
@@ -149,6 +153,63 @@ class TestOCIProjectView(BrowserTestCase):
             Name: oci-name
             """, self.getMainText(oci_project, user=owner))
 
+    def test_shows_official_recipes(self):
+        distribution = self.factory.makeDistribution(displayname="My Distro")
+        oci_project = self.factory.makeOCIProject(
+            pillar=distribution, ociprojectname="oci-name")
+        self.factory.makeOCIRecipe(oci_project=oci_project, official=True)
+        browser = self.getViewBrowser(
+            oci_project, view_name="+index", user=distribution.owner)
+        self.assertIn("Official recipes", browser.contents)
+        self.assertNotIn("non-official recipe", browser.contents)
+        self.assertNotIn(
+            "There are no recipes registered for this OCI project.",
+            browser.contents)
+
+    def test_shows_official_and_unofficial_recipes(self):
+        distribution = self.factory.makeDistribution(displayname="My Distro")
+        oci_project = self.factory.makeOCIProject(
+            pillar=distribution, ociprojectname="oci-name")
+        self.factory.makeOCIRecipe(oci_project=oci_project, official=True)
+        self.factory.makeOCIRecipe(oci_project=oci_project, official=False)
+        browser = self.getViewBrowser(
+            oci_project, view_name="+index", user=distribution.owner)
+        self.assertIn("Official recipes", browser.contents)
+        self.assertIn(
+            "There is <strong>1</strong> non-official recipe.",
+            browser.contents)
+        self.assertNotIn(
+            "There are no recipes registered for this OCI project.",
+            browser.contents)
+
+    def test_shows_unofficial_recipes(self):
+        distribution = self.factory.makeDistribution(displayname="My Distro")
+        oci_project = self.factory.makeOCIProject(
+            pillar=distribution, ociprojectname="oci-name")
+        self.factory.makeOCIRecipe(oci_project=oci_project, official=False)
+        self.factory.makeOCIRecipe(oci_project=oci_project, official=False)
+        browser = self.getViewBrowser(
+            oci_project, view_name="+index", user=distribution.owner)
+        self.assertNotIn("Official recipes", browser.contents)
+        self.assertIn(
+            "There are <strong>2</strong> non-official recipes.",
+            browser.contents)
+        self.assertNotIn(
+            "There are no recipes registered for this OCI project.",
+            browser.contents)
+
+    def test_shows_no_recipes(self):
+        distribution = self.factory.makeDistribution(displayname="My Distro")
+        oci_project = self.factory.makeOCIProject(
+            pillar=distribution, ociprojectname="oci-name")
+        browser = self.getViewBrowser(
+            oci_project, view_name="+index", user=distribution.owner)
+        self.assertNotIn("Official recipes", browser.contents)
+        self.assertNotIn("non-official recipe", browser.contents)
+        self.assertIn(
+            "There are no recipes registered for this OCI project.",
+            browser.contents)
+
 
 class TestOCIProjectEditView(BrowserTestCase):
 
diff --git a/lib/lp/registry/templates/ociproject-index.pt b/lib/lp/registry/templates/ociproject-index.pt
index 9fc5197..70b1088 100644
--- a/lib/lp/registry/templates/ociproject-index.pt
+++ b/lib/lp/registry/templates/ociproject-index.pt
@@ -18,7 +18,7 @@
   </metal:registering>
 
   <metal:side fill-slot="side">
-    <div tal:replace="structure context/@@+global-actions"/>
+    <tal:menu replace="structure context/@@+global-actions" />
   </metal:side>
 
   <metal:heading fill-slot="heading">
@@ -65,14 +65,53 @@
     </div>
 
     <h2>Recipes</h2>
-    <div id="recipe-summary"
-         tal:define="link context/menu:context/view_recipes"
-         tal:condition="link/enabled"
-         tal:content="structure link/render"/>
-    <tal:create-recipe
-        define="link context/menu:context/create_recipe"
-        condition="link/enabled"
-        replace="structure link/render"/>
+
+    <h3 tal:condition="view/official_recipe_count">Official recipes</h3>
+    <table class="listing" id="mirrors_list" tal:condition="view/official_recipe_count">
+        <tbody>
+          <tr class="head">
+            <th>Name</th>
+            <th>Owner</th>
+            <th>Source</th>
+            <th>Build file</th>
+            <th>Date created</th>
+          </tr>
+
+          <tr tal:repeat="recipe context/getOfficialRecipes">
+            <td>
+              <a tal:content="recipe/name"
+                 tal:attributes="href recipe/fmt:url" />
+            </td>
+            <td tal:content="structure recipe/owner/fmt:link" />
+            <td tal:content="recipe/git_ref/identity" />
+            <td tal:content="recipe/build_file" />
+            <td tal:content="recipe/date_created/fmt:displaydate" />
+          </tr>
+        </tbody>
+      </table>
+    <div tal:condition="python: not view.official_recipe_count and view.other_recipe_count">
+      <p>There are no official recipes for this OCI project.</p>
+    </div>
+
+    <div tal:define="count view/other_recipe_count"
+           tal:condition="count">
+      <span tal:condition="python: count == 1">
+        There is <strong>1</strong> non-official recipe.</span>
+      <span tal:condition="python: count != 1">
+        There are <strong tal:content="count" /> non-official recipes.
+      </span>
+      <p>
+        <tal:summary
+        tal:define="link context/menu:context/view_recipes"
+        tal:condition="link/enabled"
+        tal:content="structure link/render"/>
+      </p>
+    </div>
+
+    <div tal:condition="python: not view.official_recipe_count and not view.other_recipe_count">
+      <p>There are no recipes registered for this OCI project.</p>
+    </div>
+
   </div>
 </body>
 </html>