fix: integratie user + errors gefixt zodat het runt + format

This commit is contained in:
Gabriellvl 2025-03-09 23:59:31 +01:00
parent 6c4ea0eefb
commit 1b096b411b
55 changed files with 858 additions and 594 deletions

View file

@ -7,7 +7,7 @@ import learningPathRoutes from './routes/learning-paths.js';
import learningObjectRoutes from './routes/learning-objects.js'; import learningObjectRoutes from './routes/learning-objects.js';
import studentRoutes from './routes/students.js'; import studentRoutes from './routes/students.js';
import teacherRoutes from './routes/teachers.js' import teacherRoutes from './routes/teachers.js';
import groupRoutes from './routes/groups.js'; import groupRoutes from './routes/groups.js';
import submissionRoutes from './routes/submissions.js'; import submissionRoutes from './routes/submissions.js';
import classRoutes from './routes/classes.js'; import classRoutes from './routes/classes.js';

View file

@ -1,7 +1,7 @@
import { Request, Response } from 'express' import { Request, Response } from 'express';
import { getAllAssignments, getAssignment } from '../services/assignments.js'; import { getAllAssignments, getAssignment } from '../services/assignments.js';
// typescript is annoy with with parameter forwarding from class.ts // Typescript is annoy with with parameter forwarding from class.ts
interface AssignmentParams { interface AssignmentParams {
classid: string; classid: string;
id: string; id: string;
@ -9,7 +9,7 @@ interface AssignmentParams {
export async function getAllAssignmentsHandler( export async function getAllAssignmentsHandler(
req: Request<AssignmentParams>, req: Request<AssignmentParams>,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const classid = req.params.classid; const classid = req.params.classid;
const full = req.query.full === 'true'; const full = req.query.full === 'true';
@ -23,20 +23,20 @@ export async function getAllAssignmentsHandler(
export async function getAssignmentHandler( export async function getAssignmentHandler(
req: Request<AssignmentParams>, req: Request<AssignmentParams>,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const id = +req.params.id; const id = +req.params.id;
const classid = req.params.classid; const classid = req.params.classid;
if (isNaN(id)) { if (isNaN(id)) {
res.status(400).json({ error: "Assignment id must be a number" }); res.status(400).json({ error: 'Assignment id must be a number' });
return; return;
} }
const assignment = await getAssignment(classid, id); const assignment = await getAssignment(classid, id);
if (!assignment) { if (!assignment) {
res.status(404).json({ error: "Assignment not found" }); res.status(404).json({ error: 'Assignment not found' });
return; return;
} }

View file

@ -1,40 +1,44 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { getAllClasses, getClass, getClassStudents, getClassStudentsIds, getClassTeacherInvitations } from '../services/class.js'; import {
import { ClassDTO } from '../interfaces/classes.js'; getAllClasses,
getClass,
getClassStudents,
getClassStudentsIds,
getClassTeacherInvitations,
} from '../services/class.js';
export async function getAllClassesHandler( export async function getAllClassesHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const full = req.query.full === "true"; const full = req.query.full === 'true';
const classes = await getAllClasses(full); const classes = await getAllClasses(full);
res.json({ res.json({
classes: classes classes: classes,
}); });
} }
export async function getClassHandler( export async function getClassHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
try { try {
const classId = req.params.id; const classId = req.params.id;
const cls = await getClass(classId); const cls = await getClass(classId);
if (!cls) { if (!cls) {
res.status(404).json({ error: "Student not found" }); res.status(404).json({ error: 'Student not found' });
return; return;
} else {
cls.endpoints = {
self: `${req.baseUrl}/${req.params.id}`,
invitations: `${req.baseUrl}/${req.params.id}/invitations`,
assignments: `${req.baseUrl}/${req.params.id}/assignments`,
students: `${req.baseUrl}/${req.params.id}/students`,
}
res.json(cls);
} }
cls.endpoints = {
self: `${req.baseUrl}/${req.params.id}`,
invitations: `${req.baseUrl}/${req.params.id}/invitations`,
assignments: `${req.baseUrl}/${req.params.id}/assignments`,
students: `${req.baseUrl}/${req.params.id}/students`,
};
res.json(cls);
} catch (error) { } catch (error) {
console.error('Error fetching learning objects:', error); console.error('Error fetching learning objects:', error);
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Internal server error' });
@ -43,12 +47,12 @@ export async function getClassHandler(
export async function getClassStudentsHandler( export async function getClassStudentsHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const classId = req.params.id; const classId = req.params.id;
const full = req.query.full === "true"; const full = req.query.full === 'true';
let students = full const students = full
? await getClassStudents(classId) ? await getClassStudents(classId)
: await getClassStudentsIds(classId); : await getClassStudentsIds(classId);
@ -59,10 +63,10 @@ export async function getClassStudentsHandler(
export async function getTeacherInvitationsHandler( export async function getTeacherInvitationsHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const classId = req.params.id; const classId = req.params.id;
const full = req.query.full === "true"; // TODO: not implemented yet const full = req.query.full === 'true'; // TODO: not implemented yet
const invitations = await getClassTeacherInvitations(classId, full); const invitations = await getClassTeacherInvitations(classId, full);

View file

@ -1,7 +1,7 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { getAllGroups, getGroup } from '../services/groups.js'; import { getAllGroups, getGroup } from '../services/groups.js';
// typescript is annoywith with parameter forwarding from class.ts // Typescript is annoywith with parameter forwarding from class.ts
interface GroupParams { interface GroupParams {
classid: string; classid: string;
assignmentid: string; assignmentid: string;
@ -10,21 +10,21 @@ interface GroupParams {
export async function getGroupHandler( export async function getGroupHandler(
req: Request<GroupParams>, req: Request<GroupParams>,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const classId = req.params.classid; const classId = req.params.classid;
const full = req.query.full === "true"; const full = req.query.full === 'true';
const assignmentId = +req.params.assignmentid; const assignmentId = +req.params.assignmentid;
if (isNaN(assignmentId)) { if (isNaN(assignmentId)) {
res.status(400).json({ error: "Assignment id must be a number" }); res.status(400).json({ error: 'Assignment id must be a number' });
return; return;
} }
const groupId = +req.params.groupid!; // can't be undefined const groupId = +req.params.groupid!; // Can't be undefined
if (isNaN(groupId)) { if (isNaN(groupId)) {
res.status(400).json({ error: "Group id must be a number" }); res.status(400).json({ error: 'Group id must be a number' });
return; return;
} }
@ -35,15 +35,15 @@ export async function getGroupHandler(
export async function getAllGroupsHandler( export async function getAllGroupsHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const classId = req.params.classid; const classId = req.params.classid;
const full = req.query.full === "true"; const full = req.query.full === 'true';
const assignmentId = +req.params.assignmentid; const assignmentId = +req.params.assignmentid;
if (isNaN(assignmentId)) { if (isNaN(assignmentId)) {
res.status(400).json({ error: "Assignment id must be a number" }); res.status(400).json({ error: 'Assignment id must be a number' });
return; return;
} }

View file

@ -5,7 +5,7 @@ import {
getLearningObjectsFromPath, getLearningObjectsFromPath,
} from '../services/learning-objects.js'; } from '../services/learning-objects.js';
import { FALLBACK_LANG } from '../config.js'; import { FALLBACK_LANG } from '../config.js';
import { FilteredLearningObject } from '../interfaces/learning-path'; import { FilteredLearningObject } from '../interfaces/learning-path.js';
export async function getAllLearningObjects( export async function getAllLearningObjects(
req: Request, req: Request,

View file

@ -2,43 +2,62 @@ import { Request, Response } from 'express';
import { import {
getStudentClasses, getStudentClasses,
getStudentClassIds, getStudentClassIds,
StudentService StudentService,
} from '../services/students.js'; } from '../services/students.js';
import { ClassDTO } from '../interfaces/classes.js'; import { ClassDTO } from '../interfaces/class.js';
import { getAllAssignments } from '../services/assignments.js'; import { getAllAssignments } from '../services/assignments.js';
import {createUserHandler, deleteUserHandler, getAllUsersHandler, getUserHandler} from "./users.js"; import {
import { Student } from "../entities/users/student.entity.js"; createUserHandler,
deleteUserHandler,
getAllUsersHandler,
getUserHandler,
} from './users.js';
import { Student } from '../entities/users/student.entity.js';
// TODO: accept arguments (full, ...) // TODO: accept arguments (full, ...)
// TODO: endpoints // TODO: endpoints
export async function getAllStudentsHandler (req: Request, res: Response): Promise<void> { export async function getAllStudentsHandler(
req: Request,
res: Response
): Promise<void> {
await getAllUsersHandler<Student>(req, res, new StudentService()); await getAllUsersHandler<Student>(req, res, new StudentService());
} }
export async function getStudentHandler(req: Request, res: Response): Promise<void> { export async function getStudentHandler(
req: Request,
res: Response
): Promise<void> {
await getUserHandler<Student>(req, res, new StudentService()); await getUserHandler<Student>(req, res, new StudentService());
} }
export async function createStudentHandler(req: Request, res: Response): Promise<void> { export async function createStudentHandler(
req: Request,
res: Response
): Promise<void> {
await createUserHandler<Student>(req, res, new StudentService(), Student); await createUserHandler<Student>(req, res, new StudentService(), Student);
} }
export async function deleteStudentHandler(req: Request, res: Response): Promise<void> { export async function deleteStudentHandler(
req: Request,
res: Response
): Promise<void> {
await deleteUserHandler<Student>(req, res, new StudentService()); await deleteUserHandler<Student>(req, res, new StudentService());
} }
export async function getStudentClassesHandler(
export async function getStudentClassesHandler (
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
try { try {
const full = req.query.full === 'true'; const full = req.query.full === 'true';
const username = req.params.id; const username = req.params.id;
let classes: ClassDTO[] | string[]; let classes: ClassDTO[] | string[];
if (full) classes = await getStudentClasses(username); if (full) {
else classes = await getStudentClassIds(username); classes = await getStudentClasses(username);
} else {
classes = await getStudentClassIds(username);
}
res.json({ res.json({
classes: classes, classes: classes,
@ -47,7 +66,7 @@ export async function getStudentClassesHandler (
classes: `${req.baseUrl}/${req.params.id}/invitations`, classes: `${req.baseUrl}/${req.params.id}/invitations`,
questions: `${req.baseUrl}/${req.params.id}/assignments`, questions: `${req.baseUrl}/${req.params.id}/assignments`,
students: `${req.baseUrl}/${req.params.id}/students`, students: `${req.baseUrl}/${req.params.id}/students`,
} },
}); });
} catch (error) { } catch (error) {
console.error('Error fetching learning objects:', error); console.error('Error fetching learning objects:', error);
@ -57,22 +76,26 @@ export async function getStudentClassesHandler (
// TODO // TODO
// Might not be fully correct depending on if // Might not be fully correct depending on if
// a class has an assignment, that all students // A class has an assignment, that all students
// have this assignment. // Have this assignment.
export async function getStudentAssignmentsHandler( export async function getStudentAssignmentsHandler(
req: Request, req: Request,
res: Response, res: Response
): Promise<void> { ): Promise<void> {
const full = req.query.full === 'true'; const full = req.query.full === 'true';
const username = req.params.id; const username = req.params.id;
const classes = await getStudentClasses(username); const classes = await getStudentClasses(username);
const assignments = (await Promise.all(classes.map(async cls => await getAllAssignments(cls.id, full)))).flat(); const assignments = (
await Promise.all(
classes.map(async (cls) => {
return await getAllAssignments(cls.id, full);
})
)
).flat();
res.json({ res.json({
assignments: assignments assignments: assignments,
}); });
} }

View file

@ -1,92 +1,53 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { TeacherUserService, TeacherService } from '../services/teachers.js';
import { ClassDTO } from '../interfaces/class.js';
import { StudentDTO } from '../interfaces/student.js';
import { QuestionDTO, QuestionId } from '../interfaces/question.js';
import { import {
createTeacher, createUserHandler,
deleteTeacher, deleteUserHandler,
getTeacherByUsername, getAllUsersHandler,
getClassesByTeacher, getUserHandler,
getClassIdsByTeacher, } from './users.js';
getAllTeachers, import { Teacher } from '../entities/users/teacher.entity.js';
getAllTeachersIds, getStudentsByTeacher, getStudentIdsByTeacher, getQuestionsByTeacher, getQuestionIdsByTeacher
} from '../services/teachers.js';
import {TeacherDTO} from "../interfaces/teacher";
import {ClassDTO} from "../interfaces/class";
import {StudentDTO} from "../interfaces/student";
import {QuestionDTO, QuestionId} from "../interfaces/question";
export async function getTeacherHandler(req: Request, res: Response): Promise<void> { export async function getAllTeachersHandler(
try { req: Request,
const full = req.query.full === 'true'; res: Response
const username = req.query.username as string; ): Promise<void> {
await getAllUsersHandler<Teacher>(req, res, new TeacherUserService());
}
if (username){ export async function getTeacherHandler(
const teacher = await getTeacherByUsername(username); req: Request,
if (!teacher){ res: Response
res.status(404).json({ error: `Teacher with username '${username}' not found.` }); ): Promise<void> {
return; await getUserHandler<Teacher>(req, res, new TeacherUserService());
}
res.json(teacher);
return;
}
let teachers: TeacherDTO[] | string[];
if (full) teachers = await getAllTeachers();
else teachers = await getAllTeachersIds();
res.json(teachers);
} catch (error) {
console.error("❌ Error fetching teachers:", error);
res.status(500).json({ error: "Internal server error" });
}
} }
export async function createTeacherHandler( export async function createTeacherHandler(
req: Request, req: Request,
res: Response res: Response
): Promise<void> { ): Promise<void> {
try { await createUserHandler<Teacher>(
const teacherData = req.body as TeacherDTO; req,
res,
if (!teacherData.username || !teacherData.firstName || !teacherData.lastName) { new TeacherUserService(),
res.status(400).json({ error: 'Missing required fields: username, firstName, lastName' }); Teacher
return; );
}
const newTeacher = await createTeacher(teacherData);
res.status(201).json(newTeacher);
} catch (error) {
console.error('Error creating teacher:', error);
res.status(500).json({ error: 'Internal server error' });
}
} }
export async function deleteTeacherHandler( export async function deleteTeacherHandler(
req: Request, req: Request,
res: Response res: Response
): Promise<void> { ): Promise<void> {
try { await deleteUserHandler<Teacher>(req, res, new TeacherUserService());
const username = req.params.username as string;
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const deletedTeacher = await deleteTeacher(username);
if (!deletedTeacher) {
res.status(400).json({ error: `Did not find teacher with username ${username}` });
return;
}
res.status(201).json(deletedTeacher);
} catch (error) {
console.error('Error deleting teacher:', error);
res.status(500).json({ error: 'Internal server error' });
}
} }
export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> { export async function getTeacherClassHandler(
req: Request,
res: Response
): Promise<void> {
try { try {
const username = req.params.username as string; const username = req.params.username as string;
const full = req.query.full === 'true'; const full = req.query.full === 'true';
@ -96,10 +57,11 @@ export async function getTeacherClassHandler(req: Request, res: Response): Promi
return; return;
} }
let classes: ClassDTO[] | string[]; const teacherService = new TeacherService();
if (full) classes = await getClassesByTeacher(username); const classes: ClassDTO[] | string[] = full
else classes = await getClassIdsByTeacher(username); ? await teacherService.getClassesByTeacher(username)
: await teacherService.getClassIdsByTeacher(username);
res.status(201).json(classes); res.status(201).json(classes);
} catch (error) { } catch (error) {
@ -108,7 +70,10 @@ export async function getTeacherClassHandler(req: Request, res: Response): Promi
} }
} }
export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> { export async function getTeacherStudentHandler(
req: Request,
res: Response
): Promise<void> {
try { try {
const username = req.params.username as string; const username = req.params.username as string;
const full = req.query.full === 'true'; const full = req.query.full === 'true';
@ -118,10 +83,11 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro
return; return;
} }
let students: StudentDTO[] | string[]; const teacherService = new TeacherService();
if (full) students = await getStudentsByTeacher(username); const students: StudentDTO[] | string[] = full
else students = await getStudentIdsByTeacher(username); ? await teacherService.getStudentsByTeacher(username)
: await teacherService.getStudentIdsByTeacher(username);
res.status(201).json(students); res.status(201).json(students);
} catch (error) { } catch (error) {
@ -130,7 +96,10 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro
} }
} }
export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> { export async function getTeacherQuestionHandler(
req: Request,
res: Response
): Promise<void> {
try { try {
const username = req.params.username as string; const username = req.params.username as string;
const full = req.query.full === 'true'; const full = req.query.full === 'true';
@ -140,10 +109,11 @@ export async function getTeacherQuestionHandler(req: Request, res: Response): Pr
return; return;
} }
let questions: QuestionDTO[] | QuestionId[]; const teacherService = new TeacherService();
if (full) questions = await getQuestionsByTeacher(username); const questions: QuestionDTO[] | QuestionId[] = full
else questions = await getQuestionIdsByTeacher(username); ? await teacherService.getQuestionsByTeacher(username)
: await teacherService.getQuestionIdsByTeacher(username);
res.status(201).json(questions); res.status(201).json(questions);
} catch (error) { } catch (error) {
@ -151,5 +121,3 @@ export async function getTeacherQuestionHandler(req: Request, res: Response): Pr
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Internal server error' });
} }
} }

View file

@ -1,7 +1,6 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { themes } from '../data/themes.js'; import { themes } from '../data/themes.js';
import { loadTranslations } from "../util/translation-helper.js"; import { loadTranslations } from '../util/translation-helper.js';
import { FALLBACK_LANG } from '../config.js';
interface Translations { interface Translations {
curricula_page: { curricula_page: {

View file

@ -1,30 +1,29 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { UserService } from "../services/users.js"; import { UserService } from '../services/users.js';
import {UserDTO} from "../interfaces/user.js"; import { UserDTO } from '../interfaces/user.js';
import {User} from "../entities/users/user.entity.js"; import { User } from '../entities/users/user.entity.js';
export async function getAllUsersHandler<T extends User>( export async function getAllUsersHandler<T extends User>(
req: Request, req: Request,
res: Response, res: Response,
service: UserService<T> service: UserService<T>
): Promise<void> { ): Promise<void> {
try { try {
const full = req.query.full === 'true'; const full = req.query.full === 'true';
let users: UserDTO[] | string[] = full const users: UserDTO[] | string[] = full
? await service.getAllUsers() ? await service.getAllUsers()
: await service.getAllUserIds(); : await service.getAllUserIds();
if (!users){ if (!users) {
res.status(404).json({ error: `Users not found.` }); res.status(404).json({ error: `Users not found.` });
return; return;
} }
res.status(201).json(users); res.status(201).json(users);
} catch (error) { } catch (error) {
console.error("❌ Error fetching users:", error); console.error('❌ Error fetching users:', error);
res.status(500).json({ error: "Internal server error" }); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -32,7 +31,7 @@ export async function getUserHandler<T extends User>(
req: Request, req: Request,
res: Response, res: Response,
service: UserService<T> service: UserService<T>
): Promise<void> { ): Promise<void> {
try { try {
const username = req.params.username as string; const username = req.params.username as string;
@ -43,16 +42,17 @@ export async function getUserHandler<T extends User>(
const user = await service.getUserByUsername(username); const user = await service.getUserByUsername(username);
if (!user){ if (!user) {
res.status(404).json({ error: `User with username '${username}' not found.` }); res.status(404).json({
error: `User with username '${username}' not found.`,
});
return; return;
} }
res.status(201).json(user); res.status(201).json(user);
} catch (error) { } catch (error) {
console.error("❌ Error fetching users:", error); console.error('❌ Error fetching users:', error);
res.status(500).json({ error: "Internal server error" }); res.status(500).json({ error: 'Internal server error' });
} }
} }
@ -63,23 +63,25 @@ export async function createUserHandler<T extends User>(
UserClass: new () => T UserClass: new () => T
) { ) {
try { try {
console.log("req", req) console.log('req', req);
const userData = req.body as UserDTO; const userData = req.body as UserDTO;
if (!userData.username || !userData.firstName || !userData.lastName) { if (!userData.username || !userData.firstName || !userData.lastName) {
res.status(400).json({ error: "Missing required fields: username, firstName, lastName" }); res.status(400).json({
error: 'Missing required fields: username, firstName, lastName',
});
return; return;
} }
const newUser = await service.createUser(userData, UserClass); const newUser = await service.createUser(userData, UserClass);
res.status(201).json(newUser); res.status(201).json(newUser);
} catch (error) { } catch (error) {
console.error("❌ Error creating user:", error); console.error('❌ Error creating user:', error);
res.status(500).json({ error: "Internal server error" }); res.status(500).json({ error: 'Internal server error' });
} }
} }
export async function deleteUserHandler<T extends User> ( export async function deleteUserHandler<T extends User>(
req: Request, req: Request,
res: Response, res: Response,
service: UserService<T> service: UserService<T>
@ -88,19 +90,21 @@ export async function deleteUserHandler<T extends User> (
const username = req.params.username; const username = req.params.username;
if (!username) { if (!username) {
res.status(400).json({ error: "Missing required field: username" }); res.status(400).json({ error: 'Missing required field: username' });
return; return;
} }
const deletedUser = await service.deleteUser(username); const deletedUser = await service.deleteUser(username);
if (!deletedUser) { if (!deletedUser) {
res.status(404).json({ error: `User with username '${username}' not found.` }); res.status(404).json({
error: `User with username '${username}' not found.`,
});
return; return;
} }
res.status(200).json(deletedUser); res.status(200).json(deletedUser);
} catch (error) { } catch (error) {
console.error("❌ Error deleting user:", error); console.error('❌ Error deleting user:', error);
res.status(500).json({ error: "Internal server error" }); res.status(500).json({ error: 'Internal server error' });
} }
} }

View file

@ -8,21 +8,20 @@ export class GroupRepository extends DwengoEntityRepository<Group> {
groupNumber: number groupNumber: number
): Promise<Group | null> { ): Promise<Group | null> {
return this.findOne( return this.findOne(
{ {
assignment: assignment, assignment: assignment,
groupNumber: groupNumber, groupNumber: groupNumber,
}, },
{ populate: ["members"] }, { populate: ['members'] }
); );
} }
public findAllGroupsForAssignment( public findAllGroupsForAssignment(
assignment: Assignment assignment: Assignment
): Promise<Group[]> { ): Promise<Group[]> {
return this.findAll({ return this.findAll({
where: { assignment: assignment }, where: { assignment: assignment },
populate: ["members"] populate: ['members'],
}, });
);
} }
public deleteByAssignmentAndGroupNumber( public deleteByAssignmentAndGroupNumber(
assignment: Assignment, assignment: Assignment,

View file

@ -1,13 +1,13 @@
import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
import { Class } from '../../entities/classes/class.entity.js'; import { Class } from '../../entities/classes/class.entity.js';
import { Student } from '../../entities/users/student.entity.js'; import { Student } from '../../entities/users/student.entity.js';
import {Teacher} from "../../entities/users/teacher.entity"; import { Teacher } from '../../entities/users/teacher.entity';
export class ClassRepository extends DwengoEntityRepository<Class> { export class ClassRepository extends DwengoEntityRepository<Class> {
public findById(id: string): Promise<Class | null> { public findById(id: string): Promise<Class | null> {
return this.findOne( return this.findOne(
{ classId: id }, { classId: id },
{ populate: ["students", "teachers"] }, { populate: ['students', 'teachers'] }
); );
} }
public deleteById(id: string): Promise<void> { public deleteById(id: string): Promise<void> {
@ -16,14 +16,14 @@ export class ClassRepository extends DwengoEntityRepository<Class> {
public findByStudent(student: Student): Promise<Class[]> { public findByStudent(student: Student): Promise<Class[]> {
return this.find( return this.find(
{ students: student }, { students: student },
{ populate: ["students", "teachers"] } // voegt student en teacher objecten toe { populate: ['students', 'teachers'] } // Voegt student en teacher objecten toe
) );
} }
public findByTeacher(teacher: Teacher): Promise<Class[]> { public findByTeacher(teacher: Teacher): Promise<Class[]> {
return this.find( return this.find(
{ teachers: teacher }, { teachers: teacher },
{ populate: ["students", "teachers"] } { populate: ['students', 'teachers'] }
); );
} }
} }

View file

@ -1,7 +1,7 @@
import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
import { LearningObject } from '../../entities/content/learning-object.entity.js'; import { LearningObject } from '../../entities/content/learning-object.entity.js';
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
import {Teacher} from "../../entities/users/teacher.entity"; import { Teacher } from '../../entities/users/teacher.entity';
export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> { export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> {
public findByIdentifier( public findByIdentifier(

View file

@ -4,13 +4,13 @@ export abstract class DwengoEntityRepository<
T extends object, T extends object,
> extends EntityRepository<T> { > extends EntityRepository<T> {
public async save(entity: T) { public async save(entity: T) {
let em = this.getEntityManager(); const em = this.getEntityManager();
em.persist(entity); em.persist(entity);
await em.flush(); await em.flush();
} }
public async deleteWhere(query: FilterQuery<T>) { public async deleteWhere(query: FilterQuery<T>) {
let toDelete = await this.findOne(query); const toDelete = await this.findOne(query);
let em = this.getEntityManager(); const em = this.getEntityManager();
if (toDelete) { if (toDelete) {
em.remove(toDelete); em.remove(toDelete);
await em.flush(); await em.flush();

View file

@ -9,7 +9,7 @@ export class AnswerRepository extends DwengoEntityRepository<Answer> {
author: Teacher; author: Teacher;
content: string; content: string;
}): Promise<Answer> { }): Promise<Answer> {
let answerEntity = new Answer(); const answerEntity = new Answer();
answerEntity.toQuestion = answer.toQuestion; answerEntity.toQuestion = answer.toQuestion;
answerEntity.author = answer.author; answerEntity.author = answer.author;
answerEntity.content = answer.content; answerEntity.content = answer.content;

View file

@ -2,7 +2,7 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
import { Question } from '../../entities/questions/question.entity.js'; import { Question } from '../../entities/questions/question.entity.js';
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
import { Student } from '../../entities/users/student.entity.js'; import { Student } from '../../entities/users/student.entity.js';
import {LearningObject} from "../../entities/content/learning-object.entity"; import { LearningObject } from '../../entities/content/learning-object.entity';
export class QuestionRepository extends DwengoEntityRepository<Question> { export class QuestionRepository extends DwengoEntityRepository<Question> {
public createQuestion(question: { public createQuestion(question: {
@ -10,7 +10,7 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
author: Student; author: Student;
content: string; content: string;
}): Promise<Question> { }): Promise<Question> {
let questionEntity = new Question(); const questionEntity = new Question();
questionEntity.learningObjectHruid = question.loId.hruid; questionEntity.learningObjectHruid = question.loId.hruid;
questionEntity.learningObjectLanguage = question.loId.language; questionEntity.learningObjectLanguage = question.loId.language;
questionEntity.learningObjectVersion = question.loId.version; questionEntity.learningObjectVersion = question.loId.version;
@ -44,12 +44,16 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
}); });
} }
public async findAllByLearningObjects(learningObjects: LearningObject[]): Promise<Question[]> { public async findAllByLearningObjects(
const objectIdentifiers = learningObjects.map(lo => ({ learningObjects: LearningObject[]
): Promise<Question[]> {
const objectIdentifiers = learningObjects.map((lo) => {
return {
learningObjectHruid: lo.hruid, learningObjectHruid: lo.hruid,
learningObjectLanguage: lo.language, learningObjectLanguage: lo.language,
learningObjectVersion: lo.version learningObjectVersion: lo.version,
})); };
});
return this.findAll({ return this.findAll({
where: { $or: objectIdentifiers }, where: { $or: objectIdentifiers },

View file

@ -59,7 +59,9 @@ function repositoryGetter<T extends AnyEntity, R extends EntityRepository<T>>(
} }
/* Users */ /* Users */
export const getUserRepository = repositoryGetter<User, UserRepository<User>>(User); export const getUserRepository = repositoryGetter<User, UserRepository<User>>(
User
);
export const getStudentRepository = repositoryGetter< export const getStudentRepository = repositoryGetter<
Student, Student,
StudentRepository StudentRepository

View file

@ -1,4 +1,4 @@
import { Student } from '../../entities/users/student.entity.js'; import { Student } from '../../entities/users/student.entity.js';
import {UserRepository} from "./user-repository.js"; import { UserRepository } from './user-repository.js';
export class StudentRepository extends UserRepository<Student> {} export class StudentRepository extends UserRepository<Student> {}

View file

@ -1,4 +1,4 @@
import { Teacher } from '../../entities/users/teacher.entity.js'; import { Teacher } from '../../entities/users/teacher.entity.js';
import {UserRepository} from "./user-repository.js"; import { UserRepository } from './user-repository.js';
export class TeacherRepository extends UserRepository<Teacher> {} export class TeacherRepository extends UserRepository<Teacher> {}

View file

@ -11,9 +11,18 @@ import { Group } from './group.entity.js';
import { Language } from '../content/language.js'; import { Language } from '../content/language.js';
import { AssignmentRepository } from '../../data/assignments/assignment-repository.js'; import { AssignmentRepository } from '../../data/assignments/assignment-repository.js';
@Entity({ repository: () => AssignmentRepository }) @Entity({
repository: () => {
return AssignmentRepository;
},
})
export class Assignment { export class Assignment {
@ManyToOne({ entity: () => Class, primary: true }) @ManyToOne({
entity: () => {
return Class;
},
primary: true,
})
within!: Class; within!: Class;
@PrimaryKey({ type: 'number' }) @PrimaryKey({ type: 'number' })
@ -28,9 +37,18 @@ export class Assignment {
@Property({ type: 'string' }) @Property({ type: 'string' })
learningPathHruid!: string; learningPathHruid!: string;
@Enum({ items: () => Language }) @Enum({
items: () => {
return Language;
},
})
learningPathLanguage!: Language; learningPathLanguage!: Language;
@OneToMany({ entity: () => Group, mappedBy: 'assignment' }) @OneToMany({
entity: () => {
return Group;
},
mappedBy: 'assignment',
})
groups!: Group[]; groups!: Group[];
} }

View file

@ -3,14 +3,27 @@ import { Assignment } from './assignment.entity.js';
import { Student } from '../users/student.entity.js'; import { Student } from '../users/student.entity.js';
import { GroupRepository } from '../../data/assignments/group-repository.js'; import { GroupRepository } from '../../data/assignments/group-repository.js';
@Entity({ repository: () => GroupRepository }) @Entity({
repository: () => {
return GroupRepository;
},
})
export class Group { export class Group {
@ManyToOne({ entity: () => Assignment, primary: true }) @ManyToOne({
entity: () => {
return Assignment;
},
primary: true,
})
assignment!: Assignment; assignment!: Assignment;
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer' })
groupNumber!: number; groupNumber!: number;
@ManyToMany({ entity: () => Student }) @ManyToMany({
entity: () => {
return Student;
},
})
members!: Student[]; members!: Student[];
} }

View file

@ -4,12 +4,21 @@ import { Entity, Enum, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core';
import { Language } from '../content/language.js'; import { Language } from '../content/language.js';
import { SubmissionRepository } from '../../data/assignments/submission-repository.js'; import { SubmissionRepository } from '../../data/assignments/submission-repository.js';
@Entity({ repository: () => SubmissionRepository }) @Entity({
repository: () => {
return SubmissionRepository;
},
})
export class Submission { export class Submission {
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
learningObjectHruid!: string; learningObjectHruid!: string;
@Enum({ items: () => Language, primary: true }) @Enum({
items: () => {
return Language;
},
primary: true,
})
learningObjectLanguage!: Language; learningObjectLanguage!: Language;
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
@ -18,13 +27,22 @@ export class Submission {
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer' })
submissionNumber!: number; submissionNumber!: number;
@ManyToOne({ entity: () => Student }) @ManyToOne({
entity: () => {
return Student;
},
})
submitter!: Student; submitter!: Student;
@Property({ type: 'datetime' }) @Property({ type: 'datetime' })
submissionTime!: Date; submissionTime!: Date;
@ManyToOne({ entity: () => Group, nullable: true }) @ManyToOne({
entity: () => {
return Group;
},
nullable: true,
})
onBehalfOf?: Group; onBehalfOf?: Group;
@Property({ type: 'json' }) @Property({ type: 'json' })

View file

@ -3,15 +3,31 @@ import { Student } from '../users/student.entity.js';
import { Class } from './class.entity.js'; import { Class } from './class.entity.js';
import { ClassJoinRequestRepository } from '../../data/classes/class-join-request-repository.js'; import { ClassJoinRequestRepository } from '../../data/classes/class-join-request-repository.js';
@Entity({ repository: () => ClassJoinRequestRepository }) @Entity({
repository: () => {
return ClassJoinRequestRepository;
},
})
export class ClassJoinRequest { export class ClassJoinRequest {
@ManyToOne({ entity: () => Student, primary: true }) @ManyToOne({
entity: () => {
return Student;
},
primary: true,
})
requester!: Student; requester!: Student;
@ManyToOne({ entity: () => Class, primary: true }) @ManyToOne({
entity: () => {
return Class;
},
primary: true,
})
class!: Class; class!: Class;
@Enum(() => ClassJoinRequestStatus) @Enum(() => {
return ClassJoinRequestStatus;
})
status!: ClassJoinRequestStatus; status!: ClassJoinRequestStatus;
} }

View file

@ -10,7 +10,11 @@ import { Teacher } from '../users/teacher.entity.js';
import { Student } from '../users/student.entity.js'; import { Student } from '../users/student.entity.js';
import { ClassRepository } from '../../data/classes/class-repository.js'; import { ClassRepository } from '../../data/classes/class-repository.js';
@Entity({ repository: () => ClassRepository }) @Entity({
repository: () => {
return ClassRepository;
},
})
export class Class { export class Class {
@PrimaryKey() @PrimaryKey()
classId = v4(); classId = v4();
@ -18,9 +22,13 @@ export class Class {
@Property({ type: 'string' }) @Property({ type: 'string' })
displayName!: string; displayName!: string;
@ManyToMany(() => Teacher) @ManyToMany(() => {
return Teacher;
})
teachers!: Collection<Teacher>; teachers!: Collection<Teacher>;
@ManyToMany(() => Student) @ManyToMany(() => {
return Student;
})
students!: Collection<Student>; students!: Collection<Student>;
} }

View file

@ -6,14 +6,33 @@ import { TeacherInvitationRepository } from '../../data/classes/teacher-invitati
/** /**
* Invitation of a teacher into a class (in order to teach it). * Invitation of a teacher into a class (in order to teach it).
*/ */
@Entity({ repository: () => TeacherInvitationRepository }) @Entity({
repository: () => {
return TeacherInvitationRepository;
},
})
export class TeacherInvitation { export class TeacherInvitation {
@ManyToOne({ entity: () => Teacher, primary: true }) @ManyToOne({
entity: () => {
return Teacher;
},
primary: true,
})
sender!: Teacher; sender!: Teacher;
@ManyToOne({ entity: () => Teacher, primary: true }) @ManyToOne({
entity: () => {
return Teacher;
},
primary: true,
})
receiver!: Teacher; receiver!: Teacher;
@ManyToOne({ entity: () => Class, primary: true }) @ManyToOne({
entity: () => {
return Class;
},
primary: true,
})
class!: Class; class!: Class;
} }

View file

@ -2,9 +2,18 @@ import { Entity, ManyToOne, PrimaryKey, Property } from '@mikro-orm/core';
import { LearningObject } from './learning-object.entity.js'; import { LearningObject } from './learning-object.entity.js';
import { AttachmentRepository } from '../../data/content/attachment-repository.js'; import { AttachmentRepository } from '../../data/content/attachment-repository.js';
@Entity({ repository: () => AttachmentRepository }) @Entity({
repository: () => {
return AttachmentRepository;
},
})
export class Attachment { export class Attachment {
@ManyToOne({ entity: () => LearningObject, primary: true }) @ManyToOne({
entity: () => {
return LearningObject;
},
primary: true,
})
learningObject!: LearningObject; learningObject!: LearningObject;
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer' })

View file

@ -22,18 +22,31 @@ export class ReturnValue {
callbackSchema!: string; callbackSchema!: string;
} }
@Entity({ repository: () => LearningObjectRepository }) @Entity({
repository: () => {
return LearningObjectRepository;
},
})
export class LearningObject { export class LearningObject {
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
hruid!: string; hruid!: string;
@Enum({ items: () => Language, primary: true }) @Enum({
items: () => {
return Language;
},
primary: true,
})
language!: Language; language!: Language;
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
version: string = '1'; version: string = '1';
@ManyToMany({ entity: () => Teacher }) @ManyToMany({
entity: () => {
return Teacher;
},
})
admins!: Teacher[]; admins!: Teacher[];
@Property({ type: 'string' }) @Property({ type: 'string' })
@ -57,7 +70,12 @@ export class LearningObject {
@Property({ type: 'array' }) @Property({ type: 'array' })
skosConcepts!: string[]; skosConcepts!: string[];
@Embedded({ entity: () => EducationalGoal, array: true }) @Embedded({
entity: () => {
return EducationalGoal;
},
array: true,
})
educationalGoals: EducationalGoal[] = []; educationalGoals: EducationalGoal[] = [];
@Property({ type: 'string' }) @Property({ type: 'string' })
@ -72,7 +90,11 @@ export class LearningObject {
@Property({ type: 'integer' }) @Property({ type: 'integer' })
estimatedTime!: number; estimatedTime!: number;
@Embedded({ entity: () => ReturnValue }) @Embedded({
entity: () => {
return ReturnValue;
},
})
returnValue!: ReturnValue; returnValue!: ReturnValue;
@Property({ type: 'bool' }) @Property({ type: 'bool' })
@ -81,7 +103,12 @@ export class LearningObject {
@Property({ type: 'string', nullable: true }) @Property({ type: 'string', nullable: true })
contentLocation?: string; contentLocation?: string;
@OneToMany({ entity: () => Attachment, mappedBy: 'learningObject' }) @OneToMany({
entity: () => {
return Attachment;
},
mappedBy: 'learningObject',
})
attachments: Attachment[] = []; attachments: Attachment[] = [];
@Property({ type: 'blob' }) @Property({ type: 'blob' })

View file

@ -12,15 +12,28 @@ import { Language } from './language.js';
import { Teacher } from '../users/teacher.entity.js'; import { Teacher } from '../users/teacher.entity.js';
import { LearningPathRepository } from '../../data/content/learning-path-repository.js'; import { LearningPathRepository } from '../../data/content/learning-path-repository.js';
@Entity({ repository: () => LearningPathRepository }) @Entity({
repository: () => {
return LearningPathRepository;
},
})
export class LearningPath { export class LearningPath {
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
hruid!: string; hruid!: string;
@Enum({ items: () => Language, primary: true }) @Enum({
items: () => {
return Language;
},
primary: true,
})
language!: Language; language!: Language;
@ManyToMany({ entity: () => Teacher }) @ManyToMany({
entity: () => {
return Teacher;
},
})
admins!: Teacher[]; admins!: Teacher[];
@Property({ type: 'string' }) @Property({ type: 'string' })
@ -32,7 +45,12 @@ export class LearningPath {
@Property({ type: 'blob' }) @Property({ type: 'blob' })
image!: string; image!: string;
@Embedded({ entity: () => LearningPathNode, array: true }) @Embedded({
entity: () => {
return LearningPathNode;
},
array: true,
})
nodes: LearningPathNode[] = []; nodes: LearningPathNode[] = [];
} }
@ -41,7 +59,11 @@ export class LearningPathNode {
@Property({ type: 'string' }) @Property({ type: 'string' })
learningObjectHruid!: string; learningObjectHruid!: string;
@Enum({ items: () => Language }) @Enum({
items: () => {
return Language;
},
})
language!: Language; language!: Language;
@Property({ type: 'string' }) @Property({ type: 'string' })
@ -53,7 +75,12 @@ export class LearningPathNode {
@Property({ type: 'bool' }) @Property({ type: 'bool' })
startNode!: boolean; startNode!: boolean;
@Embedded({ entity: () => LearningPathTransition, array: true }) @Embedded({
entity: () => {
return LearningPathTransition;
},
array: true,
})
transitions!: LearningPathTransition[]; transitions!: LearningPathTransition[];
} }
@ -62,6 +89,10 @@ export class LearningPathTransition {
@Property({ type: 'string' }) @Property({ type: 'string' })
condition!: string; condition!: string;
@OneToOne({ entity: () => LearningPathNode }) @OneToOne({
entity: () => {
return LearningPathNode;
},
})
next!: LearningPathNode; next!: LearningPathNode;
} }

View file

@ -3,12 +3,26 @@ import { Question } from './question.entity.js';
import { Teacher } from '../users/teacher.entity.js'; import { Teacher } from '../users/teacher.entity.js';
import { AnswerRepository } from '../../data/questions/answer-repository.js'; import { AnswerRepository } from '../../data/questions/answer-repository.js';
@Entity({ repository: () => AnswerRepository }) @Entity({
repository: () => {
return AnswerRepository;
},
})
export class Answer { export class Answer {
@ManyToOne({ entity: () => Teacher, primary: true }) @ManyToOne({
entity: () => {
return Teacher;
},
primary: true,
})
author!: Teacher; author!: Teacher;
@ManyToOne({ entity: () => Question, primary: true }) @ManyToOne({
entity: () => {
return Question;
},
primary: true,
})
toQuestion!: Question; toQuestion!: Question;
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer' })

View file

@ -3,12 +3,21 @@ import { Language } from '../content/language.js';
import { Student } from '../users/student.entity.js'; import { Student } from '../users/student.entity.js';
import { QuestionRepository } from '../../data/questions/question-repository.js'; import { QuestionRepository } from '../../data/questions/question-repository.js';
@Entity({ repository: () => QuestionRepository }) @Entity({
repository: () => {
return QuestionRepository;
},
})
export class Question { export class Question {
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
learningObjectHruid!: string; learningObjectHruid!: string;
@Enum({ items: () => Language, primary: true }) @Enum({
items: () => {
return Language;
},
primary: true,
})
learningObjectLanguage!: Language; learningObjectLanguage!: Language;
@PrimaryKey({ type: 'string' }) @PrimaryKey({ type: 'string' })
@ -17,7 +26,11 @@ export class Question {
@PrimaryKey({ type: 'integer' }) @PrimaryKey({ type: 'integer' })
sequenceNumber!: number; sequenceNumber!: number;
@ManyToOne({ entity: () => Student }) @ManyToOne({
entity: () => {
return Student;
},
})
author!: Student; author!: Student;
@Property({ type: 'datetime' }) @Property({ type: 'datetime' })

View file

@ -4,11 +4,19 @@ import { Class } from '../classes/class.entity.js';
import { Group } from '../assignments/group.entity.js'; import { Group } from '../assignments/group.entity.js';
import { StudentRepository } from '../../data/users/student-repository.js'; import { StudentRepository } from '../../data/users/student-repository.js';
@Entity({ repository: () => StudentRepository }) @Entity({
repository: () => {
return StudentRepository;
},
})
export class Student extends User { export class Student extends User {
@ManyToMany(() => Class) @ManyToMany(() => {
return Class;
})
classes!: Collection<Class>; classes!: Collection<Class>;
@ManyToMany(() => Group) @ManyToMany(() => {
return Group;
})
groups!: Collection<Group>; groups!: Collection<Group>;
} }

View file

@ -3,8 +3,14 @@ import { User } from './user.entity.js';
import { Class } from '../classes/class.entity.js'; import { Class } from '../classes/class.entity.js';
import { TeacherRepository } from '../../data/users/teacher-repository.js'; import { TeacherRepository } from '../../data/users/teacher-repository.js';
@Entity({ repository: () => TeacherRepository }) @Entity({
repository: () => {
return TeacherRepository;
},
})
export class Teacher extends User { export class Teacher extends User {
@ManyToMany(() => Class) @ManyToMany(() => {
return Class;
})
classes!: Collection<Class>; classes!: Collection<Class>;
} }

View file

@ -1,14 +1,14 @@
import { Assignment } from "../entities/assignments/assignment.entity.js"; import { Assignment } from '../entities/assignments/assignment.entity.js';
import { GroupDTO, mapToGroupDTO } from "./groups.js"; import { GroupDTO, mapToGroupDTO } from './group.js';
export interface AssignmentDTO { export interface AssignmentDTO {
id: number, id: number;
class: string, // id of class 'within' class: string; // Id of class 'within'
title: string, title: string;
description: string, description: string;
learningPath: string, learningPath: string;
language: string, language: string;
groups?: GroupDTO[] | string[], // TODO groups?: GroupDTO[] | string[]; // TODO
} }
export function mapToAssignmentDTOId(assignment: Assignment): AssignmentDTO { export function mapToAssignmentDTOId(assignment: Assignment): AssignmentDTO {
@ -19,8 +19,8 @@ export function mapToAssignmentDTOId(assignment: Assignment): AssignmentDTO {
description: assignment.description, description: assignment.description,
learningPath: assignment.learningPathHruid, learningPath: assignment.learningPathHruid,
language: assignment.learningPathLanguage, language: assignment.learningPathLanguage,
// groups: assignment.groups.map(group => group.groupNumber), // Groups: assignment.groups.map(group => group.groupNumber),
} };
} }
export function mapToAssignmentDTO(assignment: Assignment): AssignmentDTO { export function mapToAssignmentDTO(assignment: Assignment): AssignmentDTO {
@ -31,6 +31,6 @@ export function mapToAssignmentDTO(assignment: Assignment): AssignmentDTO {
description: assignment.description, description: assignment.description,
learningPath: assignment.learningPathHruid, learningPath: assignment.learningPathHruid,
language: assignment.learningPathLanguage, language: assignment.learningPathLanguage,
// groups: assignment.groups.map(mapToGroupDTO), // Groups: assignment.groups.map(mapToGroupDTO),
}; };
} }

View file

@ -1,4 +1,4 @@
import { Class } from "../entities/classes/class.entity.js"; import { Class } from '../entities/classes/class.entity.js';
export interface ClassDTO { export interface ClassDTO {
id: string; id: string;
@ -18,8 +18,12 @@ export function mapToClassDTO(cls: Class): ClassDTO {
return { return {
id: cls.classId, id: cls.classId,
displayName: cls.displayName, displayName: cls.displayName,
teachers: cls.teachers.map(teacher => teacher.username), teachers: cls.teachers.map((teacher) => {
students: cls.students.map(student => student.username), return teacher.username;
}),
students: cls.students.map((student) => {
return student.username;
}),
joinRequests: [], // TODO joinRequests: [], // TODO
} };
}; }

View file

@ -1,25 +1,27 @@
import { Group } from "../entities/assignments/group.entity.js"; import { Group } from '../entities/assignments/group.entity.js';
import { AssignmentDTO, mapToAssignmentDTO } from "./assignments.js"; import { AssignmentDTO, mapToAssignmentDTO } from './assignment.js';
import { mapToStudentDTO, StudentDTO } from "./students.js"; import { mapToStudentDTO, StudentDTO } from './student.js';
export interface GroupDTO { export interface GroupDTO {
assignment: number | AssignmentDTO, assignment: number | AssignmentDTO;
groupNumber: number, groupNumber: number;
members: string[] | StudentDTO[], members: string[] | StudentDTO[];
}; }
export function mapToGroupDTO(group: Group): GroupDTO { export function mapToGroupDTO(group: Group): GroupDTO {
return { return {
assignment: mapToAssignmentDTO(group.assignment), // ERROR: , group.assignment.within), assignment: mapToAssignmentDTO(group.assignment), // ERROR: , group.assignment.within),
groupNumber: group.groupNumber, groupNumber: group.groupNumber,
members: group.members.map(mapToStudentDTO), members: group.members.map(mapToStudentDTO),
} };
} }
export function mapToGroupDTOId(group: Group): GroupDTO { export function mapToGroupDTOId(group: Group): GroupDTO {
return { return {
assignment: group.assignment.id, assignment: group.assignment.id,
groupNumber: group.groupNumber, groupNumber: group.groupNumber,
members: group.members.map(member => member.username), members: group.members.map((member) => {
} return member.username;
}),
};
} }

View file

@ -1,5 +1,5 @@
// TODO: implement something like this but with named endpoints // TODO: implement something like this but with named endpoints
export interface List<T> { export interface List<T> {
items: T[], items: T[];
endpoints?: string[], endpoints?: string[];
}; }

View file

@ -1,6 +1,4 @@
import {Question} from "../entities/questions/question.entity"; import { Question } from '../entities/questions/question.entity.js';
import {Enum, PrimaryKey} from "@mikro-orm/core";
import {Language} from "../entities/content/language";
export interface QuestionDTO { export interface QuestionDTO {
learningObjectHruid: string; learningObjectHruid: string;
@ -34,8 +32,17 @@ export function mapToQuestionDTO(question: Question): QuestionDTO {
} }
export interface QuestionId { export interface QuestionId {
learningObjectHruid: string, learningObjectHruid: string;
learningObjectLanguage: Language, learningObjectLanguage: string;
learningObjectVersion: string, learningObjectVersion: string;
sequenceNumber: number sequenceNumber: number;
}
export function mapToQuestionId(question: QuestionDTO): QuestionId {
return {
learningObjectHruid: question.learningObjectHruid,
learningObjectLanguage: question.learningObjectLanguage,
learningObjectVersion: question.learningObjectVersion,
sequenceNumber: question.sequenceNumber,
};
} }

View file

@ -1,4 +1,4 @@
import { Student } from "../entities/users/student.entity.js"; import { Student } from '../entities/users/student.entity.js';
export interface StudentDTO { export interface StudentDTO {
id: string; id: string;

View file

@ -1,22 +1,26 @@
import { TeacherInvitation } from "../entities/classes/teacher-invitation.entity.js"; import { TeacherInvitation } from '../entities/classes/teacher-invitation.entity.js';
import { ClassDTO, mapToClassDTO } from "./classes.js"; import { ClassDTO, mapToClassDTO } from './class.js';
import { mapToTeacherDTO, TeacherDTO } from "./teacher.js"; import { mapToUserDTO, UserDTO } from './user.js';
export interface TeacherInvitationDTO { export interface TeacherInvitationDTO {
sender: string | TeacherDTO, sender: string | UserDTO;
receiver: string | TeacherDTO, receiver: string | UserDTO;
class: string | ClassDTO, class: string | ClassDTO;
} }
export function mapToTeacherInvitationDTO(invitation: TeacherInvitation): TeacherInvitationDTO { export function mapToTeacherInvitationDTO(
invitation: TeacherInvitation
): TeacherInvitationDTO {
return { return {
sender: mapToTeacherDTO(invitation.sender), sender: mapToUserDTO(invitation.sender),
receiver: mapToTeacherDTO(invitation.receiver), receiver: mapToUserDTO(invitation.receiver),
class: mapToClassDTO(invitation.class), class: mapToClassDTO(invitation.class),
}; };
} }
export function mapToTeacherInvitationDTOIds(invitation: TeacherInvitation): TeacherInvitationDTO { export function mapToTeacherInvitationDTOIds(
invitation: TeacherInvitation
): TeacherInvitationDTO {
return { return {
sender: invitation.sender.username, sender: invitation.sender.username,
receiver: invitation.receiver.username, receiver: invitation.receiver.username,

View file

@ -1,36 +0,0 @@
import { Teacher } from "../entities/users/teacher.entity.js";
/**
* Teacher Data Transfer Object
*/
export interface TeacherDTO {
username: string;
firstName: string;
lastName: string;
endpoints?: {
self: string;
classes: string;
questions: string;
invitations: string;
};
}
/**
* Maps a Teacher entity to a TeacherDTO
*/
export function mapToTeacherDTO(teacher: Teacher): TeacherDTO {
return {
username: teacher.username,
firstName: teacher.firstName,
lastName: teacher.lastName,
};
}
export function mapToTeacher(teacherData: TeacherDTO): Teacher {
const teacher = new Teacher();
teacher.username = teacherData.username;
teacher.firstName = teacherData.firstName;
teacher.lastName = teacherData.lastName;
return teacher;
}

View file

@ -1,7 +1,7 @@
import { User } from "../entities/users/user.entity.js"; import { User } from '../entities/users/user.entity.js';
export interface UserDTO { export interface UserDTO {
id?: string, id?: string;
username: string; username: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
@ -22,7 +22,10 @@ export function mapToUserDTO(user: User): UserDTO {
}; };
} }
export function mapToUser<T extends User>(userData: UserDTO, userInstance: T): T { export function mapToUser<T extends User>(
userData: UserDTO,
userInstance: T
): T {
userInstance.username = userData.username; userInstance.username = userData.username;
userInstance.firstName = userData.firstName; userInstance.firstName = userData.firstName;
userInstance.lastName = userData.lastName; userInstance.lastName = userData.lastName;

View file

@ -24,11 +24,20 @@ import { Answer } from './entities/questions/answer.entity.js';
import { Question } from './entities/questions/question.entity.js'; import { Question } from './entities/questions/question.entity.js';
const entities = [ const entities = [
User, Student, Teacher, User,
Assignment, Group, Submission, Student,
Class, ClassJoinRequest, TeacherInvitation, Teacher,
Attachment, LearningObject, LearningPath, Assignment,
Answer, Question Group,
Submission,
Class,
ClassJoinRequest,
TeacherInvitation,
Attachment,
LearningObject,
LearningPath,
Answer,
Question,
]; ];
function config(testingMode: boolean = false): Options { function config(testingMode: boolean = false): Options {
@ -37,25 +46,26 @@ function config(testingMode: boolean = false): Options {
driver: SqliteDriver, driver: SqliteDriver,
dbName: getEnvVar(EnvVars.DbName), dbName: getEnvVar(EnvVars.DbName),
entities: entities, entities: entities,
// entitiesTs: entitiesTs, // EntitiesTs: entitiesTs,
// Workaround: vitest: `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION) // Workaround: vitest: `TypeError: Unknown file extension ".ts"` (ERR_UNKNOWN_FILE_EXTENSION)
// (see https://mikro-orm.io/docs/guide/project-setup#testing-the-endpoint) // (see https://mikro-orm.io/docs/guide/project-setup#testing-the-endpoint)
dynamicImportProvider: (id) => import(id), dynamicImportProvider: (id) => {
}; return import(id);
} else { },
return {
driver: PostgreSqlDriver,
host: getEnvVar(EnvVars.DbHost),
port: getNumericEnvVar(EnvVars.DbPort),
dbName: getEnvVar(EnvVars.DbName),
user: getEnvVar(EnvVars.DbUsername),
password: getEnvVar(EnvVars.DbPassword),
entities: entities,
//entitiesTs: entitiesTs,
debug: true,
}; };
} }
return {
driver: PostgreSqlDriver,
host: getEnvVar(EnvVars.DbHost),
port: getNumericEnvVar(EnvVars.DbPort),
dbName: getEnvVar(EnvVars.DbName),
user: getEnvVar(EnvVars.DbUsername),
password: getEnvVar(EnvVars.DbPassword),
entities: entities,
//EntitiesTs: entitiesTs,
debug: true,
};
} }
export default config; export default config;

View file

@ -1,31 +1,30 @@
import express from 'express' import express from 'express';
import { getAllAssignmentsHandler, getAssignmentHandler } from '../controllers/assignments.js'; import {
import groupRouter from './group.js'; getAllAssignmentsHandler,
getAssignmentHandler,
} from '../controllers/assignments.js';
import groupRouter from './groups.js';
const router = express.Router({ mergeParams: true }); const router = express.Router({ mergeParams: true });
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', getAllAssignmentsHandler); router.get('/', getAllAssignmentsHandler);
// information about an assignment with id 'id' // Information about an assignment with id 'id'
router.get('/:id', getAssignmentHandler); router.get('/:id', getAssignmentHandler);
router.get('/:id/submissions', (req, res) => { router.get('/:id/submissions', (req, res) => {
res.json({ res.json({
submissions: [ submissions: ['0'],
'0'
],
}); });
}); });
router.get('/:id/questions', (req, res) => { router.get('/:id/questions', (req, res) => {
res.json({ res.json({
questions: [ questions: ['0'],
'0'
],
}); });
}); });
router.use('/:assignmentid/groups', groupRouter); router.use('/:assignmentid/groups', groupRouter);
export default router export default router;

View file

@ -1,13 +1,18 @@
import express from 'express' import express from 'express';
import { getAllClassesHandler, getClassHandler, getClassStudentsHandler, getTeacherInvitationsHandler } from '../controllers/classes.js'; import {
import assignmentRouter from './assignment.js'; getAllClassesHandler,
getClassHandler,
getClassStudentsHandler,
getTeacherInvitationsHandler,
} from '../controllers/classes.js';
import assignmentRouter from './assignments.js';
const router = express.Router(); const router = express.Router();
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', getAllClassesHandler); router.get('/', getAllClassesHandler);
// information about an class with id 'id' // Information about an class with id 'id'
router.get('/:id', getClassHandler); router.get('/:id', getClassHandler);
router.get('/:id/teacher-invitations', getTeacherInvitationsHandler); router.get('/:id/teacher-invitations', getTeacherInvitationsHandler);
@ -16,4 +21,4 @@ router.get('/:id/students', getClassStudentsHandler);
router.use('/:classid/assignments', assignmentRouter); router.use('/:classid/assignments', assignmentRouter);
export default router export default router;

View file

@ -1,18 +1,19 @@
import express from 'express' import express from 'express';
import { getAllGroupsHandler, getGroupHandler } from '../controllers/groups.js'; import { getAllGroupsHandler, getGroupHandler } from '../controllers/groups.js';
const router = express.Router({ mergeParams: true }); const router = express.Router({ mergeParams: true });
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', getAllGroupsHandler); router.get('/', getAllGroupsHandler);
// information about a group (members, ... [TODO DOC]) // Information about a group (members, ... [TODO DOC])
router.get('/:groupid', getGroupHandler); router.get('/:groupid', getGroupHandler);
// the list of questions a group has made // The list of questions a group has made
router.get('/:id/question', (req, res) => { router.get('/:id/question', (req, res) => {
res.json({ res.json({
questions: [ '0' ], questions: ['0'],
}); });
}) });
export default router export default router;

View file

@ -1,38 +1,34 @@
import express from 'express' import express from 'express';
const router = express.Router(); const router = express.Router();
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.json({ res.json({
questions: [ questions: ['0', '1'],
'0',
'1',
]
}); });
}); });
// information about an question with id 'id' // Information about an question with id 'id'
router.get('/:id', (req, res) => { router.get('/:id', (req, res) => {
res.json({ res.json({
id: req.params.id, id: req.params.id,
student: '0', student: '0',
group: '0', group: '0',
time: new Date(2025, 1, 1), time: new Date(2025, 1, 1),
content: 'Zijn alle gehele getallen groter dan 2 gelijk aan de som van 2 priemgetallen????', content:
'Zijn alle gehele getallen groter dan 2 gelijk aan de som van 2 priemgetallen????',
learningObject: '0', learningObject: '0',
links: { links: {
self: `${req.baseUrl}/${req.params.id}`, self: `${req.baseUrl}/${req.params.id}`,
answers: `${req.baseUrl}/${req.params.id}/answers`, answers: `${req.baseUrl}/${req.params.id}/answers`,
} },
}); });
}) });
router.get('/:id/answers', (req, res) => { router.get('/:id/answers', (req, res) => {
res.json({ res.json({
answers: [ answers: ['0'],
'0' });
], });
})
})
export default router export default router;

View file

@ -1,51 +1,49 @@
import express from 'express' import express from 'express';
import { import {
createStudentHandler, deleteStudentHandler, createStudentHandler,
deleteStudentHandler,
getAllStudentsHandler, getAllStudentsHandler,
getStudentAssignmentsHandler, getStudentAssignmentsHandler,
getStudentClassesHandler, getStudentClassesHandler,
getStudentHandler getStudentHandler,
} from '../controllers/students.js'; } from '../controllers/students.js';
const router = express.Router(); const router = express.Router();
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', getAllStudentsHandler); router.get('/', getAllStudentsHandler);
router.post('/', createStudentHandler); router.post('/', createStudentHandler);
router.delete('/:username', deleteStudentHandler); router.delete('/:username', deleteStudentHandler);
// information about a student's profile // Information about a student's profile
router.get('/:username', getStudentHandler); router.get('/:username', getStudentHandler);
// The list of classes a student is in
// the list of classes a student is in
router.get('/:id/classes', getStudentClassesHandler); router.get('/:id/classes', getStudentClassesHandler);
// the list of submissions a student has made // The list of submissions a student has made
router.get('/:id/submissions', (req, res) => { router.get('/:id/submissions', (req, res) => {
res.json({ res.json({
submissions: [ '0' ], submissions: ['0'],
}); });
}) });
// The list of assignments a student has
// the list of assignments a student has
router.get('/:id/assignments', getStudentAssignmentsHandler); router.get('/:id/assignments', getStudentAssignmentsHandler);
// the list of groups a student is in // The list of groups a student is in
router.get('/:id/groups', (req, res) => { router.get('/:id/groups', (req, res) => {
res.json({ res.json({
groups: [ '0' ], groups: ['0'],
}); });
}) });
// a list of questions a user has created // A list of questions a user has created
router.get('/:id/questions', (req, res) => { router.get('/:id/questions', (req, res) => {
res.json({ res.json({
questions: [ '0' ], questions: ['0'],
}); });
}) });
export default router export default router;

View file

@ -1,17 +1,14 @@
import express from 'express' import express from 'express';
const router = express.Router(); const router = express.Router();
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', (req, res) => { router.get('/', (req, res) => {
res.json({ res.json({
submissions: [ submissions: ['0', '1'],
'0',
'1',
]
}); });
}); });
// information about an submission with id 'id' // Information about an submission with id 'id'
router.get('/:id', (req, res) => { router.get('/:id', (req, res) => {
res.json({ res.json({
id: req.params.id, id: req.params.id,
@ -21,6 +18,6 @@ router.get('/:id', (req, res) => {
content: 'Wortel 2 is rationeel', content: 'Wortel 2 is rationeel',
learningObject: '0', learningObject: '0',
}); });
}) });
export default router export default router;

View file

@ -1,17 +1,22 @@
import express from 'express' import express from 'express';
import { import {
createTeacherHandler, createTeacherHandler,
deleteTeacherHandler, deleteTeacherHandler,
getAllTeachersHandler,
getTeacherClassHandler, getTeacherClassHandler,
getTeacherHandler, getTeacherQuestionHandler, getTeacherStudentHandler getTeacherHandler,
} from "../controllers/teachers.js"; getTeacherQuestionHandler,
getTeacherStudentHandler,
} from '../controllers/teachers.js';
const router = express.Router(); const router = express.Router();
// root endpoint used to search objects // Root endpoint used to search objects
router.get('/', getTeacherHandler); router.get('/', getAllTeachersHandler);
router.post('/', createTeacherHandler); router.post('/', createTeacherHandler);
router.get('/:username', getTeacherHandler);
router.delete('/:username', deleteTeacherHandler); router.delete('/:username', deleteTeacherHandler);
router.get('/:username/classes', getTeacherClassHandler); router.get('/:username/classes', getTeacherClassHandler);
@ -20,15 +25,11 @@ router.get('/:username/students', getTeacherStudentHandler);
router.get('/:username/questions', getTeacherQuestionHandler); router.get('/:username/questions', getTeacherQuestionHandler);
// invitations to other classes a teacher received // Invitations to other classes a teacher received
router.get('/:id/invitations', (req, res) => { router.get('/:id/invitations', (req, res) => {
res.json({ res.json({
invitations: [ invitations: ['0'],
'0'
],
}); });
}); });
export default router;
export default router

View file

@ -1,7 +1,17 @@
import { getAssignmentRepository, getClassRepository } from "../data/repositories.js"; import {
import { AssignmentDTO, mapToAssignmentDTO, mapToAssignmentDTOId } from "../interfaces/assignments.js"; getAssignmentRepository,
getClassRepository,
} from '../data/repositories.js';
import {
AssignmentDTO,
mapToAssignmentDTO,
mapToAssignmentDTOId,
} from '../interfaces/assignment.js';
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> { export async function getAllAssignments(
classid: string,
full: boolean
): Promise<AssignmentDTO[]> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classid); const cls = await classRepository.findById(classid);
@ -10,7 +20,8 @@ export async function getAllAssignments(classid: string, full: boolean): Promise
} }
const assignmentRepository = getAssignmentRepository(); const assignmentRepository = getAssignmentRepository();
const assignments = await assignmentRepository.findAllAssignmentsInClass(cls); const assignments =
await assignmentRepository.findAllAssignmentsInClass(cls);
if (full) { if (full) {
return assignments.map(mapToAssignmentDTO); return assignments.map(mapToAssignmentDTO);
@ -19,7 +30,10 @@ export async function getAllAssignments(classid: string, full: boolean): Promise
return assignments.map(mapToAssignmentDTOId); return assignments.map(mapToAssignmentDTOId);
} }
export async function getAssignment(classid: string, id: number): Promise<AssignmentDTO | null> { export async function getAssignment(
classid: string,
id: number
): Promise<AssignmentDTO | null> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classid); const cls = await classRepository.findById(classid);

View file

@ -1,11 +1,23 @@
import { getClassRepository } from "../data/repositories"; import {
import { Class } from "../entities/classes/class.entity"; getClassRepository,
import { ClassDTO, mapToClassDTO } from "../interfaces/class"; getTeacherInvitationRepository,
import { mapToStudentDTO, StudentDTO } from "../interfaces/student"; } from '../data/repositories.js';
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import { mapToStudentDTO, StudentDTO } from '../interfaces/student.js';
import {
mapToTeacherInvitationDTO,
mapToTeacherInvitationDTOIds,
TeacherInvitationDTO,
} from '../interfaces/teacher-invitation.js';
export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[]> { export async function getAllClasses(
full: boolean
): Promise<ClassDTO[] | string[]> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const classes = await classRepository.find({}, { populate: ["students", "teachers"] }); const classes = await classRepository.find(
{},
{ populate: ['students', 'teachers'] }
);
if (!classes) { if (!classes) {
return []; return [];
@ -13,27 +25,30 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
if (full) { if (full) {
return classes.map(mapToClassDTO); return classes.map(mapToClassDTO);
} else {
return classes.map((cls) => cls.classId);
} }
return classes.map((cls) => {
return cls.classId;
});
} }
export async function getClass(classId: string): Promise<ClassDTO | null> { export async function getClass(classId: string): Promise<ClassDTO | null> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classId); const cls = await classRepository.findById(classId);
if (!cls) return null; if (!cls) {
else { return null;
return mapToClassDTO(cls);
} }
return mapToClassDTO(cls);
} }
async function fetchClassStudents(classId: string, full: boolean): Promise<StudentDTO[] | string[]> { async function fetchClassStudents(classId: string): Promise<StudentDTO[]> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classId); const cls = await classRepository.findById(classId);
if (!cls) if (!cls) {
return []; return [];
}
return cls.students.map(mapToStudentDTO); return cls.students.map(mapToStudentDTO);
} }
@ -43,6 +58,36 @@ export async function getClassStudents(classId: string): Promise<StudentDTO[]> {
} }
export async function getClassStudentsIds(classId: string): Promise<string[]> { export async function getClassStudentsIds(classId: string): Promise<string[]> {
return await fetchClassStudents(classId).map((student) => student.username); const students: StudentDTO[] = await fetchClassStudents(classId);
return students.map((student) => {
return student.username;
});
} }
export async function getClassTeacherInvitations(
classId: string,
full: boolean
): Promise<TeacherInvitationDTO[]> {
const classRepository = getClassRepository();
const cls = await classRepository.findById(classId);
if (!cls) {
return [];
}
const teacherInvitationRepository = getTeacherInvitationRepository();
const invitations =
await teacherInvitationRepository.findAllInvitationsForClass(cls);
console.log(invitations);
if (!invitations) {
return [];
}
if (full) {
return invitations.map(mapToTeacherInvitationDTO);
}
return invitations.map(mapToTeacherInvitationDTOIds);
}

View file

@ -1,11 +1,19 @@
import { getAssignmentRepository, getClassRepository, getGroupRepository } from "../data/repositories.js"; import {
import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from "../interfaces/groups.js"; getAssignmentRepository,
getClassRepository,
getGroupRepository,
} from '../data/repositories.js';
import {
GroupDTO,
mapToGroupDTO,
mapToGroupDTOId,
} from '../interfaces/group.js';
export async function getGroup( export async function getGroup(
classId: string, classId: string,
assignmentNumber: number, assignmentNumber: number,
groupNumber: number, groupNumber: number,
full: boolean, full: boolean
): Promise<GroupDTO | null> { ): Promise<GroupDTO | null> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classId); const cls = await classRepository.findById(classId);
@ -15,14 +23,20 @@ export async function getGroup(
} }
const assignmentRepository = getAssignmentRepository(); const assignmentRepository = getAssignmentRepository();
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber); const assignment = await assignmentRepository.findByClassAndId(
cls,
assignmentNumber
);
if (!assignment) { if (!assignment) {
return null; return null;
} }
const groupRepository = getGroupRepository(); const groupRepository = getGroupRepository();
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment, groupNumber); const group = await groupRepository.findByAssignmentAndGroupNumber(
assignment,
groupNumber
);
if (!group) { if (!group) {
return null; return null;
@ -38,7 +52,7 @@ export async function getGroup(
export async function getAllGroups( export async function getAllGroups(
classId: string, classId: string,
assignmentNumber: number, assignmentNumber: number,
full: boolean, full: boolean
): Promise<GroupDTO[]> { ): Promise<GroupDTO[]> {
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const cls = await classRepository.findById(classId); const cls = await classRepository.findById(classId);
@ -48,7 +62,10 @@ export async function getAllGroups(
} }
const assignmentRepository = getAssignmentRepository(); const assignmentRepository = getAssignmentRepository();
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber); const assignment = await assignmentRepository.findByClassAndId(
cls,
assignmentNumber
);
if (!assignment) { if (!assignment) {
return []; return [];

View file

@ -1,8 +1,11 @@
import { getClassRepository, getStudentRepository } from "../data/repositories.js"; import {
import { Class } from "../entities/classes/class.entity.js"; getClassRepository,
import { Student } from "../entities/users/student.entity.js"; getStudentRepository,
import { ClassDTO, mapToClassDTO } from "../interfaces/classes.js"; } from '../data/repositories.js';
import {UserService} from "./users.js"; import { Class } from '../entities/classes/class.entity.js';
import { Student } from '../entities/users/student.entity.js';
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import { UserService } from './users.js';
export class StudentService extends UserService<Student> { export class StudentService extends UserService<Student> {
constructor() { constructor() {
@ -14,12 +17,16 @@ async function fetchStudentClasses(username: string): Promise<Class[]> {
const studentRepository = getStudentRepository(); const studentRepository = getStudentRepository();
const student = await studentRepository.findByUsername(username); const student = await studentRepository.findByUsername(username);
if (!student) return []; if (!student) {
return [];
}
const classRepository = getClassRepository(); const classRepository = getClassRepository();
const classes = await classRepository.findByStudent(student); const classes = await classRepository.findByStudent(student);
if (!classes) return []; if (!classes) {
return [];
}
return classes; return classes;
} }
@ -31,6 +38,7 @@ export async function getStudentClasses(username: string): Promise<ClassDTO[]> {
export async function getStudentClassIds(username: string): Promise<string[]> { export async function getStudentClassIds(username: string): Promise<string[]> {
const classes = await fetchStudentClasses(username); const classes = await fetchStudentClasses(username);
return classes.map(cls => cls.classId); // 'class' is a native keyword return classes.map((cls) => {
return cls.classId;
}); // 'class' is a native keyword
} }

View file

@ -2,130 +2,108 @@ import {
getClassRepository, getClassRepository,
getLearningObjectRepository, getLearningObjectRepository,
getQuestionRepository, getQuestionRepository,
getTeacherRepository getStudentRepository,
} from "../data/repositories.js"; getTeacherRepository,
import {mapToTeacher, mapToTeacherDTO, TeacherDTO} from "../interfaces/teacher.js"; } from '../data/repositories.js';
import { Teacher } from "../entities/users/teacher.entity"; import { Teacher } from '../entities/users/teacher.entity.js';
import {ClassDTO, mapToClassDTO} from "../interfaces/class"; import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import {getClassStudents, getClassStudentsIds} from "./class"; import { getClassStudents } from './class.js';
import {StudentDTO} from "../interfaces/student"; import { StudentDTO } from '../interfaces/student.js';
import {mapToQuestionDTO, QuestionDTO, QuestionId} from "../interfaces/question"; import {
mapToQuestionDTO,
mapToQuestionId,
QuestionDTO,
QuestionId,
} from '../interfaces/question.js';
import { UserService } from './users.js';
import { mapToUser } from '../interfaces/user.js';
export class TeacherUserService extends UserService<Teacher> {
async function fetchAllTeachers(): Promise<TeacherDTO[]> { constructor() {
const teacherRepository = getTeacherRepository(); super(getTeacherRepository());
const teachers = await teacherRepository.find({}); }
return teachers.map(mapToTeacherDTO);
} }
export async function getAllTeachers(): Promise<TeacherDTO[]> { export class TeacherService {
return await fetchAllTeachers(); protected teacherService = new TeacherUserService();
} protected teacherRepository = getTeacherRepository();
protected classRepository = getClassRepository();
protected learningObjectRepository = getLearningObjectRepository();
protected questionRepository = getQuestionRepository();
export async function getAllTeachersIds(): Promise<string[]> { async fetchClassesByTeacher(username: string): Promise<ClassDTO[]> {
return await fetchAllTeachers().map((teacher) => teacher.username) const teacher = await this.teacherRepository.findByUsername(username);
} if (!teacher) {
return [];
}
export async function createTeacher(teacherData: TeacherDTO): Promise<Teacher> { const classes = await this.classRepository.findByTeacher(teacher);
const teacherRepository = getTeacherRepository(); return classes.map(mapToClassDTO);
const newTeacher = mapToTeacher(teacherData);
await teacherRepository.addTeacher(newTeacher);
return newTeacher;
}
export async function getTeacherByUsername(username: string): Promise<TeacherDTO | null> {
const teacherRepository = getTeacherRepository();
const teacher = await teacherRepository.findByUsername(username);
return teacher ? mapToTeacherDTO(teacher) : null;
}
export async function deleteTeacher(username: string): Promise<TeacherDTO | null> {
const teacherRepository = getTeacherRepository();
const teacher = await teacherRepository.findByUsername(username);
if (!teacher)
return null;
await teacherRepository.deleteByUsername(username);
return teacher;
}
async function fetchClassesByTeacher(username: string): Promise<ClassDTO[]> {
const teacherRepository = getTeacherRepository();
const classRepository = getClassRepository();
const teacher = await teacherRepository.findByUsername(username);
if (!teacher) {
return [];
} }
const classes = await classRepository.findByTeacher(teacher); async getClassesByTeacher(username: string): Promise<ClassDTO[]> {
return classes.map(mapToClassDTO); return await this.fetchClassesByTeacher(username);
}
export async function getClassesByTeacher(username: string): Promise<ClassDTO[]> {
return await fetchClassesByTeacher(username)
}
export async function getClassIdsByTeacher(): Promise<string[]> {
return await fetchClassesByTeacher(username).map((cls) => cls.id);
}
async function fetchStudentsByTeacher(username: string) {
const classes = await getClassIdsByTeacher();
return Promise.all(
classes.map( async (id) => getClassStudents(id))
);
}
export async function getStudentsByTeacher(username: string): Promise<StudentDTO[]> {
return await fetchStudentsByTeacher(username);
}
export async function getStudentIdsByTeacher(): Promise<string[]> {
return await fetchStudentsByTeacher(username).map((student) => student.username);
}
async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[]> {
const learningObjectRepository = getLearningObjectRepository();
const questionRepository = getQuestionRepository();
const teacher = getTeacherByUsername(username);
if (!teacher) {
throw new Error(`Teacher with username '${username}' not found.`);
} }
// Find all learning objects that this teacher manages async getClassIdsByTeacher(username: string): Promise<string[]> {
const learningObjects = await learningObjectRepository.findAllByTeacher(teacher); const classes = await this.fetchClassesByTeacher(username);
return classes.map((cls) => {
return cls.id;
});
}
// Fetch all questions related to these learning objects async fetchStudentsByTeacher(username: string) {
const questions = await questionRepository.findAllByLearningObjects(learningObjects); const classes = await this.getClassIdsByTeacher(username);
return questions.map(mapToQuestionDTO); return (
await Promise.all(
classes.map(async (id) => {
return getClassStudents(id);
})
)
).flat();
}
async getStudentsByTeacher(username: string): Promise<StudentDTO[]> {
return await this.fetchStudentsByTeacher(username);
}
async getStudentIdsByTeacher(username: string): Promise<string[]> {
const students = await this.fetchStudentsByTeacher(username);
return students.map((student) => {
return student.username;
});
}
async fetchTeacherQuestions(username: string): Promise<QuestionDTO[]> {
const teacherDTO =
await this.teacherService.getUserByUsername(username);
if (!teacherDTO) {
throw new Error(`Teacher with username '${username}' not found.`);
}
const teacher = mapToUser<Teacher>(teacherDTO, new Teacher());
// Find all learning objects that this teacher manages
const learningObjects =
await this.learningObjectRepository.findAllByTeacher(teacher);
// Fetch all questions related to these learning objects
const questions =
await this.questionRepository.findAllByLearningObjects(
learningObjects
);
return questions.map(mapToQuestionDTO);
}
async getQuestionsByTeacher(username: string): Promise<QuestionDTO[]> {
return await this.fetchTeacherQuestions(username);
}
async getQuestionIdsByTeacher(username: string): Promise<QuestionId[]> {
const questions = await this.fetchTeacherQuestions(username);
return questions.map(mapToQuestionId);
}
} }
export async function getQuestionsByTeacher(username: string): Promise<QuestionDTO[]> {
return await fetchTeacherQuestions(username);
}
export async function getQuestionIdsByTeacher(username: string): Promise<QuestionId[]> {
const questions = await fetchTeacherQuestions(username);
return questions.map((question) => ({
learningObjectHruid: question.learningObjectHruid,
learningObjectLanguage: question.learningObjectLanguage,
learningObjectVersion: question.learningObjectVersion,
sequenceNumber: question.sequenceNumber
}));
}

View file

@ -1,6 +1,6 @@
import { UserRepository } from "../data/users/user-repository.js"; import { UserRepository } from '../data/users/user-repository.js';
import { UserDTO, mapToUser, mapToUserDTO } from "../interfaces/user.js"; import { UserDTO, mapToUser, mapToUserDTO } from '../interfaces/user.js';
import {User} from "../entities/users/user.entity.js"; import { User } from '../entities/users/user.entity.js';
export class UserService<T extends User> { export class UserService<T extends User> {
protected repository: UserRepository<T>; protected repository: UserRepository<T>;
@ -16,11 +16,13 @@ export class UserService<T extends User> {
async getAllUserIds(): Promise<string[]> { async getAllUserIds(): Promise<string[]> {
const users = await this.getAllUsers(); const users = await this.getAllUsers();
return users.map((user) => user.username); return users.map((user) => {
return user.username;
});
} }
async getUserByUsername(username: string): Promise<UserDTO | null> { async getUserByUsername(username: string): Promise<UserDTO | null> {
const user = await this.repository.findByUsername(username) const user = await this.repository.findByUsername(username);
return user ? mapToUserDTO(user) : null; return user ? mapToUserDTO(user) : null;
} }
@ -32,8 +34,10 @@ export class UserService<T extends User> {
async deleteUser(username: string): Promise<UserDTO | null> { async deleteUser(username: string): Promise<UserDTO | null> {
const user = await this.getUserByUsername(username); const user = await this.getUserByUsername(username);
if (!user) return null; if (!user) {
await this.repository.deleteByUsername(username) return null;
}
await this.repository.deleteByUsername(username);
return mapToUserDTO(user); return mapToUserDTO(user);
} }
} }

View file

@ -1,7 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import path from 'path'; import path from 'path';
import yaml from 'js-yaml'; import yaml from 'js-yaml';
import { FALLBACK_LANG } from "../config.js"; import { FALLBACK_LANG } from '../config.js';
export function loadTranslations<T>(language: string): T { export function loadTranslations<T>(language: string): T {
try { try {
@ -13,7 +13,11 @@ export function loadTranslations<T>(language: string): T {
`Cannot load translation for ${language}, fallen back to dutch` `Cannot load translation for ${language}, fallen back to dutch`
); );
console.error(error); console.error(error);
const fallbackPath = path.join(process.cwd(), '_i18n', `${FALLBACK_LANG}.yml`); const fallbackPath = path.join(
process.cwd(),
'_i18n',
`${FALLBACK_LANG}.yml`
);
return yaml.load(fs.readFileSync(fallbackPath, 'utf8')) as T; return yaml.load(fs.readFileSync(fallbackPath, 'utf8')) as T;
} }
} }