feat: teacher kan student request aanpassen en oprvagen
This commit is contained in:
		
							parent
							
								
									815858d02f
								
							
						
					
					
						commit
						c8cff2e820
					
				
					 6 changed files with 136 additions and 139 deletions
				
			
		|  | @ -9,13 +9,11 @@ import { | |||
|     getStudentClasses, | ||||
|     getStudentGroups, | ||||
|     getStudentQuestions, | ||||
|     getStudentSubmissions, updateClassJoinRequestStatus, | ||||
|     getStudentSubmissions, | ||||
| } from '../services/students.js'; | ||||
| import { StudentDTO } from '../interfaces/student.js'; | ||||
| import {BadRequestException} from "../exceptions"; | ||||
| import {requireFields} from "./error-helper"; | ||||
| 
 | ||||
| 
 | ||||
| export async function getAllStudentsHandler(req: Request, res: Response): Promise<void> { | ||||
|     const full = req.query.full === 'true'; | ||||
| 
 | ||||
|  | @ -41,16 +39,16 @@ export async function createStudentHandler(req: Request, res: Response) { | |||
| 
 | ||||
|     const userData = req.body as StudentDTO; | ||||
| 
 | ||||
|     const student = await createStudent(userData); | ||||
|     res.status(201).json({ student }); | ||||
|     await createStudent(userData); | ||||
|     res.status(201); | ||||
| } | ||||
| 
 | ||||
| export async function deleteStudentHandler(req: Request, res: Response) { | ||||
|     const username = req.params.username; | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     const student = await deleteStudent(username); | ||||
|     res.status(200).json({ student }); | ||||
|     await deleteStudent(username); | ||||
|     res.status(200); | ||||
| } | ||||
| 
 | ||||
| export async function getStudentClassesHandler(req: Request, res: Response): Promise<void> { | ||||
|  | @ -133,16 +131,6 @@ export async function getStudentRequestHandler(req: Request, res: Response): Pro | |||
|     res.status(201).json({ requests }) | ||||
| } | ||||
| 
 | ||||
| export async function updateClassJoinRequestHandler(req: Request, res: Response) { | ||||
|     const username = req.query.username as string; | ||||
|     const classId = req.params.classId; | ||||
|     const accepted = req.query.accepted !== 'false'; // default = true
 | ||||
|     requireFields({ username, classId }); | ||||
| 
 | ||||
|     const result = await updateClassJoinRequestStatus(username, classId, accepted); | ||||
|     res.status(200).json(result); | ||||
| } | ||||
| 
 | ||||
| export async function deleteClassJoinRequestHandler(req: Request, res: Response) { | ||||
|     const username = req.params.username as string; | ||||
|     const classId = req.params.classId; | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ import { | |||
|     createTeacher, | ||||
|     deleteTeacher, | ||||
|     getAllTeachers, | ||||
|     getClassesByTeacher, | ||||
|     getClassesByTeacher, getJoinRequestsByClass, | ||||
|     getStudentsByTeacher, | ||||
|     getTeacher, | ||||
|     getTeacherQuestions | ||||
|     getTeacherQuestions, updateClassJoinRequestStatus | ||||
| } from '../services/teachers.js'; | ||||
| import { ClassDTO } from '../interfaces/class.js'; | ||||
| import { StudentDTO } from '../interfaces/student.js'; | ||||
|  | @ -19,11 +19,6 @@ export async function getAllTeachersHandler(req: Request, res: Response): Promis | |||
| 
 | ||||
|     const teachers: TeacherDTO[] | string[] = await getAllTeachers(full); | ||||
| 
 | ||||
|     if (!teachers) { | ||||
|         res.status(404).json({ error: `Teachers not found.` }); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     res.json({ teachers }); | ||||
| } | ||||
| 
 | ||||
|  | @ -31,59 +26,45 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo | |||
|     const username = req.params.username; | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     const user = await getTeacher(username); | ||||
|     const teacher = await getTeacher(username); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     res.status(201).json(user); | ||||
|     res.json({ teacher }); | ||||
| } | ||||
| 
 | ||||
| export async function createTeacherHandler(req: Request, res: Response) { | ||||
|     const username = req.body.username; | ||||
|     const firstName = req.body.firstName; | ||||
|     const lastName = req.body.lastName; | ||||
|     requireFields({ username, firstName, lastName }); | ||||
| 
 | ||||
|     const userData = req.body as TeacherDTO; | ||||
| 
 | ||||
|     if (!userData.username || !userData.firstName || !userData.lastName) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const newUser = await createTeacher(userData); | ||||
|     res.status(201).json(newUser); | ||||
|     await createTeacher(userData); | ||||
|     res.status(201); | ||||
| } | ||||
| 
 | ||||
| export async function deleteTeacherHandler(req: Request, res: Response) { | ||||
|     const username = req.params.username; | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     if (!username) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     const deletedUser = await deleteTeacher(username); | ||||
|     if (!deletedUser) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     res.status(200).json(deletedUser); | ||||
|     await deleteTeacher(username); | ||||
|     res.status(200); | ||||
| } | ||||
| 
 | ||||
| export async function getTeacherClassHandler(req: Request, res: Response): Promise<void> { | ||||
|     const username = req.params.username as string; | ||||
|     const full = req.query.full === 'true'; | ||||
| 
 | ||||
|     if (!username) { | ||||
|         return; | ||||
|     } | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     const classes: ClassDTO[] | string[] = await getClassesByTeacher(username, full); | ||||
| 
 | ||||
|     res.status(201).json(classes); | ||||
|     res.json(classes); | ||||
| } | ||||
| 
 | ||||
| export async function getTeacherStudentHandler(req: Request, res: Response): Promise<void> { | ||||
|     const username = req.params.username as string; | ||||
|     const full = req.query.full === 'true'; | ||||
| 
 | ||||
|     if (!username) { | ||||
|         return; | ||||
|     } | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     const students: StudentDTO[] | string[] = await getStudentsByTeacher(username, full); | ||||
| 
 | ||||
|  | @ -93,12 +74,28 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro | |||
| export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> { | ||||
|     const username = req.params.username as string; | ||||
|     const full = req.query.full === 'true'; | ||||
| 
 | ||||
|     if (!username) { | ||||
|         return; | ||||
|     } | ||||
|     requireFields({ username }); | ||||
| 
 | ||||
|     const questions: QuestionDTO[] | QuestionId[] = await getTeacherQuestions(username, full); | ||||
| 
 | ||||
|     res.json({ questions }); | ||||
| } | ||||
| 
 | ||||
| export async function getStudentJoinRequestHandler(req: Request, res: Response) { | ||||
|     const username = req.query.username as string; | ||||
|     const classId = req.params.classId; | ||||
|     requireFields({ username, classId }); | ||||
| 
 | ||||
|     const joinRequests = await getJoinRequestsByClass(classId); | ||||
|     res.json({ joinRequests }); | ||||
| } | ||||
| 
 | ||||
| export async function updateStudentJoinRequestHandler(req: Request, res: Response) { | ||||
|     const username = req.query.username as string; | ||||
|     const classId = req.params.classId; | ||||
|     const accepted = req.query.accepted !== 'false'; // default = true
 | ||||
|     requireFields({ username, classId }); | ||||
| 
 | ||||
|     await updateClassJoinRequestStatus(username, classId, accepted); | ||||
|     res.status(200); | ||||
| } | ||||
|  |  | |||
|  | @ -1,6 +1,6 @@ | |||
| import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; | ||||
| import { Class } from '../../entities/classes/class.entity.js'; | ||||
| import { ClassJoinRequest } from '../../entities/classes/class-join-request.entity.js'; | ||||
| import {ClassJoinRequest, ClassJoinRequestStatus} from '../../entities/classes/class-join-request.entity.js'; | ||||
| import { Student } from '../../entities/users/student.entity.js'; | ||||
| 
 | ||||
| export class ClassJoinRequestRepository extends DwengoEntityRepository<ClassJoinRequest> { | ||||
|  | @ -8,7 +8,7 @@ export class ClassJoinRequestRepository extends DwengoEntityRepository<ClassJoin | |||
|         return this.findAll({ where: { requester: requester } }); | ||||
|     } | ||||
|     public findAllOpenRequestsTo(clazz: Class): Promise<ClassJoinRequest[]> { | ||||
|         return this.findAll({ where: { class: clazz } }); | ||||
|         return this.findAll({ where: { class: clazz, status: ClassJoinRequestStatus.Open, } }); // TODO check if works like this
 | ||||
|     } | ||||
|     public findByStudentAndClass(requester: Student, clazz: Class): Promise<ClassJoinRequest | null> { | ||||
|         return this.findOne({ requester, class: clazz }); | ||||
|  |  | |||
|  | @ -2,11 +2,11 @@ import express from 'express'; | |||
| import { | ||||
|     createTeacherHandler, | ||||
|     deleteTeacherHandler, | ||||
|     getAllTeachersHandler, | ||||
|     getAllTeachersHandler, getStudentJoinRequestHandler, | ||||
|     getTeacherClassHandler, | ||||
|     getTeacherHandler, | ||||
|     getTeacherQuestionHandler, | ||||
|     getTeacherStudentHandler, | ||||
|     getTeacherStudentHandler, updateStudentJoinRequestHandler, | ||||
| } from '../controllers/teachers.js'; | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  | @ -25,6 +25,10 @@ router.get('/:username/students', getTeacherStudentHandler); | |||
| 
 | ||||
| router.get('/:username/questions', getTeacherQuestionHandler); | ||||
| 
 | ||||
| router.get('/:username/joinRequests/:classId', getStudentJoinRequestHandler); | ||||
| 
 | ||||
| router.put('/:username/joinRequests/:classId/:studentUsername', updateStudentJoinRequestHandler); | ||||
| 
 | ||||
| // Invitations to other classes a teacher received
 | ||||
| router.get('/:id/invitations', (req, res) => { | ||||
|     res.json({ | ||||
|  |  | |||
|  | @ -44,31 +44,25 @@ export async function getStudent(username: string): Promise<StudentDTO> { | |||
|     return mapToStudentDTO(user); | ||||
| } | ||||
| 
 | ||||
| export async function createStudent(userData: StudentDTO): Promise<StudentDTO | null> { | ||||
| export async function createStudent(userData: StudentDTO): Promise<void> { | ||||
|     const studentRepository = getStudentRepository(); | ||||
| 
 | ||||
|     const user = await studentRepository.findByUsername(userData.username); | ||||
| 
 | ||||
|     if (user) { | ||||
|         throw new ConflictException("Username already exists"); | ||||
|         throw new ConflictException("Student with that sername already exists"); | ||||
|     } | ||||
| 
 | ||||
|     const newStudent = studentRepository.create(mapToStudent(userData)); | ||||
|     await studentRepository.save(newStudent); | ||||
|     return mapToStudentDTO(newStudent); | ||||
| } | ||||
| 
 | ||||
| export async function deleteStudent(username: string): Promise<StudentDTO | null> { | ||||
| export async function deleteStudent(username: string): Promise<void> { | ||||
|     const studentRepository = getStudentRepository(); | ||||
| 
 | ||||
|     const user = await studentRepository.findByUsername(username); | ||||
| 
 | ||||
|     if (!user) { | ||||
|         throw new NotFoundException("Student with username not found"); | ||||
|     } | ||||
|     await fetchStudent(username); // throws error if it does not exist
 | ||||
| 
 | ||||
|     await studentRepository.deleteByUsername(username); | ||||
|     return mapToStudentDTO(user); | ||||
| } | ||||
| 
 | ||||
| export async function getStudentClasses(username: string, full: boolean): Promise<ClassDTO[] | string[]> { | ||||
|  | @ -153,7 +147,6 @@ export async function createClassJoinRequest(studentUsername: string, classId: s | |||
|     }); | ||||
| 
 | ||||
|     await requestRepo.save(request); | ||||
|     return request; | ||||
| } | ||||
| 
 | ||||
| export async function getJoinRequestsByStudent(studentUsername: string) { | ||||
|  | @ -165,30 +158,6 @@ export async function getJoinRequestsByStudent(studentUsername: string) { | |||
|     return requests.map(mapToStudentRequestDTO); | ||||
| } | ||||
| 
 | ||||
| // TODO naar teacher
 | ||||
| export async function updateClassJoinRequestStatus( studentUsername: string, classId: string, accepted: boolean = true) { | ||||
|     const requestRepo = getClassJoinRequestRepository(); | ||||
|     const classRepo = getClassRepository(); | ||||
| 
 | ||||
|     const student = await fetchStudent(studentUsername); | ||||
|     const cls = await classRepo.findById(classId); | ||||
| 
 | ||||
|     if (!cls) { | ||||
|         throw new NotFoundException('Class not found'); | ||||
|     } | ||||
| 
 | ||||
|     const request = await requestRepo.findOne({ requester: student, class: cls }); | ||||
| 
 | ||||
|     if (!request) { | ||||
|         throw new NotFoundException('Join request not found'); | ||||
|     } | ||||
| 
 | ||||
|     request.status = accepted ? ClassJoinRequestStatus.Accepted : ClassJoinRequestStatus.Declined; | ||||
| 
 | ||||
|     await requestRepo.save(request); | ||||
|     return request; | ||||
| } | ||||
| 
 | ||||
| export async function deleteClassJoinRequest(studentUsername: string, classId: string) { | ||||
|     const requestRepo = getClassJoinRequestRepository(); | ||||
|     const classRepo = getClassRepository(); | ||||
|  | @ -207,7 +176,6 @@ export async function deleteClassJoinRequest(studentUsername: string, classId: s | |||
|     } | ||||
| 
 | ||||
|     await requestRepo.deleteBy(student, cls); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,19 @@ | |||
| import { | ||||
|     getClassJoinRequestRepository, | ||||
|     getClassRepository, | ||||
|     getLearningObjectRepository, | ||||
|     getQuestionRepository, | ||||
|     getQuestionRepository, getStudentRepository, | ||||
|     getTeacherRepository, | ||||
| } from '../data/repositories.js'; | ||||
| import { ClassDTO, mapToClassDTO } from '../interfaces/class.js'; | ||||
| import { getClassStudents } from './class.js'; | ||||
| import {mapToQuestionDTO, mapToQuestionId, QuestionDTO, QuestionId} from '../interfaces/question.js'; | ||||
| import { mapToTeacher, mapToTeacherDTO, TeacherDTO } from '../interfaces/teacher.js'; | ||||
| import {ConflictException, NotFoundException} from "../exceptions"; | ||||
| import {Teacher} from "../entities/users/teacher.entity"; | ||||
| import {fetchStudent} from "./students"; | ||||
| import {ClassJoinRequestStatus} from "../entities/classes/class-join-request.entity"; | ||||
| import {mapToStudentRequestDTO} from "../interfaces/student-request"; | ||||
| 
 | ||||
| export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|  | @ -19,51 +25,45 @@ export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | stri | |||
|     return users.map((user) => user.username); | ||||
| } | ||||
| 
 | ||||
| export async function getTeacher(username: string): Promise<TeacherDTO | null> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|     const user = await teacherRepository.findByUsername(username); | ||||
|     return user ? mapToTeacherDTO(user) : null; | ||||
| } | ||||
| 
 | ||||
| export async function createTeacher(userData: TeacherDTO): Promise<TeacherDTO | null> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
| 
 | ||||
|     try { | ||||
|         const newTeacher = teacherRepository.create(mapToTeacher(userData)); | ||||
|         await teacherRepository.save(newTeacher); | ||||
| 
 | ||||
|         return mapToTeacherDTO(newTeacher); | ||||
|     } catch (e) { | ||||
|         console.log(e); | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export async function deleteTeacher(username: string): Promise<TeacherDTO | null> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
| 
 | ||||
|     const user = await teacherRepository.findByUsername(username); | ||||
| export async function fetchTeacher(username: string): Promise<Teacher> { | ||||
|     const studentRepository = getStudentRepository(); | ||||
|     const user = await studentRepository.findByUsername(username); | ||||
| 
 | ||||
|     if (!user) { | ||||
|         return null; | ||||
|         throw new NotFoundException("Teacher with username not found"); | ||||
|     } | ||||
| 
 | ||||
|     try { | ||||
|         await teacherRepository.deleteByUsername(username); | ||||
|     return user; | ||||
| } | ||||
| 
 | ||||
| export async function getTeacher(username: string): Promise<TeacherDTO | null> { | ||||
|     const user = await fetchTeacher(username); | ||||
|     return mapToTeacherDTO(user); | ||||
|     } catch (e) { | ||||
|         console.log(e); | ||||
|         return null; | ||||
| } | ||||
| 
 | ||||
| export async function createTeacher(userData: TeacherDTO): Promise<void> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
| 
 | ||||
|     const user = await teacherRepository.findByUsername(userData.username); | ||||
| 
 | ||||
|     if (user){ | ||||
|         throw new ConflictException("Teacher with that username already exists"); | ||||
|     } | ||||
| 
 | ||||
|     const newTeacher = teacherRepository.create(mapToTeacher(userData)); | ||||
|     await teacherRepository.save(newTeacher); | ||||
| } | ||||
| 
 | ||||
| export async function deleteTeacher(username: string): Promise<void> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
| 
 | ||||
|     await fetchTeacher(username); // throws error if it does not exist
 | ||||
| 
 | ||||
|     await teacherRepository.deleteByUsername(username); | ||||
| } | ||||
| 
 | ||||
| async function fetchClassesByTeacher(username: string): Promise<ClassDTO[]> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|     const teacher = await teacherRepository.findByUsername(username); | ||||
|     if (!teacher) { | ||||
|         return []; | ||||
|     } | ||||
|     const teacher = await fetchTeacher(username); | ||||
| 
 | ||||
|     const classRepository = getClassRepository(); | ||||
|     const classes = await classRepository.findByTeacher(teacher); | ||||
|  | @ -81,6 +81,11 @@ export async function getClassesByTeacher(username: string, full: boolean): Prom | |||
| 
 | ||||
| export async function getStudentsByTeacher(username: string, full: boolean) { | ||||
|     const classes = await fetchClassesByTeacher(username); | ||||
| 
 | ||||
|     if (!classes || classes.length === 0){ | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     const classIds = classes.map((cls) => cls.id); | ||||
| 
 | ||||
|     const students = (await Promise.all(classIds.map(async (id) => getClassStudents(id)))).flat(); | ||||
|  | @ -91,16 +96,16 @@ export async function getStudentsByTeacher(username: string, full: boolean) { | |||
| } | ||||
| 
 | ||||
| export async function getTeacherQuestions(username: string, full: boolean): Promise<QuestionDTO[] | QuestionId[]> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|     const teacher = await teacherRepository.findByUsername(username); | ||||
|     if (!teacher) { | ||||
|         throw new Error(`Teacher with username '${username}' not found.`); | ||||
|     } | ||||
|     const teacher = await fetchTeacher(username); | ||||
| 
 | ||||
|     // Find all learning objects that this teacher manages
 | ||||
|     const learningObjectRepository = getLearningObjectRepository(); | ||||
|     const learningObjects = await learningObjectRepository.findAllByTeacher(teacher); | ||||
| 
 | ||||
|     if (!learningObjects || learningObjects.length === 0){ | ||||
|         return []; | ||||
|     } | ||||
| 
 | ||||
|     // Fetch all questions related to these learning objects
 | ||||
|     const questionRepository = getQuestionRepository(); | ||||
|     const questions = await questionRepository.findAllByLearningObjects(learningObjects); | ||||
|  | @ -112,3 +117,38 @@ export async function getTeacherQuestions(username: string, full: boolean): Prom | |||
| 
 | ||||
|     return questionsDTO.map(mapToQuestionId); | ||||
| } | ||||
| 
 | ||||
| export async function getJoinRequestsByClass( classId: string ){ | ||||
|     const classRepository = getClassRepository(); | ||||
|     const cls = await classRepository.findById(classId); | ||||
| 
 | ||||
|     if (!cls) { | ||||
|         throw new NotFoundException("Class with id not found"); | ||||
|     } | ||||
| 
 | ||||
|     const requestRepo = getClassJoinRequestRepository(); | ||||
|     const requests = await requestRepo.findAllOpenRequestsTo(cls); | ||||
|     return requests.map(mapToStudentRequestDTO); | ||||
| } | ||||
| 
 | ||||
| export async function updateClassJoinRequestStatus( studentUsername: string, classId: string, accepted: boolean = true) { | ||||
|     const requestRepo = getClassJoinRequestRepository(); | ||||
|     const classRepo = getClassRepository(); | ||||
| 
 | ||||
|     const student = await fetchStudent(studentUsername); | ||||
|     const cls = await classRepo.findById(classId); | ||||
| 
 | ||||
|     if (!cls) { | ||||
|         throw new NotFoundException('Class not found'); | ||||
|     } | ||||
| 
 | ||||
|     const request = await requestRepo.findByStudentAndClass(student, cls); | ||||
| 
 | ||||
|     if (!request) { | ||||
|         throw new NotFoundException('Join request not found'); | ||||
|     } | ||||
| 
 | ||||
|     request.status = accepted ? ClassJoinRequestStatus.Accepted : ClassJoinRequestStatus.Declined; | ||||
| 
 | ||||
|     await requestRepo.save(request); | ||||
| } | ||||
|  |  | |||
		Reference in a new issue
	
	 Gabriellvl
						Gabriellvl