Merge branch 'feat/service-layer' into feat/service-layer-adriaan
# Conflicts: # backend/src/controllers/classes.ts # backend/src/controllers/students.ts # backend/src/data/users/teacher-repository.ts # backend/src/interfaces/assignment.ts # backend/src/interfaces/teacher.ts # backend/src/routes/classes.ts # backend/src/services/assignments.ts # backend/src/services/class.ts # backend/src/services/students.ts # backend/src/util/translation-helper.ts
This commit is contained in:
commit
6c4ea0eefb
33 changed files with 454 additions and 137 deletions
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
|
@ -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.
|
||||
*/
|
155
backend/src/controllers/teachers.ts
Normal file
155
backend/src/controllers/teachers.ts
Normal file
|
@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
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<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 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<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 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<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,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 {
|
||||
|
|
|
@ -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<Class> {
|
||||
public findById(id: string): Promise<Class | null> {
|
||||
|
@ -18,4 +19,11 @@ export class ClassRepository extends DwengoEntityRepository<Class> {
|
|||
{ populate: ["students", "teachers"] } // voegt student en teacher objecten toe
|
||||
)
|
||||
}
|
||||
|
||||
public findByTeacher(teacher: Teacher): Promise<Class[]> {
|
||||
return this.find(
|
||||
{ teachers: teacher },
|
||||
{ populate: ["students", "teachers"] }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import express from 'express';
|
|||
import {
|
||||
getAllLearningObjects,
|
||||
getLearningObject,
|
||||
} from '../controllers/learningObjects.js';
|
||||
} from '../controllers/learning-objects.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
|
@ -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();
|
||||
|
|
@ -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
|
|
@ -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
|
34
backend/src/routes/teachers.ts
Normal file
34
backend/src/routes/teachers.ts
Normal file
|
@ -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
|
|
@ -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<ClassDTO[] | string[]> {
|
||||
const classRepository = getClassRepository();
|
||||
|
@ -28,41 +28,21 @@ export async function getClass(classId: string): Promise<ClassDTO | null> {
|
|||
}
|
||||
}
|
||||
|
||||
export async function getClassStudents(classId: string, full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
async function fetchClassStudents(classId: string, full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
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<TeacherInvitationDTO[]> {
|
||||
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<StudentDTO[]> {
|
||||
return await fetchClassStudents(classId);
|
||||
}
|
||||
|
||||
export async function getClassStudentsIds(classId: string): Promise<string[]> {
|
||||
return await fetchClassStudents(classId).map((student) => student.username);
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
|
@ -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[],
|
131
backend/src/services/teachers.ts
Normal file
131
backend/src/services/teachers.ts
Normal file
|
@ -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<TeacherDTO[]> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teachers = await teacherRepository.find({});
|
||||
|
||||
return teachers.map(mapToTeacherDTO);
|
||||
}
|
||||
|
||||
export async function getAllTeachers(): Promise<TeacherDTO[]> {
|
||||
return await fetchAllTeachers();
|
||||
}
|
||||
|
||||
export async function getAllTeachersIds(): Promise<string[]> {
|
||||
return await fetchAllTeachers().map((teacher) => teacher.username)
|
||||
}
|
||||
|
||||
export async function createTeacher(teacherData: TeacherDTO): Promise<Teacher> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const newTeacher = mapToTeacher(teacherData);
|
||||
|
||||
await teacherRepository.addTeacher(newTeacher);
|
||||
return newTeacher;
|
||||
}
|
||||
|
||||
export async function getTeacherByUsername(username: string): Promise<TeacherDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacher = await teacherRepository.findByUsername(username);
|
||||
|
||||
return teacher ? mapToTeacherDTO(teacher) : null;
|
||||
}
|
||||
|
||||
export async function deleteTeacher(username: string): Promise<TeacherDTO | null> {
|
||||
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<ClassDTO[]> {
|
||||
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<ClassDTO[]> {
|
||||
return await fetchClassesByTeacher(username)
|
||||
}
|
||||
|
||||
export async function getClassIdsByTeacher(): Promise<string[]> {
|
||||
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<StudentDTO[]> {
|
||||
return await fetchStudentsByTeacher(username);
|
||||
}
|
||||
|
||||
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
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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<T>(language: string): T {
|
||||
try {
|
Loading…
Add table
Add a link
Reference in a new issue