Merge branch 'dev' into refactor/linting

This commit is contained in:
Tibo De Peuter 2025-03-30 11:27:59 +02:00
commit f9b59af2fd
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
22 changed files with 256 additions and 344 deletions

View file

@ -37,7 +37,7 @@ export async function createAssignmentHandler(req: Request<AssignmentParams>, re
return;
}
res.status(201).json({ assignment: assignment });
res.status(201).json(assignment);
}
export async function getAssignmentHandler(req: Request<AssignmentParams>, res: Response): Promise<void> {
@ -62,13 +62,14 @@ export async function getAssignmentHandler(req: Request<AssignmentParams>, res:
export async function getAssignmentsSubmissionsHandler(req: Request<AssignmentParams>, res: Response): Promise<void> {
const classid = req.params.classid;
const assignmentNumber = Number(req.params.id);
const full = req.query.full === 'true';
if (isNaN(assignmentNumber)) {
res.status(400).json({ error: 'Assignment id must be a number' });
return;
}
const submissions = await getAssignmentsSubmissions(classid, assignmentNumber);
const submissions = await getAssignmentsSubmissions(classid, assignmentNumber, full);
res.json({
submissions: submissions,

View file

@ -1,7 +1,6 @@
import { Request, Response } from 'express';
import { createClass, getAllClasses, getClass, getClassStudents, getClassStudentsIds, getClassTeacherInvitations } from '../services/class.js';
import { createClass, getAllClasses, getClass, getClassStudents, getClassStudentsIds, getClassTeacherInvitations } from '../services/classes.js';
import { ClassDTO } from '../interfaces/class.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllClassesHandler(req: Request, res: Response): Promise<void> {
const full = req.query.full === 'true';
@ -29,30 +28,19 @@ export async function createClassHandler(req: Request, res: Response): Promise<v
return;
}
res.status(201).json({ class: cls });
res.status(201).json(cls);
}
export async function getClassHandler(req: Request, res: Response): Promise<void> {
try {
const classId = req.params.id;
const cls = await getClass(classId);
const classId = req.params.id;
const cls = await getClass(classId);
if (!cls) {
res.status(404).json({ error: 'Class not found' });
return;
}
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) {
getLogger().error('Error fetching learning objects:', error);
res.status(500).json({ error: 'Internal server error' });
if (!cls) {
res.status(404).json({ error: 'Class not found' });
return;
}
res.json(cls);
}
export async function getClassStudentsHandler(req: Request, res: Response): Promise<void> {
@ -68,7 +56,7 @@ export async function getClassStudentsHandler(req: Request, res: Response): Prom
export async function getTeacherInvitationsHandler(req: Request, res: Response): Promise<void> {
const classId = req.params.id;
const full = req.query.full === 'true'; // TODO: not implemented yet
const full = req.query.full === 'true';
const invitations = await getClassTeacherInvitations(classId, full);

View file

@ -28,6 +28,11 @@ export async function getGroupHandler(req: Request<GroupParams>, res: Response):
const group = await getGroup(classId, assignmentId, groupId, full);
if (!group) {
res.status(404).json({ error: 'Group not found' });
return;
}
res.json(group);
}
@ -66,12 +71,12 @@ export async function createGroupHandler(req: Request, res: Response): Promise<v
return;
}
res.status(201).json({ group: group });
res.status(201).json(group);
}
export async function getGroupSubmissionsHandler(req: Request, res: Response): Promise<void> {
const classId = req.params.classid;
// Const full = req.query.full === 'true';
const full = req.query.full === 'true';
const assignmentId = Number(req.params.assignmentid);
@ -87,7 +92,7 @@ export async function getGroupSubmissionsHandler(req: Request, res: Response): P
return;
}
const submissions = await getGroupSubmissions(classId, assignmentId, groupId);
const submissions = await getGroupSubmissions(classId, assignmentId, groupId, full);
res.json({
submissions: submissions,

View file

@ -40,7 +40,7 @@ export async function getAllLearningObjects(req: Request, res: Response): Promis
learningObjects = await learningObjectService.getLearningObjectIdsFromPath(learningPathId);
}
res.json(learningObjects);
res.json({ learningObjects: learningObjects });
}
export async function getLearningObject(req: Request, res: Response): Promise<void> {

View file

@ -48,7 +48,7 @@ export async function getAllQuestionsHandler(req: Request, res: Response): Promi
if (!questions) {
res.status(404).json({ error: `Questions not found.` });
} else {
res.json(questions);
res.json({ questions: questions });
}
}
@ -76,12 +76,12 @@ export async function getQuestionAnswersHandler(req: Request, res: Response): Pr
return;
}
const answers = getAnswersByQuestion(questionId, full);
const answers = await getAnswersByQuestion(questionId, full);
if (!answers) {
res.status(404).json({ error: `Questions not found.` });
res.status(404).json({ error: `Questions not found` });
} else {
res.json(answers);
res.json({ answers: answers });
}
}
@ -96,7 +96,7 @@ export async function createQuestionHandler(req: Request, res: Response): Promis
const question = await createQuestion(questionDTO);
if (!question) {
res.status(400).json({ error: 'Could not add question' });
res.status(400).json({ error: 'Could not create question' });
} else {
res.json(question);
}

View file

@ -10,21 +10,20 @@ import {
getStudentSubmissions,
} from '../services/students.js';
import { StudentDTO } from '../interfaces/student.js';
import { getLogger } from '../logging/initalize.js';
// TODO: accept arguments (full, ...)
// TODO: endpoints
export async function getAllStudentsHandler(req: Request, res: Response): Promise<void> {
const full = req.query.full === 'true';
const students: StudentDTO[] | string[] = full ? await getAllStudents() : await getAllStudents();
const students = await getAllStudents(full);
if (!students) {
res.status(404).json({ error: `Student not found.` });
return;
}
res.status(201).json(students);
res.json({ students: students });
}
export async function getStudentHandler(req: Request, res: Response): Promise<void> {
@ -44,7 +43,7 @@ export async function getStudentHandler(req: Request, res: Response): Promise<vo
return;
}
res.status(201).json(user);
res.json(user);
}
export async function createStudentHandler(req: Request, res: Response): Promise<void> {
@ -58,6 +57,14 @@ export async function createStudentHandler(req: Request, res: Response): Promise
}
const newUser = await createStudent(userData);
if (!newUser) {
res.status(500).json({
error: 'Something went wrong while creating student'
});
return;
}
res.status(201).json(newUser);
}
@ -81,25 +88,12 @@ export async function deleteStudentHandler(req: Request, res: Response): Promise
}
export async function getStudentClassesHandler(req: Request, res: Response): Promise<void> {
try {
const full = req.query.full === 'true';
const username = req.params.id;
const full = req.query.full === 'true';
const username = req.params.id;
const classes = await getStudentClasses(username, full);
const classes = await getStudentClasses(username, full);
res.json({
classes: classes,
endpoints: {
self: `${req.baseUrl}/${req.params.id}`,
classes: `${req.baseUrl}/${req.params.id}/invitations`,
questions: `${req.baseUrl}/${req.params.id}/assignments`,
students: `${req.baseUrl}/${req.params.id}/students`,
},
});
} catch (error) {
getLogger().error('Error fetching learning objects:', error);
res.status(500).json({ error: 'Internal server error' });
}
res.json({ classes: classes });
}
// TODO
@ -130,8 +124,9 @@ export async function getStudentGroupsHandler(req: Request, res: Response): Prom
export async function getStudentSubmissionsHandler(req: Request, res: Response): Promise<void> {
const username = req.params.id;
const full = req.query.full === 'true';
const submissions = await getStudentSubmissions(username);
const submissions = await getStudentSubmissions(username, full);
res.json({
submissions: submissions,

View file

@ -36,10 +36,11 @@ export async function createSubmissionHandler(req: Request, res: Response): Prom
const submission = await createSubmission(submissionDTO);
if (!submission) {
res.status(404).json({ error: 'Submission not added' });
} else {
res.json(submission);
res.status(400).json({ error: 'Failed to create submission' });
return;
}
res.json(submission);
}
export async function deleteSubmissionHandler(req: Request, res: Response): Promise<void> {
@ -53,7 +54,8 @@ export async function deleteSubmissionHandler(req: Request, res: Response): Prom
if (!submission) {
res.status(404).json({ error: 'Submission not found' });
} else {
res.json(submission);
return;
}
res.json(submission);
}

View file

@ -4,30 +4,23 @@ import {
deleteTeacher,
getAllTeachers,
getClassesByTeacher,
getClassIdsByTeacher,
getQuestionIdsByTeacher,
getQuestionsByTeacher,
getStudentIdsByTeacher,
getStudentsByTeacher,
getTeacher,
} from '../services/teachers.js';
import { ClassDTO } from '../interfaces/class.js';
import { StudentDTO } from '../interfaces/student.js';
import { QuestionDTO, QuestionId } from '../interfaces/question.js';
import { TeacherDTO } from '../interfaces/teacher.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllTeachersHandler(req: Request, res: Response): Promise<void> {
const full = req.query.full === 'true';
const teachers: TeacherDTO[] | string[] = full ? await getAllTeachers() : await getAllTeachers();
const teachers = await getAllTeachers(full);
if (!teachers) {
res.status(404).json({ error: `Teacher not found.` });
return;
}
res.status(201).json(teachers);
res.json({ teachers: teachers });
}
export async function getTeacherHandler(req: Request, res: Response): Promise<void> {
@ -42,12 +35,12 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo
if (!user) {
res.status(404).json({
error: `User with username '${username}' not found.`,
error: `Teacher '${username}' not found.`,
});
return;
}
res.status(201).json(user);
res.json(user);
}
export async function createTeacherHandler(req: Request, res: Response): Promise<void> {
@ -61,6 +54,12 @@ export async function createTeacherHandler(req: Request, res: Response): Promise
}
const newUser = await createTeacher(userData);
if (!newUser) {
res.status(400).json({ error: 'Failed to create teacher' });
return;
}
res.status(201).json(newUser);
}
@ -75,7 +74,7 @@ export async function deleteTeacherHandler(req: Request, res: Response): Promise
const deletedUser = await deleteTeacher(username);
if (!deletedUser) {
res.status(404).json({
error: `User with username '${username}' not found.`,
error: `User '${username}' not found.`,
});
return;
}
@ -84,58 +83,58 @@ export async function deleteTeacherHandler(req: Request, res: Response): Promise
}
export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username;
const full = req.query.full === 'true';
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const classes: ClassDTO[] | string[] = full ? await getClassesByTeacher(username) : await getClassIdsByTeacher(username);
res.status(201).json(classes);
} catch (error) {
getLogger().error('Error fetching classes by teacher:', error);
res.status(500).json({ error: 'Internal server error' });
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const classes = await getClassesByTeacher(username, full);
if (!classes) {
res.status(404).json({ error: 'Teacher not found' });
return;
}
res.json({ classes: classes });
}
export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username;
const full = req.query.full === 'true';
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const students: StudentDTO[] | string[] = full ? await getStudentsByTeacher(username) : await getStudentIdsByTeacher(username);
res.status(201).json(students);
} catch (error) {
getLogger().error('Error fetching students by teacher:', error);
res.status(500).json({ error: 'Internal server error' });
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const students = await getStudentsByTeacher(username, full);
if (!students) {
res.status(404).json({ error: 'Teacher not found' });
return;
}
res.json({ students: students });
}
export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> {
try {
const username = req.params.username;
const full = req.query.full === 'true';
const username = req.params.username;
const full = req.query.full === 'true';
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const questions: QuestionDTO[] | QuestionId[] = full ? await getQuestionsByTeacher(username) : await getQuestionIdsByTeacher(username);
res.status(201).json(questions);
} catch (error) {
getLogger().error('Error fetching questions by teacher:', error);
res.status(500).json({ error: 'Internal server error' });
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const questions = await getQuestionsByTeacher(username, full);
if (!questions) {
res.status(404).json({ error: 'Teacher not found' });
return;
}
res.json({ questions: questions });
}

View file

@ -1,92 +0,0 @@
import { Request, Response } from 'express';
import { UserService } from '../services/users.js';
import { UserDTO } from '../interfaces/user.js';
import { User } from '../entities/users/user.entity.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllUsersHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
try {
const full = req.query.full === 'true';
const users: UserDTO[] | string[] = full ? await service.getAllUsers() : await service.getAllUserIds();
if (!users) {
res.status(404).json({ error: `Users not found.` });
return;
}
res.status(201).json(users);
} catch (error) {
getLogger().error('❌ Error fetching users:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export async function getUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
try {
const username = req.params.username;
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const user = await service.getUserByUsername(username);
if (!user) {
res.status(404).json({
error: `User with username '${username}' not found.`,
});
return;
}
res.status(201).json(user);
} catch (error) {
getLogger().error('❌ Error fetching users:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export async function createUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>, userClass: new () => T): Promise<void> {
try {
getLogger().debug({ req: req });
const userData = req.body as UserDTO;
if (!userData.username || !userData.firstName || !userData.lastName) {
res.status(400).json({
error: 'Missing required fields: username, firstName, lastName',
});
return;
}
const newUser = await service.createUser(userData, userClass);
res.status(201).json(newUser);
} catch (error) {
getLogger().error('❌ Error creating user:', error);
res.status(500).json({ error: 'Internal server error' });
}
}
export async function deleteUserHandler<T extends User>(req: Request, res: Response, service: UserService<T>): Promise<void> {
try {
const username = req.params.username;
if (!username) {
res.status(400).json({ error: 'Missing required field: username' });
return;
}
const deletedUser = await service.deleteUser(username);
if (!deletedUser) {
res.status(404).json({
error: `User with username '${username}' not found.`,
});
return;
}
res.status(200).json(deletedUser);
} catch (error) {
getLogger().error('❌ Error deleting user:', error);
res.status(500).json({ error: 'Internal server error' });
}
}

View file

@ -3,6 +3,12 @@ import { Student } from '../users/student.entity.js';
import { Class } from './class.entity.js';
import { ClassJoinRequestRepository } from '../../data/classes/class-join-request-repository.js';
export enum ClassJoinRequestStatus {
Open = 'open',
Accepted = 'accepted',
Declined = 'declined',
}
@Entity({
repository: () => ClassJoinRequestRepository,
})
@ -21,10 +27,4 @@ export class ClassJoinRequest {
@Enum(() => ClassJoinRequestStatus)
status!: ClassJoinRequestStatus;
}
export enum ClassJoinRequestStatus {
Open = 'open',
Accepted = 'accepted',
Declined = 'declined',
}
}

View file

@ -9,12 +9,6 @@ export interface ClassDTO {
teachers: string[];
students: string[];
joinRequests: string[];
endpoints?: {
self: string;
invitations: string;
assignments: string;
students: string;
};
}
export function mapToClassDTO(cls: Class): ClassDTO {

View file

@ -2,11 +2,10 @@ import { Submission } from '../entities/assignments/submission.entity.js';
import { Language } from '../entities/content/language.js';
import { GroupDTO, mapToGroupDTO } from './group.js';
import { mapToStudent, mapToStudentDTO, StudentDTO } from './student.js';
import { LearningObjectIdentifier } from './learning-content.js';
export interface SubmissionDTO {
learningObjectHruid: string;
learningObjectLanguage: Language;
learningObjectVersion: number;
learningObjectIdentifier: LearningObjectIdentifier;
submissionNumber?: number;
submitter: StudentDTO;
@ -15,11 +14,21 @@ export interface SubmissionDTO {
content: string;
}
export interface SubmissionDTOId {
learningObjectHruid: string;
learningObjectLanguage: Language;
learningObjectVersion: number;
submissionNumber?: number;
}
export function mapToSubmissionDTO(submission: Submission): SubmissionDTO {
return {
learningObjectHruid: submission.learningObjectHruid,
learningObjectLanguage: submission.learningObjectLanguage,
learningObjectVersion: submission.learningObjectVersion,
learningObjectIdentifier: {
hruid: submission.learningObjectHruid,
language: submission.learningObjectLanguage,
version: submission.learningObjectVersion,
},
submissionNumber: submission.submissionNumber,
submitter: mapToStudentDTO(submission.submitter),
@ -29,11 +38,21 @@ export function mapToSubmissionDTO(submission: Submission): SubmissionDTO {
};
}
export function mapToSubmissionDTOId(submission: Submission): SubmissionDTOId {
return {
learningObjectHruid: submission.learningObjectHruid,
learningObjectLanguage: submission.learningObjectLanguage,
learningObjectVersion: submission.learningObjectVersion,
submissionNumber: submission.submissionNumber,
};
}
export function mapToSubmission(submissionDTO: SubmissionDTO): Submission {
const submission = new Submission();
submission.learningObjectHruid = submissionDTO.learningObjectHruid;
submission.learningObjectLanguage = submissionDTO.learningObjectLanguage;
submission.learningObjectVersion = submissionDTO.learningObjectVersion;
submission.learningObjectHruid = submissionDTO.learningObjectIdentifier.hruid;
submission.learningObjectLanguage = submissionDTO.learningObjectIdentifier.language;
submission.learningObjectVersion = submissionDTO.learningObjectIdentifier.version!;
// Submission.submissionNumber = submissionDTO.submissionNumber;
submission.submitter = mapToStudent(submissionDTO.submitter);
// Submission.submissionTime = submissionDTO.time;

View file

@ -1,10 +1,7 @@
import { Response, Router } from 'express';
import studentRouter from './students.js';
import groupRouter from './groups.js';
import assignmentRouter from './assignments.js';
import submissionRouter from './submissions.js';
import teacherRouter from './teachers.js';
import classRouter from './classes.js';
import questionRouter from './questions.js';
import authRouter from './auth.js';
import themeRoutes from './themes.js';
import learningPathRoutes from './learning-paths.js';
@ -22,11 +19,8 @@ router.get('/', (_, res: Response) => {
});
router.use('/student', studentRouter /* #swagger.tags = ['Student'] */);
router.use('/group', groupRouter /* #swagger.tags = ['Group'] */);
router.use('/assignment', assignmentRouter /* #swagger.tags = ['Assignment'] */);
router.use('/submission', submissionRouter /* #swagger.tags = ['Submission'] */);
router.use('/teacher', teacherRouter /* #swagger.tags = ['Teacher'] */);
router.use('/class', classRouter /* #swagger.tags = ['Class'] */);
router.use('/question', questionRouter /* #swagger.tags = ['Question'] */);
router.use('/auth', authRouter /* #swagger.tags = ['Auth'] */);
router.use('/theme', themeRoutes /* #swagger.tags = ['Theme'] */);
router.use('/learningPath', learningPathRoutes /* #swagger.tags = ['Learning Path'] */);

View file

@ -1,7 +1,7 @@
import { getAssignmentRepository, getClassRepository, getGroupRepository, getSubmissionRepository } from '../data/repositories.js';
import { Assignment } from '../entities/assignments/assignment.entity.js';
import { AssignmentDTO, mapToAssignment, mapToAssignmentDTO, mapToAssignmentDTOId } from '../interfaces/assignment.js';
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> {
const classRepository = getClassRepository();
@ -21,7 +21,7 @@ export async function getAllAssignments(classid: string, full: boolean): Promise
return assignments.map(mapToAssignmentDTOId);
}
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<Assignment | null> {
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<AssignmentDTO | null> {
const classRepository = getClassRepository();
const cls = await classRepository.findById(classid);
@ -36,8 +36,9 @@ export async function createAssignment(classid: string, assignmentData: Assignme
const newAssignment = assignmentRepository.create(assignment);
await assignmentRepository.save(newAssignment);
return newAssignment;
} catch (_) {
return mapToAssignmentDTO(newAssignment);
} catch (e) {
getLogger().error(e);
return null;
}
}
@ -60,7 +61,11 @@ export async function getAssignment(classid: string, id: number): Promise<Assign
return mapToAssignmentDTO(assignment);
}
export async function getAssignmentsSubmissions(classid: string, assignmentNumber: number): Promise<SubmissionDTO[]> {
export async function getAssignmentsSubmissions(
classid: string,
assignmentNumber: number,
full: boolean
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
const classRepository = getClassRepository();
const cls = await classRepository.findById(classid);
@ -81,5 +86,9 @@ export async function getAssignmentsSubmissions(classid: string, assignmentNumbe
const submissionRepository = getSubmissionRepository();
const submissions = (await Promise.all(groups.map(async (group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
return submissions.map(mapToSubmissionDTO);
if (full) {
return submissions.map(mapToSubmissionDTO);
}
return submissions.map(mapToSubmissionDTOId);
}

View file

@ -1,5 +1,4 @@
import { getClassRepository, getStudentRepository, getTeacherInvitationRepository, getTeacherRepository } from '../data/repositories.js';
import { Class } from '../entities/classes/class.entity.js';
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import { mapToStudentDTO, StudentDTO } from '../interfaces/student.js';
import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds, TeacherInvitationDTO } from '../interfaces/teacher-invitation.js';
@ -21,7 +20,7 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
return classes.map((cls) => cls.classId!);
}
export async function createClass(classData: ClassDTO): Promise<Class | null> {
export async function createClass(classData: ClassDTO): Promise<ClassDTO | null> {
const teacherRepository = getTeacherRepository();
const teacherUsernames = classData.teachers || [];
const teachers = (await Promise.all(teacherUsernames.map(async (id) => teacherRepository.findByUsername(id)))).filter(
@ -34,8 +33,6 @@ export async function createClass(classData: ClassDTO): Promise<Class | null> {
(student) => student !== null
);
//Const cls = mapToClass(classData, teachers, students);
const classRepository = getClassRepository();
try {
@ -46,7 +43,7 @@ export async function createClass(classData: ClassDTO): Promise<Class | null> {
});
await classRepository.save(newClass);
return newClass;
return mapToClassDTO(newClass);
} catch (e) {
logger.error(e);
return null;

View file

@ -7,7 +7,7 @@ import {
} from '../data/repositories.js';
import { Group } from '../entities/assignments/group.entity.js';
import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
import { getLogger } from '../logging/initalize.js';
export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number, full: boolean): Promise<GroupDTO | null> {
@ -104,7 +104,12 @@ export async function getAllGroups(classId: string, assignmentNumber: number, fu
return groups.map(mapToGroupDTOId);
}
export async function getGroupSubmissions(classId: string, assignmentNumber: number, groupNumber: number): Promise<SubmissionDTO[]> {
export async function getGroupSubmissions(
classId: string,
assignmentNumber: number,
groupNumber: number,
full: boolean
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
const classRepository = getClassRepository();
const cls = await classRepository.findById(classId);
@ -129,5 +134,9 @@ export async function getGroupSubmissions(classId: string, assignmentNumber: num
const submissionRepository = getSubmissionRepository();
const submissions = await submissionRepository.findAllSubmissionsForGroup(group);
return submissions.map(mapToSubmissionDTO);
if (full) {
return submissions.map(mapToSubmissionDTO);
}
return submissions.map(mapToSubmissionDTOId);
}

View file

@ -46,6 +46,13 @@ export async function getLearningObjectById(hruid: string, language: string): Pr
return filterData(metadata, htmlUrl);
}
/**
* Generic function to fetch learning paths
*/
function fetchLearningPaths(_arg0: string[], _language: string, _arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
throw new Error('Function not implemented.');
}
/**
* Generic function to fetch learning objects (full data or just HRUIDs)
*/
@ -86,6 +93,3 @@ export async function getLearningObjectsFromPath(hruid: string, language: string
export async function getLearningObjectIdsFromPath(hruid: string, language: string): Promise<string[]> {
return (await fetchLearningObjects(hruid, false, language)) as string[];
}
function fetchLearningPaths(_arg0: string[], _language: string, _arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
throw new Error('Function not implemented.');
}

View file

@ -86,7 +86,7 @@ export async function createQuestion(questionDTO: QuestionDTO): Promise<Question
return questionDTO;
}
export async function deleteQuestion(questionId: QuestionId): Promise<Question | null> {
export async function deleteQuestion(questionId: QuestionId): Promise<QuestionDTO | null> {
const questionRepository = getQuestionRepository();
const question = await fetchQuestion(questionId);
@ -101,5 +101,5 @@ export async function deleteQuestion(questionId: QuestionId): Promise<Question |
return null;
}
return question;
return mapToQuestionDTO(question);
}

View file

@ -1,21 +1,26 @@
import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../data/repositories.js';
import {
getClassRepository,
getGroupRepository,
getStudentRepository,
getSubmissionRepository,
} from '../data/repositories.js';
import { AssignmentDTO } from '../interfaces/assignment.js';
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import { GroupDTO, mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
import { mapToStudent, mapToStudentDTO, StudentDTO } from '../interfaces/student.js';
import { mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
import { mapToSubmissionDTO, mapToSubmissionDTOId, SubmissionDTO, SubmissionDTOId } from '../interfaces/submission.js';
import { getAllAssignments } from './assignments.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllStudents(): Promise<StudentDTO[]> {
export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> {
const studentRepository = getStudentRepository();
const users = await studentRepository.findAll();
return users.map(mapToStudentDTO);
}
const students = await studentRepository.findAll();
export async function getAllStudentIds(): Promise<string[]> {
const users = await getAllStudents();
return users.map((user) => user.username);
if (full) {
return students.map(mapToStudentDTO);
}
return students.map((student) => student.username);
}
export async function getStudent(username: string): Promise<StudentDTO | null> {
@ -86,9 +91,7 @@ export async function getStudentAssignments(username: string, full: boolean): Pr
const classRepository = getClassRepository();
const classes = await classRepository.findByStudent(student);
const assignments = (await Promise.all(classes.map(async (cls) => await getAllAssignments(cls.classId!, full)))).flat();
return assignments;
return (await Promise.all(classes.map(async (cls) => await getAllAssignments(cls.classId!, full)))).flat();
}
export async function getStudentGroups(username: string, full: boolean): Promise<GroupDTO[]> {
@ -109,7 +112,7 @@ export async function getStudentGroups(username: string, full: boolean): Promise
return groups.map(mapToGroupDTOId);
}
export async function getStudentSubmissions(username: string): Promise<SubmissionDTO[]> {
export async function getStudentSubmissions(username: string, full: boolean): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
const studentRepository = getStudentRepository();
const student = await studentRepository.findByUsername(username);
@ -120,5 +123,9 @@ export async function getStudentSubmissions(username: string): Promise<Submissio
const submissionRepository = getSubmissionRepository();
const submissions = await submissionRepository.findAllSubmissionsForStudent(student);
return submissions.map(mapToSubmissionDTO);
if (full) {
return submissions.map(mapToSubmissionDTO);
}
return submissions.map(mapToSubmissionDTOId);
}

View file

@ -2,7 +2,6 @@ import { getSubmissionRepository } from '../data/repositories.js';
import { Language } from '../entities/content/language.js';
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
import { mapToSubmission, mapToSubmissionDTO, SubmissionDTO } from '../interfaces/submission.js';
import { Submission } from '../entities/assignments/submission.entity.js';
export async function getSubmission(
learningObjectHruid: string,
@ -22,7 +21,7 @@ export async function getSubmission(
return mapToSubmissionDTO(submission);
}
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<Submission | null> {
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<SubmissionDTO | null> {
const submissionRepository = getSubmissionRepository();
const submission = mapToSubmission(submissionDTO);
@ -33,7 +32,7 @@ export async function createSubmission(submissionDTO: SubmissionDTO): Promise<Su
return null;
}
return submission;
return mapToSubmissionDTO(submission);
}
export async function deleteSubmission(

View file

@ -1,20 +1,25 @@
import { getClassRepository, getLearningObjectRepository, getQuestionRepository, getTeacherRepository } from '../data/repositories.js';
import {
getClassRepository,
getLearningObjectRepository,
getQuestionRepository,
getTeacherRepository,
} from '../data/repositories.js';
import { ClassDTO, mapToClassDTO } from '../interfaces/class.js';
import { getClassStudents } from './class.js';
import { getClassStudents } from './classes.js';
import { StudentDTO } from '../interfaces/student.js';
import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js';
import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js';
import { getLogger } from '../logging/initalize.js';
export async function getAllTeachers(): Promise<TeacherDTO[]> {
export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> {
const teacherRepository = getTeacherRepository();
const users = await teacherRepository.findAll();
return users.map(mapToTeacherDTO);
}
const teachers = await teacherRepository.findAll();
export async function getAllTeacherIds(): Promise<string[]> {
const users = await getAllTeachers();
return users.map((user) => user.username);
if (full) {
return teachers.map(mapToTeacherDTO);
}
return teachers.map((teacher) => teacher.username);
}
export async function getTeacher(username: string): Promise<TeacherDTO | null> {
@ -56,11 +61,11 @@ export async function deleteTeacher(username: string): Promise<TeacherDTO | null
}
}
export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[]> {
export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[] | null> {
const teacherRepository = getTeacherRepository();
const teacher = await teacherRepository.findByUsername(username);
if (!teacher) {
return [];
return null;
}
const classRepository = getClassRepository();
@ -68,35 +73,49 @@ export async function fetchClassesByTeacher(username: string): Promise<ClassDTO[
return classes.map(mapToClassDTO);
}
export async function getClassesByTeacher(username: string): Promise<ClassDTO[]> {
return await fetchClassesByTeacher(username);
}
export async function getClassIdsByTeacher(username: string): Promise<string[]> {
export async function getClassesByTeacher(username: string, full: boolean): Promise<ClassDTO[] | string[] | null> {
const classes = await fetchClassesByTeacher(username);
if (!classes) {
return null;
}
if (full) {
return classes;
}
return classes.map((cls) => cls.id);
}
export async function fetchStudentsByTeacher(username: string): Promise<StudentDTO[]> {
const classes = await getClassIdsByTeacher(username);
export async function fetchStudentsByTeacher(username: string): Promise<StudentDTO[] | null> {
const classes = (await getClassesByTeacher(username, false)) as string[];
if (!classes) {
return null;
}
return (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat();
}
export async function getStudentsByTeacher(username: string): Promise<StudentDTO[]> {
return await fetchStudentsByTeacher(username);
}
export async function getStudentIdsByTeacher(username: string): Promise<string[]> {
export async function getStudentsByTeacher(username: string, full: boolean): Promise<StudentDTO[] | string[] | null> {
const students = await fetchStudentsByTeacher(username);
if (!students) {
return null;
}
if (full) {
return students;
}
return students.map((student) => student.username);
}
export async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[]> {
export async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[] | null> {
const teacherRepository = getTeacherRepository();
const teacher = await teacherRepository.findByUsername(username);
if (!teacher) {
throw new Error(`Teacher with username '${username}' not found.`);
return null;
}
// Find all learning objects that this teacher manages
@ -110,12 +129,16 @@ export async function fetchTeacherQuestions(username: string): Promise<QuestionD
return questions.map(mapToQuestionDTO);
}
export async function getQuestionsByTeacher(username: string): Promise<QuestionDTO[]> {
return await fetchTeacherQuestions(username);
}
export async function getQuestionIdsByTeacher(username: string): Promise<QuestionId[]> {
export async function getQuestionsByTeacher(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[] | null> {
const questions = await fetchTeacherQuestions(username);
if (!questions) {
return null;
}
if (full) {
return questions;
}
return questions.map(mapToQuestionId);
}

View file

@ -1,41 +0,0 @@
import { UserRepository } from '../data/users/user-repository.js';
import { UserDTO, mapToUser, mapToUserDTO } from '../interfaces/user.js';
import { User } from '../entities/users/user.entity.js';
export class UserService<T extends User> {
protected repository: UserRepository<T>;
constructor(repository: UserRepository<T>) {
this.repository = repository;
}
async getAllUsers(): Promise<UserDTO[]> {
const users = await this.repository.findAll();
return users.map(mapToUserDTO);
}
async getAllUserIds(): Promise<string[]> {
const users = await this.getAllUsers();
return users.map((user) => user.username);
}
async getUserByUsername(username: string): Promise<UserDTO | null> {
const user = await this.repository.findByUsername(username);
return user ? mapToUserDTO(user) : null;
}
async createUser(userData: UserDTO, userClass: new () => T): Promise<T> {
const newUser = mapToUser(userData, new userClass());
await this.repository.save(newUser);
return newUser;
}
async deleteUser(username: string): Promise<UserDTO | null> {
const user = await this.getUserByUsername(username);
if (!user) {
return null;
}
await this.repository.deleteByUsername(username);
return mapToUserDTO(user);
}
}