Skip to content

Commit

Permalink
Merge pull request #131 from SELab-2/extra_backend
Browse files Browse the repository at this point in the history
Extra backend implementatie
  • Loading branch information
Bendemeurichy authored Apr 9, 2024
2 parents d6739aa + 1007309 commit 1a5c33a
Show file tree
Hide file tree
Showing 16 changed files with 246 additions and 49 deletions.
4 changes: 4 additions & 0 deletions api/models/gebruiker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models
from django.contrib.auth.models import User
from api.models.vak import Vak


class Gebruiker(models.Model):
Expand All @@ -19,6 +20,9 @@ class Gebruiker(models.Model):

user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
is_lesgever = models.BooleanField(default=False)
gepinde_vakken = models.ManyToManyField(
Vak, related_name="gebruiker_gepinde_vakken", blank=True
)

def __str__(self):
return self.user.first_name + " " + self.user.last_name
2 changes: 1 addition & 1 deletion api/models/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class Project(models.Model):
opgave_bestand = models.FileField(upload_to=upload_to)
vak = models.ForeignKey(Vak, on_delete=models.CASCADE)
deadline = models.DateTimeField(null=True, blank=True)
extra_deadline = models.DateTimeField(null=True, blank=True, default=None)
extra_deadline = models.DateTimeField(null=True, blank=True)
max_score = models.IntegerField(default=20)
max_groep_grootte = models.IntegerField(default=1)
zichtbaar = models.BooleanField(default=True, blank=True)
Expand Down
53 changes: 50 additions & 3 deletions api/serializers/gebruiker.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ class GebruikerSerializer(serializers.ModelSerializer):

class Meta:
model = Gebruiker
fields = ["user", "is_lesgever", "first_name", "last_name", "email"]
fields = [
"user",
"is_lesgever",
"first_name",
"last_name",
"email",
"gepinde_vakken",
]

def create(self, validated_data):
"""
Expand All @@ -33,7 +40,12 @@ def create(self, validated_data):
Returns:
Gebruiker: De aangemaakte gebruiker.
"""

gepinde_vakken = validated_data.pop("gepinde_vakken")
instance = Gebruiker.objects.create(**validated_data)
validate_gepinde_vakken(instance, gepinde_vakken)
instance.gepinde_vakken.set(gepinde_vakken)

return instance

def update(self, instance, validated_data):
Expand All @@ -45,16 +57,29 @@ def update(self, instance, validated_data):
Returns:
Gebruiker: De bijgewerkte gebruiker.
"""
is_lesgever = validated_data.pop("is_lesgever")
is_lesgever = validated_data.pop("is_lesgever", instance.is_lesgever)
if instance.is_lesgever != is_lesgever:
validate_lesgever_change(instance)

instance.is_lesgever = is_lesgever

gepinde_vakken = validated_data.pop("gepinde_vakken", instance.gepinde_vakken)
validate_gepinde_vakken(instance, gepinde_vakken)
instance.gepinde_vakken.set(gepinde_vakken)

instance.save()
return instance


def validate_lesgever_change(instance):
"""
Valideert of de lesgever wijziging geldig is.
Args:
instance (Gebruiker): De gebruiker waarvoor de wijziging wordt gecontroleerd.
Raises:
serializers.ValidationError: Als de lesgever eerst verwijderd moet worden uit zijn/haar huidige vakken.
"""
if instance.is_lesgever and Vak.objects.filter(lesgevers=instance):
raise serializers.ValidationError(
f"De lesgever {instance} moet eerst verwijderd worden \
Expand All @@ -65,3 +90,25 @@ def validate_lesgever_change(instance):
f"De student {instance} moet eerst verwijderd worden \
als student in zijn huidige vakken"
)


def validate_gepinde_vakken(instance, gepinde_vakken):
"""
Valideert of de gepinde vakken geldig zijn voor de gebruiker.
Args:
instance (Gebruiker): De gebruiker voor wie de gepinde vakken worden gecontroleerd.
gepinde_vakken (list): Een lijst van vakken die als gepind worden gemarkeerd.
Raises:
serializers.ValidationError: Als de gebruiker deel moet uitmaken van alle gepinde vakken.
"""
if instance.is_lesgever:
vakken = Vak.objects.filter(lesgevers=instance.user.id)
else:
vakken = Vak.objects.filter(studenten=instance.user.id)

if not all(item in vakken for item in gepinde_vakken):
raise serializers.ValidationError(
"De gebruiker moet deel uitmaken van een vak voordat hij/zij dat vak kan markeren als een gepind vak"
)
4 changes: 2 additions & 2 deletions api/serializers/groep.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ def update(self, instance, validated_data):
Returns:
Groep: De bijgewerkte groep.
"""
students_data = validated_data.pop("studenten")
students_data = validated_data.pop("studenten", instance.studenten)
validate_students(
students_data, validated_data["project"], current_group=instance
)
new_project = validated_data.get("project")
new_project = validated_data.get("project", instance.project)
validate_project(instance, new_project)

super().update(instance=instance, validated_data=validated_data)
Expand Down
11 changes: 7 additions & 4 deletions api/serializers/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ def update(self, instance, validated_data):
Returns:
Project: Het bijgewerkte project.
"""
deadline = validated_data.pop("deadline")
extra_deadline = validated_data.pop("extra_deadline")
deadline = validated_data.pop("deadline", instance.deadline)
extra_deadline = validated_data.pop("extra_deadline", instance.extra_deadline)
validate_deadlines(deadline, extra_deadline)

new_vak = validated_data.get("vak")
new_vak = validated_data.get("vak", instance.vak)
validate_vak(instance, new_vak)

super().update(instance=instance, validated_data=validated_data)
Expand All @@ -95,10 +95,13 @@ def validate_deadlines(deadline, extra_deadline):
Raises:
serializers.ValidationError: Als de deadline in het verleden ligt.
"""

if not deadline:
return
if deadline <= timezone.now():
raise serializers.ValidationError("Deadline moet in de toekomst liggen")

if extra_deadline is not None and extra_deadline <= deadline:
if extra_deadline and extra_deadline <= deadline:
raise serializers.ValidationError(
"Extra deadline moet na de eerste deadline liggen"
)
Expand Down
4 changes: 2 additions & 2 deletions api/serializers/restrictie.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ def update(self, instance, validated_data):
Returns:
Restrictie: De bijgewerkte restrictie.
"""
validate_project(instance, validated_data.get("project"))
validate_script(validated_data.get("script"))
validate_project(instance, validated_data.get("project", instance.project))
validate_script(validated_data.get("script", instance.script))

super().update(instance=instance, validated_data=validated_data)
instance.save()
Expand Down
15 changes: 10 additions & 5 deletions api/serializers/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,19 @@ def update(self, instance, validated_data):
Returns:
Score: De bijgewerkte score.
"""
validate_score(validated_data)
validate_indiening(instance, validated_data.get("indiening"))
validate_score(
validated_data.get("indiening", instance.indiening),
validated_data.get("score", instance.score),
)
validate_indiening(
instance, validated_data.get("indiening", instance.indiening)
)
super().update(instance=instance, validated_data=validated_data)
instance.save()
return instance


def validate_score(data):
def validate_score(indiening, score):
"""
Controleert of de opgegeven score niet hoger is dan de maximale score van het bijbehorende project.
Expand All @@ -62,8 +67,8 @@ def validate_score(data):
Raises:
serializers.ValidationError: Als de score hoger is dan de maximale score van het bijbehorende project.
"""
max_score = data.get("indiening").groep.project.max_score
if data["score"] > max_score:
max_score = indiening.groep.project.max_score
if score > max_score:
raise serializers.ValidationError(
f"Score kan niet hoger zijn dan de maximale score van {max_score}"
)
Expand Down
4 changes: 2 additions & 2 deletions api/serializers/vak.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ def update(self, instance, validated_data):
Returns:
Vak: Het bijgewerkte vak.
"""
students_data = validated_data.pop("studenten", [])
teachers_data = validated_data.pop("lesgevers", [])
students_data = validated_data.pop("studenten", instance.studenten)
teachers_data = validated_data.pop("lesgevers", instance.lesgevers)

validate_students_teachers(students_data, teachers_data)

Expand Down
29 changes: 26 additions & 3 deletions api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,19 @@
from .views.views import home
from .views.gebruiker import gebruiker_list, gebruiker_detail, gebruiker_detail_me
from .views.vak import vak_list, vak_detail
from .views.project import project_list, project_detail
from .views.indiening import indiening_list, indiening_detail
from .views.project import project_list, project_detail, project_detail_download_opgave
from .views.indiening import (
indiening_list,
indiening_detail,
indiening_detail_download_bestanden,
)
from .views.score import score_list, score_detail
from .views.groep import groep_list, groep_detail
from .views.restrictie import restrictie_list, restrictie_detail
from .views.restrictie import (
restrictie_list,
restrictie_detail,
restrictie_detail_download_script,
)

urlpatterns = [
path("admin/", admin.site.urls),
Expand All @@ -39,14 +47,29 @@
path("api/vakken/<int:id>/", vak_detail, name="vak_detail"),
path("api/projecten/", project_list, name="project_list"),
path("api/projecten/<int:id>/", project_detail, name="project_detail"),
path(
"api/projecten/<int:id>/opgave_bestand/",
project_detail_download_opgave,
name="project_detail_download_opgave",
),
path("api/indieningen/", indiening_list, name="indiening_list"),
path("api/indieningen/<int:id>/", indiening_detail, name="indiening_detail"),
path(
"api/indieningen/<int:id>/indiening_bestanden/",
indiening_detail_download_bestanden,
name="indiening_detail_download_bestanden",
),
path("api/scores/", score_list, name="score_list"),
path("api/scores/<int:id>/", score_detail, name="score_detail"),
path("api/groepen/", groep_list, name="groep_list"),
path("api/groepen/<int:id>/", groep_detail, name="groep_detail"),
path("api/restricties/", restrictie_list, name="restrictie_list"),
path("api/restricties/<int:id>/", restrictie_detail, name="restrictie_detail"),
path(
"api/restricties/<int:id>/script/",
restrictie_detail_download_script,
name="restrictie_detail_download_script",
),
]

urlpatterns = format_suffix_patterns(urlpatterns)
13 changes: 9 additions & 4 deletions api/views/gebruiker.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ def gebruiker_list(request):
return Response(serializer.data)


@api_view(["GET", "PUT"])
@api_view(["GET", "PUT", "PATCH"])
def gebruiker_detail(request, id):
"""
Een view om de gegevens van een specifieke gebruiker op te halen (GET) of bij te werken (PUT).
Een view om de gegevens van een specifieke gebruiker op te halen (GET) of bij te werken (PUT, PATCH).
Args:
id (int): De primaire sleutel van de gebruiker.
Expand All @@ -63,9 +63,14 @@ def gebruiker_detail(request, id):
serializer = GebruikerSerializer(gebruiker)
return Response(serializer.data)
return Response(status=status.HTTP_403_FORBIDDEN)
elif request.method == "PUT":
elif request.method in ["PUT", "PATCH"]:
if request.user.is_superuser:
serializer = GebruikerSerializer(gebruiker, data=request.data)
if request.method == "PUT":
serializer = GebruikerSerializer(gebruiker, data=request.data)
else:
serializer = GebruikerSerializer(
gebruiker, data=request.data, partial=True
)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
Expand Down
12 changes: 8 additions & 4 deletions api/views/groep.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,11 @@ def groep_list(request, format=None):
return Response(status=status.HTTP_403_FORBIDDEN)


@api_view(["GET", "PUT", "DELETE"])
@api_view(["GET", "PUT", "PATCH", "DELETE"])
def groep_detail(request, id, format=None):
"""
Een view om de gegevens van een specifieke groep op te halen (GET), bij te werken (PUT) of te verwijderen (DELETE).
Een view om de gegevens van een specifieke groep op te halen (GET),
bij te werken (PUT, PATCH) of te verwijderen (DELETE).
Args:
id (int): De primaire sleutel van de groep.
Expand All @@ -83,8 +84,11 @@ def groep_detail(request, id, format=None):
return Response(status=status.HTTP_403_FORBIDDEN)

if is_lesgever(request.user):
if request.method == "PUT":
serializer = GroepSerializer(groep, data=request.data)
if request.method in ["PUT", "PATCH"]:
if request.method == "PUT":
serializer = GroepSerializer(groep, data=request.data)
else:
serializer = GroepSerializer(groep, data=request.data, partial=True)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
Expand Down
48 changes: 45 additions & 3 deletions api/views/indiening.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
from api.serializers.indiening import IndieningSerializer
from api.utils import is_lesgever, contains

import os
import tempfile
import zipfile
from django.http import HttpResponse


@api_view(["GET", "POST"])
def indiening_list(request, format=None):
Expand Down Expand Up @@ -108,9 +113,46 @@ def indiening_detail(request, id, format=None):
return Response(status=status.HTTP_403_FORBIDDEN)

elif request.method == "DELETE":
if is_lesgever(request.user) or contains(
indiening.groep.studenten, request.user
):
if is_lesgever(request.user):
indiening.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
return Response(status=status.HTTP_403_FORBIDDEN)


@api_view(["GET"])
def indiening_detail_download_bestanden(request, id, format=None):
"""
Een view om de bestanden van een specifieke indiening te downloaden als een zip-archief.
Args:
id (int): De primaire sleutel van de indiening.
format (str, optional): Het gewenste formaat voor de respons. Standaard is None.
Returns:
HttpResponse: Een zip-bestandsrespons met de bestanden van de indiening als bijlage,
indien de gebruiker een lesgever is of betrokken is bij de groep van de indiening.
Anders wordt een foutmelding geretourneerd.
"""
try:
indiening = Indiening.objects.get(pk=id)
except Indiening.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

if is_lesgever(request.user) or contains(indiening.groep.studenten, request.user):
indiening_bestanden = IndieningBestand.objects.filter(indiening=indiening)

temp_dir = tempfile.mkdtemp()

zip_file_name = f"groep_{indiening.groep.groep_id}_indiening_{indiening.indiening_id}_bestanden.zip"
zip_file_path = os.path.join(temp_dir, zip_file_name)
with zipfile.ZipFile(zip_file_path, "w") as zip_file:
for indiening_bestand in indiening_bestanden:
path = indiening_bestand.bestand.path
zip_file.write(path, os.path.basename(path))

with open(zip_file_path, "rb") as zip_file:
response = HttpResponse(zip_file.read(), content_type="application/zip")
response["Content-Disposition"] = f"attachment; filename={zip_file_name}"
return response

return Response(status=status.HTTP_403_FORBIDDEN)
Loading

0 comments on commit 1a5c33a

Please sign in to comment.