From eae4ad39786d8fb53da18942d417622dc34b74f4 Mon Sep 17 00:00:00 2001 From: Gerald Schmittinger Date: Wed, 14 May 2025 09:23:39 +0200 Subject: [PATCH 1/2] feat(backend): Progess voor leerpaden uit de Dwengo API --- .../database-learning-path-provider.ts | 4 +- .../dwengo-api-learning-path-provider.ts | 38 ++++++++++++++++++- .../learning-path-personalization-util.ts | 28 +++++++++++--- 3 files changed, 61 insertions(+), 9 deletions(-) diff --git a/backend/src/services/learning-paths/database-learning-path-provider.ts b/backend/src/services/learning-paths/database-learning-path-provider.ts index fe05dda1..eb980fa1 100644 --- a/backend/src/services/learning-paths/database-learning-path-provider.ts +++ b/backend/src/services/learning-paths/database-learning-path-provider.ts @@ -4,7 +4,7 @@ import { getLearningPathRepository } from '../../data/repositories.js'; import learningObjectService from '../learning-objects/learning-object-service.js'; import { LearningPathNode } from '../../entities/content/learning-path-node.entity.js'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity.js'; -import { getLastSubmissionForGroup, isTransitionPossible } from './learning-path-personalization-util.js'; +import { getLastSubmissionForGroup, idFromLearningPathNode, isTransitionPossible } from './learning-path-personalization-util.js'; import { FilteredLearningObject, LearningObjectNode, @@ -95,7 +95,7 @@ async function convertNode( personalizedFor: Group | undefined, nodesToLearningObjects: Map ): Promise { - const lastSubmission = personalizedFor ? await getLastSubmissionForGroup(node, personalizedFor) : null; + const lastSubmission = personalizedFor ? await getLastSubmissionForGroup(idFromLearningPathNode(node), personalizedFor) : null; const transitions = node.transitions .filter( (trans) => diff --git a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts index 110cd570..461ba7e8 100644 --- a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts +++ b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts @@ -3,11 +3,33 @@ import { DWENGO_API_BASE } from '../../config.js'; import { LearningPathProvider } from './learning-path-provider.js'; import { getLogger, Logger } from '../../logging/initalize.js'; import { LearningPath, LearningPathResponse } from '@dwengo-1/common/interfaces/learning-content'; +import { Group } from '../../entities/assignments/group.entity.js'; +import { getLastSubmissionForGroup, idFromLearningObjectNode } from './learning-path-personalization-util.js'; const logger: Logger = getLogger(); +/** + * Adds progress information to the learning path. Modifies the learning path in-place. + * @param learningPath The learning path to add progress to. + * @param personalizedFor The group whose progress should be shown. + * @returns the modified learning path. + */ +async function addProgressToLearningPath(learningPath: LearningPath, personalizedFor: Group): Promise { + await Promise.all( + learningPath.nodes.map(async node => { + const lastSubmission = personalizedFor ? await getLastSubmissionForGroup(idFromLearningObjectNode(node), personalizedFor) : null + node.done = Boolean(lastSubmission); + }) + ); + + learningPath.num_nodes = learningPath.nodes.length; + learningPath.num_nodes_left = learningPath.nodes.filter(it => !it.done).length; + + return learningPath; +} + const dwengoApiLearningPathProvider: LearningPathProvider = { - async fetchLearningPaths(hruids: string[], language: string, source: string): Promise { + async fetchLearningPaths(hruids: string[], language: string, source: string, personalizedFor: Group): Promise { if (hruids.length === 0) { return { success: false, @@ -32,17 +54,29 @@ const dwengoApiLearningPathProvider: LearningPathProvider = { }; } + await Promise.all( + learningPaths?.map(async it => addProgressToLearningPath(it, personalizedFor)) + ); + return { success: true, source, data: learningPaths, }; }, - async searchLearningPaths(query: string, language: string): Promise { + async searchLearningPaths(query: string, language: string, personalizedFor: Group): Promise { const apiUrl = `${DWENGO_API_BASE}/learningPath/search`; const params = { all: query, language }; const searchResults = await fetchWithLogging(apiUrl, `Search learning paths with query "${query}"`, { params }); + + if (searchResults) { + await Promise.all( + searchResults?.map(async it => addProgressToLearningPath(it, personalizedFor)) + ); + } + + return searchResults ?? []; }, }; diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index a10d5ead..ab1f26bf 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -5,18 +5,36 @@ import { getSubmissionRepository } from '../../data/repositories.js'; import { LearningObjectIdentifier } from '../../entities/content/learning-object-identifier.js'; import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity.js'; import { JSONPath } from 'jsonpath-plus'; +import { LearningObjectNode } from '@dwengo-1/common/interfaces/learning-content'; /** * Returns the last submission for the learning object associated with the given node and for the group */ -export async function getLastSubmissionForGroup(node: LearningPathNode, pathFor: Group): Promise { +export async function getLastSubmissionForGroup(learningObjectId: LearningObjectIdentifier, pathFor: Group): Promise { const submissionRepo = getSubmissionRepository(); - const learningObjectId: LearningObjectIdentifier = { + return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor); +} + +/** + * Creates a LearningObjectIdentifier describing the specified node. + */ +export function idFromLearningObjectNode(node: LearningObjectNode): LearningObjectIdentifier { + return { + hruid: node.learningobject_hruid, + language: node.language, + version: node.version + } +} + +/** + * Creates a LearningObjectIdentifier describing the specified node. + */ +export function idFromLearningPathNode(node: LearningPathNode): LearningObjectIdentifier { + return { hruid: node.learningObjectHruid, language: node.language, - version: node.version, - }; - return await submissionRepo.findMostRecentSubmissionForGroup(learningObjectId, pathFor); + version: node.version + } } /** From 97d3c5c4802dc1495e848683a8190eeb739a6fb7 Mon Sep 17 00:00:00 2001 From: Lint Action Date: Wed, 14 May 2025 07:30:54 +0000 Subject: [PATCH 2/2] style: fix linting issues met Prettier --- .../dwengo-api-learning-path-provider.ts | 15 +++++---------- .../learning-path-personalization-util.ts | 8 ++++---- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts index 461ba7e8..fd5b7f50 100644 --- a/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts +++ b/backend/src/services/learning-paths/dwengo-api-learning-path-provider.ts @@ -16,14 +16,14 @@ const logger: Logger = getLogger(); */ async function addProgressToLearningPath(learningPath: LearningPath, personalizedFor: Group): Promise { await Promise.all( - learningPath.nodes.map(async node => { - const lastSubmission = personalizedFor ? await getLastSubmissionForGroup(idFromLearningObjectNode(node), personalizedFor) : null + learningPath.nodes.map(async (node) => { + const lastSubmission = personalizedFor ? await getLastSubmissionForGroup(idFromLearningObjectNode(node), personalizedFor) : null; node.done = Boolean(lastSubmission); }) ); learningPath.num_nodes = learningPath.nodes.length; - learningPath.num_nodes_left = learningPath.nodes.filter(it => !it.done).length; + learningPath.num_nodes_left = learningPath.nodes.filter((it) => !it.done).length; return learningPath; } @@ -54,9 +54,7 @@ const dwengoApiLearningPathProvider: LearningPathProvider = { }; } - await Promise.all( - learningPaths?.map(async it => addProgressToLearningPath(it, personalizedFor)) - ); + await Promise.all(learningPaths?.map(async (it) => addProgressToLearningPath(it, personalizedFor))); return { success: true, @@ -71,12 +69,9 @@ const dwengoApiLearningPathProvider: LearningPathProvider = { const searchResults = await fetchWithLogging(apiUrl, `Search learning paths with query "${query}"`, { params }); if (searchResults) { - await Promise.all( - searchResults?.map(async it => addProgressToLearningPath(it, personalizedFor)) - ); + await Promise.all(searchResults?.map(async (it) => addProgressToLearningPath(it, personalizedFor))); } - return searchResults ?? []; }, }; diff --git a/backend/src/services/learning-paths/learning-path-personalization-util.ts b/backend/src/services/learning-paths/learning-path-personalization-util.ts index ab1f26bf..7651baa3 100644 --- a/backend/src/services/learning-paths/learning-path-personalization-util.ts +++ b/backend/src/services/learning-paths/learning-path-personalization-util.ts @@ -22,8 +22,8 @@ export function idFromLearningObjectNode(node: LearningObjectNode): LearningObje return { hruid: node.learningobject_hruid, language: node.language, - version: node.version - } + version: node.version, + }; } /** @@ -33,8 +33,8 @@ export function idFromLearningPathNode(node: LearningPathNode): LearningObjectId return { hruid: node.learningObjectHruid, language: node.language, - version: node.version - } + version: node.version, + }; } /**