diff --git a/.coverage b/.coverage index 3c3774b1..4500b425 100644 Binary files a/.coverage and b/.coverage differ diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 670440cc..00000000 --- a/.coveragerc +++ /dev/null @@ -1,11 +0,0 @@ -[run] -omit= - */tests/* - */migrations/* - */__init__.py - */__pycache__/* - */data/* - */frontend/* - */static/* - */.github/* - */usr/* diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml index ce56c185..2226443c 100644 --- a/.github/workflows/django.yml +++ b/.github/workflows/django.yml @@ -2,9 +2,9 @@ name: Django CI on: push: - branches: [ "develop", "tests" ] + branches: [ "develop", "tests", "restricties" ] pull_request: - branches: [ "develop", "tests" ] + branches: [ "develop", "tests", "restricties" ] jobs: build: diff --git a/api/admin.py b/api/admin.py index d5933479..e326c7e8 100644 --- a/api/admin.py +++ b/api/admin.py @@ -5,6 +5,7 @@ from api.models.project import Project from api.models.indiening import Indiening, IndieningBestand from api.models.score import Score +from api.models.restrictie import Restrictie admin.site.register(Gebruiker) admin.site.register(Vak) @@ -13,3 +14,4 @@ admin.site.register(Indiening) admin.site.register(Score) admin.site.register(IndieningBestand) +admin.site.register(Restrictie) diff --git a/api/models/project.py b/api/models/project.py index 1e52df64..0a555ffd 100644 --- a/api/models/project.py +++ b/api/models/project.py @@ -51,7 +51,6 @@ class Project(models.Model): max_score = models.IntegerField(default=20) zichtbaar = models.BooleanField(default=True, blank=True) gearchiveerd = models.BooleanField(default=False, blank=True) - # indiening restricties def __str__(self): return self.titel diff --git a/api/models/restrictie.py b/api/models/restrictie.py new file mode 100644 index 00000000..1a37d33d --- /dev/null +++ b/api/models/restrictie.py @@ -0,0 +1,31 @@ +from django.db import models +from api.models.project import Project + + +def upload_to(instance, filename): + """ + Functie om het pad te genereren waar het opgavebestand wordt opgeslagen. + + Args: + instance: De huidige instantie van het model. + filename (str): De oorspronkelijke bestandsnaam. + + Returns: + str: Het pad waar het opgavebestand moet worden opgeslagen. + """ + project_id = instance.project.project_id + return f"data/restricties/project_{project_id}/{filename}" + + +class Restrictie(models.Model): + """ + TODO + """ + + restrictie_id = models.AutoField(primary_key=True) + project = models.ForeignKey(Project, on_delete=models.CASCADE) + script = models.FileField(upload_to=upload_to) + moet_slagen = models.BooleanField(default=False, blank=True) + + def __str__(self): + return self.project.titel + ', restrictie: ' + str(self.script) diff --git a/api/serializers/gebruiker.py b/api/serializers/gebruiker.py index 1292e010..ab4e7b6e 100644 --- a/api/serializers/gebruiker.py +++ b/api/serializers/gebruiker.py @@ -57,9 +57,11 @@ def update(self, instance, validated_data): def validate_lesgever_change(instance): if instance.is_lesgever and Vak.objects.filter(lesgevers=instance): raise serializers.ValidationError( - f"De lesgever {instance} moet eerst verwijderd worden als lesgever in zijn huidige vakken" - ) + f"De lesgever {instance} moet eerst verwijderd worden \ + als lesgever in zijn huidige vakken" + ) elif not instance.is_lesgever and Vak.objects.filter(studenten=instance): raise serializers.ValidationError( - f"De student {instance} moet eerst verwijderd worden als student in zijn huidige vakken" - ) + f"De student {instance} moet eerst verwijderd worden \ + als student in zijn huidige vakken" + ) diff --git a/api/serializers/groep.py b/api/serializers/groep.py index 632ac869..3c4d80b8 100644 --- a/api/serializers/groep.py +++ b/api/serializers/groep.py @@ -51,6 +51,9 @@ def update(self, instance, validated_data): validate_students( students_data, validated_data["project"], current_group=instance ) + new_project = validated_data.get('project') + validate_project(instance, new_project) + super().update(instance=instance, validated_data=validated_data) instance.studenten.set(students_data) instance.save() @@ -58,6 +61,15 @@ def update(self, instance, validated_data): return instance +def validate_project(instance, new_project): + """ + TODO + """ + + if instance.project != new_project: + raise serializers.ValidationError('Het project van een groep kan niet aangepast worden') + + def validate_students(students_data, project, current_group=None): """ Controleert of de opgegeven gebruikers studenten zijn en of ze al in een andere groep voor dit project zitten. diff --git a/api/serializers/project.py b/api/serializers/project.py index ae3cc335..9622f540 100644 --- a/api/serializers/project.py +++ b/api/serializers/project.py @@ -1,6 +1,7 @@ from rest_framework import serializers from api.models.project import Project from django.utils import timezone +from api.serializers.restrictie import RestrictieSerializer class ProjectSerializer(serializers.ModelSerializer): @@ -17,9 +18,14 @@ class ProjectSerializer(serializers.ModelSerializer): update(self, instance, validated_data): Werkt een bestaand project bij in de database. """ + restricties = RestrictieSerializer(many=True, read_only=True) + class Meta: model = Project - fields = "__all__" + fields = [ + 'project_id', 'titel', 'beschrijving', 'opgave_bestand', 'vak', 'deadline', + 'extra_deadline', 'max_score', 'zichtbaar', 'gearchiveerd', 'restricties' + ] def create(self, validated_data): """ @@ -52,6 +58,9 @@ def update(self, instance, validated_data): extra_deadline = validated_data.pop("extra_deadline") validate_deadlines(deadline, extra_deadline) + new_vak = validated_data.get('vak') + validate_vak(instance, new_vak) + super().update(instance=instance, validated_data=validated_data) instance.deadline = deadline instance.extra_deadline = extra_deadline @@ -76,3 +85,12 @@ def validate_deadlines(deadline, extra_deadline): raise serializers.ValidationError( "Extra deadline moet na de eerste deadline liggen" ) + + +def validate_vak(instance, new_vak): + """ + TODO + """ + + if instance.vak != new_vak: + raise serializers.ValidationError('Het vak van een project kan niet aangepast worden') diff --git a/api/serializers/restrictie.py b/api/serializers/restrictie.py new file mode 100644 index 00000000..644c74f9 --- /dev/null +++ b/api/serializers/restrictie.py @@ -0,0 +1,45 @@ +from rest_framework import serializers +from api.models.restrictie import Restrictie + + +class RestrictieSerializer(serializers.ModelSerializer): + """ + TODO + """ + + class Meta: + model = Restrictie + fields = "__all__" + + def create(self, validated_data): + """ + TODO + """ + + validate_script(validated_data.get('script')) + return Restrictie.objects.create(**validated_data) + + def update(self, instance, validated_data): + """ + TODO + """ + validate_project(instance, validated_data.get('project')) + validate_script(instance, validated_data.get('script')) + + super().update(instance=instance, validated_data=validated_data) + instance.save() + return instance + + +def validate_script(new_script): + if not str(new_script).endswith('.sh') or not str(new_script).endswith('.py'): + raise serializers.ValidationError('Het restrictie script moet een Python of Shell script zijn') + + +def validate_project(instance, new_project): + """ + TODO + """ + + if instance.project != new_project: + raise serializers.ValidationError('Het project van een restrictie kan niet aangepast worden') diff --git a/api/serializers/score.py b/api/serializers/score.py index 7c06ad5e..25bee425 100644 --- a/api/serializers/score.py +++ b/api/serializers/score.py @@ -46,7 +46,7 @@ def update(self, instance, validated_data): Score: De bijgewerkte score. """ validate_score(validated_data) - validate_indiening(instance, validated_data) + validate_indiening(instance, validated_data.get('indiening')) super().update(instance=instance, validated_data=validated_data) instance.save() return instance @@ -69,16 +69,9 @@ def validate_score(data): ) -def validate_indiening(instance, data): +def validate_indiening(instance, new_indiening): """ - Controleert of de indiening_id niet wordt aangepast. - - Args: - instance (Score): De score die moet worden bijgewerkt. - data (dict): Gevalideerde gegevens over de score. - - Raises: - serializers.ValidationError: Als de indiening_id wordt aangepast. + TODO """ - if instance.indiening != data.get("indiening"): - raise serializers.ValidationError("indiening_id kan niet aangepast worden") + if instance.indiening != new_indiening: + raise serializers.ValidationError("De indiening van een score kan niet aangepast worden") diff --git a/api/urls.py b/api/urls.py index e55a17aa..5c9f3cd5 100644 --- a/api/urls.py +++ b/api/urls.py @@ -26,6 +26,7 @@ from .views.indiening import indiening_list, indiening_detail from .views.score import score_list, score_detail from .views.groep import groep_list, groep_detail +from .views.restrictie import restrictie_list, restrictie_detail urlpatterns = [ path( @@ -49,6 +50,8 @@ path("api/scores//", score_detail, name="score_detail"), path("api/groepen/", groep_list, name="groep_list"), path("api/groepen//", groep_detail, name="groep_detail"), + path("api/restricties/", restrictie_list, name="restrictie_list"), + path("api/restricties//", restrictie_detail, name="restrictie_detail"), ] urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/api/views/restrictie.py b/api/views/restrictie.py new file mode 100644 index 00000000..59a3bbb0 --- /dev/null +++ b/api/views/restrictie.py @@ -0,0 +1,71 @@ +from rest_framework.decorators import api_view +from rest_framework.response import Response +from rest_framework import status + +from api.models.restrictie import Restrictie +from api.serializers.restrictie import RestrictieSerializer +from api.utils import is_lesgever + + +@api_view(["GET", "POST"]) +def restrictie_list(request, format=None): + """ + TODO + """ + if is_lesgever(request.user): + if request.method == "GET": + restricties = Restrictie.objects.all() + + if "project" in request.GET: + try: + project = eval(request.GET.get("project")) + restricties = restricties.filter(project=project) + except NameError: + return Response(status=status.HTTP_400_BAD_REQUEST) + + if "moet_slagen" in request.GET and request.GET.get("moet_slagen").lower() in [ + "true", + "false", + ]: + restricties = restricties.filter( + moet_slagen=(request.GET.get("moet_slagen").lower() == "true") + ) + + serializer = RestrictieSerializer(restricties, many=True) + return Response(serializer.data) + + elif request.method == "POST": + serializer = RestrictieSerializer(data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + return Response(status=status.HTTP_403_FORBIDDEN) + + +@api_view(["GET", "PUT", "DELETE"]) +def restrictie_detail(request, id, format=None): + """ + TODO + """ + try: + restrictie = Restrictie.objects.get(pk=id) + except Restrictie.DoesNotExist: + return Response(status=status.HTTP_404_NOT_FOUND) + + if is_lesgever(request.user): + if request.method == "GET": + serializer = RestrictieSerializer(restrictie) + return Response(serializer.data) + + if request.method == "PUT": + serializer = RestrictieSerializer(restrictie, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response(serializer.data) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + elif request.method == "DELETE": + restrictie.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + return Response(status=status.HTTP_403_FORBIDDEN) diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 00000000..4df0c4b2 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,6 @@ +{ + "name": "frontend", + "lockfileVersion": 2, + "requires": true, + "packages": {} +}