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 {
|
import {
|
||||||
createTeacher,
|
createTeacher,
|
||||||
deleteTeacher,
|
deleteTeacher,
|
||||||
fetchTeacherByUsername,
|
getTeacherByUsername,
|
||||||
getClassesByTeacher,
|
getClassesByTeacher,
|
||||||
getClassIdsByTeacher,
|
getClassIdsByTeacher,
|
||||||
getAllTeachers,
|
getAllTeachers,
|
||||||
getAllTeachersIds, getStudentsByTeacher, getStudentIdsByTeacher
|
getAllTeachersIds, getStudentsByTeacher, getStudentIdsByTeacher, getQuestionsByTeacher, getQuestionIdsByTeacher
|
||||||
} from '../services/teachers.js';
|
} from '../services/teachers.js';
|
||||||
import {TeacherDTO} from "../interfaces/teacher";
|
import {TeacherDTO} from "../interfaces/teacher";
|
||||||
import {ClassDTO} from "../interfaces/class";
|
import {ClassDTO} from "../interfaces/class";
|
||||||
import {StudentDTO} from "../interfaces/student";
|
import {StudentDTO} from "../interfaces/student";
|
||||||
|
import {QuestionDTO, QuestionId} from "../interfaces/question";
|
||||||
|
|
||||||
export async function getTeacherHandler(req: Request, res: Response): Promise<void> {
|
export async function getTeacherHandler(req: Request, res: Response): Promise<void> {
|
||||||
try {
|
try {
|
||||||
|
@ -18,7 +19,7 @@ export async function getTeacherHandler(req: Request, res: Response): Promise<vo
|
||||||
const username = req.query.username as string;
|
const username = req.query.username as string;
|
||||||
|
|
||||||
if (username){
|
if (username){
|
||||||
const teacher = await fetchTeacherByUsername(username);
|
const teacher = await getTeacherByUsername(username);
|
||||||
if (!teacher){
|
if (!teacher){
|
||||||
res.status(404).json({ error: `Teacher with username '${username}' not found.` });
|
res.status(404).json({ error: `Teacher with username '${username}' not found.` });
|
||||||
return;
|
return;
|
||||||
|
@ -128,3 +129,27 @@ export async function getTeacherStudentHandler(req: Request, res: Response): Pro
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
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 { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||||
import { LearningObject } from '../../entities/content/learning-object.entity.js';
|
import { LearningObject } from '../../entities/content/learning-object.entity.js';
|
||||||
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
||||||
|
import {Teacher} from "../../entities/users/teacher.entity";
|
||||||
|
|
||||||
export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> {
|
export class LearningObjectRepository extends DwengoEntityRepository<LearningObject> {
|
||||||
public findByIdentifier(
|
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.
|
// 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 { Question } from '../../entities/questions/question.entity.js';
|
||||||
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js';
|
||||||
import { Student } from '../../entities/users/student.entity.js';
|
import { Student } from '../../entities/users/student.entity.js';
|
||||||
|
import {LearningObject} from "../../entities/content/learning-object.entity";
|
||||||
|
|
||||||
export class QuestionRepository extends DwengoEntityRepository<Question> {
|
export class QuestionRepository extends DwengoEntityRepository<Question> {
|
||||||
public createQuestion(question: {
|
public createQuestion(question: {
|
||||||
|
@ -42,4 +43,17 @@ export class QuestionRepository extends DwengoEntityRepository<Question> {
|
||||||
sequenceNumber: sequenceNumber,
|
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,
|
createTeacherHandler,
|
||||||
deleteTeacherHandler,
|
deleteTeacherHandler,
|
||||||
getTeacherClassHandler,
|
getTeacherClassHandler,
|
||||||
getTeacherHandler, getTeacherStudentHandler
|
getTeacherHandler, getTeacherQuestionHandler, getTeacherStudentHandler
|
||||||
} from "../controllers/teachers.js";
|
} from "../controllers/teachers.js";
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
|
@ -18,14 +18,7 @@ router.get('/:username/classes', getTeacherClassHandler);
|
||||||
|
|
||||||
router.get('/:username/students', getTeacherStudentHandler);
|
router.get('/:username/students', getTeacherStudentHandler);
|
||||||
|
|
||||||
// the questions students asked a teacher
|
router.get('/:username/questions', getTeacherQuestionHandler);
|
||||||
router.get('/:id/questions', (req, res) => {
|
|
||||||
res.json({
|
|
||||||
questions: [
|
|
||||||
'0'
|
|
||||||
],
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// invitations to other classes a teacher received
|
// invitations to other classes a teacher received
|
||||||
router.get('/:id/invitations', (req, res) => {
|
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
|
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 {mapToTeacher, mapToTeacherDTO, TeacherDTO} from "../interfaces/teacher.js";
|
||||||
import { Teacher } from "../entities/users/teacher.entity";
|
import { Teacher } from "../entities/users/teacher.entity";
|
||||||
import {ClassDTO, mapToClassDTO} from "../interfaces/class";
|
import {ClassDTO, mapToClassDTO} from "../interfaces/class";
|
||||||
import {getClassStudents, getClassStudentsIds} from "./class";
|
import {getClassStudents, getClassStudentsIds} from "./class";
|
||||||
import {StudentDTO} from "../interfaces/student";
|
import {StudentDTO} from "../interfaces/student";
|
||||||
|
import {mapToQuestionDTO, QuestionDTO, QuestionId} from "../interfaces/question";
|
||||||
|
|
||||||
|
|
||||||
async function fetchAllTeachers(): Promise<TeacherDTO[]> {
|
async function fetchAllTeachers(): Promise<TeacherDTO[]> {
|
||||||
|
@ -29,7 +35,7 @@ export async function createTeacher(teacherData: TeacherDTO): Promise<Teacher> {
|
||||||
return newTeacher;
|
return newTeacher;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchTeacherByUsername(username: string): Promise<TeacherDTO | null> {
|
export async function getTeacherByUsername(username: string): Promise<TeacherDTO | null> {
|
||||||
const teacherRepository = getTeacherRepository();
|
const teacherRepository = getTeacherRepository();
|
||||||
const teacher = await teacherRepository.findByUsername(username);
|
const teacher = await teacherRepository.findByUsername(username);
|
||||||
|
|
||||||
|
@ -84,5 +90,42 @@ export async function getStudentIdsByTeacher(): Promise<string[]> {
|
||||||
return await fetchStudentsByTeacher(username).map((student) => student.username);
|
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