diff --git a/backend/src/controllers/assignments.ts b/backend/src/controllers/assignments.ts index 03332469..16dbb310 100644 --- a/backend/src/controllers/assignments.ts +++ b/backend/src/controllers/assignments.ts @@ -37,7 +37,7 @@ export async function createAssignmentHandler(req: Request, re return; } - res.status(201).json({ assignment: assignment }); + res.status(201).json(assignment); } export async function getAssignmentHandler(req: Request, res: Response): Promise { @@ -62,13 +62,14 @@ export async function getAssignmentHandler(req: Request, res: export async function getAssignmentsSubmissionsHandler(req: Request, res: Response): Promise { const classid = req.params.classid; const assignmentNumber = +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, diff --git a/backend/src/controllers/classes.ts b/backend/src/controllers/classes.ts index ca2f5698..7526f7c4 100644 --- a/backend/src/controllers/classes.ts +++ b/backend/src/controllers/classes.ts @@ -1,5 +1,5 @@ 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'; export async function getAllClassesHandler(req: Request, res: Response): Promise { @@ -28,30 +28,19 @@ export async function createClassHandler(req: Request, res: Response): Promise { - 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) { - console.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 { @@ -67,7 +56,7 @@ export async function getClassStudentsHandler(req: Request, res: Response): Prom export async function getTeacherInvitationsHandler(req: Request, res: Response): Promise { 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); diff --git a/backend/src/controllers/groups.ts b/backend/src/controllers/groups.ts index b7bfd212..38d5d5d0 100644 --- a/backend/src/controllers/groups.ts +++ b/backend/src/controllers/groups.ts @@ -28,6 +28,11 @@ export async function getGroupHandler(req: Request, 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 { const classId = req.params.classid; - // Const full = req.query.full === 'true'; + const full = req.query.full === 'true'; const assignmentId = +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, diff --git a/backend/src/controllers/learning-objects.ts b/backend/src/controllers/learning-objects.ts index 455a4006..e9d0d727 100644 --- a/backend/src/controllers/learning-objects.ts +++ b/backend/src/controllers/learning-objects.ts @@ -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 { diff --git a/backend/src/controllers/questions.ts b/backend/src/controllers/questions.ts index 917b48ae..00a51329 100644 --- a/backend/src/controllers/questions.ts +++ b/backend/src/controllers/questions.ts @@ -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); } diff --git a/backend/src/controllers/students.ts b/backend/src/controllers/students.ts index 6c253cff..5190c1d6 100644 --- a/backend/src/controllers/students.ts +++ b/backend/src/controllers/students.ts @@ -9,29 +9,21 @@ import { getStudentGroups, getStudentSubmissions, } from '../services/students.js'; -import { ClassDTO } from '../interfaces/class.js'; -import { getAllAssignments } from '../services/assignments.js'; -import { getUserHandler } from './users.js'; -import { Student } from '../entities/users/student.entity.js'; import { StudentDTO } from '../interfaces/student.js'; -import { getStudentRepository } from '../data/repositories.js'; -import { UserDTO } from '../interfaces/user.js'; // TODO: accept arguments (full, ...) // TODO: endpoints export async function getAllStudentsHandler(req: Request, res: Response): Promise { const full = req.query.full === 'true'; - const studentRepository = getStudentRepository(); - - 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 { @@ -51,7 +43,7 @@ export async function getStudentHandler(req: Request, res: Response): Promise { - 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) { - console.error('Error fetching learning objects:', error); - res.status(500).json({ error: 'Internal server error' }); - } + res.json({ + classes: classes, + }); } // TODO @@ -137,8 +126,9 @@ export async function getStudentGroupsHandler(req: Request, res: Response): Prom export async function getStudentSubmissionsHandler(req: Request, res: Response): Promise { 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, diff --git a/backend/src/controllers/submissions.ts b/backend/src/controllers/submissions.ts index 1e66dbe9..512ac22e 100644 --- a/backend/src/controllers/submissions.ts +++ b/backend/src/controllers/submissions.ts @@ -36,10 +36,11 @@ export async function createSubmissionHandler(req: Request, res: Response) { 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) { @@ -53,7 +54,8 @@ export async function deleteSubmissionHandler(req: Request, res: Response) { if (!submission) { res.status(404).json({ error: 'Submission not found' }); - } else { - res.json(submission); + return; } + + res.json(submission); } diff --git a/backend/src/controllers/teachers.ts b/backend/src/controllers/teachers.ts index 52e5e713..7376abed 100644 --- a/backend/src/controllers/teachers.ts +++ b/backend/src/controllers/teachers.ts @@ -4,33 +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 { Teacher } from '../entities/users/teacher.entity.js'; import { TeacherDTO } from '../interfaces/teacher.js'; -import { getTeacherRepository } from '../data/repositories.js'; export async function getAllTeachersHandler(req: Request, res: Response): Promise { const full = req.query.full === 'true'; - const teacherRepository = getTeacherRepository(); - - 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 { @@ -45,12 +35,12 @@ export async function getTeacherHandler(req: Request, res: Response): Promise { - try { - const username = req.params.username as string; - const full = req.query.full === 'true'; + const username = req.params.username as string; + 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) { - console.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 { - try { - const username = req.params.username as string; - const full = req.query.full === 'true'; + const username = req.params.username as string; + 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) { - console.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 { - try { - const username = req.params.username as string; - const full = req.query.full === 'true'; + const username = req.params.username as string; + 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) { - console.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 }); } diff --git a/backend/src/controllers/users.ts b/backend/src/controllers/users.ts deleted file mode 100644 index 850c6549..00000000 --- a/backend/src/controllers/users.ts +++ /dev/null @@ -1,91 +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'; - -export async function getAllUsersHandler(req: Request, res: Response, service: UserService): Promise { - 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) { - console.error('❌ Error fetching users:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} - -export async function getUserHandler(req: Request, res: Response, service: UserService): Promise { - try { - const username = req.params.username as string; - - 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) { - console.error('❌ Error fetching users:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} - -export async function createUserHandler(req: Request, res: Response, service: UserService, UserClass: new () => T) { - try { - console.log('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) { - console.error('❌ Error creating user:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} - -export async function deleteUserHandler(req: Request, res: Response, service: UserService) { - 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) { - console.error('❌ Error deleting user:', error); - res.status(500).json({ error: 'Internal server error' }); - } -} diff --git a/backend/src/data/repositories.ts b/backend/src/data/repositories.ts index 02385109..cdeb50c1 100644 --- a/backend/src/data/repositories.ts +++ b/backend/src/data/repositories.ts @@ -2,8 +2,6 @@ import { AnyEntity, EntityManager, EntityName, EntityRepository } from '@mikro-o import { forkEntityManager } from '../orm.js'; import { StudentRepository } from './users/student-repository.js'; import { Student } from '../entities/users/student.entity.js'; -import { User } from '../entities/users/user.entity.js'; -import { UserRepository } from './users/user-repository.js'; import { Teacher } from '../entities/users/teacher.entity.js'; import { TeacherRepository } from './users/teacher-repository.js'; import { Class } from '../entities/classes/class.entity.js'; diff --git a/backend/src/data/users/student-repository.ts b/backend/src/data/users/student-repository.ts index 0792678d..a13fbb22 100644 --- a/backend/src/data/users/student-repository.ts +++ b/backend/src/data/users/student-repository.ts @@ -1,5 +1,4 @@ import { Student } from '../../entities/users/student.entity.js'; -import { User } from '../../entities/users/user.entity.js'; import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; // Import { UserRepository } from './user-repository.js'; diff --git a/backend/src/data/users/teacher-repository.ts b/backend/src/data/users/teacher-repository.ts index 2b2bee75..825b4d18 100644 --- a/backend/src/data/users/teacher-repository.ts +++ b/backend/src/data/users/teacher-repository.ts @@ -1,6 +1,5 @@ import { Teacher } from '../../entities/users/teacher.entity.js'; import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; -import { UserRepository } from './user-repository.js'; export class TeacherRepository extends DwengoEntityRepository { public findByUsername(username: string): Promise { diff --git a/backend/src/entities/assignments/assignment.entity.ts b/backend/src/entities/assignments/assignment.entity.ts index 692e2112..daa71ed6 100644 --- a/backend/src/entities/assignments/assignment.entity.ts +++ b/backend/src/entities/assignments/assignment.entity.ts @@ -1,4 +1,4 @@ -import { Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; +import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'; import { Class } from '../classes/class.entity.js'; import { Group } from './group.entity.js'; import { Language } from '../content/language.js'; diff --git a/backend/src/entities/assignments/group.entity.ts b/backend/src/entities/assignments/group.entity.ts index 213e0f38..cfe21f7f 100644 --- a/backend/src/entities/assignments/group.entity.ts +++ b/backend/src/entities/assignments/group.entity.ts @@ -1,4 +1,4 @@ -import { Collection, Entity, ManyToMany, ManyToOne, PrimaryKey } from '@mikro-orm/core'; +import { Entity, ManyToMany, ManyToOne, PrimaryKey } from '@mikro-orm/core'; import { Assignment } from './assignment.entity.js'; import { Student } from '../users/student.entity.js'; import { GroupRepository } from '../../data/assignments/group-repository.js'; diff --git a/backend/src/entities/classes/class-join-request.entity.ts b/backend/src/entities/classes/class-join-request.entity.ts index bdef1f52..64a597bb 100644 --- a/backend/src/entities/classes/class-join-request.entity.ts +++ b/backend/src/entities/classes/class-join-request.entity.ts @@ -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', -} +} \ No newline at end of file diff --git a/backend/src/interfaces/assignment.ts b/backend/src/interfaces/assignment.ts index 8f6120b6..eefa8c96 100644 --- a/backend/src/interfaces/assignment.ts +++ b/backend/src/interfaces/assignment.ts @@ -2,7 +2,7 @@ import { FALLBACK_LANG } from '../config.js'; import { Assignment } from '../entities/assignments/assignment.entity.js'; import { Class } from '../entities/classes/class.entity.js'; import { languageMap } from '../entities/content/language.js'; -import { GroupDTO, mapToGroupDTO } from './group.js'; +import { GroupDTO } from './group.js'; export interface AssignmentDTO { id: number; @@ -46,7 +46,5 @@ export function mapToAssignment(assignmentData: AssignmentDTO, cls: Class): Assi assignment.learningPathLanguage = languageMap[assignmentData.language] || FALLBACK_LANG; assignment.within = cls; - console.log(assignment); - return assignment; } diff --git a/backend/src/interfaces/class.ts b/backend/src/interfaces/class.ts index 371e3cae..ea1d4901 100644 --- a/backend/src/interfaces/class.ts +++ b/backend/src/interfaces/class.ts @@ -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 { diff --git a/backend/src/interfaces/question.ts b/backend/src/interfaces/question.ts index 8cca42f6..0da87eb7 100644 --- a/backend/src/interfaces/question.ts +++ b/backend/src/interfaces/question.ts @@ -1,8 +1,6 @@ import { Question } from '../entities/questions/question.entity.js'; -import { UserDTO } from './user.js'; import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js'; import { mapToStudentDTO, StudentDTO } from './student.js'; -import { TeacherDTO } from './teacher.js'; export interface QuestionDTO { learningObjectIdentifier: LearningObjectIdentifier; diff --git a/backend/src/interfaces/submission.ts b/backend/src/interfaces/submission.ts index fbaf520d..98cc4f22 100644 --- a/backend/src/interfaces/submission.ts +++ b/backend/src/interfaces/submission.ts @@ -2,13 +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 { mapToUser } from './user'; -import { Student } from '../entities/users/student.entity'; +import { LearningObjectIdentifier } from './learning-content.js'; export interface SubmissionDTO { - learningObjectHruid: string; - learningObjectLanguage: Language; - learningObjectVersion: number; + learningObjectIdentifier: LearningObjectIdentifier; submissionNumber?: number; submitter: StudentDTO; @@ -17,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), @@ -31,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; diff --git a/backend/src/routes/router.ts b/backend/src/routes/router.ts index 639857a7..99d4312c 100644 --- a/backend/src/routes/router.ts +++ b/backend/src/routes/router.ts @@ -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'] */); diff --git a/backend/src/routes/students.ts b/backend/src/routes/students.ts index 7ed7a666..6efbab39 100644 --- a/backend/src/routes/students.ts +++ b/backend/src/routes/students.ts @@ -9,7 +9,6 @@ import { getStudentHandler, getStudentSubmissionsHandler, } from '../controllers/students.js'; -import { getStudentGroups } from '../services/students.js'; const router = express.Router(); // Root endpoint used to search objects diff --git a/backend/src/services/assignments.ts b/backend/src/services/assignments.ts index be121810..a21a96fa 100644 --- a/backend/src/services/assignments.ts +++ b/backend/src/services/assignments.ts @@ -1,7 +1,6 @@ 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'; export async function getAllAssignments(classid: string, full: boolean): Promise { const classRepository = getClassRepository(); @@ -21,7 +20,7 @@ export async function getAllAssignments(classid: string, full: boolean): Promise return assignments.map(mapToAssignmentDTOId); } -export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise { +export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise { const classRepository = getClassRepository(); const cls = await classRepository.findById(classid); @@ -36,8 +35,9 @@ export async function createAssignment(classid: string, assignmentData: Assignme const newAssignment = assignmentRepository.create(assignment); await assignmentRepository.save(newAssignment); - return newAssignment; + return mapToAssignmentDTO(newAssignment); } catch (e) { + console.error(e); return null; } } @@ -60,7 +60,11 @@ export async function getAssignment(classid: string, id: number): Promise { +export async function getAssignmentsSubmissions( + classid: string, + assignmentNumber: number, + full: boolean +): Promise { const classRepository = getClassRepository(); const cls = await classRepository.findById(classid); @@ -81,5 +85,9 @@ export async function getAssignmentsSubmissions(classid: string, assignmentNumbe const submissionRepository = getSubmissionRepository(); const submissions = (await Promise.all(groups.map((group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat(); - return submissions.map(mapToSubmissionDTO); + if (full) { + return submissions.map(mapToSubmissionDTO); + } + + return submissions.map(mapToSubmissionDTOId); } diff --git a/backend/src/services/class.ts b/backend/src/services/classes.ts similarity index 94% rename from backend/src/services/class.ts rename to backend/src/services/classes.ts index 9f6e1efe..5b1e3cfc 100644 --- a/backend/src/services/class.ts +++ b/backend/src/services/classes.ts @@ -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,16 +20,14 @@ export async function getAllClasses(full: boolean): Promise cls.classId!); } -export async function createClass(classData: ClassDTO): Promise { +export async function createClass(classData: ClassDTO): Promise { const teacherRepository = getTeacherRepository(); const teacherUsernames = classData.teachers || []; - const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher != null); + const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher !== null); const studentRepository = getStudentRepository(); const studentUsernames = classData.students || []; - const students = (await Promise.all(studentUsernames.map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null); - - //Const cls = mapToClass(classData, teachers, students); + const students = (await Promise.all(studentUsernames.map((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null); const classRepository = getClassRepository(); @@ -42,7 +39,7 @@ export async function createClass(classData: ClassDTO): Promise { }); await classRepository.save(newClass); - return newClass; + return mapToClassDTO(newClass); } catch (e) { logger.error(e); return null; diff --git a/backend/src/services/groups.ts b/backend/src/services/groups.ts index 91091703..4a1cbbf0 100644 --- a/backend/src/services/groups.ts +++ b/backend/src/services/groups.ts @@ -1,4 +1,3 @@ -import { GroupRepository } from '../data/assignments/group-repository.js'; import { getAssignmentRepository, getClassRepository, @@ -8,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'; export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number, full: boolean): Promise { const classRepository = getClassRepository(); @@ -43,7 +42,7 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme const studentRepository = getStudentRepository(); const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list - const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student != null); + const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null); console.log(members); @@ -103,7 +102,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 { +export async function getGroupSubmissions( + classId: string, + assignmentNumber: number, + groupNumber: number, + full: boolean +): Promise { const classRepository = getClassRepository(); const cls = await classRepository.findById(classId); @@ -128,5 +132,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); } diff --git a/backend/src/services/learning-objects.ts b/backend/src/services/learning-objects.ts index fb579471..85141b1d 100644 --- a/backend/src/services/learning-objects.ts +++ b/backend/src/services/learning-objects.ts @@ -45,6 +45,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 { + throw new Error('Function not implemented.'); +} + /** * Generic function to fetch learning objects (full data or just HRUIDs) */ @@ -85,6 +92,4 @@ export async function getLearningObjectsFromPath(hruid: string, language: string export async function getLearningObjectIdsFromPath(hruid: string, language: string): Promise { return (await fetchLearningObjects(hruid, false, language)) as string[]; } -function fetchLearningPaths(arg0: string[], language: string, arg2: string): LearningPathResponse | PromiseLike { - throw new Error('Function not implemented.'); -} + diff --git a/backend/src/services/questions.ts b/backend/src/services/questions.ts index ee003bcd..0e52440f 100644 --- a/backend/src/services/questions.ts +++ b/backend/src/services/questions.ts @@ -103,5 +103,5 @@ export async function deleteQuestion(questionId: QuestionId) { return null; } - return question; + return mapToQuestionDTO(question); } diff --git a/backend/src/services/students.ts b/backend/src/services/students.ts index 5099a18d..4775c8a4 100644 --- a/backend/src/services/students.ts +++ b/backend/src/services/students.ts @@ -5,19 +5,18 @@ 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 { UserService } from './users.js'; -export async function getAllStudents(): Promise { +export async function getAllStudents(full: boolean): Promise { const studentRepository = getStudentRepository(); - const users = await studentRepository.findAll(); - return users.map(mapToStudentDTO); -} + const students = await studentRepository.findAll(); -export async function getAllStudentIds(): Promise { - 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 { @@ -111,7 +110,7 @@ export async function getStudentGroups(username: string, full: boolean): Promise return groups.map(mapToGroupDTOId); } -export async function getStudentSubmissions(username: string): Promise { +export async function getStudentSubmissions(username: string, full: boolean): Promise { const studentRepository = getStudentRepository(); const student = await studentRepository.findByUsername(username); @@ -122,5 +121,9 @@ export async function getStudentSubmissions(username: string): Promise { +export async function getAllTeachers(full: boolean): Promise { const teacherRepository = getTeacherRepository(); - const users = await teacherRepository.findAll(); - return users.map(mapToTeacherDTO); -} + const teachers = await teacherRepository.findAll(); -export async function getAllTeacherIds(): Promise { - 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 { @@ -64,11 +64,11 @@ export async function deleteTeacher(username: string): Promise { +export async function fetchClassesByTeacher(username: string): Promise { const teacherRepository = getTeacherRepository(); const teacher = await teacherRepository.findByUsername(username); if (!teacher) { - return []; + return null; } const classRepository = getClassRepository(); @@ -76,35 +76,49 @@ export async function fetchClassesByTeacher(username: string): Promise { - return await fetchClassesByTeacher(username); -} - -export async function getClassIdsByTeacher(username: string): Promise { +export async function getClassesByTeacher(username: string, full: boolean): Promise { 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) { - const classes = await getClassIdsByTeacher(username); +export async function fetchStudentsByTeacher(username: string): Promise { + 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 { - return await fetchStudentsByTeacher(username); -} - -export async function getStudentIdsByTeacher(username: string): Promise { +export async function getStudentsByTeacher(username: string, full: boolean): Promise { 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 { +export async function fetchTeacherQuestions(username: string): Promise { 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 @@ -118,12 +132,16 @@ export async function fetchTeacherQuestions(username: string): Promise { - return await fetchTeacherQuestions(username); -} - -export async function getQuestionIdsByTeacher(username: string): Promise { +export async function getQuestionsByTeacher(username: string, full: boolean): Promise { const questions = await fetchTeacherQuestions(username); + if (!questions) { + return null; + } + + if (full) { + return questions; + } + return questions.map(mapToQuestionId); } diff --git a/backend/src/services/users.ts b/backend/src/services/users.ts deleted file mode 100644 index 65ed5d4c..00000000 --- a/backend/src/services/users.ts +++ /dev/null @@ -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 { - protected repository: UserRepository; - - constructor(repository: UserRepository) { - this.repository = repository; - } - - async getAllUsers(): Promise { - const users = await this.repository.findAll(); - return users.map(mapToUserDTO); - } - - async getAllUserIds(): Promise { - const users = await this.getAllUsers(); - return users.map((user) => user.username); - } - - async getUserByUsername(username: string): Promise { - const user = await this.repository.findByUsername(username); - return user ? mapToUserDTO(user) : null; - } - - async createUser(userData: UserDTO, UserClass: new () => T): Promise { - const newUser = mapToUser(userData, new UserClass()); - await this.repository.save(newUser); - return newUser; - } - - async deleteUser(username: string): Promise { - const user = await this.getUserByUsername(username); - if (!user) { - return null; - } - await this.repository.deleteByUsername(username); - return mapToUserDTO(user); - } -}