diff --git a/api/models/restrictie.py b/api/models/restrictie.py index 1a37d33d..350d06a8 100644 --- a/api/models/restrictie.py +++ b/api/models/restrictie.py @@ -28,4 +28,4 @@ class Restrictie(models.Model): moet_slagen = models.BooleanField(default=False, blank=True) def __str__(self): - return self.project.titel + ', restrictie: ' + str(self.script) + return self.project.titel + ", restrictie: " + str(self.script) diff --git a/api/serializers/gebruiker.py b/api/serializers/gebruiker.py index ab4e7b6e..de16ee02 100644 --- a/api/serializers/gebruiker.py +++ b/api/serializers/gebruiker.py @@ -59,9 +59,9 @@ def validate_lesgever_change(instance): raise serializers.ValidationError( 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" - ) + ) diff --git a/api/serializers/groep.py b/api/serializers/groep.py index 3c4d80b8..fabb5341 100644 --- a/api/serializers/groep.py +++ b/api/serializers/groep.py @@ -51,7 +51,7 @@ def update(self, instance, validated_data): validate_students( students_data, validated_data["project"], current_group=instance ) - new_project = validated_data.get('project') + new_project = validated_data.get("project") validate_project(instance, new_project) super().update(instance=instance, validated_data=validated_data) @@ -67,7 +67,9 @@ def validate_project(instance, new_project): """ if instance.project != new_project: - raise serializers.ValidationError('Het project van een groep kan niet aangepast worden') + raise serializers.ValidationError( + "Het project van een groep kan niet aangepast worden" + ) def validate_students(students_data, project, current_group=None): diff --git a/api/serializers/project.py b/api/serializers/project.py index 9622f540..17069154 100644 --- a/api/serializers/project.py +++ b/api/serializers/project.py @@ -23,9 +23,18 @@ class ProjectSerializer(serializers.ModelSerializer): class Meta: model = Project fields = [ - 'project_id', 'titel', 'beschrijving', 'opgave_bestand', 'vak', 'deadline', - 'extra_deadline', 'max_score', 'zichtbaar', 'gearchiveerd', 'restricties' - ] + "project_id", + "titel", + "beschrijving", + "opgave_bestand", + "vak", + "deadline", + "extra_deadline", + "max_score", + "zichtbaar", + "gearchiveerd", + "restricties", + ] def create(self, validated_data): """ @@ -58,7 +67,7 @@ def update(self, instance, validated_data): extra_deadline = validated_data.pop("extra_deadline") validate_deadlines(deadline, extra_deadline) - new_vak = validated_data.get('vak') + new_vak = validated_data.get("vak") validate_vak(instance, new_vak) super().update(instance=instance, validated_data=validated_data) @@ -93,4 +102,6 @@ def validate_vak(instance, new_vak): """ if instance.vak != new_vak: - raise serializers.ValidationError('Het vak van een project kan niet aangepast worden') + raise serializers.ValidationError( + "Het vak van een project kan niet aangepast worden" + ) diff --git a/api/serializers/restrictie.py b/api/serializers/restrictie.py index 644c74f9..3ec7b5f0 100644 --- a/api/serializers/restrictie.py +++ b/api/serializers/restrictie.py @@ -16,15 +16,15 @@ def create(self, validated_data): TODO """ - validate_script(validated_data.get('script')) + 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')) + validate_project(instance, validated_data.get("project")) + validate_script(validated_data.get("script")) super().update(instance=instance, validated_data=validated_data) instance.save() @@ -32,8 +32,10 @@ def update(self, instance, validated_data): 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') + if not (str(new_script).endswith(".sh") or str(new_script).endswith(".py")): + raise serializers.ValidationError( + "Het restrictie script moet een Python of Shell script zijn" + ) def validate_project(instance, new_project): @@ -42,4 +44,6 @@ def validate_project(instance, new_project): """ if instance.project != new_project: - raise serializers.ValidationError('Het project van een restrictie kan niet aangepast worden') + 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 25bee425..150f6f67 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.get('indiening')) + validate_indiening(instance, validated_data.get("indiening")) super().update(instance=instance, validated_data=validated_data) instance.save() return instance @@ -74,4 +74,6 @@ def validate_indiening(instance, new_indiening): TODO """ if instance.indiening != new_indiening: - raise serializers.ValidationError("De indiening van een score kan niet aangepast worden") + raise serializers.ValidationError( + "De indiening van een score kan niet aangepast worden" + ) diff --git a/api/tests/factories/restrictie.py b/api/tests/factories/restrictie.py new file mode 100644 index 00000000..9568b19a --- /dev/null +++ b/api/tests/factories/restrictie.py @@ -0,0 +1,13 @@ +import factory +from factory.django import DjangoModelFactory +from api.models.restrictie import Restrictie +from api.tests.factories.project import ProjectFactory + + +class RestrictieFactory(DjangoModelFactory): + class Meta: + model = Restrictie + + project = factory.SubFactory(ProjectFactory) + script = factory.django.FileField(filename="test_script.sh") + moet_slagen = factory.Faker("boolean") diff --git a/api/tests/models/test_indiening.py b/api/tests/models/test_indiening.py index 617488ab..e0f065d0 100644 --- a/api/tests/models/test_indiening.py +++ b/api/tests/models/test_indiening.py @@ -1,6 +1,7 @@ from django.test import TestCase from django.core.files.uploadedfile import SimpleUploadedFile from api.tests.factories.indiening import IndieningFactory, IndieningBestandFactory +from api.models.indiening import upload_to class IndieningModelTest(TestCase): @@ -22,6 +23,13 @@ def test_status(self): def test_indiening_bestanden(self): self.assertEqual(self.indiening.indiening_bestanden.count(), 1) + def test_upload_to(self): + filename = "test_indiening.txt" + expected_path = ( + f"data/indieningen/indiening_{self.indiening.indiening_id}/{filename}" + ) + self.assertEqual(upload_to(self.indiening, filename), expected_path) + class IndieningBestandModelTest(TestCase): def setUp(self): diff --git a/api/tests/models/test_project.py b/api/tests/models/test_project.py index b9885537..9fea0ff8 100644 --- a/api/tests/models/test_project.py +++ b/api/tests/models/test_project.py @@ -1,5 +1,6 @@ from django.test import TestCase from api.tests.factories.project import ProjectFactory +from api.models.project import upload_to class ProjectModelTest(TestCase): @@ -29,3 +30,8 @@ def test_project_extra_deadline(self): def test_project_opgave_bestand(self): self.assertEqual(self.project.opgave_bestand.read(), b"file content") + + def test_upload_to(self): + filename = "test_opgave.txt" + expected_path = f"data/opgaves/vak_{self.project.vak.vak_id}/{filename}" + self.assertEqual(upload_to(self.project, filename), expected_path) diff --git a/api/tests/models/test_restrictie.py b/api/tests/models/test_restrictie.py new file mode 100644 index 00000000..c12930ae --- /dev/null +++ b/api/tests/models/test_restrictie.py @@ -0,0 +1,31 @@ +from django.test import TestCase +from api.tests.factories.restrictie import RestrictieFactory +from api.models.restrictie import upload_to + + +class RestrictieModelTest(TestCase): + def setUp(self): + self.restrictie = RestrictieFactory.create() + + def test_project(self): + self.assertIsNotNone(self.restrictie.project) + + def test_script(self): + self.assertIsNotNone(self.restrictie.script) + + def test_moet_slagen(self): + self.assertIsNotNone(self.restrictie.moet_slagen) + + def test_str_method(self): + expected_str = ( + self.restrictie.project.titel + + ", restrictie: " + + str(self.restrictie.script) + ) + self.assertEqual(str(self.restrictie), expected_str) + + def test_upload_to(self): + project = self.restrictie.project + filename = "test_script.txt" + expected_path = f"data/restricties/project_{project.project_id}/{filename}" + self.assertEqual(upload_to(self.restrictie, filename), expected_path) diff --git a/api/tests/serializers/test_restrictie.py b/api/tests/serializers/test_restrictie.py new file mode 100644 index 00000000..c600e7a9 --- /dev/null +++ b/api/tests/serializers/test_restrictie.py @@ -0,0 +1,72 @@ +from django.test import TestCase +from rest_framework.exceptions import ValidationError +from api.tests.factories.restrictie import RestrictieFactory +from api.serializers.restrictie import RestrictieSerializer +from django.core.files.uploadedfile import SimpleUploadedFile + + +class RestrictieSerializerTest(TestCase): + def setUp(self): + self.restrictie = RestrictieFactory.create() + self.serializer = RestrictieSerializer(instance=self.restrictie) + + def test_contains_expected_fields(self): + data = self.serializer.data + self.assertCountEqual( + data.keys(), ["restrictie_id", "project", "script", "moet_slagen"] + ) + + def test_restrictie_id_field_content(self): + data = self.serializer.data + self.assertEqual(data["restrictie_id"], self.restrictie.restrictie_id) + + def test_project_field_content(self): + data = self.serializer.data + self.assertEqual(data["project"], self.restrictie.project.project_id) + + def test_script_field_content(self): + data = self.serializer.data + path = self.restrictie.script.path.split("/") + self.assertEqual(data["script"], "/" + "/".join(path[-4:])) + + def test_moet_slagen_field_content(self): + data = self.serializer.data + self.assertEqual(data["moet_slagen"], self.restrictie.moet_slagen) + + def test_create(self): + data = { + "project": self.restrictie.project.project_id, + "script": SimpleUploadedFile("script.sh", b"file_content"), + "moet_slagen": False, + } + serializer = RestrictieSerializer(data=data) + self.assertTrue(serializer.is_valid()) + restrictie = serializer.save() + self.assertEqual(restrictie.project.project_id, data["project"]) + path = restrictie.script.path.split("/") + filename = path[-1] + filename = filename.split("_")[0] + filename = filename.split(".")[0] + ".sh" + self.assertEqual( + "/".join(path[-4:-1]) + "/" + filename, + f"data/restricties/project_{restrictie.project.project_id}/" + + str(data["script"]), + ) + self.assertEqual(restrictie.moet_slagen, data["moet_slagen"]) + + def test_update(self): + data = self.serializer.data + data["script"] = SimpleUploadedFile(data["script"], b"file_content") + data["moet_slagen"] = not data["moet_slagen"] + serializer = RestrictieSerializer( + instance=self.restrictie, data=data, partial=True + ) + self.assertTrue(serializer.is_valid()) + self.restrictie = serializer.save() + self.assertEqual(self.restrictie.moet_slagen, data["moet_slagen"]) + + def test_validation_for_blank_items(self): + serializer = RestrictieSerializer( + data={"project": "", "script": "", "moet_slagen": ""} + ) + self.assertRaises(ValidationError, serializer.is_valid, raise_exception=True) diff --git a/api/tests/views/test_restrictie.py b/api/tests/views/test_restrictie.py new file mode 100644 index 00000000..c91163d7 --- /dev/null +++ b/api/tests/views/test_restrictie.py @@ -0,0 +1,107 @@ +from django.urls import reverse +from rest_framework.test import APITestCase, APIClient +from api.tests.factories.restrictie import RestrictieFactory +from api.tests.factories.project import ProjectFactory +from django.core.files.uploadedfile import SimpleUploadedFile +from rest_framework import status +from api.tests.factories.gebruiker import GebruikerFactory + + +class RestrictieListViewTest(APITestCase): + def setUp(self): + self.client = APIClient() + self.restrictie = RestrictieFactory.create(moet_slagen=True) + self.url = reverse("restrictie_list") + self.teacher = GebruikerFactory.create(is_lesgever=True) + self.client.force_login(self.teacher.user) + + def test_restrictie_list_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + def test_restrictie_list_get_project(self): + response = self.client.get( + self.url, {"project": self.restrictie.project.project_id} + ) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + + def test_restrictie_list_get_moet_slagen(self): + response = self.client.get(self.url, {"moet_slagen": "true"}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 1) + response = self.client.get(self.url, {"moet_slagen": "false"}) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(len(response.data), 0) + + def test_restrictie_list_get_as_student(self): + student = GebruikerFactory.create(is_lesgever=False) + self.client.force_login(student.user) + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_restrictie_list_post(self): + project = ProjectFactory.create() + data = { + "project": project.project_id, + "script": SimpleUploadedFile("nieuw_script.sh", b"file_content"), + "moet_slagen": False, + } + response = self.client.post(self.url, data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_201_CREATED) + + +class RestrictieDetailViewTest(APITestCase): + def setUp(self): + self.client = APIClient() + self.restrictie = RestrictieFactory.create() + self.url = reverse( + "restrictie_detail", kwargs={"id": self.restrictie.restrictie_id} + ) + self.teacher = GebruikerFactory.create(is_lesgever=True) + self.client.force_login(self.teacher.user) + + def test_restrictie_detail_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["restrictie_id"], self.restrictie.restrictie_id) + + def test_restrictie_detail_get_as_student(self): + student = GebruikerFactory.create(is_lesgever=False) + self.client.force_login(student.user) + response = self.client.get(self.url) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_restrictie_put(self): + new_data = { + "restrictie_id": self.restrictie.restrictie_id, + "project": self.restrictie.project.project_id, + "script": SimpleUploadedFile(self.restrictie.script.name, b"file_content"), + "moet_slagen": not self.restrictie.moet_slagen, + } + response = self.client.put(self.url, new_data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["moet_slagen"], not self.restrictie.moet_slagen) + + def test_restrictie_put_as_student(self): + student = GebruikerFactory.create(is_lesgever=False) + self.client.force_login(student.user) + new_data = { + "restrictie_id": self.restrictie.restrictie_id, + "project": self.restrictie.project.project_id, + "script": SimpleUploadedFile(self.restrictie.script.name, b"file_content"), + "moet_slagen": not self.restrictie.moet_slagen, + } + response = self.client.put(self.url, new_data, format="multipart") + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + + def test_restrictie_delete(self): + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) + + def test_restrictie_delete_as_student(self): + student = GebruikerFactory.create(is_lesgever=False) + self.client.force_login(student.user) + response = self.client.delete(self.url) + self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) diff --git a/api/views/restrictie.py b/api/views/restrictie.py index 59a3bbb0..2c09efa6 100644 --- a/api/views/restrictie.py +++ b/api/views/restrictie.py @@ -23,7 +23,9 @@ def restrictie_list(request, format=None): except NameError: return Response(status=status.HTTP_400_BAD_REQUEST) - if "moet_slagen" in request.GET and request.GET.get("moet_slagen").lower() in [ + if "moet_slagen" in request.GET and request.GET.get( + "moet_slagen" + ).lower() in [ "true", "false", ]: