vbv/client/src/services/circle.ts

181 lines
5.3 KiB
TypeScript

import type {
CircleChild,
CircleCompletion,
CircleGoal,
CircleInterface,
CircleJobSituation,
LearningContent,
LearningSequence,
LearningUnit
} from '@/types';
function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): LearningUnit {
return {
id: 0,
title: '',
slug: '',
translation_key: '',
type: 'learnpath.LearningUnit',
learningContents: [],
minutes: 0,
parentLearningSequence: parentLearningSequence,
children: [],
last: true,
};
}
export function parseLearningSequences (children: CircleChild[]): LearningSequence[] {
let learningSequence:LearningSequence | undefined;
let learningUnit:LearningUnit | undefined;
let learningContent:LearningContent | undefined;
let previousLearningContent: LearningContent | undefined;
const result:LearningSequence[] = [];
children.forEach((child) => {
if (child.type === 'learnpath.LearningSequence') {
if (learningSequence) {
if (learningUnit) {
learningUnit.last = true;
learningSequence.learningUnits.push(learningUnit);
}
result.push(learningSequence);
}
learningSequence = Object.assign(child, {learningUnits: []});
// initialize empty learning unit if there will not come a learning unit next
learningUnit = _createEmptyLearningUnit(learningSequence);
} else if (child.type === 'learnpath.LearningUnit') {
if (!learningSequence) {
throw new Error('LearningUnit found before LearningSequence');
}
if (learningUnit && learningUnit.learningContents.length) {
learningSequence.learningUnits.push(learningUnit);
}
learningUnit = Object.assign(child, {
learningContents: [],
parentLearningSequence: learningSequence,
children: child.children.map((c) => {
c.parentLearningUnit = learningUnit;
c.parentLearningSequence = learningSequence;
return c;
})
});
} else if (child.type === 'learnpath.LearningContent') {
if (!learningUnit) {
throw new Error('LearningContent found before LearningUnit');
}
previousLearningContent = learningContent;
learningContent = Object.assign(child, {
parentLearningSequence: learningSequence,
parentLearningUnit: learningUnit,
previousLearningContent: previousLearningContent,
});
if (previousLearningContent) {
previousLearningContent.nextLearningContent = learningContent;
}
learningUnit.learningContents.push(child);
}
});
if (learningUnit && learningSequence) {
// TypeScript does not get it here...
learningUnit.last = true;
(learningSequence as LearningSequence).learningUnits.push(learningUnit);
result.push(learningSequence);
} else {
throw new Error('Finished with LearningContent but there is no LearningSequence and LearningUnit');
}
// sum minutes
result.forEach((learningSequence) => {
learningSequence.minutes = 0;
learningSequence.learningUnits.forEach((learningUnit) => {
learningUnit.minutes = 0;
learningUnit.learningContents.forEach((learningContent) => {
learningUnit.minutes += learningContent.minutes;
});
learningSequence.minutes += learningUnit.minutes;
});
});
return result;
}
export class Circle implements CircleInterface {
readonly type = 'learnpath.Circle';
readonly learningSequences: LearningSequence[];
readonly completed: boolean;
constructor(
public readonly id: number,
public readonly slug: string,
public readonly title: string,
public readonly translation_key: string,
public description: string,
public children: CircleChild[],
public goals: CircleGoal[],
public job_situations: CircleJobSituation[],
) {
this.learningSequences = parseLearningSequences(this.children);
this.completed = false;
}
public static fromJson(json: any): Circle {
// TODO add error checking when the data does not conform to the schema
return new Circle(
json.id,
json.slug,
json.title,
json.translation_key,
json.description,
json.children,
json.goals,
json.job_situations,
)
}
public get flatChildren(): CircleChild[] {
const result: CircleChild[] = [];
this.learningSequences.forEach((learningSequence) => {
learningSequence.learningUnits.forEach((learningUnit) => {
learningUnit.children.forEach((learningUnitQuestion) => {
result.push(learningUnitQuestion);
})
learningUnit.learningContents.forEach((learningContent) => {
result.push(learningContent);
});
});
});
return result;
}
public someFinishedInLearningSequence(translationKey: string): boolean {
if (translationKey) {
return this.flatChildren.filter((lc) => {
return lc.completed && lc.parentLearningSequence?.translation_key === translationKey;
}).length > 0;
}
return false;
}
public parseCompletionData(completionData: CircleCompletion[]) {
this.flatChildren.forEach((page) => {
const pageIndex = completionData.findIndex((e) => {
return e.page_key === page.translation_key;
});
if (pageIndex >= 0) {
page.completed = completionData[pageIndex].completed;
} else {
page.completed = undefined;
}
});
}
}