Repository: pypi/warehouse

Issue:
Build tree for trove classifiers
Ref #1265 and #754 

We need to build a new SCSS block that renders as a tree, as per http://odyniec.net/articles/turning-lists-into-trees/

This should be applied to the trove classifiers in the search sidebar and on the package detail pages.
This will require the markup for each to change to a unordered list.



Discussion:
Visual Spec (with checkboxes):

![3e4e544a-8720-11e5-8a52-68c9f882fd1f](https://cloud.githubusercontent.com/assets/145979/11277998/05cf6d90-8eb7-11e5-8a04-e54c97fe9cf5.png)

The standard tree without checkboxes, would be the same, but with the lines joining instead of making boxes.

@nlhkabu I'm looking into this issue - Right now going through http://odyniec.net/articles/turning-lists-into-trees/.  Will put a WIP PR after I make some progress on this.

Resolving patch:
diff --git a/warehouse/views.py b/warehouse/views.py
--- a/warehouse/views.py
+++ b/warehouse/views.py
@@ -318,6 +318,36 @@ def filter_key(item):
         except ValueError:
             return 1, 0, item[0]
 
+    def form_filters_tree(split_list):
+        """
+        Takes a list of lists, each of them containing a filter and
+        one of its children.
+        Returns a dictionary, each key being a filter and each value being
+        the filter's children.
+        """
+        d = {}
+        for l in split_list:
+            current_level = d
+            for part in l:
+                if part not in current_level:
+                    current_level[part] = {}
+                current_level = current_level[part]
+        return d
+
+    def process_available_filters():
+        """
+        Processes available filters and returns a list of dictionaries.
+        The value of a key in the dictionary represents its children
+        """
+        sorted_filters = sorted(available_filters.items(), key=filter_key)
+        output = []
+        for f in sorted_filters:
+            classifier_list = f[1]
+            split_list = [i.split(" :: ") for i in classifier_list]
+            tree = form_filters_tree(split_list)
+            output.append(tree)
+        return output
+
     metrics = request.find_service(IMetricsService, context=None)
     metrics.histogram("warehouse.views.search.results", page.item_count)
 
@@ -325,7 +355,7 @@ def filter_key(item):
         "page": page,
         "term": q,
         "order": request.params.get("o", ""),
-        "available_filters": sorted(available_filters.items(), key=filter_key),
+        "available_filters": process_available_filters(),
         "applied_filters": request.params.getall("c"),
     }
 


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
@@ -441,7 +441,12 @@ def test_with_classifiers(self, monkeypatch, db_request, metrics, page):
             "order": params.get("o", ""),
             "applied_filters": params.getall("c"),
             "available_filters": [
-                ("foo", [classifier1.classifier, classifier2.classifier])
+                {
+                    "foo": {
+                        classifier1.classifier.split(" :: ")[1]: {},
+                        classifier2.classifier.split(" :: ")[1]: {},
+                    }
+                }
             ],
         }
         assert ("fiz", [classifier3.classifier]) not in search_view["available_filters"]