Repository: pypi/warehouse

Issue:
Remove unused classifiers from filter list
We currently show all trove classifiers in the search filter panel, despite the fact that some are not applied to any projects in the DB.

It would be better to only show those classifiers that are actually applied to projects, so we avoid filtering by a classifier and returning an empty result.



Discussion:

Resolving patch:
diff --git a/warehouse/views.py b/warehouse/views.py
--- a/warehouse/views.py
+++ b/warehouse/views.py
@@ -22,13 +22,16 @@
 from elasticsearch_dsl import Q
 from sqlalchemy import func
 from sqlalchemy.orm import aliased, joinedload
+from sqlalchemy.sql import exists
 
 from warehouse.accounts import REDIRECT_FIELD_NAME
 from warehouse.accounts.models import User
 from warehouse.cache.origin import origin_cache
 from warehouse.cache.http import cache_control
 from warehouse.classifiers.models import Classifier
-from warehouse.packaging.models import Project, Release, File
+from warehouse.packaging.models import (
+    Project, Release, File, release_classifiers,
+)
 from warehouse.utils.row_counter import RowCount
 from warehouse.utils.paginate import ElasticsearchPage, paginate_url_factory
 
@@ -218,7 +221,17 @@ def search(request):
 
     available_filters = collections.defaultdict(list)
 
-    for cls in request.db.query(Classifier).order_by(Classifier.classifier):
+    classifiers_q = (
+        request.db.query(Classifier)
+        .with_entities(Classifier.classifier)
+        .filter(
+            exists([release_classifiers.c.trove_id])
+            .where(release_classifiers.c.trove_id == Classifier.id)
+        )
+        .order_by(Classifier.classifier)
+    )
+
+    for cls in classifiers_q:
         first, *_ = cls.classifier.split(' :: ')
         available_filters[first].append(cls.classifier)
 


Test patch:
diff --git a/tests/unit/test_views.py b/tests/unit/test_views.py
--- a/tests/unit/test_views.py
+++ b/tests/unit/test_views.py
@@ -293,6 +293,12 @@ def test_with_classifiers(self, monkeypatch, db_request, page):
         classifier2 = ClassifierFactory.create(classifier="foo :: baz")
         classifier3 = ClassifierFactory.create(classifier="fiz :: buz")
 
+        project = ProjectFactory.create()
+        release1 = ReleaseFactory.create(project=project)
+        release1.created = datetime.date(2011, 1, 1)
+        release1._classifiers.append(classifier1)
+        release1._classifiers.append(classifier2)
+
         page_obj = pretend.stub(page_count=(page or 1) + 10)
         page_cls = pretend.call_recorder(lambda *a, **kw: page_obj)
         monkeypatch.setattr(views, "ElasticsearchPage", page_cls)
@@ -301,19 +307,24 @@ def test_with_classifiers(self, monkeypatch, db_request, page):
         url_maker_factory = pretend.call_recorder(lambda request: url_maker)
         monkeypatch.setattr(views, "paginate_url_factory", url_maker_factory)
 
-        assert search(db_request) == {
+        search_view = search(db_request)
+        assert search_view == {
             "page": page_obj,
             "term": params.get("q", ''),
             "order": params.get("o", ''),
             "applied_filters": params.getall("c"),
             "available_filters": [
-                ('fiz', [classifier3.classifier]),
                 ('foo', [
                     classifier1.classifier,
                     classifier2.classifier,
                 ])
             ],
         }
+        assert (
+            ("fiz", [
+                classifier3.classifier
+            ]) not in search_view["available_filters"]
+        )
         assert page_cls.calls == [
             pretend.call(es_query, url_maker=url_maker, page=page or 1),
         ]