From 86d3644ca60dfe02e26fbe172f4ea805541182a0 Mon Sep 17 00:00:00 2001 From: Reto Aebersold Date: Mon, 11 Dec 2023 13:39:17 +0100 Subject: [PATCH] feat: mentor invite --- .../learning_mentor/serializers.py | 16 +++++++++ .../learning_mentor/tests/test_invitation.py | 30 ++++++++++++++-- server/vbv_lernwelt/learning_mentor/urls.py | 5 +-- server/vbv_lernwelt/learning_mentor/views.py | 35 ++++++++++++------- .../notify/email/email_services.py | 6 +++- 5 files changed, 74 insertions(+), 18 deletions(-) diff --git a/server/vbv_lernwelt/learning_mentor/serializers.py b/server/vbv_lernwelt/learning_mentor/serializers.py index 6904d5b2..146f1911 100644 --- a/server/vbv_lernwelt/learning_mentor/serializers.py +++ b/server/vbv_lernwelt/learning_mentor/serializers.py @@ -1,5 +1,7 @@ from rest_framework import serializers +from vbv_lernwelt.learning_mentor.models import MentorInvitation + class PraxisAssignmentCompletionSerializer(serializers.Serializer): status = serializers.SerializerMethodField() @@ -17,3 +19,17 @@ class PraxisAssignmentStatusSerializer(serializers.Serializer): circle_id = serializers.CharField() pending_evaluations = serializers.IntegerField() completions = PraxisAssignmentCompletionSerializer(many=True) + + +class InvitationSerializer(serializers.ModelSerializer): + class Meta: + model = MentorInvitation + fields = ["id", "email"] + read_only_fields = ["id"] + + def create(self, validated_data): + participant = self.context["course_session_user"] + invitation, _ = MentorInvitation.objects.get_or_create( + email=validated_data["email"], defaults={"participant": participant} + ) + return invitation diff --git a/server/vbv_lernwelt/learning_mentor/tests/test_invitation.py b/server/vbv_lernwelt/learning_mentor/tests/test_invitation.py index aefff4c8..a5022eff 100644 --- a/server/vbv_lernwelt/learning_mentor/tests/test_invitation.py +++ b/server/vbv_lernwelt/learning_mentor/tests/test_invitation.py @@ -39,7 +39,7 @@ class LearningMentorInvitationTest(APITestCase): def test_create_invitation(self, mock_send_mail) -> None: # GIVEN self.client.force_login(self.participant) - participant_cs_user = add_course_session_user( + add_course_session_user( self.course_session, self.participant, role=CourseSessionUser.Role.MEMBER, @@ -53,7 +53,7 @@ class LearningMentorInvitationTest(APITestCase): response = self.client.post(invite_url, data={"email": email}) # THEN - invitation_id = response.data["invitation"] + invitation_id = response.data["id"] self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertTrue( @@ -74,6 +74,32 @@ class LearningMentorInvitationTest(APITestCase): fail_silently=True, ) + def test_list_invitations(self) -> None: + # GIVEN + self.client.force_login(self.participant) + participant_cs_user = add_course_session_user( + self.course_session, + self.participant, + role=CourseSessionUser.Role.MEMBER, + ) + email = "test@exmaple.com" + + MentorInvitation.objects.create(participant=participant_cs_user, email=email) + + list_url = reverse( + "list_invitations", kwargs={"course_session_id": self.course_session.id} + ) + + # WHEN + response = self.client.get(list_url) + + # THEN + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.data, + [{"id": str(MentorInvitation.objects.get(email=email).id), "email": email}], + ) + def test_accept_invitation(self) -> None: # GIVEN invitee = create_user("invitee") diff --git a/server/vbv_lernwelt/learning_mentor/urls.py b/server/vbv_lernwelt/learning_mentor/urls.py index 0f184849..3859bafc 100644 --- a/server/vbv_lernwelt/learning_mentor/urls.py +++ b/server/vbv_lernwelt/learning_mentor/urls.py @@ -4,6 +4,7 @@ from . import views urlpatterns = [ path("summary", views.mentor_summary, name="mentor_summary"), - path("invite", views.create_invitation, name="create_invitation"), - path("accept", views.accept_invitation, name="accept_invitation"), + path("invitations", views.list_invitations, name="list_invitations"), + path("invitations/create", views.create_invitation, name="create_invitation"), + path("invitations/accept", views.accept_invitation, name="accept_invitation"), ] diff --git a/server/vbv_lernwelt/learning_mentor/views.py b/server/vbv_lernwelt/learning_mentor/views.py index 8729bac2..f381d8d5 100644 --- a/server/vbv_lernwelt/learning_mentor/views.py +++ b/server/vbv_lernwelt/learning_mentor/views.py @@ -1,4 +1,4 @@ -from rest_framework import permissions +from rest_framework import permissions, status from rest_framework.decorators import api_view, permission_classes from rest_framework.generics import get_object_or_404 from rest_framework.permissions import IsAuthenticated @@ -11,7 +11,10 @@ from vbv_lernwelt.learning_mentor.content.praxis_assignment import ( get_praxis_assignments, ) from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation -from vbv_lernwelt.learning_mentor.serializers import PraxisAssignmentStatusSerializer +from vbv_lernwelt.learning_mentor.serializers import ( + InvitationSerializer, + PraxisAssignmentStatusSerializer, +) from vbv_lernwelt.learnpath.models import Circle from vbv_lernwelt.notify.email.email_services import EmailTemplate, send_email @@ -51,9 +54,15 @@ class CourseSessionMember(permissions.BasePermission): @api_view(["GET"]) -@permission_classes([IsAuthenticated]) +@permission_classes([IsAuthenticated, CourseSessionMember]) def list_invitations(request, course_session_id: int): - pass + course_session = get_object_or_404(CourseSession, id=course_session_id) + course_session_user = get_object_or_404( + CourseSessionUser, user=request.user, course_session=course_session + ) + snippets = MentorInvitation.objects.filter(participant=course_session_user) + serializer = InvitationSerializer(snippets, many=True) + return Response(serializer.data) @api_view(["POST"]) @@ -66,19 +75,19 @@ def create_invitation(request, course_session_id: int): CourseSessionUser, user=user, course_session=course_session ) - email = request.data.get("email") - if not email: - return Response({"error": "email is required"}, status=400) - - invitation, _ = MentorInvitation.objects.get_or_create( - email=email, - participant=course_session_user, + serializer = InvitationSerializer( + data=request.data, context={"course_session_user": course_session_user} ) + if not serializer.is_valid(): + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + invitation = serializer.save() + target_url = f"/lernbegleitung/{course_session_id}/invitation/{invitation.id}" send_email( - recipient_email=email, + recipient_email=invitation.email, template=EmailTemplate.LEARNING_MENTOR_INVITATION, template_data={ "inviter_name": f"{user.first_name} {user.last_name}", @@ -89,7 +98,7 @@ def create_invitation(request, course_session_id: int): fail_silently=True, ) - return Response({"invitation": invitation.id}) + return Response(serializer.data) @api_view(["POST"]) diff --git a/server/vbv_lernwelt/notify/email/email_services.py b/server/vbv_lernwelt/notify/email/email_services.py index ed36f748..6bc0c42a 100644 --- a/server/vbv_lernwelt/notify/email/email_services.py +++ b/server/vbv_lernwelt/notify/email/email_services.py @@ -63,7 +63,11 @@ class EmailTemplate(Enum): NEW_FEEDBACK = {"de": "d-40fb94d5149949e7b8e7ddfcf0fcfdde"} # VBV - Lernbegleitung Einladung - LEARNING_MENTOR_INVITATION = {"de": "", "fr": "", "it": ""} + LEARNING_MENTOR_INVITATION = { + "de": "d-8c862afde62748b6b8410887eeee89d8", + "fr": "d-7451e3c858954c15a9f410fa9d92dc06", + "it": "d-30c6aa9accda4973a940dd25703cb4a9", + } def send_email(