Skip to content

Commit 06ee5a5

Browse files
committed
Filter out soft deleted users from lessons, quizzes and groups assignments
1 parent 7b4ecdc commit 06ee5a5

File tree

5 files changed

+64
-6
lines changed

5 files changed

+64
-6
lines changed

kolibri/core/auth/api.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from django.core.exceptions import ObjectDoesNotExist
1515
from django.core.exceptions import PermissionDenied
1616
from django.core.exceptions import ValidationError
17+
from django.db.models import Exists
1718
from django.db.models import Func
1819
from django.db.models import OuterRef
1920
from django.db.models import Q
@@ -1052,7 +1053,14 @@ class LearnerGroupViewSet(ValuesViewset):
10521053
values = ("id", "name", "parent", "user_ids")
10531054

10541055
def annotate_queryset(self, queryset):
1055-
return annotate_array_aggregate(queryset, user_ids="membership__user__id")
1056+
non_deleted_users_subquery = Exists(
1057+
FacilityUser.objects.filter(id=OuterRef("membership__user__id"))
1058+
)
1059+
return annotate_array_aggregate(
1060+
queryset,
1061+
filter=Q(non_deleted_users_subquery),
1062+
user_ids="membership__user__id",
1063+
)
10561064

10571065

10581066
class BaseSignUpViewSet(viewsets.GenericViewSet, CreateModelMixin):

kolibri/core/exams/api.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import datetime
22

3+
from django.db.models import Exists
4+
from django.db.models import OuterRef
5+
from django.db.models import Q
36
from django.http import Http404
47
from django.utils.timezone import now
58
from django_filters.rest_framework import DjangoFilterBackend
@@ -11,6 +14,7 @@
1114
from kolibri.core.auth.api import KolibriAuthPermissions
1215
from kolibri.core.auth.api import KolibriAuthPermissionsFilter
1316
from kolibri.core.auth.constants.collection_kinds import ADHOCLEARNERSGROUP
17+
from kolibri.core.auth.models import FacilityUser
1418
from kolibri.core.content.models import ContentNode
1519
from kolibri.core.content.utils.annotation import total_file_size
1620
from kolibri.core.exams import models
@@ -103,6 +107,14 @@ def annotate_queryset(self, queryset):
103107

104108
def serialize_draft(self, queryset):
105109
objects = queryset.values(*self.draft_values)
110+
111+
all_exam_learners_set = {
112+
learner_id for obj in objects for learner_id in obj.get("learner_ids", [])
113+
}
114+
non_deleted_learners = FacilityUser.objects.filter(
115+
id__in=all_exam_learners_set
116+
).values_list("id", flat=True)
117+
106118
for item in objects:
107119
# Set the draft flag to True
108120
item["draft"] = True
@@ -111,6 +123,12 @@ def serialize_draft(self, queryset):
111123
item["archive"] = False
112124
item["date_archived"] = None
113125
item["date_activated"] = None
126+
# Filter out any deleted learners
127+
item["learner_ids"] = [
128+
learner_id
129+
for learner_id in item.get("learner_ids", [])
130+
if learner_id in non_deleted_learners
131+
]
114132
return objects
115133

116134
def filter_querysets(self, exam_queryset, draft_queryset):
@@ -230,8 +248,15 @@ def consolidate(self, items, queryset):
230248
adhoc_assignments = models.ExamAssignment.objects.filter(
231249
exam_id__in=exam_ids, collection__kind=ADHOCLEARNERSGROUP
232250
)
251+
non_deleted_users_subquery = Exists(
252+
FacilityUser.objects.filter(
253+
id=OuterRef("collection__membership__user_id")
254+
)
255+
)
233256
adhoc_assignments = annotate_array_aggregate(
234-
adhoc_assignments, learner_ids="collection__membership__user_id"
257+
adhoc_assignments,
258+
filter=Q(non_deleted_users_subquery),
259+
learner_ids="collection__membership__user_id",
235260
)
236261
adhoc_assignments = {
237262
a["exam"]: a

kolibri/core/lessons/viewsets.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
from django.db.models import Exists
2+
from django.db.models import OuterRef
3+
from django.db.models import Q
14
from django_filters.rest_framework import DjangoFilterBackend
25
from rest_framework.decorators import action
36
from rest_framework.response import Response
@@ -7,6 +10,7 @@
710
from kolibri.core.auth.api import KolibriAuthPermissions
811
from kolibri.core.auth.api import KolibriAuthPermissionsFilter
912
from kolibri.core.auth.constants.collection_kinds import ADHOCLEARNERSGROUP
13+
from kolibri.core.auth.models import FacilityUser
1014
from kolibri.core.content.models import ContentNode
1115
from kolibri.core.content.utils.annotation import total_file_size
1216
from kolibri.core.lessons.models import Lesson
@@ -77,8 +81,15 @@ def consolidate(self, items, queryset):
7781
adhoc_assignments = LessonAssignment.objects.filter(
7882
lesson_id__in=lesson_ids, collection__kind=ADHOCLEARNERSGROUP
7983
)
84+
non_deleted_users_subquery = Exists(
85+
FacilityUser.objects.filter(
86+
id=OuterRef("collection__membership__user_id")
87+
)
88+
)
8089
adhoc_assignments = annotate_array_aggregate(
81-
adhoc_assignments, learner_ids="collection__membership__user_id"
90+
adhoc_assignments,
91+
filter=Q(non_deleted_users_subquery),
92+
learner_ids="collection__membership__user_id",
8293
)
8394
adhoc_assignments = {
8495
a["lesson"]: a

kolibri/core/query.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,14 @@ def get_source_field(model, field_path):
7979

8080
def annotate_array_aggregate(queryset, **kwargs):
8181
model = queryset.model
82+
query_filter = kwargs.pop("filter", None)
8283
if connection.vendor == "postgresql" and NotNullArrayAgg is not None:
8384
return queryset.annotate(
8485
**{
8586
target: NotNullArrayAgg(
86-
source, result_field=get_source_field(model, source)
87+
source,
88+
result_field=get_source_field(model, source),
89+
filter=query_filter,
8790
)
8891
for target, source in kwargs.items()
8992
}
@@ -92,7 +95,11 @@ def annotate_array_aggregate(queryset, **kwargs):
9295
# is called by row and not across the entire queryset.
9396
return queryset.values("pk").annotate(
9497
**{
95-
target: GroupConcat(source, result_field=get_source_field(model, source))
98+
target: GroupConcat(
99+
source,
100+
result_field=get_source_field(model, source),
101+
filter=query_filter,
102+
)
96103
for target, source in kwargs.items()
97104
}
98105
)

kolibri/plugins/coach/class_summary_api.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,14 @@ def serialize_coach_assigned_quiz_status(exam_data):
242242

243243

244244
def serialize_groups(queryset):
245-
queryset = annotate_array_aggregate(queryset, member_ids="membership__user__id")
245+
non_deleted_users_subquery = Exists(
246+
FacilityUser.objects.filter(id=OuterRef("membership__user__id"))
247+
)
248+
queryset = annotate_array_aggregate(
249+
queryset,
250+
filter=Q(non_deleted_users_subquery),
251+
member_ids="membership__user__id",
252+
)
246253
return list(queryset.values("id", "name", "member_ids"))
247254

248255

0 commit comments

Comments
 (0)