From 1eb0445e302ef93b6d669b0b32f5e6e191ce1a3b Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 10:52:10 +0200 Subject: [PATCH 01/10] fix: create serializer --- backend/api/serializers/course_serializer.py | 56 ++++++------------- backend/api/tests/test_course.py | 2 +- backend/api/views/course_view.py | 5 +- .../composables/services/course.service.ts | 4 +- 4 files changed, 23 insertions(+), 44 deletions(-) diff --git a/backend/api/serializers/course_serializer.py b/backend/api/serializers/course_serializer.py index 6786c605..b2db9097 100644 --- a/backend/api/serializers/course_serializer.py +++ b/backend/api/serializers/course_serializer.py @@ -43,6 +43,12 @@ class CourseSerializer(serializers.ModelSerializer): read_only=True ) + faculty_id = serializers.PrimaryKeyRelatedField( + queryset=Faculty.objects.all(), + source="faculty", + write_only=True, + ) + def validate(self, attrs: dict) -> dict: """Extra custom validation for course serializer""" attrs = super().validate(attrs) @@ -52,6 +58,17 @@ def validate(self, attrs: dict) -> dict: return attrs + def create(self, validated_data): + # Create the course + course = super().create(validated_data) + + # Compute the invitation link hash + course.invitation_link = hashlib.sha256(f'{course.id}{course.academic_startyear}'.encode()).hexdigest() + course.invitation_link_expires = timezone.now() + course.save() + + return course + def to_representation(self, instance): data = super().to_representation(instance) @@ -73,6 +90,7 @@ class Meta: "excerpt", "description", "faculty", + "faculty_id", "parent_course", "private_course", "teachers", @@ -82,44 +100,6 @@ class Meta: ] -class CreateCourseSerializer(CourseSerializer): - faculty = serializers.PrimaryKeyRelatedField( - queryset=Faculty.objects.all(), - required=False, - allow_null=True, - ) - - def create(self, validated_data): - faculty = validated_data.pop('faculty', None) - - # Create the course - course = super().create(validated_data) - - # Compute the invitation link hash - course.invitation_link = hashlib.sha256(f'{course.id}{course.academic_startyear}'.encode()).hexdigest() - course.invitation_link_expires = timezone.now() - - # Link the faculty, if specified - if faculty is not None: - course.faculty = faculty - course.save() - - return course - - def update(self, instance, validated_data): - faculty = validated_data.pop('faculty', None) - - # Update the course - course = super().update(instance, validated_data) - - # Link the faculty, if specified - if faculty is not None: - course.faculty = faculty - course.save() - - return course - - class CourseIDSerializer(serializers.Serializer): student_id = serializers.PrimaryKeyRelatedField( queryset=Course.objects.all() diff --git a/backend/api/tests/test_course.py b/backend/api/tests/test_course.py index 038481d1..b2c408f4 100644 --- a/backend/api/tests/test_course.py +++ b/backend/api/tests/test_course.py @@ -783,7 +783,7 @@ def test_create_course(self): "academic_startyear": 2022, "excerpt": "Excerpt", "description": "An introductory course on computer science.", - "faculty": faculty.id, + "faculty_id": faculty.id, }, follow=True, ) diff --git a/backend/api/views/course_view.py b/backend/api/views/course_view.py index 3adc2e6c..4fd3238a 100644 --- a/backend/api/views/course_view.py +++ b/backend/api/views/course_view.py @@ -11,7 +11,6 @@ AssistantSerializer) from api.serializers.course_serializer import (CourseCloneSerializer, CourseSerializer, - CreateCourseSerializer, SaveInvitationLinkSerializer, StudentJoinSerializer, StudentLeaveSerializer, @@ -41,7 +40,7 @@ class CourseViewSet(viewsets.ModelViewSet): def create(self, request: Request, *_): """Override the create method to add the teacher to the course""" - serializer = CreateCourseSerializer(data=request.data, context={"request": request}) + serializer = CourseSerializer(data=request.data, context={"request": request}) if serializer.is_valid(raise_exception=True): course = serializer.save() @@ -55,7 +54,7 @@ def create(self, request: Request, *_): def update(self, request: Request, *_, **__): """Override the update method to add the teacher to the course""" course = self.get_object() - serializer = CreateCourseSerializer(course, data=request.data, partial=True, context={"request": request}) + serializer = CourseSerializer(course, data=request.data, partial=True, context={"request": request}) if serializer.is_valid(raise_exception=True): serializer.save() diff --git a/frontend/src/composables/services/course.service.ts b/frontend/src/composables/services/course.service.ts index f9dad3f5..c6af6850 100644 --- a/frontend/src/composables/services/course.service.ts +++ b/frontend/src/composables/services/course.service.ts @@ -89,7 +89,7 @@ export function useCourses(): CoursesState { excerpt: courseData.excerpt, academic_startyear: courseData.academic_startyear, private_course: courseData.private_course, - faculty: courseData.faculty?.id, + faculty_id: courseData.faculty?.id, }, course, Course.fromJSON, @@ -111,7 +111,7 @@ export function useCourses(): CoursesState { name: courseData.name, description: courseData.description, excerpt: courseData.excerpt, - faculty: courseData.faculty?.id, + faculty_id: courseData.faculty?.id, private_course: courseData.private_course, }, response, From 756a136004890a9b965d205592f5473aba0441a3 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 11:05:06 +0200 Subject: [PATCH 02/10] fix: toggle show artifact --- frontend/src/assets/lang/app/en.json | 3 +- frontend/src/assets/lang/app/nl.json | 29 ++++++++++--------- .../components/projects/ExtraChecksUpload.vue | 8 +++++ .../services/extra_checks.service.ts | 1 + frontend/src/types/ExtraCheck.ts | 2 ++ 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index 5188ad3b..8deb7638 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -87,7 +87,8 @@ "dockerImage": "Docker image", "timeLimit": "Time limit for execution (in seconds)", "memoryLimit": "Memory limit for execution (in MB)", - "showLog": "Make the extra logs of the docker container visible to the students" + "showLog": "Make the extra logs of the docker container visible to the students", + "showArtifact": "Make the artifacts of the docker container visible to the students" } }, "submissions": { diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index 1b0616ee..af4af6b3 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -37,7 +37,7 @@ }, "calendar": { "title": "Kalender", - "noProjects": "Geen projecten op geselecteerde datum. \uD83E\uDD73" + "noProjects": "Geen projecten op geselecteerde datum." }, "projects": { "all": "Alle projecten", @@ -88,7 +88,8 @@ "dockerImage": "Docker image", "timeLimit": "Tijdslimiet voor de uitvoering (in seconden)", "memoryLimit": "Geheugenlimiet voor de uitvoering (in MB)", - "showLog": "Maak de extra logs van de docker container zichtbaar voor de studenten" + "showLog": "Maak de extra logs van de docker container zichtbaar voor de studenten", + "showArtifacts": "Maak de artefacten van de docker container zichtbaar voor de studenten" } }, "submissions": { @@ -125,7 +126,7 @@ "year": "Academiejaar", "enroll": "Inschrijven", "leave": "Uitschrijven", - "noProjects": "Geen projecten beschikbaar voor dit vak \uD83D\uDE2D", + "noProjects": "Geen projecten beschikbaar voor dit vak", "teachersAndAssistants": { "title": "Lesgevers", "enroll": "Voeg toe als {0}", @@ -137,7 +138,7 @@ "noRole": "Geen", "placeholder": "Zoek een gebruiker op naam", "title": "Zoek gebuikers om aan dit vak toe te voegen", - "results": "\uD83D\uDD0E 1 gebruiker gevonden voor ingestelde filters | \uD83D\uDD0E {count} gebruikers gevonden voor ingestelde filters" + "results": "1 gebruiker gevonden voor ingestelde filters | {count} gebruikers gevonden voor ingestelde filters" } }, "search": { @@ -146,7 +147,7 @@ "year": "Academiejaar", "placeholder": "Zoek een vak op naam", "title": "Zoek een vak", - "results": "\uD83D\uDD0E 1 vak gevonden voor ingestelde filters | \uD83D\uDD0E {count} vakken gevonden voor ingestelde filters" + "results": "1 vak gevonden voor ingestelde filters | {count} vakken gevonden voor ingestelde filters" }, "share": { "title": "Activeer invitatielink", @@ -182,7 +183,7 @@ "card": { "open": "Details", "newProject": "Nieuw project", - "noSubmissions": "Dit project heeft geen indieningen \uD83D\uDE2D", + "noSubmissions": "Dit project heeft geen indieningen", "submissions": "Indiening | Indieningen", "groups": "Groep | Groepen", "structureTestsSucceed": "Geslaagde structuur testen", @@ -192,16 +193,16 @@ }, "list": { "noProjects": { - "student": "Geen lopende projecten gevonden voor alle ingeschreven vakken \uD83D\uDE2D. Schrijf in op een openbaar vak met de zoekfunctie, of gebruik een uitnodiginslink van een lesgever.", - "teacher": "Geen lopende projecten gevonden voor de vakken waarvoor je lesgever bent . \uD83D\uDE2D. Maak een nieuw project voor een vak met onderstaande knop." + "student": "Geen lopende projecten gevonden voor alle ingeschreven vakken. Schrijf in op een openbaar vak met de zoekfunctie, of gebruik een uitnodiginslink van een lesgever.", + "teacher": "Geen lopende projecten gevonden voor de vakken waarvoor je lesgever bent. Maak een nieuw project voor een vak met onderstaande knop." }, "noCourses": { - "student": "Geen vakken gevonden \uD83D\uDE2D. Schrijf in op een openbaar vak met de zoekfunctie, of gebruik een uitnodiginslink van een lesgever.", - "teacher": "Geen vakken gevonden \uD83D\uDE2D. Maak een vak aan met onderstaande knop.", - "search": "Geen vakken gevonden voor de gegeven zoekcriteria \uD83D\uDE2D." + "student": "Geen vakken gevonden. Schrijf in op een openbaar vak met de zoekfunctie, of gebruik een uitnodiginslink van een lesgever.", + "teacher": "Geen vakken gevonden. Maak een vak aan met onderstaande knop.", + "search": "Geen vakken gevonden voor de gegeven zoekcriteria." }, - "noResults": "Geen resultaten \uD83D\uDE2D.", - "noIncomingProjects": "Geen projecten met een deadline binnen de 7 dagen \uD83E\uDD73.", + "noResults": "Geen resultaten.", + "noIncomingProjects": "Geen projecten met een deadline binnen de 7 dagen.", "selectCourse": "Selecteer het vak waarvoor je een project wil maken:", "showPastProjects": "Projecten met verstreken deadline" } @@ -327,7 +328,7 @@ "owner": "Eigenaar ID", "public": "Publiek" }, - "noneFound": "Geen overeenkomende data gevonden \uD83D\uDE2D.", + "noneFound": "Geen overeenkomende data gevonden.", "loading": "Aan het laden. Wacht even aub.", "safeGuard": "Bent u het zeker?" }, diff --git a/frontend/src/components/projects/ExtraChecksUpload.vue b/frontend/src/components/projects/ExtraChecksUpload.vue index 667768f9..cc76776c 100644 --- a/frontend/src/components/projects/ExtraChecksUpload.vue +++ b/frontend/src/components/projects/ExtraChecksUpload.vue @@ -212,6 +212,14 @@ async function onDockerImageUpload(event: any): Promise { + + +
+
+ + +
+
diff --git a/frontend/src/composables/services/extra_checks.service.ts b/frontend/src/composables/services/extra_checks.service.ts index 4f94fec7..607e0ca4 100644 --- a/frontend/src/composables/services/extra_checks.service.ts +++ b/frontend/src/composables/services/extra_checks.service.ts @@ -34,6 +34,7 @@ export function useExtraCheck(): ExtraCheckState { time_limit: extraCheckData.time_limit, memory_limit: extraCheckData.memory_limit, show_log: extraCheckData.show_log, + show_artifact: extraCheckData.show_artifact, }, extraCheck, ExtraCheck.fromJSON, diff --git a/frontend/src/types/ExtraCheck.ts b/frontend/src/types/ExtraCheck.ts index c6b7c73e..18ff0352 100644 --- a/frontend/src/types/ExtraCheck.ts +++ b/frontend/src/types/ExtraCheck.ts @@ -21,6 +21,7 @@ export class ExtraCheck { public time_limit: number = 30, public memory_limit: number = 128, public show_log: boolean = true, + public show_artifact: boolean = true, public docker_image: DockerImage = new DockerImage(), ) {} @@ -37,6 +38,7 @@ export class ExtraCheck { extraCheck.time_limit, extraCheck.memory_limit, extraCheck.show_log, + extraCheck.show_artifact, ); } } From 2a37d29b01378e31a97c58641afe79493037f927 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 12:05:48 +0200 Subject: [PATCH 03/10] chore: remove option private docker images --- frontend/src/assets/lang/app/en.json | 4 ++-- frontend/src/assets/lang/app/nl.json | 2 +- .../components/projects/ExtraChecksUpload.vue | 15 ++++++++++++++- .../src/components/projects/ProjectForm.vue | 13 ++++++++++++- .../src/views/projects/CreateProjectView.vue | 17 ++++++++++++++++- .../src/views/projects/UpdateProjectView.vue | 17 ++++++++++++++++- 6 files changed, 61 insertions(+), 7 deletions(-) diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index 8deb7638..8f8367f8 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -79,7 +79,7 @@ }, "extraChecks": { "title": "Automatic checks on a submission", - "empty": "No checks addeed", + "empty": "No checks added", "add": "New check", "name": "Name", "public": "Public", @@ -88,7 +88,7 @@ "timeLimit": "Time limit for execution (in seconds)", "memoryLimit": "Memory limit for execution (in MB)", "showLog": "Make the extra logs of the docker container visible to the students", - "showArtifact": "Make the artifacts of the docker container visible to the students" + "showArtifact": "Make the artifacts visible to the students" } }, "submissions": { diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index af4af6b3..160e5959 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -89,7 +89,7 @@ "timeLimit": "Tijdslimiet voor de uitvoering (in seconden)", "memoryLimit": "Geheugenlimiet voor de uitvoering (in MB)", "showLog": "Maak de extra logs van de docker container zichtbaar voor de studenten", - "showArtifacts": "Maak de artefacten van de docker container zichtbaar voor de studenten" + "showArtifact": "Maak de artefacten zichtbaar voor de studenten" } }, "submissions": { diff --git a/frontend/src/components/projects/ExtraChecksUpload.vue b/frontend/src/components/projects/ExtraChecksUpload.vue index cc76776c..9c259baa 100644 --- a/frontend/src/components/projects/ExtraChecksUpload.vue +++ b/frontend/src/components/projects/ExtraChecksUpload.vue @@ -24,7 +24,7 @@ defineProps<{ dockerImages: DockerImage[] }>(); const extraChecks = defineModel(); /* Emits */ -const emit = defineEmits(['create:docker-image']); +const emit = defineEmits(['create:docker-image', 'delete:docker-image']); /* State for the dialog to create an extra check */ const displayExtraCheckCreation = ref(false); @@ -249,6 +249,19 @@ async function onDockerImageUpload(event: any): Promise { + + + diff --git a/frontend/src/components/projects/ProjectForm.vue b/frontend/src/components/projects/ProjectForm.vue index 570db1e1..8be2d2c2 100644 --- a/frontend/src/components/projects/ProjectForm.vue +++ b/frontend/src/components/projects/ProjectForm.vue @@ -24,7 +24,7 @@ const props = defineProps<{ }>(); /* Emits */ -const emit = defineEmits(['update:project', 'create:docker-image']); +const emit = defineEmits(['update:project', 'create:docker-image', 'delete:docker-image']); /* Composable injections */ const { t } = useI18n(); @@ -72,6 +72,16 @@ function saveDockerImage(image: DockerImage, file: File): void { emit('create:docker-image', image, file); } +/** + * Delete the docker image by emitting its new value. + * + * @param image + */ +function removeDockerImage(image: DockerImage): void { + emit('delete:docker-image', image); +} + + /** * Watch for changes in the project prop and update the form values. */ @@ -217,6 +227,7 @@ watchEffect(() => { v-model="form.extra_checks" :docker-images="dockerImages" @create:docker-image="saveDockerImage" + @delete:docker-image="removeDockerImage" />
diff --git a/frontend/src/views/projects/CreateProjectView.vue b/frontend/src/views/projects/CreateProjectView.vue index 314880e4..7f0c9f86 100644 --- a/frontend/src/views/projects/CreateProjectView.vue +++ b/frontend/src/views/projects/CreateProjectView.vue @@ -28,7 +28,7 @@ const { course, getCourseByID } = useCourses(); const { project, createProject } = useProject(); const { setStructureChecks } = useStructureCheck(); const { setExtraChecks } = useExtraCheck(); -const { dockerImages, getDockerImages, createDockerImage } = useDockerImages(); +const { dockerImages, getDockerImages, createDockerImage, deleteDockerImage } = useDockerImages(); /* State */ const loading = ref(true); @@ -75,6 +75,20 @@ async function saveDockerImage(dockerImage: DockerImage, file: File): Promise { + try { + await deleteDockerImage(dockerImage.id); + await getDockerImages(); + } catch (error: any) { + processError(error); + } +} + /* Load course data */ watchImmediate( () => params.courseId.toString(), @@ -106,6 +120,7 @@ watchImmediate( :docker-images="dockerImages" @update:project="(project, numberOfGroups) => saveProject(project, numberOfGroups)" @create:docker-image="saveDockerImage" + @delete:docker-image="removeDockerImage" /> diff --git a/frontend/src/views/projects/UpdateProjectView.vue b/frontend/src/views/projects/UpdateProjectView.vue index 6d7dadf4..275bcb7c 100644 --- a/frontend/src/views/projects/UpdateProjectView.vue +++ b/frontend/src/views/projects/UpdateProjectView.vue @@ -27,7 +27,7 @@ const { addErrorMessage } = useMessagesStore(); const { project, updateProject, getProjectByID } = useProject(); const { structureChecks, setStructureChecks, getStructureCheckByProject } = useStructureCheck(); const { extraChecks, setExtraChecks, deleteExtraCheck, getExtraChecksByProject } = useExtraCheck(); -const { dockerImages, getDockerImages, createDockerImage } = useDockerImages(); +const { dockerImages, getDockerImages, createDockerImage, deleteDockerImage } = useDockerImages(); /* State */ const loading = ref(true); @@ -92,6 +92,20 @@ async function saveDockerImage(dockerImage: DockerImage, file: File): Promise { + try { + await deleteDockerImage(dockerImage.id); + await getDockerImages(); + } catch (error: any) { + processError(error); + } +} + /* Load project data */ watchImmediate( () => params.projectId, @@ -140,6 +154,7 @@ watchImmediate( :project="project" :docker-images="dockerImages" @create:docker-image="saveDockerImage" + @delete:docker-image="removeDockerImage" @update:project="saveProject" /> From c6429740bce399f95d6dee5b179a63ea2f66b078 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 12:38:53 +0200 Subject: [PATCH 04/10] fix: automatically joining individual projects --- backend/api/views/course_view.py | 8 ++++---- frontend/src/components/projects/ProjectForm.vue | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/backend/api/views/course_view.py b/backend/api/views/course_view.py index 4fd3238a..d95e4ace 100644 --- a/backend/api/views/course_view.py +++ b/backend/api/views/course_view.py @@ -197,8 +197,8 @@ def _add_student(self, request: Request, **_): individual_projects = course.projects.filter(group_size=1) for project in individual_projects: - # Check if the start date of the project is in the future - if project.start_date > timezone.now(): + # Check if the deadline of the project is in the future + if project.deadline > timezone.now(): group = Group.objects.create( project=project ) @@ -211,8 +211,8 @@ def _add_student(self, request: Request, **_): all_projects = course.projects.exclude(group_size=1) for project in all_projects: - # Check if the start date of the project is in the future - if project.start_date > timezone.now(): + # Check if the deadline of the project is in the future + if project.deadline > timezone.now(): number_groups = project.groups.count() if project.group_size * number_groups < course.students.count(): diff --git a/frontend/src/components/projects/ProjectForm.vue b/frontend/src/components/projects/ProjectForm.vue index 1606f4f7..210893f3 100644 --- a/frontend/src/components/projects/ProjectForm.vue +++ b/frontend/src/components/projects/ProjectForm.vue @@ -171,7 +171,7 @@ watchEffect(() => {
From fc5f0518733212f97d90a466eb7c191e2ba8276b Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 13:03:48 +0200 Subject: [PATCH 05/10] fix: translations --- frontend/src/assets/lang/app/en.json | 3 ++- frontend/src/assets/lang/app/nl.json | 7 +++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index c755196c..8352df0d 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -59,12 +59,12 @@ "noGroupMembers": "No group members", "publishScores": "Publish scores", "groupName": "Group name", - "groups": "Groups", "groupPopulation": "Size", "groupStatus": "Status", "start": "Start date", "submissionStatus": "Submission status", "group": "Group", + "groups": "Groups", "groupSize": "Individual | Groups of {count} people", "noGroups": "No groups available", "groupMembers": "Group members", @@ -72,6 +72,7 @@ "joinGroup": "Join group", "leaveGroup": "Leave group", "create": "Create new project", + "save": "Save project", "edit": "Save project", "name": "Project name", "description": "Description", diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index 978e7413..d55e071b 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -48,17 +48,17 @@ }, "projects": { "all": "Alle projecten", - "coming": "Aankomende deadlines", "backToCourse": "Terug naar het vak", + "coming": "Aankomende deadlines", "deadline": "Deadline", "days": "Vandaag om {hour} | Morgen om {hour} | Over {count} dagen", "ago": "1 dag geleden | {count} dagen geleden", "chooseGroupMessage": "Kies een groep voor {0}", "groupScore": "Groepsscore", "noGroupScore": "Nog geen score", + "noGroupMembers": "Geen groepsleden", "publishScores": "Publiceer scores", "groupName": "Groepsnaam", - "noGroupMembers": "Geen groepsleden", "groupPopulation": "Grootte", "groupStatus": "Status", "start": "Startdatum", @@ -94,8 +94,8 @@ }, "extraChecks": { "title": "Automatische checks op een indiening", - "add": "Nieuwe check", "empty": "Nog geen extra checks toegevoegd", + "add": "Nieuwe check", "name": "Naam", "public": "Publiek", "bashScript": "Bash script", @@ -215,7 +215,6 @@ "teacher": "Geen vakken gevonden. Maak een vak aan met onderstaande knop.", "search": "Geen vakken gevonden voor de gegeven zoekcriteria." }, - "noResults": "Geen resultaten.", "noIncomingProjects": "Geen projecten met een deadline binnen de 7 dagen.", "selectCourse": "Selecteer het vak waarvoor je een project wil maken:", "showPastProjects": "Projecten met verstreken deadline" From 64cdf6af8a363d21d22cd9967a7800cb2ef7e555 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 13:08:24 +0200 Subject: [PATCH 06/10] fix: linting --- frontend/src/components/projects/ProjectForm.vue | 1 - frontend/src/views/projects/CreateProjectView.vue | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/projects/ProjectForm.vue b/frontend/src/components/projects/ProjectForm.vue index 210893f3..dbdb2a36 100644 --- a/frontend/src/components/projects/ProjectForm.vue +++ b/frontend/src/components/projects/ProjectForm.vue @@ -81,7 +81,6 @@ function removeDockerImage(image: DockerImage): void { emit('delete:docker-image', image); } - /** * Watch for changes in the project prop and update the form values. */ diff --git a/frontend/src/views/projects/CreateProjectView.vue b/frontend/src/views/projects/CreateProjectView.vue index 99400c55..e5f71fc6 100644 --- a/frontend/src/views/projects/CreateProjectView.vue +++ b/frontend/src/views/projects/CreateProjectView.vue @@ -80,7 +80,7 @@ async function saveDockerImage(dockerImage: DockerImage, file: File): Promise { +async function removeDockerImage(dockerImage: DockerImage): Promise { try { await deleteDockerImage(dockerImage.id); await getDockerImages(); From bfe5ae679b444d1e2736205c6330672da3c1b909 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 14:25:42 +0200 Subject: [PATCH 07/10] chore: confirm dialog before deletion --- frontend/src/assets/lang/app/en.json | 4 +- frontend/src/assets/lang/app/nl.json | 4 +- .../projects/ProjectExtraChecksEditor.vue | 54 +++++++++++++++++-- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/frontend/src/assets/lang/app/en.json b/frontend/src/assets/lang/app/en.json index 8352df0d..7bfdae5a 100644 --- a/frontend/src/assets/lang/app/en.json +++ b/frontend/src/assets/lang/app/en.json @@ -100,6 +100,7 @@ "public": "Public", "bashScript": "Bash script", "dockerImage": "Docker image", + "deleteDockerImage": "Delete docker image", "timeLimit": "Time limit for execution (in seconds)", "memoryLimit": "Memory limit for execution (in MB)", "showLog": "Make the extra logs of the docker container visible to the students", @@ -309,7 +310,8 @@ "cloneCourse": "Are you sure you want to clone this coure? This will create the same course for the next academic year.", "joinCourse": "Are you sure you want to enroll in this course? This will give you access to all projects, assignments, ...", "leaveCourse": "Are you sure you want to leave this course? You will no longer have access to this course.", - "shareCourse": "By activating the invitation link, you allow students in possession of this link to enroll in this course. Please copy the generated link, only when you click on \"Activate invitation link\" will this link become active." + "shareCourse": "By activating the invitation link, you allow students in possession of this link to enroll in this course. Please copy the generated link, only when you click on \"Activate invitation link\" will this link become active.", + "deleteDockerImage": "Are you sure you want to delete this docker image? This will also remove all the checks that use this image." }, "admin": { "title": "Admin", diff --git a/frontend/src/assets/lang/app/nl.json b/frontend/src/assets/lang/app/nl.json index d55e071b..17501bd4 100644 --- a/frontend/src/assets/lang/app/nl.json +++ b/frontend/src/assets/lang/app/nl.json @@ -100,6 +100,7 @@ "public": "Publiek", "bashScript": "Bash script", "dockerImage": "Docker image", + "deleteDockerImage": "Verwijder docker image", "timeLimit": "Tijdslimiet voor de uitvoering (in seconden)", "memoryLimit": "Geheugenlimiet voor de uitvoering (in MB)", "showLog": "Maak de extra logs van de docker container zichtbaar voor de studenten", @@ -309,7 +310,8 @@ "cloneCourse": "Ben je zeker dat je dit vak wil klonen? Dit zal hetzelfde vak aanmaken voor het volgende academiejaar.", "joinCourse": "Ben je zeker dat je jezelf wil inschrijven voor dit vak? Dit zal je toegang geven tot alle projecten, opdrachten, ...", "leaveCourse": "Ben je zeker dat je dit vak wil verlaten? Je zal geen toegang meer hebben tot dit vak.", - "shareCourse": "Door het activeren van de invitatielink staat u studenten in bezit van deze link toe zich in te schrijven voor dit vak. Gelieve de gegenereerde link te kopiƫren, pas wanneer u op \"Activeer invitatielink\" klikt zal deze link actief worden." + "shareCourse": "Door het activeren van de invitatielink staat u studenten in bezit van deze link toe zich in te schrijven voor dit vak. Gelieve de gegenereerde link te kopiƫren, pas wanneer u op \"Activeer invitatielink\" klikt zal deze link actief worden.", + "deleteDockerImage": "Ben je zeker dat je deze docker image wil verwijderen? Dit zal alle checks die deze image gebruiken ook verwijderen." }, "admin": { "title": "Beheerder", diff --git a/frontend/src/components/projects/ProjectExtraChecksEditor.vue b/frontend/src/components/projects/ProjectExtraChecksEditor.vue index 9c259baa..11ce36c1 100644 --- a/frontend/src/components/projects/ProjectExtraChecksEditor.vue +++ b/frontend/src/components/projects/ProjectExtraChecksEditor.vue @@ -9,6 +9,8 @@ import InputSwitch from 'primevue/inputswitch'; import DataTable from 'primevue/datatable'; import Column from 'primevue/column'; import ErrorMessage from '@/components/forms/ErrorMessage.vue'; +import ConfirmDialog from 'primevue/confirmdialog'; +import { useConfirm } from 'primevue/useconfirm'; import { DockerImage } from '@/types/DockerImage'; import { ExtraCheck } from '@/types/ExtraCheck'; import { useI18n } from 'vue-i18n'; @@ -18,6 +20,7 @@ import { useVuelidate } from '@vuelidate/core'; /* Composable injections */ const { t } = useI18n(); +const confirm = useConfirm(); /* Props */ defineProps<{ dockerImages: DockerImage[] }>(); @@ -32,6 +35,9 @@ const displayExtraCheckCreation = ref(false); /* Form content */ let form = reactive(new ExtraCheck()); +// State to manage the visibility of the confirmation dialog and the selected docker image +const confirmDialogVisible = ref(false); + // Define validation rules for each form field const rules = computed(() => { return { @@ -92,6 +98,34 @@ async function onDockerImageUpload(event: any): Promise { emit('create:docker-image', dockerImage, event.files[0]); } + +/** + * Function to remove the docker image from the list of docker images + */ + async function confirmDelete(dockerImage: DockerImage): Promise { + confirmDialogVisible.value = true; + removeDockerImage(dockerImage); +} + +/** + * Function to remove the docker image from the list of docker images + */ + async function removeDockerImage(dockerImage: DockerImage): Promise { + // Show a confirmation dialog before removing the image, to prevent accidental clicks + confirm.require({ + message: t('confirmations.deleteDockerImage'), + header: t('views.projects.extraChecks.deleteDockerImage'), + accept: () => { + (async () => { + emit('delete:docker-image', dockerImage); + confirmDialogVisible.value = false; + })(); + }, + reject: () => { + confirmDialogVisible.value = false; + }, + }); +} From 33499046f1263747b51d42ab685ad88b81142a47 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 15:01:43 +0200 Subject: [PATCH 08/10] fix: vue validate error + form selection --- .../components/projects/ProjectExtraChecksEditor.vue | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/frontend/src/components/projects/ProjectExtraChecksEditor.vue b/frontend/src/components/projects/ProjectExtraChecksEditor.vue index 11ce36c1..64ff521e 100644 --- a/frontend/src/components/projects/ProjectExtraChecksEditor.vue +++ b/frontend/src/components/projects/ProjectExtraChecksEditor.vue @@ -50,7 +50,7 @@ const rules = computed(() => { }); // useVuelidate function to perform form validation -const v$ = useVuelidate(rules, form); +let v$ = useVuelidate(rules, form); /** * Function to save the extra check @@ -68,10 +68,10 @@ async function saveExtraCheck(): Promise { displayExtraCheckCreation.value = false; // Reset the form - form = new ExtraCheck(); + form = reactive(new ExtraCheck()); // Reset the validation - v$.value.$reset(); + v$ = useVuelidate(rules, form); } } @@ -102,7 +102,7 @@ async function onDockerImageUpload(event: any): Promise { /** * Function to remove the docker image from the list of docker images */ - async function confirmDelete(dockerImage: DockerImage): Promise { +async function confirmDelete(dockerImage: DockerImage): Promise { confirmDialogVisible.value = true; removeDockerImage(dockerImage); } @@ -110,7 +110,7 @@ async function onDockerImageUpload(event: any): Promise { /** * Function to remove the docker image from the list of docker images */ - async function removeDockerImage(dockerImage: DockerImage): Promise { +async function removeDockerImage(dockerImage: DockerImage): Promise { // Show a confirmation dialog before removing the image, to prevent accidental clicks confirm.require({ message: t('confirmations.deleteDockerImage'), @@ -307,7 +307,7 @@ async function onDockerImageUpload(event: any): Promise { @click="confirmDelete(slotProps.data)" outlined rounded - /> + /> From 33a8840280997739bbdb20717c0f0ec839930967 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 15:16:01 +0200 Subject: [PATCH 09/10] fix: cleanup --- .../projects/ProjectExtraChecksEditor.vue | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/frontend/src/components/projects/ProjectExtraChecksEditor.vue b/frontend/src/components/projects/ProjectExtraChecksEditor.vue index 64ff521e..0337191b 100644 --- a/frontend/src/components/projects/ProjectExtraChecksEditor.vue +++ b/frontend/src/components/projects/ProjectExtraChecksEditor.vue @@ -35,7 +35,7 @@ const displayExtraCheckCreation = ref(false); /* Form content */ let form = reactive(new ExtraCheck()); -// State to manage the visibility of the confirmation dialog and the selected docker image +// State to manage the visibility of the confirmation dialog for the deletion of a docker image const confirmDialogVisible = ref(false); // Define validation rules for each form field @@ -99,19 +99,12 @@ async function onDockerImageUpload(event: any): Promise { emit('create:docker-image', dockerImage, event.files[0]); } -/** - * Function to remove the docker image from the list of docker images - */ -async function confirmDelete(dockerImage: DockerImage): Promise { - confirmDialogVisible.value = true; - removeDockerImage(dockerImage); -} - /** * Function to remove the docker image from the list of docker images */ async function removeDockerImage(dockerImage: DockerImage): Promise { // Show a confirmation dialog before removing the image, to prevent accidental clicks + confirmDialogVisible.value = true; confirm.require({ message: t('confirmations.deleteDockerImage'), header: t('views.projects.extraChecks.deleteDockerImage'), @@ -304,7 +297,7 @@ async function removeDockerImage(dockerImage: DockerImage): Promise { v-if="!slotProps.data.public" icon="pi pi-trash" class="p-button-danger p-button-sm" - @click="confirmDelete(slotProps.data)" + @click="removeDockerImage(slotProps.data)" outlined rounded /> From 27ff6ab77a7d22464a7e52b58ef33edbfaf8ba58 Mon Sep 17 00:00:00 2001 From: Bram Meir Date: Thu, 23 May 2024 16:40:06 +0200 Subject: [PATCH 10/10] fix: empty docker image --- frontend/src/components/projects/ProjectExtraChecksEditor.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/projects/ProjectExtraChecksEditor.vue b/frontend/src/components/projects/ProjectExtraChecksEditor.vue index 0337191b..22d26175 100644 --- a/frontend/src/components/projects/ProjectExtraChecksEditor.vue +++ b/frontend/src/components/projects/ProjectExtraChecksEditor.vue @@ -42,7 +42,9 @@ const confirmDialogVisible = ref(false); const rules = computed(() => { return { name: { required: helpers.withMessage(t('validations.required'), required) }, - docker_image: { required: helpers.withMessage(t('validations.required'), required) }, + docker_image: { + required: helpers.withMessage(t('validations.required'), (value: DockerImage) => value.id !== ''), + }, file: { required: helpers.withMessage(t('validations.required'), required) }, time_limit: { required: helpers.withMessage(t('validations.required'), required) }, memory_limit: { required: helpers.withMessage(t('validations.required'), required) },