We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
hide not-recent learners on 'coach - recent activity' tab
See similar issue for channels: https://github.com/learningequality/kolibri/pull/1406

Now we need to do the same thing for when you drill deeper and reach the learners list. For example here, we're showing all learners regardless of whether or not they've had recent activity:

![image](https://cloud.githubusercontent.com/assets/2367265/25878301/8d2f6c62-34de-11e7-96f7-836359e3bd28.png)






--- 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/plugins/coach/serializers.py`
Content:
```
1 from dateutil.parser import parse
2 
3 from django.db.models import Case, Count, F, IntegerField, Sum, Value as V, When
4 from django.db.models.functions import Coalesce
5 from kolibri.auth.models import FacilityUser
6 from kolibri.content.models import ContentNode
7 from kolibri.logger.models import ContentSummaryLog
8 from le_utils.constants import content_kinds
9 from rest_framework import serializers
10 
11 from .utils.return_users import get_members_or_user
12 
13 
14 class UserReportSerializer(serializers.ModelSerializer):
15     progress = serializers.SerializerMethodField()
16     last_active = serializers.SerializerMethodField()
17 
18     class Meta:
19         model = FacilityUser
20         fields = (
21             'pk', 'full_name', 'progress', 'last_active',
22         )
23 
24     def get_progress(self, target_user):
25         content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])
26         # progress details for a topic node and everything under it
27         if content_node.kind == content_kinds.TOPIC:
28             kind_counts = content_node.get_descendant_kind_counts()
29             topic_details = ContentSummaryLog.objects \
30                 .filter_by_topic(content_node) \
31                 .filter(user=target_user) \
32                 .values('kind') \
33                 .annotate(total_progress=Sum('progress')) \
34                 .annotate(log_count_total=Count('pk')) \
35                 .annotate(log_count_complete=Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())))
36             # evaluate queryset so we can add data for kinds that do not have logs
37             topic_details = list(topic_details)
38             for kind in topic_details:
39                 del kind_counts[kind['kind']]
40             for key in kind_counts:
41                 topic_details.append({'kind': key, 'total_progress': 0, 'log_count_total': 0, 'log_count_complete': 0})
42             return topic_details
43         else:
44             # progress details for a leaf node (exercise, video, etc.)
45             leaf_details = ContentSummaryLog.objects \
46                 .filter(user=target_user) \
47                 .filter(content_id=content_node.content_id) \
48                 .annotate(total_progress=F('progress')) \
49                 .values('kind', 'time_spent', 'total_progress')
50             return leaf_details if leaf_details else [{'kind': content_node.kind, 'time_spent': 0, 'total_progress': 0}]
51 
52     def get_last_active(self, target_user):
53         content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])
54         try:
55             if content_node.kind == content_kinds.TOPIC:
56                 return ContentSummaryLog.objects \
57                     .filter_by_topic(content_node) \
58                     .filter(user=target_user) \
59                     .latest('end_timestamp').end_timestamp
60             else:
61                 return ContentSummaryLog.objects \
62                     .filter(user=target_user) \
63                     .get(content_id=content_node.content_id).end_timestamp
64         except ContentSummaryLog.DoesNotExist:
65             return None
66 
67 
68 class ContentReportSerializer(serializers.ModelSerializer):
69     progress = serializers.SerializerMethodField()
70     last_active = serializers.SerializerMethodField()
71     parent = serializers.SerializerMethodField()
72 
73     class Meta:
74         model = ContentNode
75         fields = (
76             'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'parent',
77         )
78 
79     def get_progress(self, target_node):
80         kwargs = self.context['view'].kwargs
81         if target_node.kind == content_kinds.TOPIC:
82             kind_counts = target_node.get_descendant_kind_counts()
83             # filter logs by each kind under target node, and sum progress over logs
84             progress_query = ContentSummaryLog.objects \
85                 .filter_by_topic(target_node) \
86                 .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))
87             if kwargs.get('last_active_time'):
88                 progress_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))
89             progress = progress_query.values('kind') \
90                 .annotate(total_progress=Sum('progress'))
91             # add kind counts under this node to progress dict
92             for kind in progress:
93                 kind['node_count'] = kind_counts[kind['kind']]
94                 del kind_counts[kind['kind']]
95             # evaluate queryset so we can add data for kinds that do not have logs
96             progress = list(progress)
97             for key in kind_counts:
98                 progress.append({'kind': key, 'node_count': kind_counts[key], 'total_progress': 0})
99             return progress
100         else:
101             # filter logs by a specific leaf node and compute stats over queryset
102             leaf_node_stats_query = ContentSummaryLog.objects \
103                 .filter(content_id=target_node.content_id) \
104                 .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))
105             if kwargs.get('last_active_time'):
106                 leaf_node_stats_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))
107             leaf_node_stats = leaf_node_stats_query.aggregate(
108                 total_progress=Coalesce(Sum('progress'), V(0)),
109                 log_count_total=Coalesce(Count('pk'), V(0)),
110                 log_count_complete=Coalesce(Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())), V(0)))
111             return [leaf_node_stats]  # return as array for consistency in api
112 
113     def get_last_active(self, target_node):
114         kwargs = self.context['view'].kwargs
115         try:
116             if target_node.kind == content_kinds.TOPIC:
117                 return ContentSummaryLog.objects \
118                     .filter_by_topic(target_node) \
119                     .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \
120                     .latest('end_timestamp').end_timestamp
121             else:
122                 return ContentSummaryLog.objects \
123                     .filter(content_id=target_node.content_id) \
124                     .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \
125                     .latest('end_timestamp').end_timestamp
126         except ContentSummaryLog.DoesNotExist:
127             return None
128 
129     def get_parent(self, target_node):
130         # returns immediate parent
131         return target_node.get_ancestors().values('pk', 'title').last()
132 
133 
134 class ContentSummarySerializer(ContentReportSerializer):
135     ancestors = serializers.SerializerMethodField()
136     num_users = serializers.SerializerMethodField()
137 
138     class Meta:
139         model = ContentNode
140         fields = (
141             'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'ancestors', 'num_users',
142         )
143 
144     def get_ancestors(self, target_node):
145         """
146         in descending order (root ancestor first, immediate parent last)
147         """
148         return target_node.get_ancestors().values('pk', 'title')
149 
150     def get_num_users(self, target_node):
151         kwargs = self.context['view'].kwargs
152         return len(get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))
153
```
--- 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()

```

diff --git a/kolibri/plugins/coach/serializers.py b/kolibri/plugins/coach/serializers.py
--- a/kolibri/plugins/coach/serializers.py
+++ b/kolibri/plugins/coach/serializers.py
@@ -18,7 +18,7 @@
     class Meta:
         model = FacilityUser
         fields = (
-            'pk', 'full_name', 'progress', 'last_active',
+            'pk', 'username', 'full_name', 'progress', 'last_active',
         )
 
     def get_progress(self, target_user):

{"golden_diff": "diff --git a/kolibri/plugins/coach/serializers.py b/kolibri/plugins/coach/serializers.py\n--- a/kolibri/plugins/coach/serializers.py\n+++ b/kolibri/plugins/coach/serializers.py\n@@ -18,7 +18,7 @@\n     class Meta:\n         model = FacilityUser\n         fields = (\n-            'pk', 'full_name', 'progress', 'last_active',\n+            'pk', 'username', 'full_name', 'progress', 'last_active',\n         )\n \n     def get_progress(self, target_user):\n", "issue": "hide not-recent learners on 'coach - recent activity' tab\nSee similar issue for channels: https://github.com/learningequality/kolibri/pull/1406\r\n\r\nNow we need to do the same thing for when you drill deeper and reach the learners list. For example here, we're showing all learners regardless of whether or not they've had recent activity:\r\n\r\n![image](https://cloud.githubusercontent.com/assets/2367265/25878301/8d2f6c62-34de-11e7-96f7-836359e3bd28.png)\r\n\r\n\r\n\r\n\r\n\n", "before_files": [{"content": "from dateutil.parser import parse\n\nfrom django.db.models import Case, Count, F, IntegerField, Sum, Value as V, When\nfrom django.db.models.functions import Coalesce\nfrom kolibri.auth.models import FacilityUser\nfrom kolibri.content.models import ContentNode\nfrom kolibri.logger.models import ContentSummaryLog\nfrom le_utils.constants import content_kinds\nfrom rest_framework import serializers\n\nfrom .utils.return_users import get_members_or_user\n\n\nclass UserReportSerializer(serializers.ModelSerializer):\n    progress = serializers.SerializerMethodField()\n    last_active = serializers.SerializerMethodField()\n\n    class Meta:\n        model = FacilityUser\n        fields = (\n            'pk', 'full_name', 'progress', 'last_active',\n        )\n\n    def get_progress(self, target_user):\n        content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])\n        # progress details for a topic node and everything under it\n        if content_node.kind == content_kinds.TOPIC:\n            kind_counts = content_node.get_descendant_kind_counts()\n            topic_details = ContentSummaryLog.objects \\\n                .filter_by_topic(content_node) \\\n                .filter(user=target_user) \\\n                .values('kind') \\\n                .annotate(total_progress=Sum('progress')) \\\n                .annotate(log_count_total=Count('pk')) \\\n                .annotate(log_count_complete=Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())))\n            # evaluate queryset so we can add data for kinds that do not have logs\n            topic_details = list(topic_details)\n            for kind in topic_details:\n                del kind_counts[kind['kind']]\n            for key in kind_counts:\n                topic_details.append({'kind': key, 'total_progress': 0, 'log_count_total': 0, 'log_count_complete': 0})\n            return topic_details\n        else:\n            # progress details for a leaf node (exercise, video, etc.)\n            leaf_details = ContentSummaryLog.objects \\\n                .filter(user=target_user) \\\n                .filter(content_id=content_node.content_id) \\\n                .annotate(total_progress=F('progress')) \\\n                .values('kind', 'time_spent', 'total_progress')\n            return leaf_details if leaf_details else [{'kind': content_node.kind, 'time_spent': 0, 'total_progress': 0}]\n\n    def get_last_active(self, target_user):\n        content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])\n        try:\n            if content_node.kind == content_kinds.TOPIC:\n                return ContentSummaryLog.objects \\\n                    .filter_by_topic(content_node) \\\n                    .filter(user=target_user) \\\n                    .latest('end_timestamp').end_timestamp\n            else:\n                return ContentSummaryLog.objects \\\n                    .filter(user=target_user) \\\n                    .get(content_id=content_node.content_id).end_timestamp\n        except ContentSummaryLog.DoesNotExist:\n            return None\n\n\nclass ContentReportSerializer(serializers.ModelSerializer):\n    progress = serializers.SerializerMethodField()\n    last_active = serializers.SerializerMethodField()\n    parent = serializers.SerializerMethodField()\n\n    class Meta:\n        model = ContentNode\n        fields = (\n            'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'parent',\n        )\n\n    def get_progress(self, target_node):\n        kwargs = self.context['view'].kwargs\n        if target_node.kind == content_kinds.TOPIC:\n            kind_counts = target_node.get_descendant_kind_counts()\n            # filter logs by each kind under target node, and sum progress over logs\n            progress_query = ContentSummaryLog.objects \\\n                .filter_by_topic(target_node) \\\n                .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n            if kwargs.get('last_active_time'):\n                progress_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))\n            progress = progress_query.values('kind') \\\n                .annotate(total_progress=Sum('progress'))\n            # add kind counts under this node to progress dict\n            for kind in progress:\n                kind['node_count'] = kind_counts[kind['kind']]\n                del kind_counts[kind['kind']]\n            # evaluate queryset so we can add data for kinds that do not have logs\n            progress = list(progress)\n            for key in kind_counts:\n                progress.append({'kind': key, 'node_count': kind_counts[key], 'total_progress': 0})\n            return progress\n        else:\n            # filter logs by a specific leaf node and compute stats over queryset\n            leaf_node_stats_query = ContentSummaryLog.objects \\\n                .filter(content_id=target_node.content_id) \\\n                .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n            if kwargs.get('last_active_time'):\n                leaf_node_stats_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))\n            leaf_node_stats = leaf_node_stats_query.aggregate(\n                total_progress=Coalesce(Sum('progress'), V(0)),\n                log_count_total=Coalesce(Count('pk'), V(0)),\n                log_count_complete=Coalesce(Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())), V(0)))\n            return [leaf_node_stats]  # return as array for consistency in api\n\n    def get_last_active(self, target_node):\n        kwargs = self.context['view'].kwargs\n        try:\n            if target_node.kind == content_kinds.TOPIC:\n                return ContentSummaryLog.objects \\\n                    .filter_by_topic(target_node) \\\n                    .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \\\n                    .latest('end_timestamp').end_timestamp\n            else:\n                return ContentSummaryLog.objects \\\n                    .filter(content_id=target_node.content_id) \\\n                    .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \\\n                    .latest('end_timestamp').end_timestamp\n        except ContentSummaryLog.DoesNotExist:\n            return None\n\n    def get_parent(self, target_node):\n        # returns immediate parent\n        return target_node.get_ancestors().values('pk', 'title').last()\n\n\nclass ContentSummarySerializer(ContentReportSerializer):\n    ancestors = serializers.SerializerMethodField()\n    num_users = serializers.SerializerMethodField()\n\n    class Meta:\n        model = ContentNode\n        fields = (\n            'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'ancestors', 'num_users',\n        )\n\n    def get_ancestors(self, target_node):\n        \"\"\"\n        in descending order (root ancestor first, immediate parent last)\n        \"\"\"\n        return target_node.get_ancestors().values('pk', 'title')\n\n    def get_num_users(self, target_node):\n        kwargs = self.context['view'].kwargs\n        return len(get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n", "path": "kolibri/plugins/coach/serializers.py"}], "after_files": [{"content": "from dateutil.parser import parse\n\nfrom django.db.models import Case, Count, F, IntegerField, Sum, Value as V, When\nfrom django.db.models.functions import Coalesce\nfrom kolibri.auth.models import FacilityUser\nfrom kolibri.content.models import ContentNode\nfrom kolibri.logger.models import ContentSummaryLog\nfrom le_utils.constants import content_kinds\nfrom rest_framework import serializers\n\nfrom .utils.return_users import get_members_or_user\n\n\nclass UserReportSerializer(serializers.ModelSerializer):\n    progress = serializers.SerializerMethodField()\n    last_active = serializers.SerializerMethodField()\n\n    class Meta:\n        model = FacilityUser\n        fields = (\n            'pk', 'username', 'full_name', 'progress', 'last_active',\n        )\n\n    def get_progress(self, target_user):\n        content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])\n        # progress details for a topic node and everything under it\n        if content_node.kind == content_kinds.TOPIC:\n            kind_counts = content_node.get_descendant_kind_counts()\n            topic_details = ContentSummaryLog.objects \\\n                .filter_by_topic(content_node) \\\n                .filter(user=target_user) \\\n                .values('kind') \\\n                .annotate(total_progress=Sum('progress')) \\\n                .annotate(log_count_total=Count('pk')) \\\n                .annotate(log_count_complete=Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())))\n            # evaluate queryset so we can add data for kinds that do not have logs\n            topic_details = list(topic_details)\n            for kind in topic_details:\n                del kind_counts[kind['kind']]\n            for key in kind_counts:\n                topic_details.append({'kind': key, 'total_progress': 0, 'log_count_total': 0, 'log_count_complete': 0})\n            return topic_details\n        else:\n            # progress details for a leaf node (exercise, video, etc.)\n            leaf_details = ContentSummaryLog.objects \\\n                .filter(user=target_user) \\\n                .filter(content_id=content_node.content_id) \\\n                .annotate(total_progress=F('progress')) \\\n                .values('kind', 'time_spent', 'total_progress')\n            return leaf_details if leaf_details else [{'kind': content_node.kind, 'time_spent': 0, 'total_progress': 0}]\n\n    def get_last_active(self, target_user):\n        content_node = ContentNode.objects.get(pk=self.context['view'].kwargs['content_node_id'])\n        try:\n            if content_node.kind == content_kinds.TOPIC:\n                return ContentSummaryLog.objects \\\n                    .filter_by_topic(content_node) \\\n                    .filter(user=target_user) \\\n                    .latest('end_timestamp').end_timestamp\n            else:\n                return ContentSummaryLog.objects \\\n                    .filter(user=target_user) \\\n                    .get(content_id=content_node.content_id).end_timestamp\n        except ContentSummaryLog.DoesNotExist:\n            return None\n\n\nclass ContentReportSerializer(serializers.ModelSerializer):\n    progress = serializers.SerializerMethodField()\n    last_active = serializers.SerializerMethodField()\n    parent = serializers.SerializerMethodField()\n\n    class Meta:\n        model = ContentNode\n        fields = (\n            'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'parent',\n        )\n\n    def get_progress(self, target_node):\n        kwargs = self.context['view'].kwargs\n        if target_node.kind == content_kinds.TOPIC:\n            kind_counts = target_node.get_descendant_kind_counts()\n            # filter logs by each kind under target node, and sum progress over logs\n            progress_query = ContentSummaryLog.objects \\\n                .filter_by_topic(target_node) \\\n                .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n            if kwargs.get('last_active_time'):\n                progress_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))\n            progress = progress_query.values('kind') \\\n                .annotate(total_progress=Sum('progress'))\n            # add kind counts under this node to progress dict\n            for kind in progress:\n                kind['node_count'] = kind_counts[kind['kind']]\n                del kind_counts[kind['kind']]\n            # evaluate queryset so we can add data for kinds that do not have logs\n            progress = list(progress)\n            for key in kind_counts:\n                progress.append({'kind': key, 'node_count': kind_counts[key], 'total_progress': 0})\n            return progress\n        else:\n            # filter logs by a specific leaf node and compute stats over queryset\n            leaf_node_stats_query = ContentSummaryLog.objects \\\n                .filter(content_id=target_node.content_id) \\\n                .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n            if kwargs.get('last_active_time'):\n                leaf_node_stats_query.filter(end_timestamp__gte=parse(kwargs.get('last_active_time')))\n            leaf_node_stats = leaf_node_stats_query.aggregate(\n                total_progress=Coalesce(Sum('progress'), V(0)),\n                log_count_total=Coalesce(Count('pk'), V(0)),\n                log_count_complete=Coalesce(Sum(Case(When(progress=1, then=1), default=0, output_field=IntegerField())), V(0)))\n            return [leaf_node_stats]  # return as array for consistency in api\n\n    def get_last_active(self, target_node):\n        kwargs = self.context['view'].kwargs\n        try:\n            if target_node.kind == content_kinds.TOPIC:\n                return ContentSummaryLog.objects \\\n                    .filter_by_topic(target_node) \\\n                    .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \\\n                    .latest('end_timestamp').end_timestamp\n            else:\n                return ContentSummaryLog.objects \\\n                    .filter(content_id=target_node.content_id) \\\n                    .filter(user__in=get_members_or_user(kwargs['collection_kind'], kwargs['collection_id'])) \\\n                    .latest('end_timestamp').end_timestamp\n        except ContentSummaryLog.DoesNotExist:\n            return None\n\n    def get_parent(self, target_node):\n        # returns immediate parent\n        return target_node.get_ancestors().values('pk', 'title').last()\n\n\nclass ContentSummarySerializer(ContentReportSerializer):\n    ancestors = serializers.SerializerMethodField()\n    num_users = serializers.SerializerMethodField()\n\n    class Meta:\n        model = ContentNode\n        fields = (\n            'pk', 'content_id', 'title', 'progress', 'kind', 'last_active', 'ancestors', 'num_users',\n        )\n\n    def get_ancestors(self, target_node):\n        \"\"\"\n        in descending order (root ancestor first, immediate parent last)\n        \"\"\"\n        return target_node.get_ancestors().values('pk', 'title')\n\n    def get_num_users(self, target_node):\n        kwargs = self.context['view'].kwargs\n        return len(get_members_or_user(kwargs['collection_kind'], kwargs['collection_id']))\n", "path": "kolibri/plugins/coach/serializers.py"}]}