prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  recipients column in coach tools in not accurate
  
  ### Observed behavior
  
  recipients column always indicates assignment to the entire class, even when that's not true:
  
  ![groups](https://user-images.githubusercontent.com/2367265/69926711-644bce80-146a-11ea-8f2b-82ec444fb367.gif)
  
  
  ### Expected behavior
  
  recipients column should be accurate
  
  ### User-facing consequences
  
  confusion
  
  ### Errors and logs
  
  none observed
  
  ### Steps to reproduce
  
  create a lesson and assign it to a group
  
  ### Context
  
  0.13.0 beta 3
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `kolibri/core/lessons/viewsets.py`
  Content:
  ```
  1 import json
  2 from functools import partial
  3 from itertools import chain
  4 
  5 from django.db import connection
  6 from django.db.models import CharField
  7 from django_filters.rest_framework import DjangoFilterBackend
  8 
  9 from .serializers import LessonSerializer
  10 from kolibri.core.api import ValuesViewset
  11 from kolibri.core.auth.api import KolibriAuthPermissions
  12 from kolibri.core.auth.api import KolibriAuthPermissionsFilter
  13 from kolibri.core.lessons.models import Lesson
  14 from kolibri.core.lessons.models import LessonAssignment
  15 from kolibri.core.query import ArrayAgg
  16 from kolibri.core.query import GroupConcat
  17 from kolibri.core.query import process_uuid_aggregate
  18 
  19 
  20 def _ensure_raw_dict(d):
  21     if hasattr(d, "dict"):
  22         d = d.dict()
  23     return dict(d)
  24 
  25 
  26 class LessonPermissions(KolibriAuthPermissions):
  27     # Overrides the default validator to sanitize the Lesson POST Payload
  28     # before validation
  29     def validator(self, request, view, datum):
  30         model = view.get_serializer_class().Meta.model
  31         validated_data = view.get_serializer().to_internal_value(
  32             _ensure_raw_dict(datum)
  33         )
  34         # Cannot have create assignments without creating the Lesson first,
  35         # so this doesn't try to validate the Lesson with a non-empty lesson_assignments list
  36         validated_data.pop("lesson_assignments")
  37         return request.user.can_create(model, validated_data)
  38 
  39 
  40 def _map_lesson_classroom(item):
  41     return {
  42         "id": item.pop("collection__id"),
  43         "name": item.pop("collection__name"),
  44         "parent": item.pop("collection__parent_id"),
  45     }
  46 
  47 
  48 class LessonViewset(ValuesViewset):
  49     serializer_class = LessonSerializer
  50     filter_backends = (KolibriAuthPermissionsFilter, DjangoFilterBackend)
  51     filter_fields = ("collection",)
  52     permission_classes = (LessonPermissions,)
  53     queryset = Lesson.objects.all().order_by("-date_created")
  54 
  55     values = (
  56         "id",
  57         "title",
  58         "description",
  59         "resources",
  60         "is_active",
  61         "collection",  # classroom
  62         "collection__id",
  63         "collection__name",
  64         "collection__parent_id",
  65         "created_by",
  66         "assignment_ids",
  67     )
  68 
  69     field_map = {
  70         "classroom": _map_lesson_classroom,
  71         "resources": lambda x: json.loads(x["resources"]),
  72         "assignment_ids": partial(process_uuid_aggregate, key="assignment_ids"),
  73     }
  74 
  75     def consolidate(self, items):
  76         assignment_ids = []
  77         for item in items:
  78             assignment_ids.extend(item["assignment_ids"])
  79         assignments = LessonAssignment.objects.filter(id__in=assignment_ids)
  80         if connection.vendor == "postgresql" and ArrayAgg is not None:
  81             assignments = assignments.annotate(
  82                 learner_ids=ArrayAgg("collection__membership__user__id"),
  83             )
  84 
  85             def _process_item(item):
  86                 item["learner_ids"] = map(
  87                     lambda x: x.hex, filter(lambda x: x, item["learner_ids"])
  88                 )
  89                 return item
  90 
  91         else:
  92             assignments = assignments.values("id").annotate(
  93                 learner_ids=GroupConcat(
  94                     "collection__membership__user__id", output_field=CharField(),
  95                 ),
  96             )
  97 
  98             def _process_item(item):
  99                 item["learner_ids"] = (
  100                     item["learner_ids"].split(",") if item["learner_ids"] else []
  101                 )
  102                 return item
  103 
  104         assignments = {
  105             a["id"]: _process_item(a)
  106             for a in assignments.values(
  107                 "id", "collection", "collection__kind", "learner_ids", "assigned_by"
  108             )
  109         }
  110         for item in items:
  111             item_ids = item.pop("assignment_ids")
  112             item["lesson_assignments"] = [assignments[a] for a in item_ids]
  113             item["learner_ids"] = list(
  114                 set(
  115                     chain.from_iterable(
  116                         a.pop("learner_ids") for a in item["lesson_assignments"]
  117                     )
  118                 )
  119             )
  120         return items
  121 
  122     def annotate_queryset(self, queryset):
  123         if connection.vendor == "postgresql" and ArrayAgg is not None:
  124             return queryset.annotate(assignment_ids=ArrayAgg("lesson_assignments__id"),)
  125         return queryset.values("id").annotate(
  126             assignment_ids=GroupConcat(
  127                 "lesson_assignments__id", output_field=CharField(),
  128             ),
  129         )
  130
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/kolibri/core/lessons/viewsets.py b/kolibri/core/lessons/viewsets.py
  --- a/kolibri/core/lessons/viewsets.py
  +++ b/kolibri/core/lessons/viewsets.py
  @@ -4,6 +4,7 @@
   
   from django.db import connection
   from django.db.models import CharField
  +from django.db.models import F
   from django_filters.rest_framework import DjangoFilterBackend
   
   from .serializers import LessonSerializer
  @@ -104,7 +105,11 @@
           assignments = {
               a["id"]: _process_item(a)
               for a in assignments.values(
  -                "id", "collection", "collection__kind", "learner_ids", "assigned_by"
  +                "id",
  +                "collection",
  +                "learner_ids",
  +                "assigned_by",
  +                collection_kind=F("collection__kind"),
               )
           }
           for item in items:
  
verification_info: {"golden_diff": "diff --git a/kolibri/core/lessons/viewsets.py b/kolibri/core/lessons/viewsets.py\n--- a/kolibri/core/lessons/viewsets.py\n+++ b/kolibri/core/lessons/viewsets.py\n@@ -4,6 +4,7 @@\n \n from django.db import connection\n from django.db.models import CharField\n+from django.db.models import F\n from django_filters.rest_framework import DjangoFilterBackend\n \n from .serializers import LessonSerializer\n@@ -104,7 +105,11 @@\n         assignments = {\n             a[\"id\"]: _process_item(a)\n             for a in assignments.values(\n-                \"id\", \"collection\", \"collection__kind\", \"learner_ids\", \"assigned_by\"\n+                \"id\",\n+                \"collection\",\n+                \"learner_ids\",\n+                \"assigned_by\",\n+                collection_kind=F(\"collection__kind\"),\n             )\n         }\n         for item in items:\n", "issue": "recipients column in coach tools in not accurate\n\r\n### Observed behavior\r\n\r\nrecipients column always indicates assignment to the entire class, even when that's not true:\r\n\r\n![groups](https://user-images.githubusercontent.com/2367265/69926711-644bce80-146a-11ea-8f2b-82ec444fb367.gif)\r\n\r\n\r\n### Expected behavior\r\n\r\nrecipients column should be accurate\r\n\r\n### User-facing consequences\r\n\r\nconfusion\r\n\r\n### Errors and logs\r\n\r\nnone observed\r\n\r\n### Steps to reproduce\r\n\r\ncreate a lesson and assign it to a group\r\n\r\n### Context\r\n\r\n0.13.0 beta 3\r\n\n", "before_files": [{"content": "import json\nfrom functools import partial\nfrom itertools import chain\n\nfrom django.db import connection\nfrom django.db.models import CharField\nfrom django_filters.rest_framework import DjangoFilterBackend\n\nfrom .serializers import LessonSerializer\nfrom kolibri.core.api import ValuesViewset\nfrom kolibri.core.auth.api import KolibriAuthPermissions\nfrom kolibri.core.auth.api import KolibriAuthPermissionsFilter\nfrom kolibri.core.lessons.models import Lesson\nfrom kolibri.core.lessons.models import LessonAssignment\nfrom kolibri.core.query import ArrayAgg\nfrom kolibri.core.query import GroupConcat\nfrom kolibri.core.query import process_uuid_aggregate\n\n\ndef _ensure_raw_dict(d):\n    if hasattr(d, \"dict\"):\n        d = d.dict()\n    return dict(d)\n\n\nclass LessonPermissions(KolibriAuthPermissions):\n    # Overrides the default validator to sanitize the Lesson POST Payload\n    # before validation\n    def validator(self, request, view, datum):\n        model = view.get_serializer_class().Meta.model\n        validated_data = view.get_serializer().to_internal_value(\n            _ensure_raw_dict(datum)\n        )\n        # Cannot have create assignments without creating the Lesson first,\n        # so this doesn't try to validate the Lesson with a non-empty lesson_assignments list\n        validated_data.pop(\"lesson_assignments\")\n        return request.user.can_create(model, validated_data)\n\n\ndef _map_lesson_classroom(item):\n    return {\n        \"id\": item.pop(\"collection__id\"),\n        \"name\": item.pop(\"collection__name\"),\n        \"parent\": item.pop(\"collection__parent_id\"),\n    }\n\n\nclass LessonViewset(ValuesViewset):\n    serializer_class = LessonSerializer\n    filter_backends = (KolibriAuthPermissionsFilter, DjangoFilterBackend)\n    filter_fields = (\"collection\",)\n    permission_classes = (LessonPermissions,)\n    queryset = Lesson.objects.all().order_by(\"-date_created\")\n\n    values = (\n        \"id\",\n        \"title\",\n        \"description\",\n        \"resources\",\n        \"is_active\",\n        \"collection\",  # classroom\n        \"collection__id\",\n        \"collection__name\",\n        \"collection__parent_id\",\n        \"created_by\",\n        \"assignment_ids\",\n    )\n\n    field_map = {\n        \"classroom\": _map_lesson_classroom,\n        \"resources\": lambda x: json.loads(x[\"resources\"]),\n        \"assignment_ids\": partial(process_uuid_aggregate, key=\"assignment_ids\"),\n    }\n\n    def consolidate(self, items):\n        assignment_ids = []\n        for item in items:\n            assignment_ids.extend(item[\"assignment_ids\"])\n        assignments = LessonAssignment.objects.filter(id__in=assignment_ids)\n        if connection.vendor == \"postgresql\" and ArrayAgg is not None:\n            assignments = assignments.annotate(\n                learner_ids=ArrayAgg(\"collection__membership__user__id\"),\n            )\n\n            def _process_item(item):\n                item[\"learner_ids\"] = map(\n                    lambda x: x.hex, filter(lambda x: x, item[\"learner_ids\"])\n                )\n                return item\n\n        else:\n            assignments = assignments.values(\"id\").annotate(\n                learner_ids=GroupConcat(\n                    \"collection__membership__user__id\", output_field=CharField(),\n                ),\n            )\n\n            def _process_item(item):\n                item[\"learner_ids\"] = (\n                    item[\"learner_ids\"].split(\",\") if item[\"learner_ids\"] else []\n                )\n                return item\n\n        assignments = {\n            a[\"id\"]: _process_item(a)\n            for a in assignments.values(\n                \"id\", \"collection\", \"collection__kind\", \"learner_ids\", \"assigned_by\"\n            )\n        }\n        for item in items:\n            item_ids = item.pop(\"assignment_ids\")\n            item[\"lesson_assignments\"] = [assignments[a] for a in item_ids]\n            item[\"learner_ids\"] = list(\n                set(\n                    chain.from_iterable(\n                        a.pop(\"learner_ids\") for a in item[\"lesson_assignments\"]\n                    )\n                )\n            )\n        return items\n\n    def annotate_queryset(self, queryset):\n        if connection.vendor == \"postgresql\" and ArrayAgg is not None:\n            return queryset.annotate(assignment_ids=ArrayAgg(\"lesson_assignments__id\"),)\n        return queryset.values(\"id\").annotate(\n            assignment_ids=GroupConcat(\n                \"lesson_assignments__id\", output_field=CharField(),\n            ),\n        )\n", "path": "kolibri/core/lessons/viewsets.py"}], "after_files": [{"content": "import json\nfrom functools import partial\nfrom itertools import chain\n\nfrom django.db import connection\nfrom django.db.models import CharField\nfrom django.db.models import F\nfrom django_filters.rest_framework import DjangoFilterBackend\n\nfrom .serializers import LessonSerializer\nfrom kolibri.core.api import ValuesViewset\nfrom kolibri.core.auth.api import KolibriAuthPermissions\nfrom kolibri.core.auth.api import KolibriAuthPermissionsFilter\nfrom kolibri.core.lessons.models import Lesson\nfrom kolibri.core.lessons.models import LessonAssignment\nfrom kolibri.core.query import ArrayAgg\nfrom kolibri.core.query import GroupConcat\nfrom kolibri.core.query import process_uuid_aggregate\n\n\ndef _ensure_raw_dict(d):\n    if hasattr(d, \"dict\"):\n        d = d.dict()\n    return dict(d)\n\n\nclass LessonPermissions(KolibriAuthPermissions):\n    # Overrides the default validator to sanitize the Lesson POST Payload\n    # before validation\n    def validator(self, request, view, datum):\n        model = view.get_serializer_class().Meta.model\n        validated_data = view.get_serializer().to_internal_value(\n            _ensure_raw_dict(datum)\n        )\n        # Cannot have create assignments without creating the Lesson first,\n        # so this doesn't try to validate the Lesson with a non-empty lesson_assignments list\n        validated_data.pop(\"lesson_assignments\")\n        return request.user.can_create(model, validated_data)\n\n\ndef _map_lesson_classroom(item):\n    return {\n        \"id\": item.pop(\"collection__id\"),\n        \"name\": item.pop(\"collection__name\"),\n        \"parent\": item.pop(\"collection__parent_id\"),\n    }\n\n\nclass LessonViewset(ValuesViewset):\n    serializer_class = LessonSerializer\n    filter_backends = (KolibriAuthPermissionsFilter, DjangoFilterBackend)\n    filter_fields = (\"collection\",)\n    permission_classes = (LessonPermissions,)\n    queryset = Lesson.objects.all().order_by(\"-date_created\")\n\n    values = (\n        \"id\",\n        \"title\",\n        \"description\",\n        \"resources\",\n        \"is_active\",\n        \"collection\",  # classroom\n        \"collection__id\",\n        \"collection__name\",\n        \"collection__parent_id\",\n        \"created_by\",\n        \"assignment_ids\",\n    )\n\n    field_map = {\n        \"classroom\": _map_lesson_classroom,\n        \"resources\": lambda x: json.loads(x[\"resources\"]),\n        \"assignment_ids\": partial(process_uuid_aggregate, key=\"assignment_ids\"),\n    }\n\n    def consolidate(self, items):\n        assignment_ids = []\n        for item in items:\n            assignment_ids.extend(item[\"assignment_ids\"])\n        assignments = LessonAssignment.objects.filter(id__in=assignment_ids)\n        if connection.vendor == \"postgresql\" and ArrayAgg is not None:\n            assignments = assignments.annotate(\n                learner_ids=ArrayAgg(\"collection__membership__user__id\"),\n            )\n\n            def _process_item(item):\n                item[\"learner_ids\"] = map(\n                    lambda x: x.hex, filter(lambda x: x, item[\"learner_ids\"])\n                )\n                return item\n\n        else:\n            assignments = assignments.values(\"id\").annotate(\n                learner_ids=GroupConcat(\n                    \"collection__membership__user__id\", output_field=CharField(),\n                ),\n            )\n\n            def _process_item(item):\n                item[\"learner_ids\"] = (\n                    item[\"learner_ids\"].split(\",\") if item[\"learner_ids\"] else []\n                )\n                return item\n\n        assignments = {\n            a[\"id\"]: _process_item(a)\n            for a in assignments.values(\n                \"id\",\n                \"collection\",\n                \"learner_ids\",\n                \"assigned_by\",\n                collection_kind=F(\"collection__kind\"),\n            )\n        }\n        for item in items:\n            item_ids = item.pop(\"assignment_ids\")\n            item[\"lesson_assignments\"] = [assignments[a] for a in item_ids]\n            item[\"learner_ids\"] = list(\n                set(\n                    chain.from_iterable(\n                        a.pop(\"learner_ids\") for a in item[\"lesson_assignments\"]\n                    )\n                )\n            )\n        return items\n\n    def annotate_queryset(self, queryset):\n        if connection.vendor == \"postgresql\" and ArrayAgg is not None:\n            return queryset.annotate(assignment_ids=ArrayAgg(\"lesson_assignments__id\"),)\n        return queryset.values(\"id\").annotate(\n            assignment_ids=GroupConcat(\n                \"lesson_assignments__id\", output_field=CharField(),\n            ),\n        )\n", "path": "kolibri/core/lessons/viewsets.py"}]}