feat: mentor invitation URLs
This commit is contained in:
parent
3205eac33f
commit
16a6334802
|
|
@ -53,7 +53,6 @@ from vbv_lernwelt.importer.views import (
|
|||
coursesessions_trainers_import,
|
||||
t2l_sync,
|
||||
)
|
||||
from vbv_lernwelt.learning_mentor.views import mentor_summary
|
||||
from vbv_lernwelt.notify.views import email_notification_settings
|
||||
from wagtail import urls as wagtail_urls
|
||||
from wagtail.admin import urls as wagtailadmin_urls
|
||||
|
|
@ -126,9 +125,7 @@ urlpatterns = [
|
|||
request_course_completion_for_user,
|
||||
name="request_course_completion_for_user"),
|
||||
|
||||
path(r"api/mentor/<signed_int:course_session_id>/",
|
||||
mentor_summary,
|
||||
name="mentor_summary"),
|
||||
path("api/mentor/<signed_int:course_session_id>/", include("vbv_lernwelt.learning_mentor.urls")),
|
||||
|
||||
# assignment
|
||||
path(
|
||||
|
|
|
|||
|
|
@ -48,6 +48,17 @@ def is_course_session_expert(user, course_session_id: int):
|
|||
return is_supervisor or is_expert
|
||||
|
||||
|
||||
def is_course_session_member(user, course_session_id: int | None = None):
|
||||
if course_session_id is None:
|
||||
return False
|
||||
|
||||
return CourseSessionUser.objects.filter(
|
||||
course_session_id=course_session_id,
|
||||
user=user,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
).exists()
|
||||
|
||||
|
||||
def can_evaluate_assignments(user, course_session_id: int):
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -1,31 +1,55 @@
|
|||
# Generated by Django 3.2.20 on 2023-12-07 13:46
|
||||
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
import django.db.models.deletion
|
||||
import django_extensions.db.fields
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('course', '0005_course_enable_circle_documents'),
|
||||
('learning_mentor', '0001_initial'),
|
||||
("course", "0005_course_enable_circle_documents"),
|
||||
("learning_mentor", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MentorInvitation',
|
||||
name="MentorInvitation",
|
||||
fields=[
|
||||
('created', django_extensions.db.fields.CreationDateTimeField(auto_now_add=True, verbose_name='created')),
|
||||
('modified', django_extensions.db.fields.ModificationDateTimeField(auto_now=True, verbose_name='modified')),
|
||||
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||
('email', models.EmailField(max_length=254)),
|
||||
('participant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.coursesessionuser')),
|
||||
(
|
||||
"created",
|
||||
django_extensions.db.fields.CreationDateTimeField(
|
||||
auto_now_add=True, verbose_name="created"
|
||||
),
|
||||
),
|
||||
(
|
||||
"modified",
|
||||
django_extensions.db.fields.ModificationDateTimeField(
|
||||
auto_now=True, verbose_name="modified"
|
||||
),
|
||||
),
|
||||
(
|
||||
"id",
|
||||
models.UUIDField(
|
||||
default=uuid.uuid4,
|
||||
editable=False,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
),
|
||||
),
|
||||
("email", models.EmailField(max_length=254)),
|
||||
(
|
||||
"participant",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="course.coursesessionuser",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Mentor Invitation',
|
||||
'verbose_name_plural': 'Mentor Invitations',
|
||||
"verbose_name": "Mentor Invitation",
|
||||
"verbose_name_plural": "Mentor Invitations",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,18 +4,23 @@ from django.db import migrations
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('learning_mentor', '0002_mentorinvitation'),
|
||||
("learning_mentor", "0002_mentorinvitation"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='learningmentor',
|
||||
options={'verbose_name': 'Lernbegleiter', 'verbose_name_plural': 'Lernbegleiter'},
|
||||
name="learningmentor",
|
||||
options={
|
||||
"verbose_name": "Lernbegleiter",
|
||||
"verbose_name_plural": "Lernbegleiter",
|
||||
},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='mentorinvitation',
|
||||
options={'verbose_name': 'Lernbegleiter Einladung', 'verbose_name_plural': 'Lernbegleiter Einladungen'},
|
||||
name="mentorinvitation",
|
||||
options={
|
||||
"verbose_name": "Lernbegleiter Einladung",
|
||||
"verbose_name_plural": "Lernbegleiter Einladungen",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 3.2.20 on 2023-12-11 09:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("course", "0005_course_enable_circle_documents"),
|
||||
("learning_mentor", "0003_auto_20231207_1448"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="mentorinvitation",
|
||||
unique_together={("email", "participant")},
|
||||
),
|
||||
]
|
||||
|
|
@ -33,9 +33,7 @@ class LearningMentor(models.Model):
|
|||
class MentorInvitation(TimeStampedModel):
|
||||
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
||||
email = models.EmailField()
|
||||
participant = models.ForeignKey(
|
||||
CourseSessionUser, on_delete=models.CASCADE
|
||||
)
|
||||
participant = models.ForeignKey(CourseSessionUser, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.email} ({self.participant})"
|
||||
|
|
@ -43,3 +41,4 @@ class MentorInvitation(TimeStampedModel):
|
|||
class Meta:
|
||||
verbose_name = "Lernbegleiter Einladung"
|
||||
verbose_name_plural = "Lernbegleiter Einladungen"
|
||||
unique_together = [["email", "participant"]]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
from django.urls import reverse
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from vbv_lernwelt.course.creators.test_utils import (
|
||||
add_course_session_user,
|
||||
create_course,
|
||||
create_course_session,
|
||||
create_user,
|
||||
)
|
||||
from vbv_lernwelt.course.models import CourseSessionUser
|
||||
|
||||
|
||||
class LearningMentorInvitationTest(APITestCase):
|
||||
def setUp(self) -> None:
|
||||
self.course, self.course_page = create_course("Test Course")
|
||||
self.course_session = create_course_session(course=self.course, title="Test VV")
|
||||
|
||||
self.participant = create_user("participant")
|
||||
|
||||
def test_create_invitation_not_member(self) -> None:
|
||||
# GIVEN
|
||||
self.client.force_login(self.participant)
|
||||
invite_url = reverse(
|
||||
"create_invitation", kwargs={"course_session_id": self.course_session.id}
|
||||
)
|
||||
|
||||
# WHEN
|
||||
response = self.client.post(invite_url)
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||
|
||||
def test_create_invitation(self) -> None:
|
||||
# GIVEN
|
||||
self.client.force_login(self.participant)
|
||||
add_course_session_user(
|
||||
self.course_session,
|
||||
self.participant,
|
||||
role=CourseSessionUser.Role.MEMBER,
|
||||
)
|
||||
invite_url = reverse(
|
||||
"create_invitation", kwargs={"course_session_id": self.course_session.id}
|
||||
)
|
||||
|
||||
# WHEN
|
||||
response = self.client.post(invite_url, data={"email": "test@example.com"})
|
||||
|
||||
# THEN
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
urlpatterns = [
|
||||
path("summary", views.mentor_summary, name="mentor_summary"),
|
||||
path("invite", views.create_invitation, name="create_invitation"),
|
||||
]
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
from django.shortcuts import redirect
|
||||
from rest_framework import permissions
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.generics import get_object_or_404
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
|
||||
from vbv_lernwelt.core.serializers import UserSerializer
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.iam.permissions import is_course_session_member
|
||||
from vbv_lernwelt.learning_mentor.content.praxis_assignment import (
|
||||
get_praxis_assignments,
|
||||
)
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor
|
||||
from vbv_lernwelt.learning_mentor.models import LearningMentor, MentorInvitation
|
||||
from vbv_lernwelt.learning_mentor.serializers import PraxisAssignmentStatusSerializer
|
||||
from vbv_lernwelt.learnpath.models import Circle
|
||||
|
||||
|
|
@ -41,6 +43,13 @@ def mentor_summary(request, course_session_id: int):
|
|||
)
|
||||
|
||||
|
||||
class CourseSessionMember(permissions.BasePermission):
|
||||
def has_permission(self, request, view):
|
||||
return is_course_session_member(
|
||||
request.user, view.kwargs.get("course_session_id")
|
||||
)
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
@permission_classes([IsAuthenticated])
|
||||
def list_invitations(request, course_session_id: int):
|
||||
|
|
@ -48,12 +57,23 @@ def list_invitations(request, course_session_id: int):
|
|||
|
||||
|
||||
@api_view(["POST"])
|
||||
@permission_classes([IsAuthenticated])
|
||||
@permission_classes([IsAuthenticated, CourseSessionMember])
|
||||
def create_invitation(request, course_session_id: int):
|
||||
# Validate request
|
||||
# Create invitation
|
||||
# Send email
|
||||
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
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
return Response({})
|
||||
|
||||
|
||||
@api_view(["GET"])
|
||||
|
|
|
|||
Loading…
Reference in New Issue