Merge branch 'dev' into chore/development-workflow
This commit is contained in:
commit
8389414ea4
31 changed files with 1907 additions and 124 deletions
|
@ -8,7 +8,7 @@ interface Translations {
|
|||
};
|
||||
}
|
||||
|
||||
export function getThemes(req: Request, res: Response) {
|
||||
export function getThemesHandler(req: Request, res: Response) {
|
||||
const language = (req.query.language as string)?.toLowerCase() || 'nl';
|
||||
const translations = loadTranslations<Translations>(language);
|
||||
const themeList = themes.map((theme) => ({
|
||||
|
@ -21,8 +21,14 @@ export function getThemes(req: Request, res: Response) {
|
|||
res.json(themeList);
|
||||
}
|
||||
|
||||
export function getThemeByTitle(req: Request, res: Response) {
|
||||
export function getHruidsByThemeHandler(req: Request, res: Response) {
|
||||
const themeKey = req.params.theme;
|
||||
|
||||
if (!themeKey) {
|
||||
res.status(400).json({ error: 'Missing required field: theme' });
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themes.find((t) => t.title === themeKey);
|
||||
|
||||
if (theme) {
|
||||
|
|
|
@ -19,7 +19,7 @@ export class Submission {
|
|||
learningObjectVersion: number = 1;
|
||||
|
||||
@PrimaryKey({ type: 'integer', autoincrement: true })
|
||||
submissionNumber!: number;
|
||||
submissionNumber?: number;
|
||||
|
||||
@ManyToOne({
|
||||
entity: () => Student,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import express from 'express';
|
||||
import { getThemes, getThemeByTitle } from '../controllers/themes.js';
|
||||
import { getThemesHandler, getHruidsByThemeHandler } from '../controllers/themes.js';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Query: language
|
||||
// Route to fetch list of {key, title, description, image} themes in their respective language
|
||||
router.get('/', getThemes);
|
||||
router.get('/', getThemesHandler);
|
||||
|
||||
// Arg: theme (key)
|
||||
// Route to fetch list of hruids based on theme
|
||||
router.get('/:theme', getThemeByTitle);
|
||||
router.get('/:theme', getHruidsByThemeHandler);
|
||||
|
||||
export default router;
|
||||
|
|
|
@ -39,14 +39,18 @@ async function getLearningObjectsForNodes(nodes: LearningPathNode[]): Promise<Ma
|
|||
* Convert the given learning path entity to an object which conforms to the learning path content.
|
||||
*/
|
||||
async function convertLearningPath(learningPath: LearningPathEntity, order: number, personalizedFor?: PersonalizationTarget): Promise<LearningPath> {
|
||||
// Fetch the corresponding learning object for each node since some parts of the expected response contains parts
|
||||
// With information which is not available in the LearningPathNodes themselves.
|
||||
const nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject> = await getLearningObjectsForNodes(learningPath.nodes);
|
||||
|
||||
const targetAges = Array.from(nodesToLearningObjects.values()).flatMap((it) => it.targetAges || []);
|
||||
|
||||
const keywords = Array.from(nodesToLearningObjects.values()).flatMap((it) => it.keywords || []);
|
||||
// The target ages of a learning path are the union of the target ages of all learning objects.
|
||||
const targetAges = [...new Set(Array.from(nodesToLearningObjects.values()).flatMap((it) => it.targetAges || []))];
|
||||
// The keywords of the learning path consist of the union of the keywords of all learning objects.
|
||||
const keywords = [...new Set(Array.from(nodesToLearningObjects.values()).flatMap((it) => it.keywords || []))];
|
||||
|
||||
const image = learningPath.image ? learningPath.image.toString('base64') : undefined;
|
||||
|
||||
// Convert the learning object notes as retrieved from the database into the expected response format-
|
||||
const convertedNodes = await convertNodes(nodesToLearningObjects, personalizedFor);
|
||||
|
||||
return {
|
||||
|
@ -67,34 +71,55 @@ async function convertLearningPath(learningPath: LearningPathEntity, order: numb
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function converting a single learning path node (as represented in the database) and the corresponding
|
||||
* learning object into a learning path node as it should be represented in the API.
|
||||
*
|
||||
* @param node Learning path node as represented in the database.
|
||||
* @param learningObject Learning object the learning path node refers to.
|
||||
* @param personalizedFor Personalization target if a personalized learning path is desired.
|
||||
* @param nodesToLearningObjects Mapping from learning path nodes to the corresponding learning objects.
|
||||
*/
|
||||
async function convertNode(
|
||||
node: LearningPathNode,
|
||||
learningObject: FilteredLearningObject,
|
||||
personalizedFor: PersonalizationTarget | undefined,
|
||||
nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject>
|
||||
): Promise<LearningObjectNode> {
|
||||
const lastSubmission = personalizedFor ? await getLastSubmissionForCustomizationTarget(node, personalizedFor) : null;
|
||||
const transitions = node.transitions
|
||||
.filter(
|
||||
(trans) =>
|
||||
!personalizedFor || // If we do not want a personalized learning path, keep all transitions
|
||||
isTransitionPossible(trans, optionalJsonStringToObject(lastSubmission?.content)) // Otherwise remove all transitions that aren't possible.
|
||||
)
|
||||
.map((trans, i) => convertTransition(trans, i, nodesToLearningObjects));
|
||||
return {
|
||||
_id: learningObject.uuid,
|
||||
language: learningObject.language,
|
||||
start_node: node.startNode,
|
||||
created_at: node.createdAt.toISOString(),
|
||||
updatedAt: node.updatedAt.toISOString(),
|
||||
learningobject_hruid: node.learningObjectHruid,
|
||||
version: learningObject.version,
|
||||
transitions,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function converting pairs of learning path nodes (as represented in the database) and the corresponding
|
||||
* learning objects into a list of learning path nodes as they should be represented in the API.
|
||||
* @param nodesToLearningObjects
|
||||
* @param personalizedFor
|
||||
*
|
||||
* @param nodesToLearningObjects Mapping from learning path nodes to the corresponding learning objects.
|
||||
* @param personalizedFor Personalization target if a personalized learning path is desired.
|
||||
*/
|
||||
async function convertNodes(
|
||||
nodesToLearningObjects: Map<LearningPathNode, FilteredLearningObject>,
|
||||
personalizedFor?: PersonalizationTarget
|
||||
): Promise<LearningObjectNode[]> {
|
||||
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map(async (entry) => {
|
||||
const [node, learningObject] = entry;
|
||||
const lastSubmission = personalizedFor ? await getLastSubmissionForCustomizationTarget(node, personalizedFor) : null;
|
||||
return {
|
||||
_id: learningObject.uuid,
|
||||
language: learningObject.language,
|
||||
start_node: node.startNode,
|
||||
created_at: node.createdAt.toISOString(),
|
||||
updatedAt: node.updatedAt.toISOString(),
|
||||
learningobject_hruid: node.learningObjectHruid,
|
||||
version: learningObject.version,
|
||||
transitions: node.transitions
|
||||
.filter(
|
||||
(trans) => !personalizedFor || isTransitionPossible(trans, optionalJsonStringToObject(lastSubmission?.content)) // If we want a personalized learning path, remove all transitions that aren't possible.
|
||||
)
|
||||
.map((trans, i) => convertTransition(trans, i, nodesToLearningObjects)), // Then convert all the transition
|
||||
};
|
||||
});
|
||||
const nodesPromise = Array.from(nodesToLearningObjects.entries()).map((entry) =>
|
||||
convertNode(entry[0], entry[1], personalizedFor, nodesToLearningObjects)
|
||||
);
|
||||
return await Promise.all(nodesPromise);
|
||||
}
|
||||
|
||||
|
@ -112,9 +137,10 @@ function optionalJsonStringToObject(jsonString?: string): object | null {
|
|||
* Helper function which converts a transition in the database representation to a transition in the representation
|
||||
* the Dwengo API uses.
|
||||
*
|
||||
* @param transition
|
||||
* @param index
|
||||
* @param nodesToLearningObjects
|
||||
* @param transition The transition to convert
|
||||
* @param index The sequence number of the transition to convert
|
||||
* @param nodesToLearningObjects Map which maps each learning path node of the current learning path to the learning
|
||||
* object it refers to.
|
||||
*/
|
||||
function convertTransition(
|
||||
transition: LearningPathTransition,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue