Merge branch 'dev' into refactor/common

This commit is contained in:
Tibo De Peuter 2025-04-01 21:38:50 +02:00
commit a4408b5bc0
Signed by: tdpeuter
GPG key ID: 38297DE43F75FFE2
145 changed files with 3437 additions and 2822 deletions

View file

@ -3,6 +3,7 @@ import { mapToAssignment, mapToAssignmentDTO, mapToAssignmentDTOId } from '../in
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
import { AssignmentDTO } from 'dwengo-1-common/src/interfaces/assignment';
import { SubmissionDTO, SubmissionDTOId } from 'dwengo-1-common/src/interfaces/submission';
import { getLogger } from '../logging/initalize.js';
export async function getAllAssignments(classid: string, full: boolean): Promise<AssignmentDTO[]> {
const classRepository = getClassRepository();
@ -39,7 +40,7 @@ export async function createAssignment(classid: string, assignmentData: Assignme
return mapToAssignmentDTO(newAssignment);
} catch (e) {
console.error(e);
getLogger().error(e);
return null;
}
}
@ -85,7 +86,7 @@ export async function getAssignmentsSubmissions(
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
const submissionRepository = getSubmissionRepository();
const submissions = (await Promise.all(groups.map((group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
const submissions = (await Promise.all(groups.map(async (group) => submissionRepository.findAllSubmissionsForGroup(group)))).flat();
if (full) {
return submissions.map(mapToSubmissionDTO);

View file

@ -26,11 +26,15 @@ export async function getAllClasses(full: boolean): Promise<ClassDTO[] | string[
export async function createClass(classData: ClassDTO): Promise<ClassDTO | null> {
const teacherRepository = getTeacherRepository();
const teacherUsernames = classData.teachers || [];
const teachers = (await Promise.all(teacherUsernames.map((id) => teacherRepository.findByUsername(id)))).filter((teacher) => teacher !== null);
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((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null);
const students = (await Promise.all(studentUsernames.map(async (id) => studentRepository.findByUsername(id)))).filter(
(student) => student !== null
);
const classRepository = getClassRepository();

View file

@ -10,6 +10,7 @@ import { mapToGroupDTO, mapToGroupDTOId } from '../interfaces/group.js';
import { mapToSubmissionDTO, mapToSubmissionDTOId } from '../interfaces/submission.js';
import { GroupDTO } from 'dwengo-1-common/src/interfaces/group';
import { SubmissionDTO, SubmissionDTOId } from 'dwengo-1-common/src/interfaces/submission';
import { getLogger } from '../logging/initalize.js';
export async function getGroup(classId: string, assignmentNumber: number, groupNumber: number, full: boolean): Promise<GroupDTO | null> {
const classRepository = getClassRepository();
@ -44,9 +45,11 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme
const studentRepository = getStudentRepository();
const memberUsernames = (groupData.members as string[]) || []; // TODO check if groupdata.members is a list
const members = (await Promise.all([...memberUsernames].map((id) => studentRepository.findByUsername(id)))).filter((student) => student !== null);
const members = (await Promise.all([...memberUsernames].map(async (id) => studentRepository.findByUsername(id)))).filter(
(student) => student !== null
);
console.log(members);
getLogger().debug(members);
const classRepository = getClassRepository();
const cls = await classRepository.findById(classid);
@ -72,7 +75,7 @@ export async function createGroup(groupData: GroupDTO, classid: string, assignme
return newGroup;
} catch (e) {
console.log(e);
getLogger().error(e);
return null;
}
}
@ -96,8 +99,7 @@ export async function getAllGroups(classId: string, assignmentNumber: number, fu
const groups = await groupRepository.findAllGroupsForAssignment(assignment);
if (full) {
console.log('full');
console.log(groups);
getLogger().debug({ full: full, groups: groups });
return groups.map(mapToGroupDTO);
}

View file

@ -7,6 +7,7 @@ import {
LearningObjectNode,
LearningPathResponse,
} from 'dwengo-1-common/src/interfaces/learning-content';
import { getLogger } from '../logging/initalize.js';
function filterData(data: LearningObjectMetadata, htmlUrl: string): FilteredLearningObject {
return {
@ -43,7 +44,7 @@ export async function getLearningObjectById(hruid: string, language: string): Pr
);
if (!metadata) {
console.error(`⚠️ WARNING: Learning object "${hruid}" not found.`);
getLogger().error(`⚠️ WARNING: Learning object "${hruid}" not found.`);
return null;
}
@ -54,7 +55,7 @@ export async function getLearningObjectById(hruid: string, language: string): Pr
/**
* Generic function to fetch learning paths
*/
function fetchLearningPaths(arg0: string[], language: string, arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
function fetchLearningPaths(_arg0: string[], _language: string, _arg2: string): LearningPathResponse | PromiseLike<LearningPathResponse> {
throw new Error('Function not implemented.');
}
@ -66,7 +67,7 @@ async function fetchLearningObjects(hruid: string, full: boolean, language: stri
const learningPathResponse: LearningPathResponse = await fetchLearningPaths([hruid], language, `Learning path for HRUID "${hruid}"`);
if (!learningPathResponse.success || !learningPathResponse.data?.length) {
console.error(`⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`);
getLogger().error(`⚠️ WARNING: Learning path "${hruid}" exists but contains no learning objects.`);
return [];
}
@ -80,7 +81,7 @@ async function fetchLearningObjects(hruid: string, full: boolean, language: stri
objects.filter((obj): obj is FilteredLearningObject => obj !== null)
);
} catch (error) {
console.error('❌ Error fetching learning objects:', error);
getLogger().error('❌ Error fetching learning objects:', error);
return [];
}
}

View file

@ -4,7 +4,7 @@ import { Attachment } from '../../entities/content/attachment.entity.js';
import { LearningObjectIdentifier } from 'dwengo-1-common/src/interfaces/learning-content';
const attachmentService = {
getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
async getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
const attachmentRepo = getAttachmentRepository();
if (learningObjectId.version) {

View file

@ -41,10 +41,10 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
};
}
function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
async function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
const learningObjectRepo = getLearningObjectRepository();
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
return learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
}
/**
@ -65,11 +65,11 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
const learningObjectRepo = getLearningObjectRepository();
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language as Language);
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(id.hruid, id.language);
if (!learningObject) {
return null;
}
return await processingService.render(learningObject, (id) => findLearningObjectEntityById(id));
return await processingService.render(learningObject, async (id) => findLearningObjectEntityById(id));
},
/**
@ -96,7 +96,7 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
throw new NotFoundError('The learning path with the given ID could not be found.');
}
const learningObjects = await Promise.all(
learningPath.nodes.map((it) => {
learningPath.nodes.map(async (it) => {
const learningObject = learningObjectService.getLearningObjectById({
hruid: it.learningObjectHruid,
language: it.language,

View file

@ -90,7 +90,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
metadataUrl,
`Metadata for Learning Object HRUID "${id.hruid}" (language ${id.language})`,
{
params: id,
params: { ...id },
}
);
@ -123,7 +123,7 @@ const dwengoApiLearningObjectProvider: LearningObjectProvider = {
async getLearningObjectHTML(id: LearningObjectIdentifier): 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,
params: { ...id },
});
if (!html) {

View file

@ -1,11 +1,11 @@
import dwengoApiLearningObjectProvider from './dwengo-api-learning-object-provider.js';
import { LearningObjectProvider } from './learning-object-provider.js';
import { EnvVars, getEnvVar } from '../../util/envvars.js';
import { envVars, getEnvVar } from '../../util/envVars.js';
import databaseLearningObjectProvider from './database-learning-object-provider.js';
import { FilteredLearningObject, LearningObjectIdentifier, LearningPathIdentifier } from 'dwengo-1-common/src/interfaces/learning-content';
function getProvider(id: LearningObjectIdentifier): LearningObjectProvider {
if (id.hruid.startsWith(getEnvVar(EnvVars.UserContentPrefix))) {
if (id.hruid.startsWith(getEnvVar(envVars.UserContentPrefix))) {
return databaseLearningObjectProvider;
}
return dwengoApiLearningObjectProvider;
@ -18,28 +18,28 @@ const learningObjectService = {
/**
* Fetches a single learning object by its HRUID
*/
getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
async getLearningObjectById(id: LearningObjectIdentifier): Promise<FilteredLearningObject | null> {
return getProvider(id).getLearningObjectById(id);
},
/**
* Fetch full learning object data (metadata)
*/
getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
async getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
return getProvider(id).getLearningObjectsFromPath(id);
},
/**
* Fetch only learning object HRUIDs
*/
getLearningObjectIdsFromPath(id: LearningPathIdentifier): Promise<string[]> {
async getLearningObjectIdsFromPath(id: LearningPathIdentifier): Promise<string[]> {
return getProvider(id).getLearningObjectIdsFromPath(id);
},
/**
* Obtain a HTML-rendering of the learning object with the given identifier (as a string).
*/
getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
async getLearningObjectHTML(id: LearningObjectIdentifier): Promise<string | null> {
return getProvider(id).getLearningObjectHTML(id);
},
};

View file

@ -14,7 +14,7 @@ class AudioProcessor extends StringProcessor {
super(DwengoContentType.AUDIO_MPEG);
}
protected renderFn(audioUrl: string): string {
override renderFn(audioUrl: string): string {
return DOMPurify.sanitize(`<audio controls>
<source src="${audioUrl}" type=${type}>
Your browser does not support the audio element.

View file

@ -15,7 +15,7 @@ class ExternProcessor extends StringProcessor {
super(DwengoContentType.EXTERN);
}
override renderFn(externURL: string) {
override renderFn(externURL: string): string {
if (!isValidHttpUrl(externURL)) {
throw new ProcessingError('The url is not valid: ' + externURL);
}

View file

@ -32,7 +32,7 @@ class GiftProcessor extends StringProcessor {
super(DwengoContentType.GIFT);
}
override renderFn(giftString: string) {
override renderFn(giftString: string): string {
const quizQuestions: GIFTQuestion[] = parse(giftString);
let html = "<div class='learning-object-gift'>\n";

View file

@ -3,7 +3,7 @@ import { Category } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class CategoryQuestionRenderer extends GIFTQuestionRenderer<Category> {
render(question: Category, questionNumber: number): string {
override render(_question: Category, _questionNumber: number): string {
throw new ProcessingError("The question type 'Category' is not supported yet!");
}
}

View file

@ -3,7 +3,7 @@ import { Description } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class DescriptionQuestionRenderer extends GIFTQuestionRenderer<Description> {
render(question: Description, questionNumber: number): string {
override render(_question: Description, _questionNumber: number): string {
throw new ProcessingError("The question type 'Description' is not supported yet!");
}
}

View file

@ -2,7 +2,7 @@ import { GIFTQuestionRenderer } from './gift-question-renderer.js';
import { Essay } from 'gift-pegjs';
export class EssayQuestionRenderer extends GIFTQuestionRenderer<Essay> {
render(question: Essay, questionNumber: number): string {
override render(question: Essay, questionNumber: number): string {
let renderedHtml = '';
if (question.title) {
renderedHtml += `<h2 class='gift-title' id='gift-q${questionNumber}-title'>${question.title}</h2>\n`;

View file

@ -3,7 +3,7 @@ import { Matching } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class MatchingQuestionRenderer extends GIFTQuestionRenderer<Matching> {
render(question: Matching, questionNumber: number): string {
override render(_question: Matching, _questionNumber: number): string {
throw new ProcessingError("The question type 'Matching' is not supported yet!");
}
}

View file

@ -2,7 +2,7 @@ import { GIFTQuestionRenderer } from './gift-question-renderer.js';
import { MultipleChoice } from 'gift-pegjs';
export class MultipleChoiceQuestionRenderer extends GIFTQuestionRenderer<MultipleChoice> {
render(question: MultipleChoice, questionNumber: number): string {
override render(question: MultipleChoice, questionNumber: number): string {
let renderedHtml = '';
if (question.title) {
renderedHtml += `<h2 class='gift-title' id='gift-q${questionNumber}-title'>${question.title}</h2>\n`;

View file

@ -3,7 +3,7 @@ import { Numerical } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class NumericalQuestionRenderer extends GIFTQuestionRenderer<Numerical> {
render(question: Numerical, questionNumber: number): string {
override render(_question: Numerical, _questionNumber: number): string {
throw new ProcessingError("The question type 'Numerical' is not supported yet!");
}
}

View file

@ -3,7 +3,7 @@ import { ShortAnswer } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class ShortQuestionRenderer extends GIFTQuestionRenderer<ShortAnswer> {
render(question: ShortAnswer, questionNumber: number): string {
override render(_question: ShortAnswer, _questionNumber: number): string {
throw new ProcessingError("The question type 'ShortAnswer' is not supported yet!");
}
}

View file

@ -3,7 +3,7 @@ import { TrueFalse } from 'gift-pegjs';
import { ProcessingError } from '../../processing-error.js';
export class TrueFalseQuestionRenderer extends GIFTQuestionRenderer<TrueFalse> {
render(question: TrueFalse, questionNumber: number): string {
override render(_question: TrueFalse, _questionNumber: number): string {
throw new ProcessingError("The question type 'TrueFalse' is not supported yet!");
}
}

View file

@ -10,7 +10,7 @@ class BlockImageProcessor extends InlineImageProcessor {
super();
}
override renderFn(imageUrl: string) {
override renderFn(imageUrl: string): string {
const inlineHtml = super.render(imageUrl);
return DOMPurify.sanitize(`<div>${inlineHtml}</div>`);
}

View file

@ -13,7 +13,7 @@ class InlineImageProcessor extends StringProcessor {
super(contentType);
}
override renderFn(imageUrl: string) {
override renderFn(imageUrl: string): string {
if (!isValidHttpUrl(imageUrl)) {
throw new ProcessingError(`Image URL is invalid: ${imageUrl}`);
}

View file

@ -14,26 +14,24 @@ class MarkdownProcessor extends StringProcessor {
super(DwengoContentType.TEXT_MARKDOWN);
}
override renderFn(mdText: string) {
let html = '';
try {
marked.use({ renderer: dwengoMarkedRenderer });
html = marked(mdText, { async: false });
html = this.replaceLinks(html); // Replace html image links path
} catch (e: any) {
throw new ProcessingError(e.message);
}
return html;
}
replaceLinks(html: string) {
static replaceLinks(html: string): string {
const proc = new InlineImageProcessor();
html = html.replace(
/<img.*?src="(.*?)".*?(alt="(.*?)")?.*?(title="(.*?)")?.*?>/g,
(match: string, src: string, alt: string, altText: string, title: string, titleText: string) => proc.render(src)
(_match: string, src: string, _alt: string, _altText: string, _title: string, _titleText: string) => proc.render(src)
);
return html;
}
override renderFn(mdText: string): string {
try {
marked.use({ renderer: dwengoMarkedRenderer });
const html = marked(mdText, { async: false });
return MarkdownProcessor.replaceLinks(html); // Replace html image links path
} catch (e: unknown) {
throw new ProcessingError('Unknown error while processing markdown: ' + e);
}
}
}
export { MarkdownProcessor };

View file

@ -15,7 +15,7 @@ class PdfProcessor extends StringProcessor {
super(DwengoContentType.APPLICATION_PDF);
}
override renderFn(pdfUrl: string) {
override renderFn(pdfUrl: string): string {
if (!isValidHttpUrl(pdfUrl)) {
throw new ProcessingError(`PDF URL is invalid: ${pdfUrl}`);
}

View file

@ -21,7 +21,7 @@ const EMBEDDED_LEARNING_OBJECT_PLACEHOLDER = /<learning-object hruid="([^"]+)" l
const LEARNING_OBJECT_DOES_NOT_EXIST = "<div class='non-existing-learning-object' />";
class ProcessingService {
private processors!: Map<DwengoContentType, Processor<any>>;
private processors!: Map<DwengoContentType, Processor<DwengoContentType>>;
constructor() {
const processors = [

View file

@ -9,7 +9,9 @@ import { DwengoContentType } from './content-type.js';
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/processor.js
*/
abstract class Processor<T> {
protected constructor(public contentType: DwengoContentType) {}
protected constructor(public contentType: DwengoContentType) {
// Do nothing
}
/**
* Render the given object.

View file

@ -11,7 +11,7 @@ class TextProcessor extends StringProcessor {
super(DwengoContentType.TEXT_PLAIN);
}
override renderFn(text: string) {
override renderFn(text: string): string {
// Sanitize plain text to prevent xss.
return DOMPurify.sanitize(text);
}

View file

@ -24,14 +24,14 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise<Ma
// Its corresponding learning object.
const nullableNodesToLearningObjects = new Map<LearningPathNode, FilteredLearningObject | null>(
await Promise.all(
nodes.map((node) =>
nodes.map(async (node) =>
learningObjectService
.getLearningObjectById({
hruid: node.learningObjectHruid,
version: node.version,
language: node.language,
})
.then((learningObject) => <[LearningPathNode, FilteredLearningObject | null]>[node, learningObject])
.then((learningObject) => [node, learningObject] as [LearningPathNode, FilteredLearningObject | null])
)
)
);
@ -123,7 +123,7 @@ async function convertNodes(
nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject>,
personalizedFor?: PersonalizationTarget
): Promise<LearningObjectNode[]> {
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map((entry) =>
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map(async (entry) =>
convertNode(entry[0], entry[1], personalizedFor, nodesToLearningObjects)
);
return await Promise.all(nodesPromise);
@ -158,7 +158,7 @@ function convertTransition(
throw new Error(`Learning object ${transition.next.learningObjectHruid}/${transition.next.language}/${transition.next.version} not found!`);
} else {
return {
_id: '' + index, // Retained for backwards compatibility. The index uniquely identifies the transition within the learning path.
_id: String(index), // Retained for backwards compatibility. The index uniquely identifies the transition within the learning path.
default: false, // We don't work with default transitions but retain this for backwards compatibility.
next: {
_id: nextNode._id + index, // Construct a unique ID for the transition for backwards compatibility.
@ -185,11 +185,11 @@ const databaseLearningPathProvider: LearningPathProvider = {
): Promise<LearningPathResponse> {
const learningPathRepo = getLearningPathRepository();
const learningPaths = (await Promise.all(hruids.map((hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)))).filter(
const learningPaths = (await Promise.all(hruids.map(async (hruid) => learningPathRepo.findByHruidAndLanguage(hruid, language)))).filter(
(learningPath) => learningPath !== null
);
const filteredLearningPaths = await Promise.all(
learningPaths.map((learningPath, index) => convertLearningPath(learningPath, index, personalizedFor))
learningPaths.map(async (learningPath, index) => convertLearningPath(learningPath, index, personalizedFor))
);
return {
@ -206,7 +206,7 @@ const databaseLearningPathProvider: LearningPathProvider = {
const learningPathRepo = getLearningPathRepository();
const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language);
return await Promise.all(searchResults.map((result, index) => convertLearningPath(result, index, personalizedFor)));
return await Promise.all(searchResults.map(async (result, index) => convertLearningPath(result, index, personalizedFor)));
},
};

View file

@ -1,11 +1,11 @@
import dwengoApiLearningPathProvider from './dwengo-api-learning-path-provider.js';
import databaseLearningPathProvider from './database-learning-path-provider.js';
import { EnvVars, getEnvVar } from '../../util/envvars.js';
import { envVars, getEnvVar } from '../../util/envVars.js';
import { PersonalizationTarget } from './learning-path-personalization-util.js';
import { LearningPath, LearningPathResponse } from 'dwengo-1-common/src/interfaces/learning-content';
import { Language } from 'dwengo-1-common/src/util/language.js';
const userContentPrefix = getEnvVar(EnvVars.UserContentPrefix);
const userContentPrefix = getEnvVar(envVars.UserContentPrefix);
const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider];
/**
@ -49,7 +49,9 @@ const learningPathService = {
* Search learning paths in the data source using the given search string.
*/
async searchLearningPaths(query: string, language: Language, personalizedFor?: PersonalizationTarget): Promise<LearningPath[]> {
const providerResponses = await Promise.all(allProviders.map((provider) => provider.searchLearningPaths(query, language, personalizedFor)));
const providerResponses = await Promise.all(
allProviders.map(async (provider) => provider.searchLearningPaths(query, language, personalizedFor))
);
return providerResponses.flat();
},
};

View file

@ -5,10 +5,9 @@ import { Answer } from '../entities/questions/answer.entity.js';
import { mapToAnswerDTO, mapToAnswerId } from '../interfaces/answer.js';
import { QuestionRepository } from '../data/questions/question-repository.js';
import { LearningObjectIdentifier } from '../entities/content/learning-object-identifier.js';
import { mapToUser } from '../interfaces/user.js';
import { Student } from '../entities/users/student.entity.js';
import { mapToStudent } from '../interfaces/student.js';
import { QuestionDTO, QuestionId } from 'dwengo-1-common/src/interfaces/question';
import { AnswerDTO, AnswerId } from 'dwengo-1-common/src/interfaces/answer';
export async function getAllQuestions(id: LearningObjectIdentifier, full: boolean): Promise<QuestionDTO[] | QuestionId[]> {
const questionRepository: QuestionRepository = getQuestionRepository();
@ -48,7 +47,7 @@ export async function getQuestion(questionId: QuestionId): Promise<QuestionDTO |
return mapToQuestionDTO(question);
}
export async function getAnswersByQuestion(questionId: QuestionId, full: boolean) {
export async function getAnswersByQuestion(questionId: QuestionId, full: boolean): Promise<AnswerDTO[] | AnswerId[]> {
const answerRepository = getAnswerRepository();
const question = await fetchQuestion(questionId);
@ -71,7 +70,7 @@ export async function getAnswersByQuestion(questionId: QuestionId, full: boolean
return answersDTO.map(mapToAnswerId);
}
export async function createQuestion(questionDTO: QuestionDTO) {
export async function createQuestion(questionDTO: QuestionDTO): Promise<QuestionDTO | null> {
const questionRepository = getQuestionRepository();
const author = mapToStudent(questionDTO.author);
@ -82,14 +81,14 @@ export async function createQuestion(questionDTO: QuestionDTO) {
author,
content: questionDTO.content,
});
} catch (e) {
} catch (_) {
return null;
}
return questionDTO;
}
export async function deleteQuestion(questionId: QuestionId) {
export async function deleteQuestion(questionId: QuestionId): Promise<QuestionDTO | null> {
const questionRepository = getQuestionRepository();
const question = await fetchQuestion(questionId);
@ -100,7 +99,7 @@ export async function deleteQuestion(questionId: QuestionId) {
try {
await questionRepository.removeQuestionByLearningObjectAndSequenceNumber(questionId.learningObjectIdentifier, questionId.sequenceNumber);
} catch (e) {
} catch (_) {
return null;
}

View file

@ -9,6 +9,7 @@ import { ClassDTO } from 'dwengo-1-common/src/interfaces/class';
import { GroupDTO } from 'dwengo-1-common/src/interfaces/group';
import { SubmissionDTO, SubmissionDTOId } from 'dwengo-1-common/src/interfaces/submission';
import { StudentDTO } from 'dwengo-1-common/src/interfaces/student';
import { getLogger } from '../logging/initalize.js';
export async function getAllStudents(full: boolean): Promise<StudentDTO[] | string[]> {
const studentRepository = getStudentRepository();
@ -49,7 +50,7 @@ export async function deleteStudent(username: string): Promise<StudentDTO | null
return mapToStudentDTO(user);
} catch (e) {
console.log(e);
getLogger().error(e);
return null;
}
}

View file

@ -22,21 +22,26 @@ export async function getSubmission(
return mapToSubmissionDTO(submission);
}
export async function createSubmission(submissionDTO: SubmissionDTO) {
export async function createSubmission(submissionDTO: SubmissionDTO): Promise<SubmissionDTO | null> {
const submissionRepository = getSubmissionRepository();
const submission = mapToSubmission(submissionDTO);
try {
const newSubmission = await submissionRepository.create(submission);
const newSubmission = submissionRepository.create(submission);
await submissionRepository.save(newSubmission);
} catch (e) {
} catch (_) {
return null;
}
return mapToSubmissionDTO(submission);
}
export async function deleteSubmission(learningObjectHruid: string, language: Language, version: number, submissionNumber: number) {
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);

View file

@ -7,6 +7,7 @@ import { ClassDTO } from 'dwengo-1-common/src/interfaces/class';
import { TeacherDTO } from 'dwengo-1-common/src/interfaces/teacher';
import { StudentDTO } from 'dwengo-1-common/src/interfaces/student';
import { QuestionDTO, QuestionId } from 'dwengo-1-common/src/interfaces/question';
import { getLogger } from '../logging/initalize.js';
export async function getAllTeachers(full: boolean): Promise<TeacherDTO[] | string[]> {
const teacherRepository = getTeacherRepository();
@ -48,7 +49,7 @@ export async function deleteTeacher(username: string): Promise<TeacherDTO | null
return mapToTeacherDTO(user);
} catch (e) {
console.log(e);
getLogger().error(e);
return null;
}
}