feat: question-teacher route
This commit is contained in:
		
							parent
							
								
									4968d7cb07
								
							
						
					
					
						commit
						16b73b9e18
					
				
					 6 changed files with 138 additions and 22 deletions
				
			
		|  | @ -2,15 +2,16 @@ import { Request, Response } from 'express'; | |||
| import { | ||||
|     createTeacher, | ||||
|     deleteTeacher, | ||||
|     fetchTeacherByUsername, | ||||
|     getTeacherByUsername, | ||||
|     getClassesByTeacher, | ||||
|     getClassIdsByTeacher, | ||||
|     getAllTeachers, | ||||
|     getAllTeachersIds, getStudentsByTeacher, getStudentIdsByTeacher | ||||
|     getAllTeachersIds, getStudentsByTeacher, getStudentIdsByTeacher, getQuestionsByTeacher, getQuestionIdsByTeacher | ||||
| } from '../services/teachers.js'; | ||||
| import {TeacherDTO} from "../interfaces/teacher"; | ||||
| import {ClassDTO} from "../interfaces/class"; | ||||
| import {StudentDTO} from "../interfaces/student"; | ||||
| import {QuestionDTO, QuestionId} from "../interfaces/question"; | ||||
| 
 | ||||
| export async function getTeacherHandler(req: Request, res: Response): Promise<void> { | ||||
|     try { | ||||
|  | @ -18,7 +19,7 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo | |||
|         const username = req.query.username as string; | ||||
| 
 | ||||
|         if (username){ | ||||
|             const teacher = await fetchTeacherByUsername(username); | ||||
|             const teacher = await getTeacherByUsername(username); | ||||
|             if (!teacher){ | ||||
|                 res.status(404).json({ error: `Teacher with username '${username}' not found.` }); | ||||
|                 return; | ||||
|  | @ -128,3 +129,27 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro | |||
|         res.status(500).json({ error: 'Internal server error' }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export async function getTeacherQuestionHandler(req: Request, res: Response): Promise<void> { | ||||
|     try { | ||||
|         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; | ||||
|         } | ||||
| 
 | ||||
|         let questions: QuestionDTO[] | QuestionId[]; | ||||
| 
 | ||||
|         if (full) questions = await getQuestionsByTeacher(username); | ||||
|         else questions = 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' }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; | ||||
| import { LearningObject } from '../../entities/content/learning-object.entity.js'; | ||||
| import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; | ||||
| import {Teacher} from "../../entities/users/teacher.entity"; | ||||
| 
 | ||||
| export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> { | ||||
|     public findByIdentifier( | ||||
|  | @ -13,4 +14,11 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj | |||
|         }); | ||||
|     } | ||||
|     // This repository is read-only for now since creating own learning object is an extension feature.
 | ||||
| 
 | ||||
|     public findAllByTeacher(teacher: Teacher): Promise<LearningObject[]> { | ||||
|         return this.find( | ||||
|             { admins: teacher }, | ||||
|             { populate: ['admins'] } // Make sure to load admin relations
 | ||||
|         ); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; | |||
| import { Question } from '../../entities/questions/question.entity.js'; | ||||
| import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; | ||||
| import { Student } from '../../entities/users/student.entity.js'; | ||||
| import {LearningObject} from "../../entities/content/learning-object.entity"; | ||||
| 
 | ||||
| export class QuestionRepository extends DwengoEntityRepository<Question> { | ||||
|     public createQuestion(question: { | ||||
|  | @ -42,4 +43,17 @@ export class QuestionRepository extends DwengoEntityRepository<Question> { | |||
|             sequenceNumber: sequenceNumber, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     public async findAllByLearningObjects(learningObjects: LearningObject[]): Promise<Question[]> { | ||||
|         const objectIdentifiers = learningObjects.map(lo => ({ | ||||
|                 learningObjectHruid: lo.hruid, | ||||
|                 learningObjectLanguage: lo.language, | ||||
|                 learningObjectVersion: lo.version | ||||
|             })); | ||||
| 
 | ||||
|         return this.findAll({ | ||||
|             where: { $or: objectIdentifiers }, | ||||
|             orderBy: { timestamp: 'ASC' }, | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  |  | |||
							
								
								
									
										41
									
								
								backend/src/interfaces/question.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								backend/src/interfaces/question.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | |||
| import {Question} from "../entities/questions/question.entity"; | ||||
| import {Enum, PrimaryKey} from "@mikro-orm/core"; | ||||
| import {Language} from "../entities/content/language"; | ||||
| 
 | ||||
| export interface QuestionDTO { | ||||
|     learningObjectHruid: string; | ||||
|     learningObjectLanguage: string; | ||||
|     learningObjectVersion: string; | ||||
|     sequenceNumber: number; | ||||
|     authorUsername: string; | ||||
|     timestamp: string; | ||||
|     content: string; | ||||
|     endpoints?: { | ||||
|         classes: string; | ||||
|         questions: string; | ||||
|         invitations: string; | ||||
|         groups: string; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Convert a Question entity to a DTO format. | ||||
|  */ | ||||
| export function mapToQuestionDTO(question: Question): QuestionDTO { | ||||
|     return { | ||||
|         learningObjectHruid: question.learningObjectHruid, | ||||
|         learningObjectLanguage: question.learningObjectLanguage, | ||||
|         learningObjectVersion: question.learningObjectVersion, | ||||
|         sequenceNumber: question.sequenceNumber, | ||||
|         authorUsername: question.author.username, | ||||
|         timestamp: question.timestamp.toISOString(), | ||||
|         content: question.content, | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| export interface QuestionId { | ||||
|     learningObjectHruid: string, | ||||
|     learningObjectLanguage: Language, | ||||
|     learningObjectVersion: string, | ||||
|     sequenceNumber: number | ||||
| } | ||||
|  | @ -3,7 +3,7 @@ import { | |||
|     createTeacherHandler, | ||||
|     deleteTeacherHandler, | ||||
|     getTeacherClassHandler, | ||||
|     getTeacherHandler, getTeacherStudentHandler | ||||
|     getTeacherHandler, getTeacherQuestionHandler, getTeacherStudentHandler | ||||
| } from "../controllers/teachers.js"; | ||||
| const router = express.Router(); | ||||
| 
 | ||||
|  | @ -18,14 +18,7 @@ router.get('/:username/classes', getTeacherClassHandler); | |||
| 
 | ||||
| router.get('/:username/students', getTeacherStudentHandler); | ||||
| 
 | ||||
| // the questions students asked a teacher
 | ||||
| router.get('/:id/questions', (req, res) => { | ||||
|     res.json({ | ||||
|         questions: [ | ||||
|             '0' | ||||
|         ], | ||||
|     }); | ||||
| }); | ||||
| router.get('/:username/questions', getTeacherQuestionHandler); | ||||
| 
 | ||||
| // invitations to other classes a teacher received
 | ||||
| router.get('/:id/invitations', (req, res) => { | ||||
|  | @ -36,14 +29,6 @@ router.get('/:id/invitations', (req, res) => { | |||
|     }); | ||||
| }); | ||||
| 
 | ||||
| // a list with ids of classes a teacher is in
 | ||||
| router.get('/:id/classes', (req, res) => { | ||||
|     res.json({ | ||||
|         classes: [ | ||||
|             '0' | ||||
|         ], | ||||
|     }); | ||||
| }); | ||||
| 
 | ||||
| 
 | ||||
| export default router | ||||
|  |  | |||
|  | @ -1,9 +1,15 @@ | |||
| import {getClassRepository, getTeacherRepository} from "../data/repositories.js"; | ||||
| import { | ||||
|     getClassRepository, | ||||
|     getLearningObjectRepository, | ||||
|     getQuestionRepository, | ||||
|     getTeacherRepository | ||||
| } from "../data/repositories.js"; | ||||
| import {mapToTeacher, mapToTeacherDTO, TeacherDTO} from "../interfaces/teacher.js"; | ||||
| import { Teacher } from "../entities/users/teacher.entity"; | ||||
| import {ClassDTO, mapToClassDTO} from "../interfaces/class"; | ||||
| import {getClassStudents, getClassStudentsIds} from "./class"; | ||||
| import {StudentDTO} from "../interfaces/student"; | ||||
| import {mapToQuestionDTO, QuestionDTO, QuestionId} from "../interfaces/question"; | ||||
| 
 | ||||
| 
 | ||||
| async function fetchAllTeachers(): Promise<TeacherDTO[]> { | ||||
|  | @ -29,7 +35,7 @@ export async function createTeacher(teacherData: TeacherDTO): Promise<Teacher> { | |||
|     return newTeacher; | ||||
| } | ||||
| 
 | ||||
| export async function fetchTeacherByUsername(username: string): Promise<TeacherDTO | null> { | ||||
| export async function getTeacherByUsername(username: string): Promise<TeacherDTO | null> { | ||||
|     const teacherRepository = getTeacherRepository(); | ||||
|     const teacher = await teacherRepository.findByUsername(username); | ||||
| 
 | ||||
|  | @ -84,5 +90,42 @@ export async function getStudentIdsByTeacher(): Promise<string[]> { | |||
|     return await fetchStudentsByTeacher(username).map((student) => student.username); | ||||
| } | ||||
| 
 | ||||
| async function fetchTeacherQuestions(username: string): Promise<QuestionDTO[]> { | ||||
|     const learningObjectRepository = getLearningObjectRepository(); | ||||
|     const questionRepository = getQuestionRepository(); | ||||
| 
 | ||||
|     const teacher = getTeacherByUsername(username); | ||||
|     if (!teacher) { | ||||
|         throw new Error(`Teacher with username '${username}' not found.`); | ||||
|     } | ||||
| 
 | ||||
|     // Find all learning objects that this teacher manages
 | ||||
|     const learningObjects = await learningObjectRepository.findAllByTeacher(teacher); | ||||
| 
 | ||||
|     // Fetch all questions related to these learning objects
 | ||||
|     const questions = await questionRepository.findAllByLearningObjects(learningObjects); | ||||
| 
 | ||||
|     return questions.map(mapToQuestionDTO); | ||||
| } | ||||
| 
 | ||||
| export async function getQuestionsByTeacher(username: string): Promise<QuestionDTO[]> { | ||||
|     return await fetchTeacherQuestions(username); | ||||
| } | ||||
| 
 | ||||
| export async function getQuestionIdsByTeacher(username: string): Promise<QuestionId[]> { | ||||
|     const questions = await fetchTeacherQuestions(username); | ||||
| 
 | ||||
|     return questions.map((question) => ({ | ||||
|         learningObjectHruid: question.learningObjectHruid, | ||||
|         learningObjectLanguage: question.learningObjectLanguage, | ||||
|         learningObjectVersion: question.learningObjectVersion, | ||||
|         sequenceNumber: question.sequenceNumber | ||||
|     })); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
		Reference in a new issue
	
	 Gabriellvl
						Gabriellvl