diff --git a/backend/src/app.ts b/backend/src/app.ts index 8f01dea6..37cfec67 100644 --- a/backend/src/app.ts +++ b/backend/src/app.ts @@ -3,21 +3,23 @@ import { initORM } from './orm.js'; import { EnvVars, getNumericEnvVar } from './util/envvars.js'; import themeRoutes from './routes/themes.js'; -import learningPathRoutes from './routes/learningPaths.js'; -import learningObjectRoutes from './routes/learningObjects.js'; +import learningPathRoutes from './routes/learning-paths.js'; +import learningObjectRoutes from './routes/learning-objects.js'; -import studentRouter from './routes/student.js'; -import groupRouter from './routes/group.js'; -import submissionRouter from './routes/submission.js'; -import classRouter from './routes/class.js'; -import questionRouter from './routes/question.js'; -import loginRouter from './routes/login.js'; +import studentRoutes from './routes/students.js'; +import teacherRoutes from './routes/teachers.js' +import groupRoutes from './routes/groups.js'; +import submissionRoutes from './routes/submissions.js'; +import classRoutes from './routes/classes.js'; +import questionRoutes from './routes/questions.js'; const app: Express = express(); const port: string | number = getNumericEnvVar(EnvVars.Port); app.use(express.json()); +app.use(express.json()); + // TODO Replace with Express routes app.get('/', (_, res: Response) => { res.json({ @@ -25,12 +27,12 @@ app.get('/', (_, res: Response) => { }); }); -app.use('/student', studentRouter); -app.use('/group', groupRouter); -app.use('/submission', submissionRouter); -app.use('/class', classRouter); -app.use('/question', questionRouter); -app.use('/login', loginRouter); +app.use('/student', studentRoutes); +app.use('/teacher', teacherRoutes); +app.use('/group', groupRoutes); +app.use('/submission', submissionRoutes); +app.use('/class', classRoutes); +app.use('/question', questionRoutes); app.use('/theme', themeRoutes); app.use('/learningPath', learningPathRoutes); diff --git a/backend/src/controllers/classes.ts b/backend/src/controllers/classes.ts index 9f8f4669..6fb9d7f4 100644 --- a/backend/src/controllers/classes.ts +++ b/backend/src/controllers/classes.ts @@ -1,5 +1,6 @@ import { Request, Response } from 'express'; -import { getAllClasses, getClass, getClassStudents, getClassTeacherInvitations } from '../services/class.js'; +import { getAllClasses, getClass, getClassStudents, getClassStudentsIds, getClassTeacherInvitations } from '../services/class.js'; +import { ClassDTO } from '../interfaces/classes.js'; export async function getAllClassesHandler( req: Request, @@ -47,7 +48,9 @@ export async function getClassStudentsHandler( const classId = req.params.id; const full = req.query.full === "true"; - const students = await getClassStudents(classId, full); + let students = full + ? await getClassStudents(classId) + : await getClassStudentsIds(classId); res.json({ students: students, diff --git a/backend/src/controllers/learningObjects.ts b/backend/src/controllers/learning-objects.ts similarity index 93% rename from backend/src/controllers/learningObjects.ts rename to backend/src/controllers/learning-objects.ts index 4295326a..c8a51734 100644 --- a/backend/src/controllers/learningObjects.ts +++ b/backend/src/controllers/learning-objects.ts @@ -3,9 +3,9 @@ import { getLearningObjectById, getLearningObjectIdsFromPath, getLearningObjectsFromPath, -} from '../services/learningObjects.js'; +} from '../services/learning-objects.js'; import { FALLBACK_LANG } from '../config.js'; -import { FilteredLearningObject } from '../interfaces/learningPath'; +import { FilteredLearningObject } from '../interfaces/learning-path'; export async function getAllLearningObjects( req: Request, diff --git a/backend/src/controllers/learningPaths.ts b/backend/src/controllers/learning-paths.ts similarity index 97% rename from backend/src/controllers/learningPaths.ts rename to backend/src/controllers/learning-paths.ts index 903451be..fb0fd07c 100644 --- a/backend/src/controllers/learningPaths.ts +++ b/backend/src/controllers/learning-paths.ts @@ -4,7 +4,7 @@ import { FALLBACK_LANG } from '../config.js'; import { fetchLearningPaths, searchLearningPaths, -} from '../services/learningPaths.js'; +} from '../services/learning-paths.js'; /** * Fetch learning paths based on query parameters. */ diff --git a/backend/src/controllers/teachers.ts b/backend/src/controllers/teachers.ts new file mode 100644 index 00000000..d3b6a4db --- /dev/null +++ b/backend/src/controllers/teachers.ts @@ -0,0 +1,155 @@ +import { Request, Response } from 'express'; +import { + createTeacher, + deleteTeacher, + getTeacherByUsername, + getClassesByTeacher, + getClassIdsByTeacher, + getAllTeachers, + 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 { + try { + const full = req.query.full === 'true'; + const username = req.query.username as string; + + if (username){ + const teacher = await getTeacherByUsername(username); + if (!teacher){ + res.status(404).json({ error: `Teacher with username '${username}' not found.` }); + return; + } + res.json(teacher); + return; + } + + let teachers: TeacherDTO[] | string[]; + + if (full) teachers = await getAllTeachers(); + else teachers = await getAllTeachersIds(); + + res.json(teachers); + } catch (error) { + console.error("❌ Error fetching teachers:", error); + res.status(500).json({ error: "Internal server error" }); + } +} + +export async function createTeacherHandler( + req: Request, + res: Response +): Promise { + try { + const teacherData = req.body as TeacherDTO; + + if (!teacherData.username || !teacherData.firstName || !teacherData.lastName) { + res.status(400).json({ error: 'Missing required fields: username, firstName, lastName' }); + return; + } + + const newTeacher = await createTeacher(teacherData); + res.status(201).json(newTeacher); + } catch (error) { + console.error('Error creating teacher:', error); + res.status(500).json({ error: 'Internal server error' }); + } +} + +export async function deleteTeacherHandler( + req: Request, + res: Response +): Promise { + try { + const username = req.params.username as string; + + if (!username) { + res.status(400).json({ error: 'Missing required field: username' }); + return; + } + + const deletedTeacher = await deleteTeacher(username); + + if (!deletedTeacher) { + res.status(400).json({ error: `Did not find teacher with username ${username}` }); + return; + } + + res.status(201).json(deletedTeacher); + } catch (error) { + console.error('Error deleting teacher:', error); + res.status(500).json({ error: 'Internal server error' }); + } +} + +export async function getTeacherClassHandler(req: Request, res: Response): Promise { + 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 classes: ClassDTO[] | string[]; + + if (full) classes = await getClassesByTeacher(username); + else classes = 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' }); + } +} + +export async function getTeacherStudentHandler(req: Request, res: Response): Promise { + 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 students: StudentDTO[] | string[]; + + if (full) students = await getStudentsByTeacher(username); + else students = 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' }); + } +} + +export async function getTeacherQuestionHandler(req: Request, res: Response): Promise { + 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' }); + } +} + + diff --git a/backend/src/controllers/themes.ts b/backend/src/controllers/themes.ts index 4b59751e..208b27d1 100644 --- a/backend/src/controllers/themes.ts +++ b/backend/src/controllers/themes.ts @@ -1,6 +1,6 @@ import { Request, Response } from 'express'; import { themes } from '../data/themes.js'; -import { loadTranslations } from "../util/translationHelper.js"; +import { loadTranslations } from "../util/translation-helper.js"; import { FALLBACK_LANG } from '../config.js'; interface Translations { diff --git a/backend/src/data/classes/class-repository.ts b/backend/src/data/classes/class-repository.ts index cd0e44fc..9fd50d75 100644 --- a/backend/src/data/classes/class-repository.ts +++ b/backend/src/data/classes/class-repository.ts @@ -1,6 +1,7 @@ import { DwengoEntityRepository } from '../dwengo-entity-repository.js'; import { Class } from '../../entities/classes/class.entity.js'; import { Student } from '../../entities/users/student.entity.js'; +import {Teacher} from "../../entities/users/teacher.entity"; export class ClassRepository extends DwengoEntityRepository { public findById(id: string): Promise { @@ -18,4 +19,11 @@ export class ClassRepository extends DwengoEntityRepository { { populate: ["students", "teachers"] } // voegt student en teacher objecten toe ) } + + public findByTeacher(teacher: Teacher): Promise { + return this.find( + { teachers: teacher }, + { populate: ["students", "teachers"] } + ); + } } diff --git a/backend/src/data/content/learning-object-repository.ts b/backend/src/data/content/learning-object-repository.ts index 5d30b956..d8b07943 100644 --- a/backend/src/data/content/learning-object-repository.ts +++ b/backend/src/data/content/learning-object-repository.ts @@ -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 { public findByIdentifier( @@ -13,4 +14,11 @@ export class LearningObjectRepository extends DwengoEntityRepository { + return this.find( + { admins: teacher }, + { populate: ['admins'] } // Make sure to load admin relations + ); + } } diff --git a/backend/src/data/questions/question-repository.ts b/backend/src/data/questions/question-repository.ts index 517305f1..05430ddd 100644 --- a/backend/src/data/questions/question-repository.ts +++ b/backend/src/data/questions/question-repository.ts @@ -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 { public createQuestion(question: { @@ -42,4 +43,17 @@ export class QuestionRepository extends DwengoEntityRepository { sequenceNumber: sequenceNumber, }); } + + public async findAllByLearningObjects(learningObjects: LearningObject[]): Promise { + const objectIdentifiers = learningObjects.map(lo => ({ + learningObjectHruid: lo.hruid, + learningObjectLanguage: lo.language, + learningObjectVersion: lo.version + })); + + return this.findAll({ + where: { $or: objectIdentifiers }, + orderBy: { timestamp: 'ASC' }, + }); + } } diff --git a/backend/src/interfaces/assignments.ts b/backend/src/interfaces/assignment.ts similarity index 100% rename from backend/src/interfaces/assignments.ts rename to backend/src/interfaces/assignment.ts diff --git a/backend/src/interfaces/classes.ts b/backend/src/interfaces/class.ts similarity index 100% rename from backend/src/interfaces/classes.ts rename to backend/src/interfaces/class.ts diff --git a/backend/src/interfaces/groups.ts b/backend/src/interfaces/group.ts similarity index 100% rename from backend/src/interfaces/groups.ts rename to backend/src/interfaces/group.ts diff --git a/backend/src/interfaces/learningPath.ts b/backend/src/interfaces/learning-path.ts similarity index 100% rename from backend/src/interfaces/learningPath.ts rename to backend/src/interfaces/learning-path.ts diff --git a/backend/src/interfaces/question.ts b/backend/src/interfaces/question.ts new file mode 100644 index 00000000..1a8c914a --- /dev/null +++ b/backend/src/interfaces/question.ts @@ -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 +} diff --git a/backend/src/interfaces/students.ts b/backend/src/interfaces/student.ts similarity index 100% rename from backend/src/interfaces/students.ts rename to backend/src/interfaces/student.ts diff --git a/backend/src/interfaces/teacher.ts b/backend/src/interfaces/teacher.ts index 4f489c57..15c89520 100644 --- a/backend/src/interfaces/teacher.ts +++ b/backend/src/interfaces/teacher.ts @@ -1,23 +1,36 @@ import { Teacher } from "../entities/users/teacher.entity.js"; +/** + * Teacher Data Transfer Object + */ export interface TeacherDTO { - id: string; username: string; firstName: string; lastName: string; endpoints?: { + self: string; classes: string; questions: string; invitations: string; - groups: string; }; } +/** + * Maps a Teacher entity to a TeacherDTO + */ export function mapToTeacherDTO(teacher: Teacher): TeacherDTO { return { - id: teacher.username, username: teacher.username, firstName: teacher.firstName, lastName: teacher.lastName, }; } + +export function mapToTeacher(teacherData: TeacherDTO): Teacher { + const teacher = new Teacher(); + teacher.username = teacherData.username; + teacher.firstName = teacherData.firstName; + teacher.lastName = teacherData.lastName; + + return teacher; +} diff --git a/backend/src/routes/assignment.ts b/backend/src/routes/assignments.ts similarity index 100% rename from backend/src/routes/assignment.ts rename to backend/src/routes/assignments.ts diff --git a/backend/src/routes/class.ts b/backend/src/routes/classes.ts similarity index 100% rename from backend/src/routes/class.ts rename to backend/src/routes/classes.ts diff --git a/backend/src/routes/group.ts b/backend/src/routes/groups.ts similarity index 100% rename from backend/src/routes/group.ts rename to backend/src/routes/groups.ts diff --git a/backend/src/routes/learningObjects.ts b/backend/src/routes/learning-objects.ts similarity index 94% rename from backend/src/routes/learningObjects.ts rename to backend/src/routes/learning-objects.ts index 416602b5..3717095a 100644 --- a/backend/src/routes/learningObjects.ts +++ b/backend/src/routes/learning-objects.ts @@ -2,7 +2,7 @@ import express from 'express'; import { getAllLearningObjects, getLearningObject, -} from '../controllers/learningObjects.js'; +} from '../controllers/learning-objects.js'; const router = express.Router(); diff --git a/backend/src/routes/learningPaths.ts b/backend/src/routes/learning-paths.ts similarity index 91% rename from backend/src/routes/learningPaths.ts rename to backend/src/routes/learning-paths.ts index ce580745..efe17312 100644 --- a/backend/src/routes/learningPaths.ts +++ b/backend/src/routes/learning-paths.ts @@ -1,5 +1,5 @@ import express from 'express'; -import { getLearningPaths } from '../controllers/learningPaths.js'; +import { getLearningPaths } from '../controllers/learning-paths.js'; const router = express.Router(); diff --git a/backend/src/routes/login.ts b/backend/src/routes/login.ts deleted file mode 100644 index 550e6d93..00000000 --- a/backend/src/routes/login.ts +++ /dev/null @@ -1,14 +0,0 @@ -import express from 'express' -const router = express.Router(); - -// returns login paths for IDP -router.get('/', (req, res) => { - res.json({ - // dummy variables, needs to be changed - // with IDP endpoints - leerkracht: '/login-leerkracht', - leerling: '/login-leerling', - }); -}) - -export default router \ No newline at end of file diff --git a/backend/src/routes/question.ts b/backend/src/routes/questions.ts similarity index 100% rename from backend/src/routes/question.ts rename to backend/src/routes/questions.ts diff --git a/backend/src/routes/student.ts b/backend/src/routes/students.ts similarity index 100% rename from backend/src/routes/student.ts rename to backend/src/routes/students.ts diff --git a/backend/src/routes/submission.ts b/backend/src/routes/submissions.ts similarity index 100% rename from backend/src/routes/submission.ts rename to backend/src/routes/submissions.ts diff --git a/backend/src/routes/teacher.ts b/backend/src/routes/teacher.ts deleted file mode 100644 index 37b3b04b..00000000 --- a/backend/src/routes/teacher.ts +++ /dev/null @@ -1,58 +0,0 @@ -import express from 'express' -const router = express.Router(); - -// root endpoint used to search objects -router.get('/', (req, res) => { - res.json({ - teachers: [ - '0', - '1', - ] - }); -}); - -// information about a teacher -router.get('/:id', (req, res) => { - res.json({ - id: req.params.id, - firstName: 'John', - lastName: 'Doe', - username: 'JohnDoe1', - links: { - self: `${req.baseUrl}/${req.params.id}`, - classes: `${req.baseUrl}/${req.params.id}/classes`, - questions: `${req.baseUrl}/${req.params.id}/questions`, - invitations: `${req.baseUrl}/${req.params.id}/invitations`, - }, - }); -}) - -// the questions students asked a teacher -router.get('/:id/questions', (req, res) => { - res.json({ - questions: [ - '0' - ], - }); -}); - -// invitations to other classes a teacher received -router.get('/:id/invitations', (req, res) => { - res.json({ - invitations: [ - '0' - ], - }); -}); - -// a list with ids of classes a teacher is in -router.get('/:id/classes', (req, res) => { - res.json({ - classes: [ - '0' - ], - }); -}); - - -export default router \ No newline at end of file diff --git a/backend/src/routes/teachers.ts b/backend/src/routes/teachers.ts new file mode 100644 index 00000000..2a29897b --- /dev/null +++ b/backend/src/routes/teachers.ts @@ -0,0 +1,34 @@ +import express from 'express' +import { + createTeacherHandler, + deleteTeacherHandler, + getTeacherClassHandler, + getTeacherHandler, getTeacherQuestionHandler, getTeacherStudentHandler +} from "../controllers/teachers.js"; +const router = express.Router(); + +// root endpoint used to search objects +router.get('/', getTeacherHandler); + +router.post('/', createTeacherHandler); + +router.delete('/:username', deleteTeacherHandler); + +router.get('/:username/classes', getTeacherClassHandler); + +router.get('/:username/students', getTeacherStudentHandler); + +router.get('/:username/questions', getTeacherQuestionHandler); + +// invitations to other classes a teacher received +router.get('/:id/invitations', (req, res) => { + res.json({ + invitations: [ + '0' + ], + }); +}); + + + +export default router diff --git a/backend/src/services/class.ts b/backend/src/services/class.ts index eaeb771d..3867d910 100644 --- a/backend/src/services/class.ts +++ b/backend/src/services/class.ts @@ -1,7 +1,7 @@ -import { getClassRepository, getTeacherInvitationRepository } from "../data/repositories.js"; -import { ClassDTO, mapToClassDTO } from "../interfaces/classes.js"; -import { mapToStudentDTO, StudentDTO } from "../interfaces/students.js"; -import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds, TeacherInvitationDTO } from "../interfaces/teacher-invitation.js"; +import { getClassRepository } from "../data/repositories"; +import { Class } from "../entities/classes/class.entity"; +import { ClassDTO, mapToClassDTO } from "../interfaces/class"; +import { mapToStudentDTO, StudentDTO } from "../interfaces/student"; export async function getAllClasses(full: boolean): Promise { const classRepository = getClassRepository(); @@ -28,41 +28,21 @@ export async function getClass(classId: string): Promise { } } -export async function getClassStudents(classId: string, full: boolean): Promise { +async function fetchClassStudents(classId: string, full: boolean): Promise { const classRepository = getClassRepository(); const cls = await classRepository.findById(classId); - if (!cls) { + if (!cls) return []; - } - if (full) { - return cls.students.map(mapToStudentDTO); - } else { - return cls.students.map((student) => student.username); - } + return cls.students.map(mapToStudentDTO); } -export async function getClassTeacherInvitations(classId: string, full: boolean): Promise { - const classRepository = getClassRepository(); - const cls = await classRepository.findById(classId); - - if (!cls) { - return []; - } - - const teacherInvitationRepository = getTeacherInvitationRepository(); - const invitations = await teacherInvitationRepository.findAllInvitationsForClass(cls); - - console.log(invitations); - - if (!invitations) { - return []; - } - - if (full) { - return invitations.map(mapToTeacherInvitationDTO); - } - - return invitations.map(mapToTeacherInvitationDTOIds); +export async function getClassStudents(classId: string): Promise { + return await fetchClassStudents(classId); } + +export async function getClassStudentsIds(classId: string): Promise { + return await fetchClassStudents(classId).map((student) => student.username); +} + diff --git a/backend/src/services/learningObjects.ts b/backend/src/services/learning-objects.ts similarity index 96% rename from backend/src/services/learningObjects.ts rename to backend/src/services/learning-objects.ts index d1d34ad2..65ad11a6 100644 --- a/backend/src/services/learningObjects.ts +++ b/backend/src/services/learning-objects.ts @@ -1,12 +1,12 @@ import { DWENGO_API_BASE } from '../config.js'; -import { fetchWithLogging } from '../util/apiHelper.js'; +import { fetchWithLogging } from '../util/api-helper.js'; import { FilteredLearningObject, LearningObjectMetadata, LearningObjectNode, LearningPathResponse, -} from '../interfaces/learningPath.js'; -import { fetchLearningPaths } from './learningPaths.js'; +} from '../interfaces/learning-path.js'; +import { fetchLearningPaths } from './learning-paths.js'; function filterData( data: LearningObjectMetadata, diff --git a/backend/src/services/learningPaths.ts b/backend/src/services/learning-paths.ts similarity index 93% rename from backend/src/services/learningPaths.ts rename to backend/src/services/learning-paths.ts index 2a9f15a3..3353b58d 100644 --- a/backend/src/services/learningPaths.ts +++ b/backend/src/services/learning-paths.ts @@ -1,9 +1,9 @@ -import { fetchWithLogging } from '../util/apiHelper.js'; +import { fetchWithLogging } from '../util/api-helper.js'; import { DWENGO_API_BASE } from '../config.js'; import { LearningPath, LearningPathResponse, -} from '../interfaces/learningPath.js'; +} from '../interfaces/learning-path.js'; export async function fetchLearningPaths( hruids: string[], diff --git a/backend/src/services/teachers.ts b/backend/src/services/teachers.ts new file mode 100644 index 00000000..4b8affc5 --- /dev/null +++ b/backend/src/services/teachers.ts @@ -0,0 +1,131 @@ +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 { + const teacherRepository = getTeacherRepository(); + const teachers = await teacherRepository.find({}); + + return teachers.map(mapToTeacherDTO); +} + +export async function getAllTeachers(): Promise { + return await fetchAllTeachers(); +} + +export async function getAllTeachersIds(): Promise { + return await fetchAllTeachers().map((teacher) => teacher.username) +} + +export async function createTeacher(teacherData: TeacherDTO): Promise { + const teacherRepository = getTeacherRepository(); + const newTeacher = mapToTeacher(teacherData); + + await teacherRepository.addTeacher(newTeacher); + return newTeacher; +} + +export async function getTeacherByUsername(username: string): Promise { + const teacherRepository = getTeacherRepository(); + const teacher = await teacherRepository.findByUsername(username); + + return teacher ? mapToTeacherDTO(teacher) : null; +} + +export async function deleteTeacher(username: string): Promise { + const teacherRepository = getTeacherRepository(); + const teacher = await teacherRepository.findByUsername(username); + + if (!teacher) + return null; + + await teacherRepository.deleteByUsername(username); + return teacher; +} + +async function fetchClassesByTeacher(username: string): Promise { + const teacherRepository = getTeacherRepository(); + const classRepository = getClassRepository(); + + const teacher = await teacherRepository.findByUsername(username); + if (!teacher) { + return []; + } + + const classes = await classRepository.findByTeacher(teacher); + return classes.map(mapToClassDTO); +} + +export async function getClassesByTeacher(username: string): Promise { + return await fetchClassesByTeacher(username) +} + +export async function getClassIdsByTeacher(): Promise { + return await fetchClassesByTeacher(username).map((cls) => cls.id); +} + +async function fetchStudentsByTeacher(username: string) { + const classes = await getClassIdsByTeacher(); + + return Promise.all( + classes.map( async (id) => getClassStudents(id)) + ); +} + +export async function getStudentsByTeacher(username: string): Promise { + return await fetchStudentsByTeacher(username); +} + +export async function getStudentIdsByTeacher(): Promise { + return await fetchStudentsByTeacher(username).map((student) => student.username); +} + +async function fetchTeacherQuestions(username: string): Promise { + 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 { + return await fetchTeacherQuestions(username); +} + +export async function getQuestionIdsByTeacher(username: string): Promise { + const questions = await fetchTeacherQuestions(username); + + return questions.map((question) => ({ + learningObjectHruid: question.learningObjectHruid, + learningObjectLanguage: question.learningObjectLanguage, + learningObjectVersion: question.learningObjectVersion, + sequenceNumber: question.sequenceNumber + })); +} + + + + + + + diff --git a/backend/src/util/apiHelper.ts b/backend/src/util/api-helper.ts similarity index 100% rename from backend/src/util/apiHelper.ts rename to backend/src/util/api-helper.ts diff --git a/backend/src/util/translationHelper.ts b/backend/src/util/translation-helper.ts similarity index 93% rename from backend/src/util/translationHelper.ts rename to backend/src/util/translation-helper.ts index eb7141ee..d6d842ff 100644 --- a/backend/src/util/translationHelper.ts +++ b/backend/src/util/translation-helper.ts @@ -1,7 +1,7 @@ import fs from 'fs'; import path from 'path'; import yaml from 'js-yaml'; -import {FALLBACK_LANG} from "../config.js"; +import { FALLBACK_LANG } from "../config.js"; export function loadTranslations(language: string): T { try {