fix(backend): Foute entity-structuur van leerpaden verbeterd.
Ook testen geschreven voor LearningPathRepository en LearningObjectRepository.
This commit is contained in:
parent
4d999c78ba
commit
1417907933
24 changed files with 474 additions and 64 deletions
|
@ -24,5 +24,4 @@ export class LearningObjectRepository extends DwengoEntityRepository<LearningObj
|
|||
}
|
||||
});
|
||||
}
|
||||
// This repository is read-only for now since creating own learning object is an extension feature.
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||
import { LearningPath } from '../../entities/content/learning-path.entity.js';
|
||||
import { Language } from '../../entities/content/language.js';
|
||||
import {DwengoEntityRepository} from '../dwengo-entity-repository.js';
|
||||
import {LearningPath} from '../../entities/content/learning-path.entity.js';
|
||||
import {Language} from '../../entities/content/language.js';
|
||||
|
||||
export class LearningPathRepository extends DwengoEntityRepository<LearningPath> {
|
||||
public findByHruidAndLanguage(
|
||||
|
@ -17,7 +17,7 @@ export class LearningPathRepository extends DwengoEntityRepository<LearningPath>
|
|||
* @param query The query string we want to seach for in the title or description.
|
||||
* @param language The language of the learning paths we want to find.
|
||||
*/
|
||||
public findByQueryStringAndLanguage(query: string, language: Language): Promise<LearningPath[]> {
|
||||
public async findByQueryStringAndLanguage(query: string, language: Language): Promise<LearningPath[]> {
|
||||
return this.findAll({
|
||||
where: {
|
||||
language: language,
|
||||
|
|
|
@ -45,7 +45,7 @@ export class LearningObject {
|
|||
keywords: string[] = [];
|
||||
|
||||
@Property({ type: 'array', nullable: true })
|
||||
targetAges?: number[];
|
||||
targetAges?: number[] = [];
|
||||
|
||||
@Property({ type: 'bool' })
|
||||
teacherExclusive: boolean = false;
|
||||
|
|
37
backend/src/entities/content/learning-path-node.entity.ts
Normal file
37
backend/src/entities/content/learning-path-node.entity.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
import {Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property} from "@mikro-orm/core";
|
||||
import {Language} from "./language";
|
||||
import {LearningPath} from "./learning-path.entity";
|
||||
import {LearningPathTransition} from "./learning-path-transition.entity";
|
||||
|
||||
@Entity()
|
||||
export class LearningPathNode {
|
||||
@ManyToOne({ entity: () => LearningPath, primary: true })
|
||||
learningPath!: LearningPath;
|
||||
|
||||
@PrimaryKey({ type: "numeric", autoincrement: true })
|
||||
nodeNumber!: number;
|
||||
|
||||
@Property({ type: 'string' })
|
||||
learningObjectHruid!: string;
|
||||
|
||||
@Enum({ items: () => Language })
|
||||
language!: Language;
|
||||
|
||||
@Property({ type: 'number' })
|
||||
version!: number;
|
||||
|
||||
@Property({ type: 'text', nullable: true })
|
||||
instruction?: string;
|
||||
|
||||
@Property({ type: 'bool' })
|
||||
startNode!: boolean;
|
||||
|
||||
@OneToMany({ entity: () => LearningPathTransition, mappedBy: "node" })
|
||||
transitions: LearningPathTransition[] = [];
|
||||
|
||||
@Property({ length: 3 })
|
||||
createdAt: Date = new Date();
|
||||
|
||||
@Property({ length: 3, onUpdate: () => new Date() })
|
||||
updatedAt: Date = new Date();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import {Entity, ManyToOne, PrimaryKey, Property} from "@mikro-orm/core";
|
||||
import {LearningPathNode} from "./learning-path-node.entity";
|
||||
|
||||
@Entity()
|
||||
export class LearningPathTransition {
|
||||
@ManyToOne({entity: () => LearningPathNode })
|
||||
node!: LearningPathNode;
|
||||
|
||||
@PrimaryKey({ type: 'numeric' })
|
||||
transitionNumber!: number;
|
||||
|
||||
@Property({ type: 'string' })
|
||||
condition!: string;
|
||||
|
||||
@ManyToOne({ entity: () => LearningPathNode })
|
||||
next!: LearningPathNode;
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
import {
|
||||
Embeddable,
|
||||
Embedded,
|
||||
Entity,
|
||||
Enum,
|
||||
ManyToMany,
|
||||
OneToOne,
|
||||
ManyToMany, OneToMany,
|
||||
PrimaryKey,
|
||||
Property,
|
||||
} from '@mikro-orm/core';
|
||||
import { Language } from './language.js';
|
||||
import { Teacher } from '../users/teacher.entity.js';
|
||||
import {LearningPathRepository} from "../../data/content/learning-path-repository";
|
||||
import {LearningPathNode} from "./learning-path-node.entity";
|
||||
|
||||
@Entity({repository: () => LearningPathRepository})
|
||||
export class LearningPath {
|
||||
|
@ -29,45 +27,9 @@ export class LearningPath {
|
|||
@Property({ type: 'text' })
|
||||
description!: string;
|
||||
|
||||
@Property({ type: 'blob' })
|
||||
image!: string;
|
||||
@Property({ type: 'blob', nullable: true })
|
||||
image: Buffer | null = null;
|
||||
|
||||
@Embedded({ entity: () => LearningPathNode, array: true })
|
||||
@OneToMany({ entity: () => LearningPathNode, mappedBy: "learningPath" })
|
||||
nodes: LearningPathNode[] = [];
|
||||
}
|
||||
|
||||
@Embeddable()
|
||||
export class LearningPathNode {
|
||||
@Property({ type: 'string' })
|
||||
learningObjectHruid!: string;
|
||||
|
||||
@Enum({ items: () => Language })
|
||||
language!: Language;
|
||||
|
||||
@Property({ type: 'number' })
|
||||
version!: number;
|
||||
|
||||
@Property({ type: 'longtext' })
|
||||
instruction!: string;
|
||||
|
||||
@Property({ type: 'bool' })
|
||||
startNode!: boolean;
|
||||
|
||||
@Embedded({ entity: () => LearningPathTransition, array: true })
|
||||
transitions!: LearningPathTransition[];
|
||||
|
||||
@Property({ length: 3 })
|
||||
createdAt: Date = new Date();
|
||||
|
||||
@Property({ length: 3, onUpdate: () => new Date() })
|
||||
updatedAt: Date = new Date();
|
||||
}
|
||||
|
||||
@Embeddable()
|
||||
export class LearningPathTransition {
|
||||
@Property({ type: 'string' })
|
||||
condition!: string;
|
||||
|
||||
@OneToOne({ entity: () => LearningPathNode })
|
||||
next!: LearningPathNode;
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@ import {getAttachmentRepository} from "../../data/repositories";
|
|||
import {Attachment} from "../../entities/content/attachment.entity";
|
||||
import {LearningObjectIdentifier} from "../../interfaces/learning-content";
|
||||
|
||||
const attachmentRepo = getAttachmentRepository();
|
||||
|
||||
const attachmentService = {
|
||||
getAttachment(learningObjectId: LearningObjectIdentifier, attachmentName: string): Promise<Attachment | null> {
|
||||
const attachmentRepo = getAttachmentRepository();
|
||||
|
||||
if (learningObjectId.version) {
|
||||
return attachmentRepo.findByLearningObjectIdAndName({
|
||||
hruid: learningObjectId.hruid,
|
||||
|
|
|
@ -11,8 +11,6 @@ import {getUrlStringForLearningObject} from "../../util/links";
|
|||
import processingService from "./processing/processing-service";
|
||||
import {NotFoundError} from "@mikro-orm/core";
|
||||
|
||||
const learningObjectRepo = getLearningObjectRepository();
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
function convertLearningObject(learningObject: LearningObject | null): FilteredLearningObject | null {
|
||||
if (!learningObject) {
|
||||
|
@ -45,6 +43,8 @@ function convertLearningObject(learningObject: LearningObject | null): FilteredL
|
|||
}
|
||||
|
||||
function findLearningObjectEntityById(id: LearningObjectIdentifier): Promise<LearningObject | null> {
|
||||
const learningObjectRepo = getLearningObjectRepository();
|
||||
|
||||
return learningObjectRepo.findLatestByHruidAndLanguage(
|
||||
id.hruid, id.language as Language
|
||||
);
|
||||
|
@ -66,6 +66,8 @@ 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> {
|
||||
const learningObjectRepo = getLearningObjectRepository();
|
||||
|
||||
const learningObject = await learningObjectRepo.findLatestByHruidAndLanguage(
|
||||
id.hruid, id.language as Language
|
||||
);
|
||||
|
@ -82,6 +84,8 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
|||
* Fetch the HRUIDs of all learning objects on this path.
|
||||
*/
|
||||
async getLearningObjectIdsFromPath(id: LearningPathIdentifier): Promise<string[]> {
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language);
|
||||
if (!learningPath) {
|
||||
throw new NotFoundError("The learning path with the given ID could not be found.");
|
||||
|
@ -93,6 +97,8 @@ const databaseLearningObjectProvider: LearningObjectProvider = {
|
|||
* Fetch the full metadata of all learning objects on this path.
|
||||
*/
|
||||
async getLearningObjectsFromPath(id: LearningPathIdentifier): Promise<FilteredLearningObject[]> {
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
const learningPath = await learningPathRepo.findByHruidAndLanguage(id.hruid, id.language);
|
||||
if (!learningPath) {
|
||||
throw new NotFoundError("The learning path with the given ID could not be found.");
|
||||
|
|
|
@ -1,15 +1,20 @@
|
|||
/**
|
||||
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/learing_object_markdown_renderer.js [sic!]
|
||||
*/
|
||||
import PdfProcessor from "../pdf/pdf-processor.js";
|
||||
import AudioProcessor from "../audio/audio-processor.js";
|
||||
import ExternProcessor from "../extern/extern-processor.js";
|
||||
import InlineImageProcessor from "../image/inline-image-processor.js";
|
||||
import {RendererObject, Tokens} from "marked";
|
||||
import * as marked from "marked";
|
||||
import {getUrlStringForLearningObjectHTML, isValidHttpUrl} from "../../../../util/links";
|
||||
import {ProcessingError} from "../processing-error";
|
||||
import {LearningObjectIdentifier} from "../../../../interfaces/learning-content";
|
||||
import {Language} from "../../../../entities/content/language";
|
||||
import Image = Tokens.Image;
|
||||
import Heading = Tokens.Heading;
|
||||
import Link = Tokens.Link;
|
||||
|
||||
import Image = marked.Tokens.Image;
|
||||
import Heading = marked.Tokens.Heading;
|
||||
import Link = marked.Tokens.Link;
|
||||
import RendererObject = marked.RendererObject;
|
||||
|
||||
const prefixes = {
|
||||
learningObject: '@learning-object',
|
|
@ -1,6 +1,5 @@
|
|||
/**
|
||||
* Based on https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/markdown_processor.js
|
||||
* and https://github.com/dwengovzw/Learning-Object-Repository/blob/main/app/processors/markdown/learing_object_markdown_renderer.js [sic!]
|
||||
*/
|
||||
|
||||
import {marked} from 'marked'
|
||||
|
@ -8,7 +7,7 @@ import Processor from '../processor.js';
|
|||
import InlineImageProcessor from '../image/inline-image-processor.js';
|
||||
import {DwengoContentType} from "../content-type";
|
||||
import {ProcessingError} from "../processing-error";
|
||||
import dwengoMarkedRenderer from "./learning-object-markdown-renderer";
|
||||
import dwengoMarkedRenderer from "./dwengo-marked-renderer";
|
||||
|
||||
class MarkdownProcessor extends Processor<string> {
|
||||
constructor() {
|
||||
|
|
|
@ -35,9 +35,9 @@ class ProcessingService {
|
|||
new GiftProcessor()
|
||||
];
|
||||
|
||||
processors.forEach(processor => {
|
||||
this.processors.set(processor.contentType, processor);
|
||||
});
|
||||
this.processors = new Map(
|
||||
processors.map(processor => [processor.contentType, processor])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,8 +15,6 @@ import {getLearningPathRepository} from "../../data/repositories";
|
|||
import {Language} from "../../entities/content/language";
|
||||
import learningObjectService from "../learning-objects/learning-object-service";
|
||||
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
/**
|
||||
* Fetches the corresponding learning object for each of the nodes and creates a map that maps each node to its
|
||||
* corresponding learning object.
|
||||
|
@ -138,6 +136,8 @@ const databaseLearningPathProvider: LearningPathProvider = {
|
|||
* Fetch the learning paths with the given hruids from the database.
|
||||
*/
|
||||
async fetchLearningPaths(hruids: string[], language: Language, source: string): Promise<LearningPathResponse> {
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
const learningPaths = await Promise.all(
|
||||
hruids.map(hruid => learningPathRepo.findByHruidAndLanguage(hruid, language))
|
||||
);
|
||||
|
@ -158,6 +158,8 @@ const databaseLearningPathProvider: LearningPathProvider = {
|
|||
* Search learning paths in the database using the given search string.
|
||||
*/
|
||||
async searchLearningPaths(query: string, language: Language): Promise<LearningPath[]> {
|
||||
const learningPathRepo = getLearningPathRepository();
|
||||
|
||||
const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language);
|
||||
return await Promise.all(
|
||||
searchResults.map((result, index) =>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue