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
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue