Repository: pypi/warehouse

Issue:
Link classifiers in the project description
The classifiers need to be linked to some sort of "browse" page.

Link classifiers in the project description
The classifiers need to be linked to some sort of "browse" page.



Discussion:
Actually, we're not currently linking to the classifiers at all... Maybe this is something we need to look at.  Perhaps on the 'similar packages' tab??

We can now link to a search for a single classifier like this: https://warehouse.python.org/search/?c=Development+Status+%3A%3A+1+-+Planning

Where would we be linking _from_? An additional tab on the project description page?

This issue was opened before the new design. The new design doesn't list the classifiers anywhere, but it probably should. See #1061.

Actually, we're not currently linking to the classifiers at all... Maybe this is something we need to look at.  Perhaps on the 'similar packages' tab??

We can now link to a search for a single classifier like this: https://warehouse.python.org/search/?c=Development+Status+%3A%3A+1+-+Planning

Where would we be linking _from_? An additional tab on the project description page?

This issue was opened before the new design. The new design doesn't list the classifiers anywhere, but it probably should. See #1061.


Resolving patch:
diff --git a/warehouse/packaging/models.py b/warehouse/packaging/models.py
--- a/warehouse/packaging/models.py
+++ b/warehouse/packaging/models.py
@@ -12,7 +12,10 @@
 
 import enum
 
-from collections import OrderedDict
+from collections import (
+    OrderedDict,
+    defaultdict,
+)
 
 from citext import CIText
 from pyramid.security import Allow
@@ -265,6 +268,15 @@ def __table_args__(cls):  # noqa
     )
     classifiers = association_proxy("_classifiers", "classifier")
 
+    @property
+    def structured_classifiers(self):
+        structured = defaultdict(list)
+        for classifier in self.classifiers:
+            key, *value = classifier.split(' :: ', 1)
+            if value:
+                structured[key].append(value[0])
+        return structured
+
     files = orm.relationship(
         "File",
         backref="release",


Test patch:
diff --git a/tests/unit/packaging/test_models.py b/tests/unit/packaging/test_models.py
--- a/tests/unit/packaging/test_models.py
+++ b/tests/unit/packaging/test_models.py
@@ -25,6 +25,9 @@
     ProjectFactory as DBProjectFactory, ReleaseFactory as DBReleaseFactory,
     FileFactory as DBFileFactory, RoleFactory as DBRoleFactory,
 )
+from ...common.db.classifiers import (
+    ClassifierFactory as DBClassifierFactory,
+)
 
 
 class TestProjectFactory:
@@ -216,6 +219,26 @@ def test_urls(self, db_session, home_page, download_url, project_urls,
         # TODO: It'd be nice to test for the actual ordering here.
         assert dict(release.urls) == dict(expected)
 
+    def test_structured_classifiers(self, db_session):
+        release = DBReleaseFactory.create()
+        DBClassifierFactory.create(
+            classifier='Foo :: Bar :: Baz',
+            project_releases=[release],
+        )
+        DBClassifierFactory.create(
+            classifier='Foo :: Bar :: Qux',
+            project_releases=[release],
+        )
+        DBClassifierFactory.create(
+            classifier='Vleep',
+            project_releases=[release],
+        )
+        expected = {
+            'Foo': ['Bar :: Baz', 'Bar :: Qux'],
+        }
+
+        assert release.structured_classifiers == expected
+
 
 class TestFile: