From c31b4713714670b03636abafcfb70579405c9936 Mon Sep 17 00:00:00 2001 From: Gabriellvl Date: Fri, 21 Mar 2025 22:51:42 +0100 Subject: [PATCH] feat: questions via student --- backend/src/controllers/students.ts | 108 ++++++++++-------- backend/src/controllers/teachers.ts | 100 ++++++---------- .../src/data/questions/question-repository.ts | 8 ++ backend/src/routes/students.ts | 18 +-- backend/src/routes/teachers.ts | 2 - backend/src/services/students.ts | 33 ++++-- backend/src/services/teachers.ts | 55 +++------ 7 files changed, 153 insertions(+), 171 deletions(-) diff --git a/backend/src/controllers/students.ts b/backend/src/controllers/students.ts index 6c253cff..cde6b5ef 100644 --- a/backend/src/controllers/students.ts +++ b/backend/src/controllers/students.ts @@ -1,53 +1,43 @@ import { Request, Response } from 'express'; import { createStudent, - deleteStudent, + deleteStudent, getAllStudentIds, getAllStudents, getStudent, getStudentAssignments, getStudentClasses, - getStudentGroups, + getStudentGroups, getStudentQuestions, 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 {MISSING_FIELDS_ERROR, MISSING_USERNAME_ERROR, NAME_NOT_FOUND_ERROR} from './users.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: StudentDTO[] | string[] = full ? await getAllStudents() : await getAllStudentIds(); if (!students) { - res.status(404).json({ error: `Student not found.` }); + res.status(404).json({ error: `Students not found.` }); return; } - res.status(201).json(students); + res.json({students}); } export async function getStudentHandler(req: Request, res: Response): Promise { const username = req.params.username; if (!username) { - res.status(400).json({ error: 'Missing required field: username' }); + res.status(400).json(MISSING_USERNAME_ERROR); return; } const user = await getStudent(username); if (!user) { - res.status(404).json({ - error: `User with username '${username}' not found.`, - }); + res.status(404).json(NAME_NOT_FOUND_ERROR(username)); return; } @@ -58,9 +48,7 @@ export async function createStudentHandler(req: Request, res: Response) { const userData = req.body as StudentDTO; if (!userData.username || !userData.firstName || !userData.lastName) { - res.status(400).json({ - error: 'Missing required fields: username, firstName, lastName', - }); + res.status(400).json(MISSING_FIELDS_ERROR); return; } @@ -72,15 +60,13 @@ export async function deleteStudentHandler(req: Request, res: Response) { const username = req.params.username; if (!username) { - res.status(400).json({ error: 'Missing required field: username' }); + res.status(400).json(MISSING_USERNAME_ERROR); return; } const deletedUser = await deleteStudent(username); if (!deletedUser) { - res.status(404).json({ - error: `User with username '${username}' not found.`, - }); + res.status(404).json(NAME_NOT_FOUND_ERROR(username)); return; } @@ -88,25 +74,19 @@ export async function deleteStudentHandler(req: Request, res: Response) { } export async function getStudentClassesHandler(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.username; - 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' }); + if (!username) { + res.status(400).json(MISSING_USERNAME_ERROR); + return; } + + const classes = await getStudentClasses(username, full); + + res.json({ + classes, + }); } // TODO @@ -115,32 +95,62 @@ export async function getStudentClassesHandler(req: Request, res: Response): Pro // Have this assignment. export async function getStudentAssignmentsHandler(req: Request, res: Response): Promise { const full = req.query.full === 'true'; - const username = req.params.id; + const username = req.params.username; + + if (!username) { + res.status(400).json(MISSING_USERNAME_ERROR); + return; + } const assignments = getStudentAssignments(username, full); res.json({ - assignments: assignments, + assignments, }); } export async function getStudentGroupsHandler(req: Request, res: Response): Promise { const full = req.query.full === 'true'; - const username = req.params.id; + const username = req.params.username; + + if (!username) { + res.status(400).json(MISSING_USERNAME_ERROR); + return; + } const groups = await getStudentGroups(username, full); res.json({ - groups: groups, + groups, }); } export async function getStudentSubmissionsHandler(req: Request, res: Response): Promise { - const username = req.params.id; + const username = req.params.username; + + if (!username) { + res.status(400).json(MISSING_USERNAME_ERROR); + return; + } const submissions = await getStudentSubmissions(username); res.json({ - submissions: submissions, + submissions, }); } + +export async function getStudentQuestionsHandler(req: Request, res: Response): Promise { + const username = req.params.username; + + if (!username) { + res.status(400).json(MISSING_USERNAME_ERROR); + return; + } + + const questions = await getStudentQuestions(username, full); + + res.json({ + questions, + }) +} diff --git a/backend/src/controllers/teachers.ts b/backend/src/controllers/teachers.ts index 52e5e713..fb4c2a0d 100644 --- a/backend/src/controllers/teachers.ts +++ b/backend/src/controllers/teachers.ts @@ -4,49 +4,40 @@ 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'; +import {MISSING_FIELDS_ERROR, MISSING_USERNAME_ERROR, NAME_NOT_FOUND_ERROR} from "./users"; 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: TeacherDTO[] | string[] = await getAllTeachers(full); if (!teachers) { - res.status(404).json({ error: `Teacher not found.` }); + res.status(404).json({ error: `Teachers not found.` }); return; } - res.status(201).json(teachers); + res.json({teachers}); } export async function getTeacherHandler(req: Request, res: Response): Promise { const username = req.params.username; if (!username) { - res.status(400).json({ error: 'Missing required field: username' }); + res.status(400).json(MISSING_USERNAME_ERROR); return; } const user = await getTeacher(username); if (!user) { - res.status(404).json({ - error: `User with username '${username}' not found.`, - }); + res.status(404).json(NAME_NOT_FOUND_ERROR(username)); return; } @@ -57,9 +48,7 @@ export async function createTeacherHandler(req: Request, res: Response) { const userData = req.body as TeacherDTO; if (!userData.username || !userData.firstName || !userData.lastName) { - res.status(400).json({ - error: 'Missing required fields: username, firstName, lastName', - }); + res.status(400).json(MISSING_FIELDS_ERROR); return; } @@ -71,15 +60,13 @@ export async function deleteTeacherHandler(req: Request, res: Response) { const username = req.params.username; if (!username) { - res.status(400).json({ error: 'Missing required field: username' }); + res.status(400).json(MISSING_USERNAME_ERROR); return; } const deletedUser = await deleteTeacher(username); if (!deletedUser) { - res.status(404).json({ - error: `User with username '${username}' not found.`, - }); + res.status(404).json(NAME_NOT_FOUND_ERROR(username)); return; } @@ -87,58 +74,43 @@ export async function deleteTeacherHandler(req: Request, res: Response) { } export async function getTeacherClassHandler(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(MISSING_USERNAME_ERROR); + return; } + + const classes: ClassDTO[] | string[] = await getClassesByTeacher(username, full); + + res.status(201).json(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(MISSING_USERNAME_ERROR); + return; } + + const students: StudentDTO[] | string[] = await getStudentsByTeacher(username, full); + + res.json({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(MISSING_USERNAME_ERROR); + return; } + + const questions: QuestionDTO[] | QuestionId[] = await getQuestionsByTeacher(username, full); + + res.json({questions}); } diff --git a/backend/src/data/questions/question-repository.ts b/backend/src/data/questions/question-repository.ts index 9207e1dd..8789c2e9 100644 --- a/backend/src/data/questions/question-repository.ts +++ b/backend/src/data/questions/question-repository.ts @@ -54,4 +54,12 @@ export class QuestionRepository extends DwengoEntityRepository { orderBy: { timestamp: 'ASC' }, }); } + + public findAllByAuthor(author: Student): Promise { + return this.findAll({ + where: { author }, + orderBy: { timestamp: 'DESC' }, // new to old + }); + } + } diff --git a/backend/src/routes/students.ts b/backend/src/routes/students.ts index 7ed7a666..77775b8f 100644 --- a/backend/src/routes/students.ts +++ b/backend/src/routes/students.ts @@ -6,7 +6,7 @@ import { getStudentAssignmentsHandler, getStudentClassesHandler, getStudentGroupsHandler, - getStudentHandler, + getStudentHandler, getStudentQuestionsHandler, getStudentSubmissionsHandler, } from '../controllers/students.js'; import { getStudentGroups } from '../services/students.js'; @@ -17,30 +17,24 @@ router.get('/', getAllStudentsHandler); router.post('/', createStudentHandler); -router.delete('/', deleteStudentHandler); - router.delete('/:username', deleteStudentHandler); // Information about a student's profile router.get('/:username', getStudentHandler); // The list of classes a student is in -router.get('/:id/classes', getStudentClassesHandler); +router.get('/:username/classes', getStudentClassesHandler); // The list of submissions a student has made -router.get('/:id/submissions', getStudentSubmissionsHandler); +router.get('/:username/submissions', getStudentSubmissionsHandler); // The list of assignments a student has -router.get('/:id/assignments', getStudentAssignmentsHandler); +router.get('/:username/assignments', getStudentAssignmentsHandler); // The list of groups a student is in -router.get('/:id/groups', getStudentGroupsHandler); +router.get('/:username/groups', getStudentGroupsHandler); // A list of questions a user has created -router.get('/:id/questions', (req, res) => { - res.json({ - questions: ['0'], - }); -}); +router.get('/:username/questions', getStudentQuestionsHandler); export default router; diff --git a/backend/src/routes/teachers.ts b/backend/src/routes/teachers.ts index c04e1575..8e7f709d 100644 --- a/backend/src/routes/teachers.ts +++ b/backend/src/routes/teachers.ts @@ -15,8 +15,6 @@ router.get('/', getAllTeachersHandler); router.post('/', createTeacherHandler); -router.delete('/', deleteTeacherHandler); - router.get('/:username', getTeacherHandler); router.delete('/:username', deleteTeacherHandler); diff --git a/backend/src/services/students.ts b/backend/src/services/students.ts index 5099a18d..70bed549 100644 --- a/backend/src/services/students.ts +++ b/backend/src/services/students.ts @@ -1,13 +1,17 @@ -import { getClassRepository, getGroupRepository, getStudentRepository, getSubmissionRepository } from '../data/repositories.js'; -import { Class } from '../entities/classes/class.entity.js'; -import { Student } from '../entities/users/student.entity.js'; +import { + getClassRepository, + getGroupRepository, + getQuestionRepository, + 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 { getAllAssignments } from './assignments.js'; -import { UserService } from './users.js'; +import {mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId} from "../interfaces/question"; export async function getAllStudents(): Promise { const studentRepository = getStudentRepository(); @@ -88,9 +92,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 { @@ -124,3 +126,20 @@ export async function getStudentSubmissions(username: string): Promise { + const studentRepository = getStudentRepository(); + const student = await studentRepository.findByUsername(username); + + if (!student) { + return []; + } + + const questionRepository = getQuestionRepository(); + const questions = await questionRepository.findAllByAuthor(student); + + if (full) + return questions.map(mapToQuestionDTO) + + return questions.map(mapToQuestionId); +} diff --git a/backend/src/services/teachers.ts b/backend/src/services/teachers.ts index f4dbedfe..5b0d8144 100644 --- a/backend/src/services/teachers.ts +++ b/backend/src/services/teachers.ts @@ -5,23 +5,17 @@ import { getStudentRepository, getTeacherRepository, } from '../data/repositories.js'; -import { Teacher } from '../entities/users/teacher.entity.js'; import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; import { getClassStudents } from './class.js'; -import { StudentDTO } from '../interfaces/student.js'; -import { mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId } from '../interfaces/question.js'; -import { UserService } from './users.js'; -import { mapToUser } from '../interfaces/user.js'; +import { mapToQuestionDTO, mapToQuestionId, QuestionDTO } from '../interfaces/question.js'; import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js'; -export async function getAllTeachers(): Promise { +export async function getAllTeachers(full: boolean): Promise { const teacherRepository = getTeacherRepository(); const users = await teacherRepository.findAll(); - return users.map(mapToTeacherDTO); -} -export async function getAllTeacherIds(): Promise { - const users = await getAllTeachers(); + if (full) + return users.map(mapToTeacherDTO); return users.map((user) => user.username); } @@ -64,7 +58,7 @@ export async function deleteTeacher(username: string): Promise { +async function fetchClassesByTeacher(username: string): Promise { const teacherRepository = getTeacherRepository(); const teacher = await teacherRepository.findByUsername(username); if (!teacher) { @@ -76,31 +70,24 @@ 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 (full) + return classes; return classes.map((cls) => cls.id); } -export async function fetchStudentsByTeacher(username: string) { - const classes = await getClassIdsByTeacher(username); +export async function getStudentsByTeacher(username: string, full: boolean) { + const classes = await getClassesByTeacher(username, false); - 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 { - const students = await fetchStudentsByTeacher(username); + const students = (await Promise.all(classes.map(async (id) => getClassStudents(id)))).flat(); + if (full) + return students return students.map((student) => student.username); } -export async function fetchTeacherQuestions(username: string): Promise { +export async function getTeacherQuestions(username: string, full: boolean): Promise { const teacherRepository = getTeacherRepository(); const teacher = await teacherRepository.findByUsername(username); if (!teacher) { @@ -115,15 +102,9 @@ export async function fetchTeacherQuestions(username: string): Promise { - return await fetchTeacherQuestions(username); -} - -export async function getQuestionIdsByTeacher(username: string): Promise { - const questions = await fetchTeacherQuestions(username); + if (full) + return questions.map(mapToQuestionDTO); return questions.map(mapToQuestionId); } +