Skip to content

Commit

Permalink
Merge pull request #68 from SELab-2/course-logic
Browse files Browse the repository at this point in the history
chore: serializers for validation
  • Loading branch information
EwoutV authored Mar 9, 2024
2 parents a6cbd08 + 4082b4a commit 19db3c7
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 53 deletions.
5 changes: 5 additions & 0 deletions backend/api/models/course.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
from typing import Self
from django.db import models

Expand Down Expand Up @@ -31,6 +32,10 @@ def __str__(self) -> str:
"""The string representation of the course."""
return str(self.name)

def is_past(self) -> bool:
"""Returns whether the course is from a past academic year"""
return datetime(self.academic_startyear + 1, 10, 1) < datetime.now()

def clone(self, clone_assistants=True) -> Self:
"""Clone the course to the next academic start year"""
course = Course(
Expand Down
6 changes: 6 additions & 0 deletions backend/api/serializers/assistant_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,9 @@ class Meta:
"create_time",
"courses",
]


class AssistantIDSerializer(serializers.Serializer):
assistant_id = serializers.PrimaryKeyRelatedField(
queryset=Assistant.objects.all()
)
49 changes: 48 additions & 1 deletion backend/api/serializers/course_serializer.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from django.utils.translation import gettext
from rest_framework import serializers
from ..models.course import Course
from rest_framework.exceptions import ValidationError
from api.serializers.student_serializer import StudentIDSerializer
from api.models.course import Course


class CourseSerializer(serializers.ModelSerializer):
Expand Down Expand Up @@ -40,3 +43,47 @@ class Meta:
"students",
"projects",
]


class CourseIDSerializer(serializers.Serializer):
student_id = serializers.PrimaryKeyRelatedField(
queryset=Course.objects.all()
)


class StudentJoinSerializer(StudentIDSerializer):
def validate(self, data):
# The validator needs the course context.
if "course" not in self.context:
raise ValidationError(gettext("courses.error.context"))

course: Course = self.context["course"]

# Check if the student isn't already enrolled.
if course.students.contains(data["student_id"]):
raise ValidationError(gettext("courses.error.students.already_present"))

# Check if the course is not from a past academic year.
if course.is_past():
raise ValidationError(gettext("courses.error.students.past_course"))

return data


class StudentLeaveSerializer(StudentIDSerializer):
def validate(self, data):
# The validator needs the course context.
if "course" not in self.context:
raise ValidationError(gettext("courses.error.context"))

course: Course = self.context["course"]

# Check if the student isn't already enrolled.
if not course.students.contains(data["student_id"]):
raise ValidationError(gettext("courses.error.students.not_present"))

# Check if the course is not from a past academic year.
if course.is_past():
raise ValidationError(gettext("courses.error.students.past_course"))

return data
6 changes: 6 additions & 0 deletions backend/api/serializers/student_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,9 @@ class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = '__all__'


class StudentIDSerializer(serializers.Serializer):
student_id = serializers.PrimaryKeyRelatedField(
queryset=Student.objects.all()
)
107 changes: 55 additions & 52 deletions backend/api/views/course_view.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from django.utils.translation import gettext
from rest_framework import viewsets
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAdminUser
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.request import Request
from api.models.course import Course
from api.models.assistant import Assistant
from api.models.student import Student
from api.permissions.course_permissions import CoursePermission, CourseAssistantPermission, CourseStudentPermission
from api.permissions.course_permissions import (
CoursePermission,
CourseAssistantPermission,
CourseStudentPermission
)
from api.permissions.role_permissions import IsTeacher
from api.serializers.course_serializer import CourseSerializer
from api.serializers.course_serializer import CourseSerializer, StudentJoinSerializer, StudentLeaveSerializer
from api.serializers.teacher_serializer import TeacherSerializer
from api.serializers.assistant_serializer import AssistantSerializer
from api.serializers.assistant_serializer import AssistantSerializer, AssistantIDSerializer
from api.serializers.student_serializer import StudentSerializer
from api.serializers.project_serializer import ProjectSerializer

Expand Down Expand Up @@ -42,40 +43,38 @@ def _add_assistant(self, request: Request, **_):
"""Add an assistant to the course"""
course = self.get_object()

try:
# Add assistant to course
assistant = Assistant.objects.get(
id=request.data.get("id")
)
# Add assistant to course
serializer = AssistantIDSerializer(
data=request.data
)

course.assistants.add(assistant)
if serializer.is_valid(raise_exception=True):
course.assistants.add(
serializer.validated_data["assistant_id"]
)

return Response({
"message": gettext("courses.success.assistants.add")
})
except Assistant.DoesNotExist:
# Not found
raise NotFound(gettext("assistants.error.404"))
return Response({
"message": gettext("courses.success.assistants.add")
})

@assistants.mapping.delete
def _remove_assistant(self, request: Request, **_):
"""Remove an assistant from the course"""
course = self.get_object()

try:
# Add assistant to course
assistant = Assistant.objects.get(
id=request.data.get("id")
)
# Remove assistant from course
serializer = AssistantIDSerializer(
data=request.data
)

course.assistants.remove(assistant)
if serializer.is_valid(raise_exception=True):
course.assistants.remove(
serializer.validated_data["assistant_id"]
)

return Response({
"message": gettext("courses.success.assistants.delete")
})
except Assistant.DoesNotExist:
# Not found
raise NotFound(gettext("assistants.error.404"))
return Response({
"message": gettext("courses.success.assistants.add")
})

@action(detail=True, methods=["get"], permission_classes=[IsAdminUser | CourseStudentPermission])
def students(self, request, **_):
Expand All @@ -94,40 +93,42 @@ def students(self, request, **_):
@students.mapping.put
def _add_student(self, request: Request, **_):
"""Add a student to the course"""
# Get the course
course = self.get_object()

try:
# Add student to course
student = Student.objects.get(
id=request.data.get("id")
)
# Add student to course
serializer = StudentJoinSerializer(data=request.data, context={
"course": course
})

course.students.add(student)
if serializer.is_valid(raise_exception=True):
course.students.add(
serializer.validated_data["student_id"]
)

return Response({
"message": gettext("courses.success.students.add")
})
except Student.DoesNotExist:
raise NotFound(gettext("students.error.404"))
return Response({
"message": gettext("courses.success.students.add")
})

@students.mapping.delete
def _remove_student(self, request: Request, **_):
"""Remove a student from the course"""
# Get the course
course = self.get_object()

try:
# Add student to course
student = Student.objects.get(
id=request.data.get("id")
)
# Add student to course
serializer = StudentLeaveSerializer(data=request.data, context={
"course": course
})

course.students.remove(student)
if serializer.is_valid(raise_exception=True):
course.students.remove(
serializer.validated_data["student_id"]
)

return Response({
"message": gettext("courses.success.students.remove")
})
except Student.DoesNotExist:
raise NotFound(gettext("students.error.404"))
return Response({
"message": gettext("courses.success.students.add")
})

@action(detail=True, methods=["get"])
def teachers(self, request, **_):
Expand Down Expand Up @@ -161,8 +162,10 @@ def clone(self, request: Request, **__):
course: Course = self.get_object()

try:
# We should return the already cloned course, if present
course = course.child_course
except Course.DoesNotExist:
# Else, we clone the course
course = course.clone(
clone_assistants=request.data.get("clone_assistants")
)
Expand Down

0 comments on commit 19db3c7

Please sign in to comment.