Merge remote-tracking branch 'origin/dev' into feat/endpoints-beschermen-met-authenticatie-#105
# Conflicts: # backend/src/controllers/assignments.ts # backend/src/controllers/questions.ts # backend/src/data/questions/question-repository.ts # backend/src/interfaces/question.ts # backend/src/routes/assignments.ts # backend/src/routes/classes.ts # backend/src/routes/groups.ts # backend/src/routes/teachers.ts # backend/src/services/questions.ts # common/src/interfaces/question.ts
This commit is contained in:
commit
ac399153b6
71 changed files with 2075 additions and 2603 deletions
|
@ -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,23 +1,17 @@
|
|||
import {
|
||||
getAnswerRepository, getAssignmentRepository,
|
||||
getClassRepository,
|
||||
getGroupRepository,
|
||||
getQuestionRepository
|
||||
} from '../data/repositories.js';
|
||||
import {mapToLearningObjectID, mapToQuestionDTO, mapToQuestionDTOId} from '../interfaces/question.js';
|
||||
import { getAnswerRepository, getAssignmentRepository, getClassRepository, getGroupRepository, getQuestionRepository } from '../data/repositories.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 {QuestionData, 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 {fetchStudent} from "./students";
|
||||
import {mapToAssignment} from "../interfaces/assignment";
|
||||
import { NotFoundException } from '../exceptions/not-found-exception.js';
|
||||
import {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment";
|
||||
import {FALLBACK_VERSION_NUM} from "../config";
|
||||
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,
|
||||
|
@ -92,14 +86,14 @@ export async function createQuestion(loId: LearningObjectIdentifier, questionDat
|
|||
const author = await fetchStudent(questionData.author!);
|
||||
const content = questionData.content;
|
||||
|
||||
const clazz = await getClassRepository().findById((questionData.inGroup.assignment as AssignmentDTO).class);
|
||||
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 inGroup = await getGroupRepository().findByAssignmentAndGroupNumber(assignment, questionData.inGroup.groupNumber);
|
||||
|
||||
const question = await questionRepository.createQuestion({
|
||||
loId,
|
||||
inGroup,
|
||||
author,
|
||||
inGroup: inGroup!,
|
||||
content,
|
||||
});
|
||||
|
||||
|
|
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 { 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 getSubmission(
|
||||
learningObjectHruid: string,
|
||||
language: Language,
|
||||
version: number,
|
||||
submissionNumber: number
|
||||
): Promise<SubmissionDTO | null> {
|
||||
const loId = new LearningObjectIdentifier(learningObjectHruid, language, version);
|
||||
|
||||
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 = await getExistingGroupFromGroupDTO(submissionDTO.group);
|
||||
|
||||
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.
|
||||
*/
|
||||
|
|
87
backend/src/services/teacher-invitations.ts
Normal file
87
backend/src/services/teacher-invitations.ts
Normal file
|
@ -0,0 +1,87 @@
|
|||
import { fetchTeacher } from './teachers';
|
||||
import { getTeacherInvitationRepository } from '../data/repositories';
|
||||
import { mapToInvitation, mapToTeacherInvitationDTO } from '../interfaces/teacher-invitation';
|
||||
import { addClassTeacher, fetchClass } from './classes';
|
||||
import { TeacherInvitationData, TeacherInvitationDTO } from '@dwengo-1/common/interfaces/teacher-invitation';
|
||||
import { ConflictException } from '../exceptions/conflict-exception';
|
||||
import { NotFoundException } from '../exceptions/not-found-exception';
|
||||
import { TeacherInvitation } from '../entities/classes/teacher-invitation.entity';
|
||||
import { ClassStatus } from '@dwengo-1/common/util/class-join-request';
|
||||
|
||||
export async function getAllInvitations(username: string, sent: boolean): Promise<TeacherInvitationDTO[]> {
|
||||
const teacher = await fetchTeacher(username);
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
|
||||
let invitations;
|
||||
if (sent) {
|
||||
invitations = await teacherInvitationRepository.findAllInvitationsBy(teacher);
|
||||
} else {
|
||||
invitations = await teacherInvitationRepository.findAllInvitationsFor(teacher);
|
||||
}
|
||||
return invitations.map(mapToTeacherInvitationDTO);
|
||||
}
|
||||
|
||||
export async function createInvitation(data: TeacherInvitationData): Promise<TeacherInvitationDTO> {
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
const sender = await fetchTeacher(data.sender);
|
||||
const receiver = await fetchTeacher(data.receiver);
|
||||
|
||||
const cls = await fetchClass(data.class);
|
||||
|
||||
if (!cls.teachers.contains(sender)) {
|
||||
throw new ConflictException('The teacher sending the invite is not part of the class');
|
||||
}
|
||||
|
||||
const newInvitation = mapToInvitation(sender, receiver, cls);
|
||||
await teacherInvitationRepository.save(newInvitation, { preventOverwrite: true });
|
||||
|
||||
return mapToTeacherInvitationDTO(newInvitation);
|
||||
}
|
||||
|
||||
async function fetchInvitation(usernameSender: string, usernameReceiver: string, classId: string): Promise<TeacherInvitation> {
|
||||
const sender = await fetchTeacher(usernameSender);
|
||||
const receiver = await fetchTeacher(usernameReceiver);
|
||||
const cls = await fetchClass(classId);
|
||||
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
const invite = await teacherInvitationRepository.findBy(cls, sender, receiver);
|
||||
|
||||
if (!invite) {
|
||||
throw new NotFoundException('Teacher invite not found');
|
||||
}
|
||||
|
||||
return invite;
|
||||
}
|
||||
|
||||
export async function getInvitation(sender: string, receiver: string, classId: string): Promise<TeacherInvitationDTO> {
|
||||
const invitation = await fetchInvitation(sender, receiver, classId);
|
||||
return mapToTeacherInvitationDTO(invitation);
|
||||
}
|
||||
|
||||
export async function updateInvitation(data: TeacherInvitationData): Promise<TeacherInvitationDTO> {
|
||||
const invitation = await fetchInvitation(data.sender, data.receiver, data.class);
|
||||
invitation.status = ClassStatus.Declined;
|
||||
|
||||
if (data.accepted) {
|
||||
invitation.status = ClassStatus.Accepted;
|
||||
await addClassTeacher(data.class, data.receiver);
|
||||
}
|
||||
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
await teacherInvitationRepository.save(invitation);
|
||||
|
||||
return mapToTeacherInvitationDTO(invitation);
|
||||
}
|
||||
|
||||
export async function deleteInvitation(data: TeacherInvitationData): Promise<TeacherInvitationDTO> {
|
||||
const invitation = await fetchInvitation(data.sender, data.receiver, data.class);
|
||||
|
||||
const sender = await fetchTeacher(data.sender);
|
||||
const receiver = await fetchTeacher(data.receiver);
|
||||
const cls = await fetchClass(data.class);
|
||||
|
||||
const teacherInvitationRepository = getTeacherInvitationRepository();
|
||||
await teacherInvitationRepository.deleteBy(cls, sender, receiver);
|
||||
|
||||
return mapToTeacherInvitationDTO(invitation);
|
||||
}
|
|
@ -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 { ClassStatus } 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 = ClassStatus.Declined;
|
||||
|
||||
if (accepted) {
|
||||
request.status = ClassStatus.Accepted;
|
||||
await addClassStudent(classId, studentUsername);
|
||||
}
|
||||
|
||||
await requestRepo.save(request);
|
||||
|
||||
return mapToStudentRequestDTO(request);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue