From b73f4aa6708544a53ad632ce97a2bb20de8b1d28 Mon Sep 17 00:00:00 2001 From: faucomte97 Date: Wed, 20 Nov 2024 15:37:55 +0000 Subject: [PATCH 1/4] fix: Allow requester indies to retrieve school --- .../user/permissions/is_independent.py | 31 ++++++++++++++- codeforlife/user/views/school.py | 23 ++++++++--- codeforlife/user/views/school_test.py | 39 +++++++++++++++++-- 3 files changed, 83 insertions(+), 10 deletions(-) diff --git a/codeforlife/user/permissions/is_independent.py b/codeforlife/user/permissions/is_independent.py index 6e1d0d1d..7ce63372 100644 --- a/codeforlife/user/permissions/is_independent.py +++ b/codeforlife/user/permissions/is_independent.py @@ -4,13 +4,31 @@ Created on 12/12/2023 at 13:55:47(+00:00). """ -from ...permissions import IsAuthenticated +import typing as t + from ..models import User +from ...permissions import IsAuthenticated class IsIndependent(IsAuthenticated): """Request's user must be independent.""" + def __init__( + self, + is_requesting_to_join_class: t.Optional[bool] = None, + ): + # pylint: disable=line-too-long + """Initialize permission. + + Args: + is_requesting_to_join_class: Check if the independent is (not) + requesting to join a class. If None, don't check. + """ + # pylint: enable=line-too-long + super().__init__() + + self.is_requesting_to_join_class = is_requesting_to_join_class + def has_permission(self, request, view): user = request.user return ( @@ -19,4 +37,15 @@ def has_permission(self, request, view): and user.teacher is None and user.student is not None and user.student.class_field is None + and ( + self.is_requesting_to_join_class is None + or ( + self.is_requesting_to_join_class + and user.student.pending_class_request is not None + ) + or ( + not self.is_requesting_to_join_class + and user.student.pending_class_request is None + ) + ) ) diff --git a/codeforlife/user/views/school.py b/codeforlife/user/views/school.py index f9024f4d..8b99e660 100644 --- a/codeforlife/user/views/school.py +++ b/codeforlife/user/views/school.py @@ -7,7 +7,7 @@ from ...views import ModelViewSet from ..models import School from ..models import User as RequestUser -from ..permissions import IsStudent, IsTeacher +from ..permissions import IsStudent, IsTeacher, IsIndependent from ..serializers import SchoolSerializer @@ -21,16 +21,27 @@ def get_permissions(self): if self.action == "list": return [AllowNone()] - return [OR(IsStudent(), IsTeacher(in_school=True))] + return [ + OR( + OR(IsStudent(), IsTeacher(in_school=True)), + IsIndependent(is_requesting_to_join_class=True), + ) + ] # pylint: disable-next=missing-function-docstring def get_queryset(self): user = self.request.auth_user if user.student: - return School.objects.filter( - # TODO: should be user.student.school_id - id=user.student.class_field.teacher.school_id - ) + if user.student.class_field: + return School.objects.filter( + # TODO: should be user.student.school_id + id=user.student.class_field.teacher.school_id + ) + if user.student.pending_class_request: + return School.objects.filter( + # TODO: should be user.requesting_to_join_class.school_id + id=user.student.pending_class_request.teacher.school_id + ) user = self.request.school_teacher_user return School.objects.filter(id=user.teacher.school_id) diff --git a/codeforlife/user/views/school_test.py b/codeforlife/user/views/school_test.py index 9c59e6c1..328ed701 100644 --- a/codeforlife/user/views/school_test.py +++ b/codeforlife/user/views/school_test.py @@ -5,8 +5,14 @@ from ...permissions import OR, AllowNone from ...tests import ModelViewSetTestCase -from ..models import School, SchoolTeacherUser, StudentUser, User -from ..permissions import IsStudent, IsTeacher +from ..models import ( + IndependentUser, + School, + SchoolTeacherUser, + StudentUser, + User, +) +from ..permissions import IsIndependent, IsStudent, IsTeacher from ..views import SchoolViewSet RequestUser = User @@ -16,6 +22,7 @@ class TestSchoolViewSet(ModelViewSetTestCase[RequestUser, School]): basename = "school" model_view_set_class = SchoolViewSet + fixtures = ["school_1", "independent"] # test: get permissions @@ -29,7 +36,12 @@ def test_get_permissions__list(self): def test_get_permissions__retrieve(self): """Only student and school-teachers can retrieve a school.""" self.assert_get_permissions( - permissions=[OR(IsStudent(), IsTeacher(in_school=True))], + permissions=[ + OR( + OR(IsStudent(), IsTeacher(in_school=True)), + IsIndependent(is_requesting_to_join_class=True), + ) + ], action="retrieve", ) @@ -55,6 +67,27 @@ def test_get_queryset__student(self): request=self.client.request_factory.get(user=user), ) + def test_get_queryset__independent(self): + """ + An independent-user can only target the school they are requesting + to join. + """ + user = IndependentUser.objects.get(username="indy.requester@email.com") + assert user + + # klass = Class.objects.first() + # assert klass + + # user.student.pending_class_request = klass + # user.student.save() + + # assert user.student.pending_class_request is not None + + self.assert_get_queryset( + values=[user.student.pending_class_request.teacher.school], + request=self.client.request_factory.get(user=user), + ) + # test: actions def test_retrieve(self): From 5b8915924bdf23e827bbd03c16937b6cc1852e32 Mon Sep 17 00:00:00 2001 From: faucomte97 Date: Wed, 20 Nov 2024 15:38:50 +0000 Subject: [PATCH 2/4] Remove comments --- codeforlife/user/views/school_test.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/codeforlife/user/views/school_test.py b/codeforlife/user/views/school_test.py index 328ed701..5b66ff4d 100644 --- a/codeforlife/user/views/school_test.py +++ b/codeforlife/user/views/school_test.py @@ -75,14 +75,6 @@ def test_get_queryset__independent(self): user = IndependentUser.objects.get(username="indy.requester@email.com") assert user - # klass = Class.objects.first() - # assert klass - - # user.student.pending_class_request = klass - # user.student.save() - - # assert user.student.pending_class_request is not None - self.assert_get_queryset( values=[user.student.pending_class_request.teacher.school], request=self.client.request_factory.get(user=user), From 7d4d747e9c87d0b78f9983c20591a4899a14a887 Mon Sep 17 00:00:00 2001 From: faucomte97 Date: Wed, 20 Nov 2024 15:41:03 +0000 Subject: [PATCH 3/4] fix imports --- codeforlife/user/permissions/is_independent.py | 2 +- codeforlife/user/views/school.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/codeforlife/user/permissions/is_independent.py b/codeforlife/user/permissions/is_independent.py index 7ce63372..0873c013 100644 --- a/codeforlife/user/permissions/is_independent.py +++ b/codeforlife/user/permissions/is_independent.py @@ -6,8 +6,8 @@ import typing as t -from ..models import User from ...permissions import IsAuthenticated +from ..models import User class IsIndependent(IsAuthenticated): diff --git a/codeforlife/user/views/school.py b/codeforlife/user/views/school.py index 8b99e660..1f1082ac 100644 --- a/codeforlife/user/views/school.py +++ b/codeforlife/user/views/school.py @@ -7,7 +7,7 @@ from ...views import ModelViewSet from ..models import School from ..models import User as RequestUser -from ..permissions import IsStudent, IsTeacher, IsIndependent +from ..permissions import IsIndependent, IsStudent, IsTeacher from ..serializers import SchoolSerializer From 036b00ad019fd7f4743a0051140c07cbfd545df7 Mon Sep 17 00:00:00 2001 From: faucomte97 Date: Thu, 21 Nov 2024 23:46:50 +0000 Subject: [PATCH 4/4] Feedback --- codeforlife/user/views/school.py | 9 ++++----- codeforlife/user/views/school_test.py | 4 +++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/codeforlife/user/views/school.py b/codeforlife/user/views/school.py index 1f1082ac..804ddaef 100644 --- a/codeforlife/user/views/school.py +++ b/codeforlife/user/views/school.py @@ -32,16 +32,15 @@ def get_permissions(self): def get_queryset(self): user = self.request.auth_user if user.student: - if user.student.class_field: - return School.objects.filter( - # TODO: should be user.student.school_id - id=user.student.class_field.teacher.school_id - ) if user.student.pending_class_request: return School.objects.filter( # TODO: should be user.requesting_to_join_class.school_id id=user.student.pending_class_request.teacher.school_id ) + return School.objects.filter( + # TODO: should be user.student.school_id + id=user.student.class_field.teacher.school_id + ) user = self.request.school_teacher_user return School.objects.filter(id=user.teacher.school_id) diff --git a/codeforlife/user/views/school_test.py b/codeforlife/user/views/school_test.py index 5b66ff4d..e569f51e 100644 --- a/codeforlife/user/views/school_test.py +++ b/codeforlife/user/views/school_test.py @@ -72,7 +72,9 @@ def test_get_queryset__independent(self): An independent-user can only target the school they are requesting to join. """ - user = IndependentUser.objects.get(username="indy.requester@email.com") + user = IndependentUser.objects.filter( + new_student__pending_class_request__isnull=False + ).first() assert user self.assert_get_queryset(