Add ts types for circle data

This commit is contained in:
Daniel Egger 2022-06-20 11:16:38 +02:00
parent b6f26eaa61
commit c1a7f4551d
4 changed files with 263 additions and 46 deletions

View File

@ -13,7 +13,6 @@ module.exports = {
'vue/setup-compiler-macros': true
},
'rules': {
'quotes': ['error', 'single'],
'@typescript-eslint/no-unused-vars': ['warn'],
}
}

View File

@ -0,0 +1,126 @@
import {describe, it} from 'vitest'
import type {Circle} from '../circle';
import {parseLearningSequences} from '../circle';
describe('circleService.parseLearningSequences', () => {
it('can parse learning sequences from api response', () => {
const input = {
"id": 10,
"title": "Analyse",
"slug": "analyse",
"type": "learnpath.Circle",
"translation_key": "c9832aaf-02b2-47af-baeb-bde60d8ec1f5",
"children": [
{
"id": 18,
"title": "Anwenden",
"slug": "anwenden",
"type": "learnpath.LearningSequence",
"translation_key": "2e4c431a-9602-4398-ad18-20dd4bb189fa",
"icon": "it-icon-ls-apply"
},
{
"id": 19,
"title": "Prämien einsparen",
"slug": "pramien-einsparen",
"type": "learnpath.LearningUnit",
"translation_key": "75c1f31a-ae25-4d9c-9206-a4e7fdae8c13",
"questions": []
},
{
"id": 20,
"title": "Versicherungsbedarf für Familien",
"slug": "versicherungsbedarf-für-familien",
"type": "learnpath.LearningContent",
"translation_key": "2a422da3-a3ad-468a-831e-9141c122ffef",
"minutes": 60,
"contents": [
{
"type": "exercise",
"value": {
"description": "Beispiel Aufgabe"
},
"id": "ee0bcef7-702b-42f3-a891-88a0332fce6f"
}
]
},
{
"id": 21,
"title": "Alles klar?",
"slug": "alles-klar",
"type": "learnpath.LearningContent",
"translation_key": "7dc9d96d-07f9-4b9f-bec1-43ba67cf9010",
"minutes": 60,
"contents": [
{
"type": "exercise",
"value": {
"description": "Beispiel Aufgabe"
},
"id": "a556ebb2-f902-4d78-9b76-38b7933118b8"
}
]
},
{
"id": 22,
"title": "Sich selbständig machen",
"slug": "sich-selbstandig-machen",
"type": "learnpath.LearningUnit",
"translation_key": "c40d5266-3c94-4b9b-8469-9ac6b32a6231",
"questions": []
},
{
"id": 23,
"title": "GmbH oder AG",
"slug": "gmbh-oder-ag",
"type": "learnpath.LearningContent",
"translation_key": "59331843-9f52-4b41-9cd1-2293a8d90064",
"minutes": 120,
"contents": [
{
"type": "video",
"value": {
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
},
"id": "a4974834-f404-4fb8-af94-a24c6db56bb8"
}
]
},
{
"id": 24,
"title": "Tiertherapie Patrizia Feller",
"slug": "tiertherapie-patrizia-feller",
"type": "learnpath.LearningContent",
"translation_key": "13f6d661-1d10-4b59-b8e5-01fcec47a38f",
"minutes": 120,
"contents": [
{
"type": "exercise",
"value": {
"description": "Beispiel Aufgabe"
},
"id": "5947c947-8656-44b5-826c-1787057c2df2"
}
]
},
{
"id": 25,
"title": "Auto verkaufen",
"slug": "auto-verkaufen",
"type": "learnpath.LearningUnit",
"translation_key": "3b42e514-0bbe-4c23-9c88-3f5263e47cf9",
"questions": []
},
],
"description": "Nach dem Gespräch werten sie die Analyse aus...",
"job_situations": [],
"goals": [],
"experts": []
} as Circle;
const learningSequences = parseLearningSequences(input.children);
expect(learningSequences.length).toBe(4);
})
})

View File

@ -0,0 +1,134 @@
export interface LearningContentBlock {
type: 'video' | 'web-based-training' | 'podcast' | 'competence' | 'exercise' | 'document' | 'knowledge';
value: {
description: string;
url?: string;
},
id: string;
}
export interface CircleGoal {
type: 'goal';
value: string;
id: string;
}
export interface CircleJobSituation {
type: 'job_situation';
value: string;
id: string;
}
export interface LearningWagtailPage {
id: number;
title: string;
slug: string;
translation_key: string;
}
export interface LearningContent extends LearningWagtailPage {
type: 'learnpath.LearningContent';
minutes: number;
contents: LearningContentBlock[];
}
export interface LearningUnit extends LearningWagtailPage {
type: 'learnpath.LearningUnit';
questions: [];
learningContents: LearningContent[];
minutes: number;
}
export interface LearningSequence extends LearningWagtailPage {
type: 'learnpath.LearningSequence';
icon: string;
learningUnits: LearningUnit[];
minutes: number;
}
type CircleChild = LearningContent | LearningUnit | LearningSequence;
export interface Circle extends LearningWagtailPage {
type: 'learnpath.Circle';
children: CircleChild[];
description: string;
}
function createEmptyLearningUnit(): LearningUnit {
return {
id: -1,
title: '',
slug: '',
translation_key: '',
type: 'learnpath.LearningUnit',
questions: [],
learningContents: [],
minutes: 0,
};
}
export function parseLearningSequences (children: CircleChild[]): LearningSequence[] {
let learningSequence:LearningSequence|null = null;
let learningUnit:LearningUnit|null = null;
const result:LearningSequence[] = [];
children.forEach((child) => {
// FIXME add error detection if the data does not conform to expectations
if (child.type === 'learnpath.LearningSequence') {
if (learningSequence) {
if (learningUnit) {
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();
} else if (child.type === 'learnpath.LearningUnit') {
if (learningSequence === null) {
throw new Error('LearningUnit found before LearningSequence');
}
if (learningUnit && learningUnit.learningContents.length) {
learningSequence.learningUnits.push(learningUnit);
}
learningUnit = Object.assign(child, {learningContents: []});
} else if (child.type === 'learnpath.LearningContent') {
if (learningUnit === null) {
throw new Error('LearningContent found before LearningUnit');
}
learningUnit.learningContents.push(child);
}
});
if (learningUnit && learningSequence) {
(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;
}

View File

@ -4,6 +4,7 @@ import * as log from 'loglevel';
import MainNavigationBar from '../components/MainNavigationBar.vue';
import LearningSequence from '../components/circle/LearningSequence.vue';
import { itGet, itPost } from '../fetchHelpers';
import { parseLearningSequences } from '../services/circle';
export default {
components: { LearningSequence, MainNavigationBar },
@ -29,56 +30,13 @@ export default {
this.completionData = data;
});
},
createLearningSequences(circleData) {
// aggregate wagtail data into LearningSequence > LearningUnit > LearningContent hierarchy
let learningSequence = null;
let learningUnit = null;
circleData.children.forEach((child) => {
// FIXME add error detection if the data does not conform to expectations
if(child.type === 'learnpath.LearningSequence') {
if(learningSequence) {
if(learningUnit) {
learningSequence.learningUnits.push(learningUnit);
}
this.learningSequences.push(learningSequence);
}
learningSequence = Object.assign({}, child, { learningUnits: [] });
learningUnit = { id: null, title: '', learningContents: [] };
} else if(child.type === 'learnpath.LearningUnit') {
if(learningUnit && learningUnit.learningContents.length) {
learningSequence.learningUnits.push(learningUnit);
}
learningUnit = Object.assign({}, child, { learningContents: [] });
} else {
learningUnit.learningContents.push(child);
}
});
if(learningUnit) {
learningSequence.learningUnits.push(learningUnit);
}
this.learningSequences.push(learningSequence);
// sum minutes
this.learningSequences.forEach((learningSequence) => {
learningSequence.minutes = 0;
learningSequence.learningUnits.forEach((learningUnit) => {
learningUnit.minutes = 0;
learningUnit.learningContents.forEach((learningContent) => {
learningUnit.minutes += learningContent.minutes;
});
learningSequence.minutes += learningUnit.minutes;
});
});
log.debug(this.learningSequences);
},
},
mounted() {
log.debug('CircleView mounted', this.circleSlug);
itGet(`/learnpath/api/circle/${this.circleSlug}/`).then((data) => {
this.circleData = data;
this.createLearningSequences(data);
this.learningSequences = parseLearningSequences(this.circleData.children);
log.debug(this.learningSequences);
itGet(`/api/completion/circle/${this.circleData.translation_key}/`).then((completionData) => {
this.completionData = completionData;