Merge remote-tracking branch 'origin/dev' into feat/questions-answers-en-submissions-groep-specifiek-maken-#163
# Conflicts: # backend/src/controllers/questions.ts # backend/src/controllers/submissions.ts # backend/src/data/questions/question-repository.ts # backend/src/interfaces/group.ts # backend/src/interfaces/question.ts # backend/src/interfaces/submission.ts # backend/src/routes/submissions.ts # backend/src/services/groups.ts # backend/src/services/questions.ts # backend/src/services/students.ts # backend/src/services/submissions.ts # common/src/interfaces/question.ts
This commit is contained in:
commit
d6dd7fb3bf
90 changed files with 2934 additions and 792 deletions
70
backend/src/services/answers.ts
Normal file
70
backend/src/services/answers.ts
Normal file
|
@ -0,0 +1,70 @@
|
|||
import { getAnswerRepository } from '../data/repositories.js';
|
||||
import { Answer } from '../entities/questions/answer.entity.js';
|
||||
import { mapToAnswerDTO, mapToAnswerDTOId } from '../interfaces/answer.js';
|
||||
import { fetchTeacher } from './teachers.js';
|
||||
import { fetchQuestion } from './questions.js';
|
||||
import { QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { AnswerData, AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
|
||||
export async function getAnswersByQuestion(questionId: QuestionId, full: boolean): Promise<AnswerDTO[] | AnswerId[]> {
|
||||
const answerRepository = getAnswerRepository();
|
||||
const question = await fetchQuestion(questionId);
|
||||
|
||||
const answers: Answer[] = await answerRepository.findAllAnswersToQuestion(question);
|
||||
|
||||
if (full) {
|
||||
return answers.map(mapToAnswerDTO);
|
||||
}
|
||||
|
||||
return answers.map(mapToAnswerDTOId);
|
||||
}
|
||||
|
||||
export async function createAnswer(questionId: QuestionId, answerData: AnswerData): Promise<AnswerDTO> {
|
||||
const answerRepository = getAnswerRepository();
|
||||
const toQuestion = await fetchQuestion(questionId);
|
||||
const author = await fetchTeacher(answerData.author);
|
||||
const content = answerData.content;
|
||||
|
||||
const answer = await answerRepository.createAnswer({
|
||||
toQuestion,
|
||||
author,
|
||||
content,
|
||||
});
|
||||
return mapToAnswerDTO(answer);
|
||||
}
|
||||
|
||||
async function fetchAnswer(questionId: QuestionId, sequenceNumber: number): Promise<Answer> {
|
||||
const answerRepository = getAnswerRepository();
|
||||
const question = await fetchQuestion(questionId);
|
||||
const answer = await answerRepository.findAnswer(question, sequenceNumber);
|
||||
|
||||
if (!answer) {
|
||||
throw new NotFoundException('Answer with questionID and sequence number not found');
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
export async function getAnswer(questionId: QuestionId, sequenceNumber: number): Promise<AnswerDTO> {
|
||||
const answer = await fetchAnswer(questionId, sequenceNumber);
|
||||
return mapToAnswerDTO(answer);
|
||||
}
|
||||
|
||||
export async function deleteAnswer(questionId: QuestionId, sequenceNumber: number): Promise<AnswerDTO> {
|
||||
const answerRepository = getAnswerRepository();
|
||||
|
||||
const question = await fetchQuestion(questionId);
|
||||
const answer = await fetchAnswer(questionId, sequenceNumber);
|
||||
|
||||
await answerRepository.removeAnswerByQuestionAndSequenceNumber(question, sequenceNumber);
|
||||
return mapToAnswerDTO(answer);
|
||||
}
|
||||
|
||||
export async function updateAnswer(questionId: QuestionId, sequenceNumber: number, answerData: AnswerData): Promise<AnswerDTO> {
|
||||
const answerRepository = getAnswerRepository();
|
||||
const answer = await fetchAnswer(questionId, sequenceNumber);
|
||||
|
||||
const newAnswer = await answerRepository.updateContent(answer, answerData.content);
|
||||
return mapToAnswerDTO(newAnswer);
|
||||
}
|
|
@ -1,18 +1,43 @@
|
|||
import { getAssignmentRepository, getClassRepository, getGroupRepository, getSubmissionRepository } from '../data/repositories.js';
|
||||
import { mapToAssignment, mapToAssignmentDTO, mapToAssignmentDTOId } from '../interfaces/assignment.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
|
||||
import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment';
|
||||
import {
|
||||
getAssignmentRepository,
|
||||
getClassRepository,
|
||||
getGroupRepository,
|
||||
getQuestionRepository,
|
||||
getSubmissionRepository,
|
||||
} from '../data/repositories.js';
|
||||
import { Assignment } from '../entities/assignments/assignment.entity.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { mapToAssignment, mapToAssignmentDTO, mapToAssignmentDTOId } from '../interfaces/assignment.js';
|
||||
import { mapToQuestionDTO } from '../interfaces/question.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
|
||||
import { fetchClass } from './classes.js';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { EntityDTO } from '@mikro-orm/core';
|
||||
import { putObject } from './service-helper.js';
|
||||
|
||||
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> {
|
||||
export async function fetchAssignment(classid: string, assignmentNumber: number): Promise<Assignment> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
throw new NotFoundException("Could not find assignment's class");
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
throw new NotFoundException('Could not find assignment');
|
||||
}
|
||||
|
||||
return assignment;
|
||||
}
|
||||
|
||||
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> {
|
||||
const cls = await fetchClass(classid);
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignments = await assignmentRepository.findAllAssignmentsInClass(cls);
|
||||
|
||||
|
@ -23,42 +48,37 @@ export async function getAllAssignments(classid: string, full: boolean): Promise
|
|||
return assignments.map(mapToAssignmentDTOId);
|
||||
}
|
||||
|
||||
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<AssignmentDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
if (!cls) {
|
||||
return null;
|
||||
}
|
||||
export async function createAssignment(classid: string, assignmentData: AssignmentDTO): Promise<AssignmentDTO> {
|
||||
const cls = await fetchClass(classid);
|
||||
|
||||
const assignment = mapToAssignment(assignmentData, cls);
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const newAssignment = assignmentRepository.create(assignment);
|
||||
await assignmentRepository.save(newAssignment, { preventOverwrite: true });
|
||||
|
||||
try {
|
||||
const newAssignment = assignmentRepository.create(assignment);
|
||||
await assignmentRepository.save(newAssignment);
|
||||
|
||||
return mapToAssignmentDTO(newAssignment);
|
||||
} catch (e) {
|
||||
getLogger().error(e);
|
||||
return null;
|
||||
}
|
||||
return mapToAssignmentDTO(newAssignment);
|
||||
}
|
||||
|
||||
export async function getAssignment(classid: string, id: number): Promise<AssignmentDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
export async function getAssignment(classid: string, id: number): Promise<AssignmentDTO> {
|
||||
const assignment = await fetchAssignment(classid, id);
|
||||
return mapToAssignmentDTO(assignment);
|
||||
}
|
||||
|
||||
if (!cls) {
|
||||
return null;
|
||||
}
|
||||
export async function putAssignment(classid: string, id: number, assignmentData: Partial<EntityDTO<Assignment>>): Promise<AssignmentDTO> {
|
||||
const assignment = await fetchAssignment(classid, id);
|
||||
|
||||
await putObject<Assignment>(assignment, assignmentData, getAssignmentRepository());
|
||||
|
||||
return mapToAssignmentDTO(assignment);
|
||||
}
|
||||
|
||||
export async function deleteAssignment(classid: string, id: number): Promise<AssignmentDTO> {
|
||||
const assignment = await fetchAssignment(classid, id);
|
||||
const cls = await fetchClass(classid);
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, id);
|
||||
|
||||
if (!assignment) {
|
||||
return null;
|
||||
}
|
||||
await assignmentRepository.deleteByClassAndId(cls, id);
|
||||
|
||||
return mapToAssignmentDTO(assignment);
|
||||
}
|
||||
|
@ -68,19 +88,7 @@ export async function getAssignmentsSubmissions(
|
|||
assignmentNumber: number,
|
||||
full: boolean
|
||||
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
return [];
|
||||
}
|
||||
const assignment = await fetchAssignment(classid, assignmentNumber);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
|
||||
|
@ -94,3 +102,16 @@ export async function getAssignmentsSubmissions(
|
|||
|
||||
return submissions.map(mapToSubmissionDTOId);
|
||||
}
|
||||
|
||||
export async function getAssignmentsQuestions(classid: string, assignmentNumber: number, full: boolean): Promise<QuestionDTO[] | QuestionId[]> {
|
||||
const assignment = await fetchAssignment(classid, assignmentNumber);
|
||||
|
||||
const questionRepository = getQuestionRepository();
|
||||
const questions = await questionRepository.findAllByAssignment(assignment);
|
||||
|
||||
if (full) {
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
|
|
@ -1,22 +1,25 @@
|
|||
import { getClassRepository, getStudentRepository, getTeacherInvitationRepository, getTeacherRepository } from '../data/repositories.js';
|
||||
import { getClassRepository, getTeacherInvitationRepository } from '../data/repositories.js';
|
||||
import { mapToClassDTO } from '../interfaces/class.js';
|
||||
import { mapToStudentDTO } from '../interfaces/student.js';
|
||||
import { mapToTeacherInvitationDTO, mapToTeacherInvitationDTOIds } from '../interfaces/teacher-invitation.js';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { Class } from '../entities/classes/class.entity.js';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { TeacherInvitationDTO } from '@dwengo-1/common/interfaces/teacher-invitation';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
import { fetchTeacher } from './teachers.js';
|
||||
import { fetchStudent } from './students.js';
|
||||
import { TeacherDTO } from '@dwengo-1/common/interfaces/teacher';
|
||||
import { mapToTeacherDTO } from '../interfaces/teacher.js';
|
||||
import { EntityDTO } from '@mikro-orm/core';
|
||||
import { putObject } from './service-helper.js';
|
||||
|
||||
const logger = getLogger();
|
||||
|
||||
export async function fetchClass(classId: string): Promise<Class> {
|
||||
export async function fetchClass(classid: string): Promise<Class> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
if (!cls) {
|
||||
throw new NotFoundException('Class with id not found');
|
||||
throw new NotFoundException('Class not found');
|
||||
}
|
||||
|
||||
return cls;
|
||||
|
@ -24,11 +27,7 @@ export async function fetchClass(classId: string): Promise<Class> {
|
|||
|
||||
export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const classes = await classRepository.find({}, { populate: ['students', 'teachers'] });
|
||||
|
||||
if (!classes) {
|
||||
return [];
|
||||
}
|
||||
const classes = await classRepository.findAll({ populate: ['students', 'teachers'] });
|
||||
|
||||
if (full) {
|
||||
return classes.map(mapToClassDTO);
|
||||
|
@ -36,74 +35,71 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
|
|||
return classes.map((cls) => cls.classId!);
|
||||
}
|
||||
|
||||
export async function createClass(classData: ClassDTO): Promise<ClassDTO | null> {
|
||||
const teacherRepository = getTeacherRepository();
|
||||
const teacherUsernames = classData.teachers || [];
|
||||
const teachers = (await Promise.all(teacherUsernames.map(async (id) => teacherRepository.findByUsername(id)))).filter(
|
||||
(teacher) => teacher !== null
|
||||
);
|
||||
|
||||
const studentRepository = getStudentRepository();
|
||||
const studentUsernames = classData.students || [];
|
||||
const students = (await Promise.all(studentUsernames.map(async (id) => studentRepository.findByUsername(id)))).filter(
|
||||
(student) => student !== null
|
||||
);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
|
||||
try {
|
||||
const newClass = classRepository.create({
|
||||
displayName: classData.displayName,
|
||||
teachers: teachers,
|
||||
students: students,
|
||||
});
|
||||
await classRepository.save(newClass);
|
||||
|
||||
return mapToClassDTO(newClass);
|
||||
} catch (e) {
|
||||
logger.error(e);
|
||||
return null;
|
||||
}
|
||||
export async function getClass(classId: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
export async function getClass(classId: string): Promise<ClassDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
export async function createClass(classData: ClassDTO): Promise<ClassDTO> {
|
||||
const teacherUsernames = classData.teachers || [];
|
||||
const teachers = await Promise.all(teacherUsernames.map(async (id) => fetchTeacher(id)));
|
||||
|
||||
if (!cls) {
|
||||
return null;
|
||||
}
|
||||
const studentUsernames = classData.students || [];
|
||||
const students = await Promise.all(studentUsernames.map(async (id) => fetchStudent(id)));
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const newClass = classRepository.create({
|
||||
displayName: classData.displayName,
|
||||
teachers: teachers,
|
||||
students: students,
|
||||
});
|
||||
await classRepository.save(newClass, { preventOverwrite: true });
|
||||
|
||||
return mapToClassDTO(newClass);
|
||||
}
|
||||
|
||||
export async function putClass(classId: string, classData: Partial<EntityDTO<Class>>): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
await putObject<Class>(cls, classData, getClassRepository());
|
||||
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
async function fetchClassStudents(classId: string): Promise<StudentDTO[]> {
|
||||
export async function deleteClass(classId: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
await classRepository.deleteById(classId);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
export async function getClassStudents(classId: string, full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
if (full) {
|
||||
return cls.students.map(mapToStudentDTO);
|
||||
}
|
||||
return cls.students.map((student) => student.username);
|
||||
}
|
||||
|
||||
export async function getClassStudentsDTO(classId: string): Promise<StudentDTO[]> {
|
||||
const cls = await fetchClass(classId);
|
||||
return cls.students.map(mapToStudentDTO);
|
||||
}
|
||||
|
||||
export async function getClassStudents(classId: string): Promise<StudentDTO[]> {
|
||||
return await fetchClassStudents(classId);
|
||||
}
|
||||
export async function getClassTeachers(classId: string, full: boolean): Promise<TeacherDTO[] | string[]> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
export async function getClassStudentsIds(classId: string): Promise<string[]> {
|
||||
const students: StudentDTO[] = await fetchClassStudents(classId);
|
||||
return students.map((student) => student.username);
|
||||
if (full) {
|
||||
return cls.teachers.map(mapToTeacherDTO);
|
||||
}
|
||||
return cls.teachers.map((student) => student.username);
|
||||
}
|
||||
|
||||
export async function getClassTeacherInvitations(classId: string, full: boolean): Promise<TeacherInvitationDTO[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
}
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
const invitations = await teacherInvitationRepository.findAllInvitationsForClass(cls);
|
||||
|
@ -114,3 +110,41 @@ export async function getClassTeacherInvitations(classId: string, full: boolean)
|
|||
|
||||
return invitations.map(mapToTeacherInvitationDTOIds);
|
||||
}
|
||||
|
||||
export async function deleteClassStudent(classId: string, username: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const newStudents = { students: cls.students.filter((student) => student.username !== username) };
|
||||
await putObject<Class>(cls, newStudents, getClassRepository());
|
||||
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
export async function deleteClassTeacher(classId: string, username: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const newTeachers = { teachers: cls.teachers.filter((teacher) => teacher.username !== username) };
|
||||
await putObject<Class>(cls, newTeachers, getClassRepository());
|
||||
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
export async function addClassStudent(classId: string, username: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
const newStudent = await fetchStudent(username);
|
||||
|
||||
const newStudents = { students: [...cls.students, newStudent] };
|
||||
await putObject<Class>(cls, newStudents, getClassRepository());
|
||||
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
||||
export async function addClassTeacher(classId: string, username: string): Promise<ClassDTO> {
|
||||
const cls = await fetchClass(classId);
|
||||
const newTeacher = await fetchTeacher(username);
|
||||
|
||||
const newTeachers = { teachers: [...cls.teachers, newTeacher] };
|
||||
await putObject<Class>(cls, newTeachers, getClassRepository());
|
||||
|
||||
return mapToClassDTO(cls);
|
||||
}
|
||||
|
|
|
@ -1,105 +1,90 @@
|
|||
import {
|
||||
getAssignmentRepository,
|
||||
getClassRepository,
|
||||
getGroupRepository,
|
||||
getStudentRepository,
|
||||
getSubmissionRepository,
|
||||
} from '../data/repositories.js';
|
||||
import { EntityDTO } from '@mikro-orm/core';
|
||||
import { getGroupRepository, getStudentRepository, getSubmissionRepository } from '../data/repositories.js';
|
||||
import { Group } from '../entities/assignments/group.entity.js';
|
||||
import { mapToGroupDTO, mapToShallowGroupDTO } from '../interfaces/group.js';
|
||||
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
|
||||
import { GroupDTO } from '@dwengo-1/common/interfaces/group';
|
||||
import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission';
|
||||
import { getLogger } from '../logging/initalize.js';
|
||||
import { fetchAssignment } from './assignments.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { putObject } from './service-helper.js';
|
||||
|
||||
export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number, full: boolean): Promise<GroupDTO | null> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
return null;
|
||||
}
|
||||
export async function fetchGroup(classId: string, assignmentNumber: number, groupNumber: number): Promise<Group> {
|
||||
const assignment = await fetchAssignment(classId, assignmentNumber);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment, groupNumber);
|
||||
|
||||
if (!group) {
|
||||
return null;
|
||||
throw new NotFoundException('Could not find group');
|
||||
}
|
||||
|
||||
if (full) {
|
||||
return mapToGroupDTO(group);
|
||||
}
|
||||
|
||||
return mapToShallowGroupDTO(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
export async function createGroup(groupData: GroupDTO, classid: string, assignmentNumber: number): Promise<Group | null> {
|
||||
export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number): Promise<GroupDTO> {
|
||||
const group = await fetchGroup(classId, assignmentNumber, groupNumber);
|
||||
return mapToGroupDTO(group);
|
||||
}
|
||||
|
||||
export async function putGroup(
|
||||
classId: string,
|
||||
assignmentNumber: number,
|
||||
groupNumber: number,
|
||||
groupData: Partial<EntityDTO<Group>>
|
||||
): Promise<GroupDTO> {
|
||||
const group = await fetchGroup(classId, assignmentNumber, groupNumber);
|
||||
|
||||
await putObject<Group>(group, groupData, getGroupRepository());
|
||||
|
||||
return mapToGroupDTO(group);
|
||||
}
|
||||
|
||||
export async function deleteGroup(classId: string, assignmentNumber: number, groupNumber: number): Promise<GroupDTO> {
|
||||
const group = await fetchGroup(classId, assignmentNumber, groupNumber);
|
||||
const assignment = await fetchAssignment(classId, assignmentNumber);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
await groupRepository.deleteByAssignmentAndGroupNumber(assignment, groupNumber);
|
||||
|
||||
return mapToGroupDTO(group);
|
||||
}
|
||||
|
||||
export async function getExistingGroupFromGroupDTO(groupData: GroupDTO): Promise<Group> {
|
||||
const classId = typeof groupData.class === 'string' ? groupData.class : groupData.class.id;
|
||||
const assignmentNumber = typeof groupData.assignment === 'number' ? groupData.assignment : groupData.assignment.id;
|
||||
const groupNumber = groupData.groupNumber;
|
||||
|
||||
return await fetchGroup(classId, assignmentNumber, groupNumber);
|
||||
}
|
||||
|
||||
export async function createGroup(groupData: GroupDTO, classid: string, assignmentNumber: number): Promise<GroupDTO> {
|
||||
const studentRepository = getStudentRepository();
|
||||
|
||||
const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list
|
||||
const memberUsernames = (groupData.members as string[]) || [];
|
||||
const members = (await Promise.all([...memberUsernames].map(async (id) => studentRepository.findByUsername(id)))).filter(
|
||||
(student) => student !== null
|
||||
);
|
||||
|
||||
getLogger().debug(members);
|
||||
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classid);
|
||||
|
||||
if (!cls) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
return null;
|
||||
}
|
||||
const assignment = await fetchAssignment(classid, assignmentNumber);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
try {
|
||||
const newGroup = groupRepository.create({
|
||||
assignment: assignment,
|
||||
members: members,
|
||||
});
|
||||
await groupRepository.save(newGroup);
|
||||
const newGroup = groupRepository.create({
|
||||
assignment: assignment,
|
||||
members: members,
|
||||
});
|
||||
await groupRepository.save(newGroup);
|
||||
|
||||
return newGroup;
|
||||
} catch (e) {
|
||||
getLogger().error(e);
|
||||
return null;
|
||||
}
|
||||
return mapToGroupDTO(newGroup);
|
||||
}
|
||||
|
||||
export async function getAllGroups(classId: string, assignmentNumber: number, full: boolean): Promise<GroupDTO[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
return [];
|
||||
}
|
||||
const assignment = await fetchAssignment(classId, assignmentNumber);
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
|
||||
|
||||
if (full) {
|
||||
getLogger().debug({ full: full, groups: groups });
|
||||
return groups.map(mapToGroupDTO);
|
||||
}
|
||||
|
||||
|
@ -112,26 +97,7 @@ export async function getGroupSubmissions(
|
|||
groupNumber: number,
|
||||
full: boolean
|
||||
): Promise<SubmissionDTO[] | SubmissionDTOId[]> {
|
||||
const classRepository = getClassRepository();
|
||||
const cls = await classRepository.findById(classId);
|
||||
|
||||
if (!cls) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const assignmentRepository = getAssignmentRepository();
|
||||
const assignment = await assignmentRepository.findByClassAndId(cls, assignmentNumber);
|
||||
|
||||
if (!assignment) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const groupRepository = getGroupRepository();
|
||||
const group = await groupRepository.findByAssignmentAndGroupNumber(assignment, groupNumber);
|
||||
|
||||
if (!group) {
|
||||
return [];
|
||||
}
|
||||
const group = await fetchGroup(classId, assignmentNumber, groupNumber);
|
||||
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = await submissionRepository.findAllSubmissionsForGroup(group);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { getAttachmentRepository } from '../../data/repositories.js';
|
||||
import { Attachment } from '../../entities/content/attachment.entity.js';
|
||||
|
||||
import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
const attachmentService = {
|
||||
async getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
|
||||
async getAttachment(learningObjectId: LearningObjectIdentifierDTO, attachmentName: string): Promise<Attachment | null> {
|
||||
const attachmentRepo = getAttachmentRepository();
|
||||
|
||||
if (learningObjectId.version) {
|
||||
|
|
|
@ -6,7 +6,7 @@ import processingService from './processing/processing-service.js';
|
|||
import { NotFoundError } from '@mikro-orm/core';
|
||||
import learningObjectService from './learning-object-service.js';
|
||||
import { getLogger, Logger } from '../../logging/initalize.js';
|
||||
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
const logger: Logger = getLogger();
|
||||
|
||||
|
@ -40,7 +40,7 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
|
|||
};
|
||||
}
|
||||
|
||||
async function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
||||
async function findLearningObjectEntityById(id: LearningObjectIdentifierDTO): Promise<LearningObject | null> {
|
||||
const learningObjectRepo = getLearningObjectRepository();
|
||||
|
||||
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
|
||||
|
@ -53,7 +53,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
|||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
|
||||
async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> {
|
||||
const learningObject = await findLearningObjectEntityById(id);
|
||||
return convertLearningObject(learningObject);
|
||||
},
|
||||
|
@ -61,7 +61,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
|||
/**
|
||||
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
|
||||
*/
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> {
|
||||
const learningObjectRepo = getLearningObjectRepository();
|
||||
|
||||
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
|
||||
|
|
|
@ -5,7 +5,7 @@ import { LearningObjectProvider } from './learning-object-provider.js';
|
|||
import { getLogger, Logger } from '../../logging/initalize.js';
|
||||
import {
|
||||
FilteredLearningObject,
|
||||
LearningObjectIdentifier,
|
||||
LearningObjectIdentifierDTO,
|
||||
LearningObjectMetadata,
|
||||
LearningObjectNode,
|
||||
LearningPathIdentifier,
|
||||
|
@ -67,7 +67,7 @@ async function fetchLearningObjects(learningPathId: LearningPathIdentifier, full
|
|||
|
||||
const objects = await Promise.all(
|
||||
nodes.map(async (node) => {
|
||||
const learningObjectId: LearningObjectIdentifier = {
|
||||
const learningObjectId: LearningObjectIdentifierDTO = {
|
||||
hruid: node.learningobject_hruid,
|
||||
language: learningPathId.language,
|
||||
};
|
||||
|
@ -85,7 +85,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
|
|||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
|
||||
async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> {
|
||||
const metadataUrl = `${DWENGO_API_BASE}/learningObject/getMetadata`;
|
||||
const metadata = await fetchWithLogging<LearningObjectMetadata>(
|
||||
metadataUrl,
|
||||
|
@ -121,7 +121,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
|
|||
* Obtain a HTML-rendering of the learning object with the given identifier (as a string). For learning objects
|
||||
* from the Dwengo API, this means passing through the HTML rendering from there.
|
||||
*/
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> {
|
||||
const htmlUrl = `${DWENGO_API_BASE}/learningObject/getRaw`;
|
||||
const html = await fetchWithLogging<string>(htmlUrl, `Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`, {
|
||||
params: { ...id },
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
export interface LearningObjectProvider {
|
||||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null>;
|
||||
getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null>;
|
||||
|
||||
/**
|
||||
* Fetch full learning object data (metadata)
|
||||
|
@ -19,5 +19,5 @@ export interface LearningObjectProvider {
|
|||
/**
|
||||
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
|
||||
*/
|
||||
getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null>;
|
||||
getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null>;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,9 @@ import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provid
|
|||
import { LearningObjectProvider } from './learning-object-provider.js';
|
||||
import { envVars, getEnvVar } from '../../util/envVars.js';
|
||||
import databaseLearningObjectProvider from './database-learning-object-provider.js';
|
||||
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { FilteredLearningObject, LearningObjectIdentifierDTO, LearningPathIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
|
||||
function getProvider(id: LearningObjectIdentifier): LearningObjectProvider {
|
||||
function getProvider(id: LearningObjectIdentifierDTO): LearningObjectProvider {
|
||||
if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) {
|
||||
return databaseLearningObjectProvider;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ const learningObjectService = {
|
|||
/**
|
||||
* Fetches a single learning object by its HRUID
|
||||
*/
|
||||
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
|
||||
async getLearningObjectById(id: LearningObjectIdentifierDTO): Promise<FilteredLearningObject | null> {
|
||||
return getProvider(id).getLearningObjectById(id);
|
||||
},
|
||||
|
||||
|
@ -39,7 +39,7 @@ const learningObjectService = {
|
|||
/**
|
||||
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
|
||||
*/
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
|
||||
async getLearningObjectHTML(id: LearningObjectIdentifierDTO): Promise<string | null> {
|
||||
return getProvider(id).getLearningObjectHTML(id);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -12,7 +12,7 @@ import Image = marked.Tokens.Image;
|
|||
import Heading = marked.Tokens.Heading;
|
||||
import Link = marked.Tokens.Link;
|
||||
import RendererObject = marked.RendererObject;
|
||||
import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
|
||||
const prefixes = {
|
||||
|
@ -25,7 +25,7 @@ const prefixes = {
|
|||
blockly: '@blockly',
|
||||
};
|
||||
|
||||
function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifier {
|
||||
function extractLearningObjectIdFromHref(href: string): LearningObjectIdentifierDTO {
|
||||
const [hruid, language, version] = href.split(/\/(.+)/, 2)[1].split('/');
|
||||
return {
|
||||
hruid,
|
||||
|
|
|
@ -14,7 +14,7 @@ import { LearningObject } from '../../../entities/content/learning-object.entity
|
|||
import Processor from './processor.js';
|
||||
import { DwengoContentType } from './content-type.js';
|
||||
import { replaceAsync } from '../../../util/async.js';
|
||||
import { LearningObjectIdentifier } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { LearningObjectIdentifierDTO } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
|
||||
const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = /<learning-object hruid="([^"]+)" language="([^"]+)" version="([^"]+)"\/>/g;
|
||||
|
@ -50,7 +50,7 @@ class ProcessingService {
|
|||
*/
|
||||
async render(
|
||||
learningObject: LearningObject,
|
||||
fetchEmbeddedLearningObjects?: (loId: LearningObjectIdentifier) => Promise<LearningObject | null>
|
||||
fetchEmbeddedLearningObjects?: (loId: LearningObjectIdentifierDTO) => Promise<LearningObject | null>
|
||||
): Promise<string> {
|
||||
const html = this.processors.get(learningObject.contentType)!.renderLearningObject(learningObject);
|
||||
if (fetchEmbeddedLearningObjects) {
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import { getAnswerRepository, getAssignmentRepository, getClassRepository, getGroupRepository, getQuestionRepository } from '../data/repositories.js';
|
||||
import { mapToQuestionDTO, mapToQuestionDTOId } from '../interfaces/question.js';
|
||||
import {mapToLearningObjectID, mapToQuestionDTO, mapToQuestionDTOId} from '../interfaces/question.js';
|
||||
import { Question } from '../entities/questions/question.entity.js';
|
||||
import { Answer } from '../entities/questions/answer.entity.js';
|
||||
import { mapToAnswerDTO, mapToAnswerDTOId } from '../interfaces/answer.js';
|
||||
import { QuestionRepository } from '../data/questions/question-repository.js';
|
||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||
import { mapToStudent } from '../interfaces/student.js';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import {QuestionData, QuestionDTO, QuestionId} from '@dwengo-1/common/interfaces/question';
|
||||
import { AnswerDTO, AnswerId } from '@dwengo-1/common/interfaces/answer';
|
||||
import { AssignmentDTO } from '@dwengo-1/common/interfaces/assignment';
|
||||
import { mapToAssignment } from '../interfaces/assignment.js';
|
||||
import {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment";
|
||||
import { fetchStudent } from './students.js';
|
||||
import {NotFoundException} from "../exceptions/not-found-exception";
|
||||
import { FALLBACK_VERSION_NUM } from '../config.js';
|
||||
|
||||
export async function getQuestionsAboutLearningObjectInAssignment(
|
||||
loId: LearningObjectIdentifier,
|
||||
|
@ -32,10 +34,6 @@ export async function getAllQuestions(id: LearningObjectIdentifier, full: boolea
|
|||
const questionRepository: QuestionRepository = getQuestionRepository();
|
||||
const questions = await questionRepository.findAllQuestionsAboutLearningObject(id);
|
||||
|
||||
if (!questions) {
|
||||
return [];
|
||||
}
|
||||
|
||||
if (full) {
|
||||
return questions.map(mapToQuestionDTO);
|
||||
}
|
||||
|
@ -43,24 +41,22 @@ export async function getAllQuestions(id: LearningObjectIdentifier, full: boolea
|
|||
return questions.map(mapToQuestionDTOId);
|
||||
}
|
||||
|
||||
async function fetchQuestion(questionId: QuestionId): Promise<Question | null> {
|
||||
export async function fetchQuestion(questionId: QuestionId): Promise<Question> {
|
||||
const questionRepository = getQuestionRepository();
|
||||
|
||||
return await questionRepository.findOne({
|
||||
learningObjectHruid: questionId.learningObjectIdentifier.hruid,
|
||||
learningObjectLanguage: questionId.learningObjectIdentifier.language,
|
||||
learningObjectVersion: questionId.learningObjectIdentifier.version,
|
||||
sequenceNumber: questionId.sequenceNumber,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO | null> {
|
||||
const question = await fetchQuestion(questionId);
|
||||
const question = await questionRepository.findByLearningObjectAndSequenceNumber(
|
||||
mapToLearningObjectID(questionId.learningObjectIdentifier),
|
||||
questionId.sequenceNumber
|
||||
);
|
||||
|
||||
if (!question) {
|
||||
return null;
|
||||
throw new NotFoundException('Question with loID and sequence number not found');
|
||||
}
|
||||
|
||||
return question;
|
||||
}
|
||||
|
||||
export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO> {
|
||||
const question = await fetchQuestion(questionId);
|
||||
return mapToQuestionDTO(question);
|
||||
}
|
||||
|
||||
|
@ -85,53 +81,43 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean
|
|||
return answers.map(mapToAnswerDTOId);
|
||||
}
|
||||
|
||||
export async function createQuestion(questionDTO: QuestionDTO): Promise<QuestionDTO | null> {
|
||||
export async function createQuestion(loId: LearningObjectIdentifier, questionData: QuestionData): Promise<QuestionDTO> {
|
||||
const questionRepository = getQuestionRepository();
|
||||
const author = await fetchStudent(questionData.author!);
|
||||
const content = questionData.content;
|
||||
|
||||
const author = mapToStudent(questionDTO.author);
|
||||
const clazz = await getClassRepository().findById((questionData.inGroup.assignment as AssignmentDTO).within);
|
||||
const assignment = mapToAssignment(questionData.inGroup.assignment as AssignmentDTO, clazz!);
|
||||
const inGroup = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber);
|
||||
|
||||
const loId: LearningObjectIdentifier = {
|
||||
...questionDTO.learningObjectIdentifier,
|
||||
version: questionDTO.learningObjectIdentifier.version ?? 1,
|
||||
};
|
||||
|
||||
const clazz = await getClassRepository().findById((questionDTO.inGroup.assignment as AssignmentDTO).class);
|
||||
const assignment = mapToAssignment(questionDTO.inGroup.assignment as AssignmentDTO, clazz!);
|
||||
const inGroup = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionDTO.inGroup.groupNumber);
|
||||
|
||||
try {
|
||||
await questionRepository.createQuestion({
|
||||
loId,
|
||||
author,
|
||||
inGroup: inGroup!,
|
||||
content: questionDTO.content,
|
||||
});
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return questionDTO;
|
||||
}
|
||||
|
||||
export async function deleteQuestion(questionId: QuestionId): Promise<QuestionDTO | null> {
|
||||
const questionRepository = getQuestionRepository();
|
||||
|
||||
const question = await fetchQuestion(questionId);
|
||||
|
||||
if (!question) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const loId: LearningObjectIdentifier = {
|
||||
...questionId.learningObjectIdentifier,
|
||||
version: questionId.learningObjectIdentifier.version ?? 1,
|
||||
};
|
||||
|
||||
try {
|
||||
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(loId, questionId.sequenceNumber);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
const question = await questionRepository.createQuestion({
|
||||
loId,
|
||||
author,
|
||||
inGroup: inGroup!,
|
||||
content,
|
||||
});
|
||||
|
||||
return mapToQuestionDTO(question);
|
||||
}
|
||||
|
||||
export async function deleteQuestion(questionId: QuestionId): Promise<QuestionDTO> {
|
||||
const questionRepository = getQuestionRepository();
|
||||
const question = await fetchQuestion(questionId); // Throws error if not found
|
||||
|
||||
const loId: LearningObjectIdentifier = {
|
||||
hruid: questionId.learningObjectIdentifier.hruid,
|
||||
language: questionId.learningObjectIdentifier.language,
|
||||
version: questionId.learningObjectIdentifier.version || FALLBACK_VERSION_NUM,
|
||||
};
|
||||
|
||||
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(loId, questionId.sequenceNumber);
|
||||
return mapToQuestionDTO(question);
|
||||
}
|
||||
|
||||
export async function updateQuestion(questionId: QuestionId, questionData: QuestionData): Promise<QuestionDTO> {
|
||||
const questionRepository = getQuestionRepository();
|
||||
const question = await fetchQuestion(questionId);
|
||||
|
||||
const newQuestion = await questionRepository.updateContent(question, questionData.content);
|
||||
return mapToQuestionDTO(newQuestion);
|
||||
}
|
||||
|
|
20
backend/src/services/service-helper.ts
Normal file
20
backend/src/services/service-helper.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { EntityDTO, FromEntityType } from '@mikro-orm/core';
|
||||
import { DwengoEntityRepository } from '../data/dwengo-entity-repository';
|
||||
|
||||
/**
|
||||
* Utility function to perform an PUT on an object.
|
||||
*
|
||||
* @param object The object that needs to be changed
|
||||
* @param data The datafields and their values that will be updated
|
||||
* @param repo The repository on which this action needs to be performed
|
||||
*
|
||||
* @returns Nothing.
|
||||
*/
|
||||
export async function putObject<T extends object>(
|
||||
object: T,
|
||||
data: Partial<EntityDTO<FromEntityType<T>>>,
|
||||
repo: DwengoEntityRepository<T>
|
||||
): Promise<void> {
|
||||
repo.assign(object, data);
|
||||
await repo.getEntityManager().flush();
|
||||
}
|
|
@ -23,6 +23,7 @@ import { GroupDTO } from '@dwengo-1/common/interfaces/group';
|
|||
import { SubmissionDTO, SubmissionDTOId } from '@dwengo-1/common/interfaces/submission';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request';
|
||||
import { ConflictException } from '../exceptions/conflict-exception.js';
|
||||
import { Submission } from '../entities/assignments/submission.entity';
|
||||
|
||||
export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> {
|
||||
|
@ -137,6 +138,10 @@ export async function createClassJoinRequest(username: string, classId: string):
|
|||
const student = await fetchStudent(username); // Throws error if student not found
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
if (cls.students.contains(student)) {
|
||||
throw new ConflictException('Student already in this class');
|
||||
}
|
||||
|
||||
const request = mapToStudentRequest(student, cls);
|
||||
await requestRepo.save(request, { preventOverwrite: true });
|
||||
return mapToStudentRequestDTO(request);
|
||||
|
|
|
@ -1,61 +1,56 @@
|
|||
import { getAssignmentRepository, getSubmissionRepository } from '../data/repositories.js';
|
||||
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { mapToSubmission, mapToSubmissionDTO } from '../interfaces/submission.js';
|
||||
import { SubmissionDTO } from '@dwengo-1/common/interfaces/submission';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
|
||||
export async function getSubmission(
|
||||
learningObjectHruid: string,
|
||||
language: Language,
|
||||
version: number,
|
||||
submissionNumber: number
|
||||
): Promise<SubmissionDTO | null> {
|
||||
const loId = new LearningObjectIdentifier(learningObjectHruid, language, version);
|
||||
import { fetchStudent } from './students.js';
|
||||
import { getExistingGroupFromGroupDTO } from './groups.js';
|
||||
import { Submission } from '../entities/assignments/submission.entity.js';
|
||||
import {Language} from "@dwengo-1/common/util/language";
|
||||
|
||||
export async function fetchSubmission(loId: LearningObjectIdentifier, submissionNumber: number): Promise<Submission> {
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submission = await submissionRepository.findSubmissionByLearningObjectAndSubmissionNumber(loId, submissionNumber);
|
||||
|
||||
if (!submission) {
|
||||
return null;
|
||||
throw new NotFoundException('Could not find submission');
|
||||
}
|
||||
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<SubmissionDTO | null> {
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submission = mapToSubmission(submissionDTO);
|
||||
|
||||
try {
|
||||
const newSubmission = submissionRepository.create(submission);
|
||||
await submissionRepository.save(newSubmission);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
export async function deleteSubmission(
|
||||
learningObjectHruid: string,
|
||||
language: Language,
|
||||
version: number,
|
||||
submissionNumber: number
|
||||
): Promise<SubmissionDTO | null> {
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
|
||||
const submission = getSubmission(learningObjectHruid, language, version, submissionNumber);
|
||||
|
||||
if (!submission) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const loId = new LearningObjectIdentifier(learningObjectHruid, language, version);
|
||||
await submissionRepository.deleteSubmissionByLearningObjectAndSubmissionNumber(loId, submissionNumber);
|
||||
|
||||
return submission;
|
||||
}
|
||||
|
||||
export async function getSubmission(loId: LearningObjectIdentifier, submissionNumber: number): Promise<SubmissionDTO> {
|
||||
const submission = await fetchSubmission(loId, submissionNumber);
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
export async function getAllSubmissions(loId: LearningObjectIdentifier): Promise<SubmissionDTO[]> {
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submissions = await submissionRepository.findByLearningObject(loId);
|
||||
|
||||
return submissions.map(mapToSubmissionDTO);
|
||||
}
|
||||
|
||||
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<SubmissionDTO> {
|
||||
const submitter = await fetchStudent(submissionDTO.submitter.username);
|
||||
const group = submissionDTO.group ? await getExistingGroupFromGroupDTO(submissionDTO.group) : undefined;
|
||||
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
const submission = mapToSubmission(submissionDTO, submitter, group);
|
||||
await submissionRepository.save(submission);
|
||||
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
export async function deleteSubmission(loId: LearningObjectIdentifier, submissionNumber: number): Promise<SubmissionDTO> {
|
||||
const submission = await fetchSubmission(loId, submissionNumber);
|
||||
|
||||
const submissionRepository = getSubmissionRepository();
|
||||
await submissionRepository.deleteSubmissionByLearningObjectAndSubmissionNumber(loId, submissionNumber);
|
||||
|
||||
return mapToSubmissionDTO(submission);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the submissions made by on behalf of any group the given student is in.
|
||||
*/
|
||||
|
|
|
@ -22,13 +22,14 @@ import { Question } from '../entities/questions/question.entity.js';
|
|||
import { ClassJoinRequestRepository } from '../data/classes/class-join-request-repository.js';
|
||||
import { Student } from '../entities/users/student.entity.js';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import { getClassStudents } from './classes.js';
|
||||
import { addClassStudent, fetchClass, getClassStudentsDTO } from './classes.js';
|
||||
import { TeacherDTO } from '@dwengo-1/common/interfaces/teacher';
|
||||
import { ClassDTO } from '@dwengo-1/common/interfaces/class';
|
||||
import { StudentDTO } from '@dwengo-1/common/interfaces/student';
|
||||
import { QuestionDTO, QuestionId } from '@dwengo-1/common/interfaces/question';
|
||||
import { ClassJoinRequestDTO } from '@dwengo-1/common/interfaces/class-join-request';
|
||||
import { ClassJoinRequestStatus } from '@dwengo-1/common/util/class-join-request';
|
||||
import { ConflictException } from '../exceptions/conflict-exception.js';
|
||||
|
||||
export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> {
|
||||
const teacherRepository: TeacherRepository = getTeacherRepository();
|
||||
|
@ -99,10 +100,12 @@ export async function getStudentsByTeacher(username: string, full: boolean): Pro
|
|||
|
||||
const classIds: string[] = classes.map((cls) => cls.id);
|
||||
|
||||
const students: StudentDTO[] = (await Promise.all(classIds.map(async (id) => getClassStudents(id)))).flat();
|
||||
const students: StudentDTO[] = (await Promise.all(classIds.map(async (username) => await getClassStudentsDTO(username)))).flat();
|
||||
|
||||
if (full) {
|
||||
return students;
|
||||
}
|
||||
|
||||
return students.map((student) => student.username);
|
||||
}
|
||||
|
||||
|
@ -143,13 +146,12 @@ export async function getJoinRequestsByClass(classId: string): Promise<ClassJoin
|
|||
|
||||
export async function updateClassJoinRequestStatus(studentUsername: string, classId: string, accepted = true): Promise<ClassJoinRequestDTO> {
|
||||
const requestRepo: ClassJoinRequestRepository = getClassJoinRequestRepository();
|
||||
const classRepo: ClassRepository = getClassRepository();
|
||||
|
||||
const student: Student = await fetchStudent(studentUsername);
|
||||
const cls: Class | null = await classRepo.findById(classId);
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
if (!cls) {
|
||||
throw new NotFoundException('Class not found');
|
||||
if (cls.students.contains(student)) {
|
||||
throw new ConflictException('Student already in this class');
|
||||
}
|
||||
|
||||
const request: ClassJoinRequest | null = await requestRepo.findByStudentAndClass(student, cls);
|
||||
|
@ -158,8 +160,14 @@ export async function updateClassJoinRequestStatus(studentUsername: string, clas
|
|||
throw new NotFoundException('Join request not found');
|
||||
}
|
||||
|
||||
request.status = accepted ? ClassJoinRequestStatus.Accepted : ClassJoinRequestStatus.Declined;
|
||||
request.status = ClassJoinRequestStatus.Declined;
|
||||
|
||||
if (accepted) {
|
||||
request.status = ClassJoinRequestStatus.Accepted;
|
||||
await addClassStudent(classId, studentUsername);
|
||||
}
|
||||
|
||||
await requestRepo.save(request);
|
||||
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue