Repository: learningequality/kolibri

Issue:
exam creation breaks when clicking 'select all' on a large topic

### Observed behavior

Just filing this is a note for later to investigate further. I'm currently experiencing this in our `coach-data` working branch. Haven't tested in `develop` yet.

Clicking 'select all' on KA Math when creating an exam does two things:

* browser freezes and CPU goes to 100% for a longer period than I've been willing to wait
* server reports a 'URI string too long' type error

I briefly looked at the request (but forget which endpoint) and it indeed had an array with thousands of items.


### Expected behavior

buttery smoothness

### User-facing consequences

:/

### Errors and logs

`414 Request-URI Too Large`

### Steps to reproduce

select-all in KA math

### Context

`coach-data` branch


Discussion:
Have replicated this. I have some ideas on how to deal with it, but won't be implemented today.

Resolving patch:
diff --git a/kolibri/core/content/api.py b/kolibri/core/content/api.py
--- a/kolibri/core/content/api.py
+++ b/kolibri/core/content/api.py
@@ -267,7 +267,7 @@ def descendants(self, request):
             def copy_node(new_node):
                 new_node['ancestor_id'] = node.id
                 return new_node
-            node_data = node.get_descendants()
+            node_data = node.get_descendants().filter(available=True)
             if kind:
                 node_data = node_data.filter(kind=kind)
             data += map(copy_node, node_data.values('id', 'title', 'kind', 'content_id'))
@@ -294,7 +294,7 @@ def node_assessments(self, request):
         ids = self.request.query_params.get('ids', '').split(',')
         data = 0
         if ids and ids[0]:
-            nodes = models.ContentNode.objects.filter(id__in=ids).prefetch_related('assessmentmetadata')
+            nodes = models.ContentNode.objects.filter(id__in=ids, available=True).prefetch_related('assessmentmetadata')
             data = nodes.aggregate(Sum('assessmentmetadata__number_of_assessments'))['assessmentmetadata__number_of_assessments__sum'] or 0
         return Response(data)
 


Test patch:
diff --git a/kolibri/core/content/test/test_content_app.py b/kolibri/core/content/test/test_content_app.py
--- a/kolibri/core/content/test/test_content_app.py
+++ b/kolibri/core/content/test/test_content_app.py
@@ -440,6 +440,28 @@ def test_contentnode_descendants_topic_parent_child_ancestor_ids(self):
         for node in root.get_descendants(include_self=False).filter(available=True):
             self.assertTrue(next(item for item in root_items if item["id"] == node.id))
 
+    def test_contentnode_descendants_availability(self):
+        content.ContentNode.objects.all().update(available=False)
+        root = content.ContentNode.objects.get(parent__isnull=True)
+        topics = content.ContentNode.objects.filter(parent=root, kind=content_kinds.TOPIC)
+        topic_ids = topics.values_list('id', flat=True)
+        response = self.client.get(reverse("kolibri:core:contentnode-descendants"), data={"ids": ",".join(topic_ids)})
+        self.assertEqual(len(response.data), 0)
+
+    def test_contentnode_node_assessments_available(self):
+        content.ContentNode.objects.all().update(available=True)
+        root = content.ContentNode.objects.get(parent__isnull=True)
+        exercise_ids = root.get_descendants().filter(kind=content_kinds.EXERCISE).values_list('id', flat=True)
+        response = self.client.get(reverse("kolibri:core:contentnode-node-assessments"), data={"ids": ",".join(exercise_ids)})
+        self.assertEqual(response.data, 1)
+
+    def test_contentnode_node_assessments_not_available(self):
+        content.ContentNode.objects.all().update(available=False)
+        root = content.ContentNode.objects.get(parent__isnull=True)
+        exercise_ids = root.get_descendants().filter(kind=content_kinds.EXERCISE).values_list('id', flat=True)
+        response = self.client.get(reverse("kolibri:core:contentnode-node-assessments"), data={"ids": ",".join(exercise_ids)})
+        self.assertEqual(response.data, 0)
+
     def test_contentnode_slim_recommendations(self):
         node_id = content.ContentNode.objects.get(title="c2c2").id
         response = self.client.get(reverse("kolibri:core:contentnode_slim-recommendations-for", kwargs={'pk': node_id}))