From 69659426dee62396a9f5a976a9f3bf52cade151f Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 18:31:06 +0200 Subject: [PATCH 001/172] fix: werkende aan assignment fix --- backend/src/controllers/assignments.ts | 6 +++++ .../data/assignments/assignment-repository.ts | 4 +-- .../entities/assignments/assignment.entity.ts | 2 +- backend/src/interfaces/assignment.ts | 24 +++++++++-------- backend/src/services/assignments.ts | 27 ++++++++++++++----- common/src/interfaces/assignment.ts | 2 +- 6 files changed, 44 insertions(+), 21 deletions(-) diff --git a/backend/src/controllers/assignments.ts b/backend/src/controllers/assignments.ts index 2ecb35cb..adb0e479 100644 --- a/backend/src/controllers/assignments.ts +++ b/backend/src/controllers/assignments.ts @@ -12,6 +12,8 @@ import { requireFields } from './error-helper.js'; import { BadRequestException } from '../exceptions/bad-request-exception.js'; import { Assignment } from '../entities/assignments/assignment.entity.js'; import { EntityDTO } from '@mikro-orm/core'; +import { createGroup } from '../services/groups.js'; +import { getLogger } from '../logging/initalize.js'; export async function getAllAssignmentsHandler(req: Request, res: Response): Promise { const classId = req.params.classid; @@ -32,8 +34,12 @@ export async function createAssignmentHandler(req: Request, res: Response): Prom requireFields({ description, language, learningPath, title }); const assignmentData = req.body as AssignmentDTO; + Object.entries(assignmentData).forEach(getLogger().info); const assignment = await createAssignment(classid, assignmentData); + // should probably use Promise.all + //assignmentData.groups.forEach(group => await createGroup({}, classid, assignment.id)); + res.json({ assignment }); } diff --git a/backend/src/data/assignments/assignment-repository.ts b/backend/src/data/assignments/assignment-repository.ts index 3de5031d..2645de1e 100644 --- a/backend/src/data/assignments/assignment-repository.ts +++ b/backend/src/data/assignments/assignment-repository.ts @@ -4,10 +4,10 @@ import { Class } from '../../entities/classes/class.entity.js'; export class AssignmentRepository extends DwengoEntityRepository { public async findByClassAndId(within: Class, id: number): Promise { - return this.findOne({ within: within, id: id }); + return this.findOne({ within: within, id: id }, { populate: [ "groups" ]}); } public async findAllAssignmentsInClass(within: Class): Promise { - return this.findAll({ where: { within: within } }); + return this.findAll({ where: { within: within }, populate: [ "groups" ] }); } public async deleteByClassAndId(within: Class, id: number): Promise { return this.deleteWhere({ within: within, id: id }); diff --git a/backend/src/entities/assignments/assignment.entity.ts b/backend/src/entities/assignments/assignment.entity.ts index 36b24344..14d22756 100644 --- a/backend/src/entities/assignments/assignment.entity.ts +++ b/backend/src/entities/assignments/assignment.entity.ts @@ -14,7 +14,7 @@ export class Assignment { }) within!: Class; - @PrimaryKey({ type: 'number', autoincrement: true }) + @PrimaryKey({ type: 'integer', autoincrement: true }) id?: number; @Property({ type: 'string' }) diff --git a/backend/src/interfaces/assignment.ts b/backend/src/interfaces/assignment.ts index 7abb3d3c..a3c41e48 100644 --- a/backend/src/interfaces/assignment.ts +++ b/backend/src/interfaces/assignment.ts @@ -4,6 +4,9 @@ import { Assignment } from '../entities/assignments/assignment.entity.js'; import { Class } from '../entities/classes/class.entity.js'; import { getLogger } from '../logging/initalize.js'; import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment'; +import { mapToGroupDTO, mapToGroupDTOId } from './group.js'; +import { getAssignmentHandler } from '../controllers/assignments.js'; +import { getAssignmentRepository, getClassRepository } from '../data/repositories.js'; export function mapToAssignmentDTOId(assignment: Assignment): AssignmentDTO { return { @@ -13,6 +16,7 @@ export function mapToAssignmentDTOId(assignment: Assignment): AssignmentDTO { description: assignment.description, learningPath: assignment.learningPathHruid, language: assignment.learningPathLanguage, + groups: assignment.groups.map(mapToGroupDTOId), }; } @@ -24,19 +28,17 @@ export function mapToAssignmentDTO(assignment: Assignment): AssignmentDTO { description: assignment.description, learningPath: assignment.learningPathHruid, language: assignment.learningPathLanguage, - // Groups: assignment.groups.map(mapToGroupDTO), + groups: assignment.groups.map(mapToGroupDTO), }; } export function mapToAssignment(assignmentData: AssignmentDTO, cls: Class): Assignment { - const assignment = new Assignment(); - assignment.title = assignmentData.title; - assignment.description = assignmentData.description; - assignment.learningPathHruid = assignmentData.learningPath; - assignment.learningPathLanguage = languageMap[assignmentData.language] || FALLBACK_LANG; - assignment.within = cls; - - getLogger().debug(assignment); - - return assignment; + return getAssignmentRepository().create({ + within: cls, + title: assignmentData.title, + description: assignmentData.description, + learningPathHruid: assignmentData.learningPath, + learningPathLanguage: languageMap[assignmentData.language], + groups: [], + }) } diff --git a/backend/src/services/assignments.ts b/backend/src/services/assignments.ts index 5fd8f67f..99b6e20b 100644 --- a/backend/src/services/assignments.ts +++ b/backend/src/services/assignments.ts @@ -14,8 +14,10 @@ import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submissi import { fetchClass } from './classes.js'; import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question'; import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission'; -import { EntityDTO } from '@mikro-orm/core'; +import { assign, EntityDTO } from '@mikro-orm/core'; import { putObject } from './service-helper.js'; +import { getLogger } from '../logging/initalize.js'; +import { languageMap } from '@dwengo-1/common/util/language'; export async function fetchAssignment(classid: string, assignmentNumber: number): Promise { const classRepository = getClassRepository(); @@ -51,13 +53,26 @@ export async function getAllAssignments(classid: string, full: boolean): Promise export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise { const cls = await fetchClass(classid); - const assignment = mapToAssignment(assignmentData, cls); - const assignmentRepository = getAssignmentRepository(); - const newAssignment = assignmentRepository.create(assignment); - await assignmentRepository.save(newAssignment, { preventOverwrite: true }); + const assignment = assignmentRepository.create({ + within: cls, + title: assignmentData.title, + description: assignmentData.description, + learningPathHruid: assignmentData.learningPath, + learningPathLanguage: languageMap[assignmentData.language], + groups: [], + }) + // const assignment = mapToAssignment(assignmentData, cls); + Object.entries(assignmentData).forEach(getLogger().info); - return mapToAssignmentDTO(newAssignment); + try { + await assignmentRepository.save(assignment, { preventOverwrite: true }); + } catch(e) { + getLogger().error(e); + } + + getLogger().info(`Saved assignment ${assignment.id}`); + return mapToAssignmentDTO(assignment); } export async function getAssignment(classid: string, id: number): Promise { diff --git a/common/src/interfaces/assignment.ts b/common/src/interfaces/assignment.ts index 5cb8feff..b5fe8a79 100644 --- a/common/src/interfaces/assignment.ts +++ b/common/src/interfaces/assignment.ts @@ -7,5 +7,5 @@ export interface AssignmentDTO { description: string; learningPath: string; language: string; - groups?: GroupDTO[] | string[]; // TODO + groups: GroupDTO[] | string[]; } From 1c99b03554c02869a5ec69c7645de744fce619ce Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:26:38 +0200 Subject: [PATCH 002/172] feat: class queries useClass en useClasses --- frontend/src/queries/classes.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 frontend/src/queries/classes.ts diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts new file mode 100644 index 00000000..68e07cc0 --- /dev/null +++ b/frontend/src/queries/classes.ts @@ -0,0 +1,30 @@ +import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +const classController = new ClassController(); + +function classesQueryKey() { + return ["students"]; +} +function classQueryKey(classid: string) { + return ["student", classid]; +} + + +export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => (classesQueryKey())), + queryFn: async () => classController.getAll(toValue(full)), + }); +} + +export function useClassQuery( + id: MaybeRefOrGetter, +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classQueryKey(toValue(id)!)), + queryFn: async () => classController.getById(toValue(id)!), + enabled: () => Boolean(toValue(id)), + }); +} \ No newline at end of file From c05ad9a702c61e3fe60011c37095676eec0891a0 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:42:02 +0200 Subject: [PATCH 003/172] feat: class queries students, teachers, teacher invitations GET --- frontend/src/queries/classes.ts | 48 ++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 68e07cc0..897d7e9e 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -1,16 +1,25 @@ import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; +import type { StudentsResponse } from "@/controllers/students"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); function classesQueryKey() { - return ["students"]; + return ["classes"]; } function classQueryKey(classid: string) { - return ["student", classid]; + return ["class", classid]; +} +function classStudentsKey(classid: string) { + return ["class-students", classid]; +} +function classTeachersKey(classid: string) { + return ["class-teachers", classid]; +} +function classTeacherInvitationsKey(classid: string) { + return ["class-teacher-invitations", classid]; } - export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ @@ -27,4 +36,37 @@ export function useClassQuery( queryFn: async () => classController.getById(toValue(id)!), enabled: () => Boolean(toValue(id)), }); +} + +export function useClassStudentsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classStudentsKey(toValue(id)!)), + queryFn: async () => classController.getStudents(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }) +} + +export function useClassTeachersQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classTeachersKey(toValue(id)!)), + queryFn: async () => classController.getTeachers(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); +} + +export function useClassTeacherInvitationsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classTeacherInvitationsKey(toValue(id)!)), + queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); } \ No newline at end of file From a2ae4319577e28e8c9f8c2fad47dafca49c8e346 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 19:46:05 +0200 Subject: [PATCH 004/172] feat: class queries assignments GET --- frontend/src/queries/classes.ts | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 897d7e9e..00eb3bf9 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -20,6 +20,9 @@ function classTeachersKey(classid: string) { function classTeacherInvitationsKey(classid: string) { return ["class-teacher-invitations", classid]; } +function classAssignmentsKey(classid: string) { + return ["class-assignments", classid]; +} export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ @@ -69,4 +72,15 @@ export function useClassTeacherInvitationsQuery( queryFn: async () => classController.getTeacherInvitations(toValue(id)!, toValue(full)!), enabled: () => Boolean(toValue(id)), }); +} + +export function useClassAssignmentsQuery( + id: MaybeRefOrGetter, + full: MaybeRefOrGetter = true +): UseQueryReturnType { + return useQuery({ + queryKey: computed(() => classAssignmentsKey(toValue(id)!)), + queryFn: async () => classController.getAssignments(toValue(id)!, toValue(full)!), + enabled: () => Boolean(toValue(id)), + }); } \ No newline at end of file From 45a36a94a58f5df8548a17111e80156f26532f5b Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:16:28 +0200 Subject: [PATCH 005/172] feat: class query get, delete, put geimplementeerd --- frontend/src/queries/classes.ts | 45 ++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 00eb3bf9..6aca5e7f 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -1,6 +1,7 @@ import { ClassController, type ClassesResponse, type ClassResponse } from "@/controllers/classes"; import type { StudentsResponse } from "@/controllers/students"; -import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import type { ClassDTO } from "@dwengo-1/common/interfaces/class"; +import { QueryClient, useMutation, useQuery, useQueryClient, type UseMutationReturnType, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); @@ -24,6 +25,15 @@ function classAssignmentsKey(classid: string) { return ["class-assignments", classid]; } +async function invalidateAll(classid: string, queryClient: QueryClient): Promise { + await queryClient.invalidateQueries({ queryKey: ["classes"] }); + await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classAssignmentsKey(classid) }); + await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid) }); +} + export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ queryKey: computed(() => (classesQueryKey())), @@ -41,6 +51,39 @@ export function useClassQuery( }); } +export function useCreateClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => classController.createClass(data), + onSuccess: async () => { + await queryClient.invalidateQueries({ queryKey: ["classes"] }); + }, + }); +} + +export function useDeleteClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id) => classController.deleteClass(id), + onSuccess: async (data) => { + await invalidateAll(data.class.id, queryClient); + }, + }); +} + +export function useUpdateClassMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (data) => classController.updateClass(data.id, data), + onSuccess: async (data) => { + await invalidateAll(data.class.id, queryClient); + }, + }); +} + export function useClassStudentsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 02076212fee06958bbf2dd650277414ed8789694 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:23:17 +0200 Subject: [PATCH 006/172] feat: class query student en teacher POST --- frontend/src/queries/classes.ts | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 6aca5e7f..30ca7bee 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -6,6 +6,7 @@ import { computed, toValue, type MaybeRefOrGetter } from "vue"; const classController = new ClassController(); +/* Query cache keys */ function classesQueryKey() { return ["classes"]; } @@ -25,6 +26,7 @@ function classAssignmentsKey(classid: string) { return ["class-assignments", classid]; } +/* Function to invalidate all caches with certain class id */ async function invalidateAll(classid: string, queryClient: QueryClient): Promise { await queryClient.invalidateQueries({ queryKey: ["classes"] }); await queryClient.invalidateQueries({ queryKey: classQueryKey(classid) }); @@ -34,6 +36,7 @@ async function invalidateAll(classid: string, queryClient: QueryClient): Promise await queryClient.invalidateQueries({ queryKey: classTeacherInvitationsKey(classid) }); } +/* Queries */ export function useClassesQuery(full: MaybeRefOrGetter = true): UseQueryReturnType { return useQuery({ queryKey: computed(() => (classesQueryKey())), @@ -95,6 +98,18 @@ export function useClassStudentsQuery( }) } +export function useCreateClassStudentMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.addStudent(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + }, + }); +} + export function useClassTeachersQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true @@ -106,6 +121,18 @@ export function useClassTeachersQuery( }); } +export function useCreateClassTeacherMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.addTeacher(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + }, + }); +} + export function useClassTeacherInvitationsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 77ca390bd2ea1bc5cbeabf2f23df2a3c00164bc2 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:28:04 +0200 Subject: [PATCH 007/172] feat: class queries student & teacher DELETE --- frontend/src/queries/classes.ts | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/src/queries/classes.ts b/frontend/src/queries/classes.ts index 30ca7bee..1ecc0bce 100644 --- a/frontend/src/queries/classes.ts +++ b/frontend/src/queries/classes.ts @@ -98,7 +98,7 @@ export function useClassStudentsQuery( }) } -export function useCreateClassStudentMutation(): UseMutationReturnType { +export function useClassAddStudentMutation(): UseMutationReturnType { const queryClient = useQueryClient(); return useMutation({ @@ -110,6 +110,18 @@ export function useCreateClassStudentMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.deleteStudent(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classStudentsKey(data.class.id) }); + }, + }); +} + export function useClassTeachersQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true @@ -121,7 +133,7 @@ export function useClassTeachersQuery( }); } -export function useCreateClassTeacherMutation(): UseMutationReturnType { +export function useClassAddTeacherMutation(): UseMutationReturnType { const queryClient = useQueryClient(); return useMutation({ @@ -133,6 +145,18 @@ export function useCreateClassTeacherMutation(): UseMutationReturnType { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, username }) => classController.deleteTeacher(id, username), + onSuccess: async (data) => { + await queryClient.invalidateQueries({ queryKey: classQueryKey(data.class.id) }); + await queryClient.invalidateQueries({ queryKey: classTeachersKey(data.class.id) }); + }, + }); +} + export function useClassTeacherInvitationsQuery( id: MaybeRefOrGetter, full: MaybeRefOrGetter = true From 5c9314aa5968a37ecfda9ba868899c65ef87c8c4 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:53:41 +0200 Subject: [PATCH 008/172] feat: group queries voor alle GETs --- frontend/src/controllers/groups.ts | 6 ++- frontend/src/queries/groups.ts | 74 ++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 frontend/src/queries/groups.ts diff --git a/frontend/src/controllers/groups.ts b/frontend/src/controllers/groups.ts index de6592b5..361dd9d3 100644 --- a/frontend/src/controllers/groups.ts +++ b/frontend/src/controllers/groups.ts @@ -16,11 +16,15 @@ export class GroupController extends BaseController { super(`class/${classid}/assignments/${assignmentNumber}/groups`); } + update(classid: string, assignmentNumber: number) { + this.basePath = `class/${classid}/assignments/${assignmentNumber}/groups`; + } + async getAll(full = true): Promise { return this.get(`/`, { full }); } - async getByNumber(num: number): Promise { + async getByNumber(num: number | string): Promise { return this.get(`/${num}`); } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts new file mode 100644 index 00000000..6465ce87 --- /dev/null +++ b/frontend/src/queries/groups.ts @@ -0,0 +1,74 @@ +import type { ClassesResponse } from "@/controllers/classes"; +import { GroupController, type GroupResponse, type GroupsResponse } from "@/controllers/groups"; +import type { QuestionsResponse } from "@/controllers/questions"; +import type { SubmissionsResponse } from "@/controllers/submissions"; +import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; +import { computed, toValue, type MaybeRefOrGetter } from "vue"; + +const groupController = new GroupController('', 0); + +function groupsQueryKey(classid: string, assignmentNumber: number) { + return [ "groups", classid, assignmentNumber ]; +} +function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group", classid, assignmentNumber, groupNumber ]; +} +function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group-submissions", classid, assignmentNumber, groupNumber ]; +} + +export function useGroupsQuery( + classid: string, + assignmentNumber: number, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => (groupsQueryKey(classid, assignmentNumber))), + queryFn: async () => groupController.getAll(), + }); +} + +export function useGroupQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getByNumber(toValue(groupNumber)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} + +export function useGroupSubmissionsQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} + +export function useGroupQuestionsQuery( + classid: string, + assignmentNumber: number, + groupNumber: MaybeRefOrGetter, + full: MaybeRefOrGetter = true, +): UseQueryReturnType { + groupController.update(classid, assignmentNumber); + + return useQuery({ + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), + enabled: () => !isNaN(Number(toValue(groupNumber))), + }); +} From f8ac37397d63a7d177b8deee897c41b12f5dc7fb Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 21:56:39 +0200 Subject: [PATCH 009/172] fix: foute key in group query questions gefixt --- frontend/src/queries/groups.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index 6465ce87..a0acd37a 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -16,6 +16,9 @@ function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: n function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { return [ "group-submissions", classid, assignmentNumber, groupNumber ]; } +function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { + return [ "group-questions", classid, assignmentNumber, groupNumber ]; +} export function useGroupsQuery( classid: string, @@ -67,7 +70,7 @@ export function useGroupQuestionsQuery( groupController.update(classid, assignmentNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); From d0044fa2195faa5f6ed06e60795950066eb4fc12 Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 22:06:52 +0200 Subject: [PATCH 010/172] fix: group query toValue used in wrong places --- frontend/src/queries/groups.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index a0acd37a..dec35013 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -13,11 +13,11 @@ function groupsQueryKey(classid: string, assignmentNumber: number) { function groupQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { return [ "group", classid, assignmentNumber, groupNumber ]; } -function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "group-submissions", classid, assignmentNumber, groupNumber ]; +function groupSubmissionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { + return [ "group-submissions", classid, assignmentNumber, groupNumber, full ]; } -function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number) { - return [ "group-questions", classid, assignmentNumber, groupNumber ]; +function groupQuestionsQueryKey(classid: string, assignmentNumber: number, groupNumber: number, full: boolean) { + return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; } export function useGroupsQuery( @@ -55,7 +55,7 @@ export function useGroupSubmissionsQuery( groupController.update(classid, assignmentNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); @@ -67,10 +67,10 @@ export function useGroupQuestionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + groupController.update(toValue(classid)!, toValue(assignmentNumber)); return useQuery({ - queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), + queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), enabled: () => !isNaN(Number(toValue(groupNumber))), }); From 73e3871af0724aa11265bebabea3bfff533e219d Mon Sep 17 00:00:00 2001 From: Adriaan Jacquet Date: Wed, 9 Apr 2025 22:29:08 +0200 Subject: [PATCH 011/172] fix: groups queries types gefixt --- frontend/src/controllers/groups.ts | 36 ++++++++++--------- frontend/src/queries/groups.ts | 56 +++++++++++++++++++----------- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/frontend/src/controllers/groups.ts b/frontend/src/controllers/groups.ts index 361dd9d3..2ba54cb7 100644 --- a/frontend/src/controllers/groups.ts +++ b/frontend/src/controllers/groups.ts @@ -12,39 +12,43 @@ export interface GroupResponse { } export class GroupController extends BaseController { - constructor(classid: string, assignmentNumber: number) { - super(`class/${classid}/assignments/${assignmentNumber}/groups`); + constructor() { + super(''); } update(classid: string, assignmentNumber: number) { this.basePath = `class/${classid}/assignments/${assignmentNumber}/groups`; } - async getAll(full = true): Promise { - return this.get(`/`, { full }); + protected getBasePath(classid: string, assignmentNumber: number) { + return `class/${classid}/assignments/${assignmentNumber}/groups`; } - async getByNumber(num: number | string): Promise { - return this.get(`/${num}`); + async getAll(classid: string, assignmentNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/`, { full }); } - async createGroup(data: GroupDTO): Promise { - return this.post(`/`, data); + async getByNumber(classid: string, assignmentNumber: number, num: number | string): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${num}`); } - async deleteGroup(num: number): Promise { - return this.delete(`/${num}`); + async createGroup(classid: string, assignmentNumber: number, data: GroupDTO): Promise { + return this.post(`${this.getBasePath(classid, assignmentNumber)}/`, data); } - async updateGroup(num: number, data: Partial): Promise { - return this.put(`/${num}`, data); + async deleteGroup(classid: string, assignmentNumber: number, num: number): Promise { + return this.delete(`${this.getBasePath(classid, assignmentNumber)}/${num}`); } - async getSubmissions(groupNumber: number, full = true): Promise { - return this.get(`/${groupNumber}/submissions`, { full }); + async updateGroup(classid: string, assignmentNumber: number, num: number, data: Partial): Promise { + return this.put(`${this.getBasePath(classid, assignmentNumber)}/${num}`, data); } - async getQuestions(groupNumber: number, full = true): Promise { - return this.get(`/${groupNumber}/questions`, { full }); + async getSubmissions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/submissions`, { full }); + } + + async getQuestions(classid: string, assignmentNumber: number, groupNumber: number, full = true): Promise { + return this.get(`${this.getBasePath(classid, assignmentNumber)}/${groupNumber}/questions`, { full }); } } diff --git a/frontend/src/queries/groups.ts b/frontend/src/queries/groups.ts index dec35013..def17524 100644 --- a/frontend/src/queries/groups.ts +++ b/frontend/src/queries/groups.ts @@ -5,7 +5,7 @@ import type { SubmissionsResponse } from "@/controllers/submissions"; import { useQuery, type UseQueryReturnType } from "@tanstack/vue-query"; import { computed, toValue, type MaybeRefOrGetter } from "vue"; -const groupController = new GroupController('', 0); +const groupController = new GroupController(); function groupsQueryKey(classid: string, assignmentNumber: number) { return [ "groups", classid, assignmentNumber ]; @@ -20,15 +20,31 @@ function groupQuestionsQueryKey(classid: string, assignmentNumber: number, group return [ "group-questions", classid, assignmentNumber, groupNumber, full ]; } +function checkEnabled( + classid: string | undefined, + assignmentNumber: number | undefined, + groupNumber: number | undefined, +): boolean { + return Boolean(classid) && !isNaN(Number(groupNumber)) && !isNaN(Number(assignmentNumber)); +} +function toValues( + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, + groupNumber: MaybeRefOrGetter, +) { + return { cid: toValue(classid), an: toValue(assignmentNumber), gn: toValue(groupNumber) }; +} + export function useGroupsQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, 1); return useQuery({ - queryKey: computed(() => (groupsQueryKey(classid, assignmentNumber))), - queryFn: async () => groupController.getAll(), + queryKey: computed(() => (groupsQueryKey(cid!, an!))), + queryFn: async () => groupController.getAll(cid!, an!), + enabled: () => checkEnabled(cid, an, 1), }); } @@ -37,12 +53,12 @@ export function useGroupQuery( assignmentNumber: number, groupNumber: MaybeRefOrGetter, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupQueryKey(classid, assignmentNumber, toValue(groupNumber)!)), - queryFn: async () => groupController.getByNumber(toValue(groupNumber)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupQueryKey(cid!, an!, gn!)), + queryFn: async () => groupController.getByNumber(cid!, an!, gn!), + enabled: () => checkEnabled(cid, an, gn), }); } @@ -52,26 +68,26 @@ export function useGroupSubmissionsQuery( groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(classid, assignmentNumber); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupSubmissionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), - queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupSubmissionsQueryKey(cid!, an!, gn!, toValue(full))), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + enabled: () => checkEnabled(cid, an, gn), }); } export function useGroupQuestionsQuery( - classid: string, - assignmentNumber: number, + classid: MaybeRefOrGetter, + assignmentNumber: MaybeRefOrGetter, groupNumber: MaybeRefOrGetter, full: MaybeRefOrGetter = true, ): UseQueryReturnType { - groupController.update(toValue(classid)!, toValue(assignmentNumber)); + const { cid, an, gn } = toValues(classid, assignmentNumber, groupNumber); return useQuery({ - queryKey: computed(() => groupQuestionsQueryKey(classid, assignmentNumber, toValue(groupNumber)!, toValue(full)!)), - queryFn: async () => groupController.getSubmissions(toValue(groupNumber)!, toValue(full)!), - enabled: () => !isNaN(Number(toValue(groupNumber))), + queryKey: computed(() => groupQuestionsQueryKey(cid!, an!, gn!, toValue(full))), + queryFn: async () => groupController.getSubmissions(cid!, an!, gn!, toValue(full)), + enabled: () => checkEnabled(cid, an, gn), }); } From 0694324c83849d0ccf58b045e81139358fb177c7 Mon Sep 17 00:00:00 2001 From: laurejablonski Date: Fri, 11 Apr 2025 12:05:29 +0200 Subject: [PATCH 012/172] refactor: gebruikt using-query result component om klassen te laden --- frontend/src/views/classes/StudentClasses.vue | 124 +++++++----------- 1 file changed, 50 insertions(+), 74 deletions(-) diff --git a/frontend/src/views/classes/StudentClasses.vue b/frontend/src/views/classes/StudentClasses.vue index 2bfdc1b8..8fdd79af 100644 --- a/frontend/src/views/classes/StudentClasses.vue +++ b/frontend/src/views/classes/StudentClasses.vue @@ -1,7 +1,7 @@