refactor(backend): Streamlining van de testdata voor leerpaden en leerobjecten + integratie in seed
Hierbij ook testdata functionaliteit toegevoegd om makkelijk nieuwe leerpaden aan te maken.
This commit is contained in:
parent
4092f1f617
commit
202cf4e33c
32 changed files with 691 additions and 493 deletions
|
@ -1,6 +1,10 @@
|
|||
import { DwengoEntityRepository } from '../dwengo-entity-repository.js';
|
||||
import { LearningPath } from '../../entities/content/learning-path.entity.js';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
import {LearningPathNode} from "../../entities/content/learning-path-node.entity";
|
||||
import {RequiredEntityData} from "@mikro-orm/core";
|
||||
import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity";
|
||||
import {EntityAlreadyExistsException} from "../../exceptions/entity-already-exists-exception";
|
||||
|
||||
export class LearningPathRepository extends DwengoEntityRepository<LearningPath> {
|
||||
public async findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> {
|
||||
|
@ -23,4 +27,33 @@ export class LearningPathRepository extends DwengoEntityRepository<LearningPath>
|
|||
populate: ['nodes', 'nodes.transitions'],
|
||||
});
|
||||
}
|
||||
|
||||
public createNode(
|
||||
nodeData: RequiredEntityData<LearningPathNode, never, false>
|
||||
): LearningPathNode {
|
||||
return this.em.create(LearningPathNode, nodeData);
|
||||
}
|
||||
|
||||
public createTransition(
|
||||
transitionData: RequiredEntityData<LearningPathTransition, never, false>
|
||||
): LearningPathTransition {
|
||||
return this.em.create(LearningPathTransition, transitionData)
|
||||
}
|
||||
|
||||
public async saveLearningPathNodesAndTransitions(
|
||||
path: LearningPath,
|
||||
nodes: LearningPathNode[],
|
||||
transitions: LearningPathTransition[],
|
||||
options?: {preventOverwrite?: boolean}
|
||||
): Promise<void> {
|
||||
if (options?.preventOverwrite && (await this.findOne(path))) {
|
||||
throw new EntityAlreadyExistsException(
|
||||
"A learning path with this hruid/language combination already exists."
|
||||
);
|
||||
}
|
||||
const em = this.getEntityManager();
|
||||
await em.persistAndFlush(path);
|
||||
await Promise.all(nodes.map(it => em.persistAndFlush(it)));
|
||||
await Promise.all(transitions.map(it => em.persistAndFlush(it)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property, Rel } from '@mikro-orm/core';
|
||||
import {Collection, Entity, Enum, ManyToOne, OneToMany, PrimaryKey, Property, Rel} from '@mikro-orm/core';
|
||||
import { LearningPath } from './learning-path.entity.js';
|
||||
import { LearningPathTransition } from './learning-path-transition.entity.js';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
|
@ -27,7 +27,7 @@ export class LearningPathNode {
|
|||
startNode!: boolean;
|
||||
|
||||
@OneToMany({ entity: () => LearningPathTransition, mappedBy: 'node' })
|
||||
transitions: LearningPathTransition[] = [];
|
||||
transitions: Collection<LearningPathTransition> = new Collection<LearningPathTransition>(this);
|
||||
|
||||
@Property({ length: 3 })
|
||||
createdAt: Date = new Date();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Entity, Enum, ManyToMany, OneToMany, PrimaryKey, Property } from '@mikro-orm/core';
|
||||
import {Collection, Entity, Enum, ManyToMany, OneToMany, PrimaryKey, Property} from '@mikro-orm/core';
|
||||
import { Teacher } from '../users/teacher.entity.js';
|
||||
import { LearningPathRepository } from '../../data/content/learning-path-repository.js';
|
||||
import { LearningPathNode } from './learning-path-node.entity.js';
|
||||
|
@ -25,5 +25,5 @@ export class LearningPath {
|
|||
image: Buffer | null = null;
|
||||
|
||||
@OneToMany({ entity: () => LearningPathNode, mappedBy: 'learningPath' })
|
||||
nodes: LearningPathNode[] = [];
|
||||
nodes: Collection<LearningPathNode> = new Collection<LearningPathNode>(this);
|
||||
}
|
||||
|
|
|
@ -14,13 +14,14 @@ import {
|
|||
} from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
import {Group} from "../../entities/assignments/group.entity";
|
||||
import {Collection} from "@mikro-orm/core";
|
||||
|
||||
/**
|
||||
* Fetches the corresponding learning object for each of the nodes and creates a map that maps each node to its
|
||||
* corresponding learning object.
|
||||
* @param nodes The nodes to find the learning object for.
|
||||
*/
|
||||
async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise<Map<LearningPathNode, FilteredLearningObject>> {
|
||||
async function getLearningObjectsForNodes(nodes: Collection<LearningPathNode>): Promise<Map<LearningPathNode, FilteredLearningObject>> {
|
||||
// Fetching the corresponding learning object for each of the nodes and creating a map that maps each node to
|
||||
// Its corresponding learning object.
|
||||
const nullableNodesToLearningObjects = new Map<LearningPathNode, FilteredLearningObject | null>(
|
||||
|
@ -208,7 +209,7 @@ const databaseLearningPathProvider: LearningPathProvider = {
|
|||
|
||||
const searchResults = await learningPathRepo.findByQueryStringAndLanguage(query, language);
|
||||
return await Promise.all(searchResults.map(async (result, index) => convertLearningPath(result, index, personalizedFor)));
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export default databaseLearningPathProvider;
|
||||
|
|
|
@ -1,13 +1,79 @@
|
|||
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 { LearningPath, LearningPathResponse } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import {LearningObjectNode, LearningPath, LearningPathResponse} from '@dwengo-1/common/interfaces/learning-content';
|
||||
import { Language } from '@dwengo-1/common/util/language';
|
||||
import {Group} from "../../entities/assignments/group.entity";
|
||||
import {LearningPath as LearningPathEntity} from "../../entities/content/learning-path.entity";
|
||||
import {getLearningPathRepository} from "../../data/repositories";
|
||||
import {LearningPathNode} from "../../entities/content/learning-path-node.entity";
|
||||
import {LearningPathTransition} from "../../entities/content/learning-path-transition.entity";
|
||||
import {base64ToArrayBuffer} from "../../util/base64-buffer-conversion";
|
||||
import {TeacherDTO} from "@dwengo-1/common/interfaces/teacher";
|
||||
import {mapToTeacher} from "../../interfaces/teacher";
|
||||
import {Collection} from "@mikro-orm/core";
|
||||
|
||||
const userContentPrefix = getEnvVar(envVars.UserContentPrefix);
|
||||
const allProviders = [dwengoApiLearningPathProvider, databaseLearningPathProvider];
|
||||
|
||||
export function mapToLearningPath(
|
||||
dto: LearningPath, adminsDto: TeacherDTO[]
|
||||
): LearningPathEntity {
|
||||
const admins = adminsDto.map(admin => mapToTeacher(admin));
|
||||
const repo = getLearningPathRepository();
|
||||
const path = repo.create({
|
||||
hruid: dto.hruid,
|
||||
language: dto.language as Language,
|
||||
description: dto.description,
|
||||
title: dto.title,
|
||||
admins,
|
||||
image: dto.image ? Buffer.from(base64ToArrayBuffer(dto.image)) : null
|
||||
});
|
||||
const nodes = dto.nodes.map((nodeDto: LearningObjectNode, i: number) =>
|
||||
repo.createNode({
|
||||
learningPath: path,
|
||||
learningObjectHruid: nodeDto.learningobject_hruid,
|
||||
language: nodeDto.language,
|
||||
version: nodeDto.version,
|
||||
startNode: nodeDto.start_node ?? false,
|
||||
nodeNumber: i,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date()
|
||||
})
|
||||
);
|
||||
dto.nodes.forEach(nodeDto => {
|
||||
const fromNode = nodes.find(it =>
|
||||
it.learningObjectHruid === nodeDto.learningobject_hruid
|
||||
&& it.language === nodeDto.language
|
||||
&& it.version === nodeDto.version
|
||||
)!;
|
||||
const transitions = nodeDto.transitions.map((transDto, i) => {
|
||||
const toNode = nodes.find(it =>
|
||||
it.learningObjectHruid === transDto.next.hruid
|
||||
&& it.language === transDto.next.language
|
||||
&& it.version === transDto.next.version
|
||||
);
|
||||
|
||||
if (toNode) {
|
||||
return repo.createTransition({
|
||||
transitionNumber: i,
|
||||
node: fromNode,
|
||||
next: toNode,
|
||||
condition: transDto.condition ?? "true"
|
||||
});
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}).filter(it => it).map(it => it!);
|
||||
|
||||
fromNode.transitions = new Collection<LearningPathTransition>(transitions);
|
||||
});
|
||||
|
||||
path.nodes = new Collection<LearningPathNode>(nodes);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service providing access to data about learning paths from the appropriate data source (database or Dwengo-api)
|
||||
*/
|
||||
|
@ -54,6 +120,17 @@ const learningPathService = {
|
|||
);
|
||||
return providerResponses.flat();
|
||||
},
|
||||
|
||||
/**
|
||||
* Add a new learning path to the database.
|
||||
* @param dto Learning path DTO from which the learning path will be created.
|
||||
* @param admins Teachers who should become an admin of the learning path.
|
||||
*/
|
||||
async createNewLearningPath(dto: LearningPath, admins: TeacherDTO[]): Promise<void> {
|
||||
const repo = getLearningPathRepository();
|
||||
const path = mapToLearningPath(dto, admins);
|
||||
await repo.save(path, {preventOverwrite: true})
|
||||
}
|
||||
};
|
||||
|
||||
export default learningPathService;
|
||||
|
|
12
backend/src/util/base64-buffer-conversion.ts
Normal file
12
backend/src/util/base64-buffer-conversion.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Convert a Base64-encoded string into a buffer with the same data.
|
||||
* @param base64 The Base64 encoded string.
|
||||
*/
|
||||
export function base64ToArrayBuffer(base64: string) {
|
||||
var binaryString = atob(base64);
|
||||
var bytes = new Uint8Array(binaryString.length);
|
||||
for (var i = 0; i < binaryString.length; i++) {
|
||||
bytes[i] = binaryString.charCodeAt(i);
|
||||
}
|
||||
return bytes.buffer;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue