VBV-80: Refactor next learning content

This commit is contained in:
Daniel Egger 2022-08-30 16:34:08 +02:00
parent 97f01e0a08
commit a32c8ccbff
7 changed files with 66 additions and 75 deletions

View File

@ -15,6 +15,7 @@
"@headlessui/vue": "^1.6.7",
"axios": "^0.26.1",
"d3": "^7.6.1",
"lodash": "^4.17.21",
"loglevel": "^1.8.0",
"pinia": "^2.0.21",
"vue": "^3.2.38",

View File

@ -24,7 +24,7 @@ function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): Lea
}
}
export function parseLearningSequences (children: CircleChild[]): LearningSequence[] {
export function parseLearningSequences (circle: Circle, children: CircleChild[]): LearningSequence[] {
let learningSequence:LearningSequence | undefined;
let learningUnit:LearningUnit | undefined;
let learningContent:LearningContent | undefined;
@ -69,6 +69,7 @@ export function parseLearningSequences (children: CircleChild[]): LearningSequen
previousLearningContent = learningContent;
learningContent = Object.assign(child, {
parentCircle: circle,
parentLearningSequence: learningSequence,
parentLearningUnit: learningUnit,
previousLearningContent: previousLearningContent,
@ -111,6 +112,9 @@ export class Circle implements LearningWagtailPage {
readonly learningSequences: LearningSequence[];
readonly completed: boolean;
nextCircle?: Circle;
previousCircle?: Circle;
constructor(
public readonly id: number,
public readonly slug: string,
@ -121,7 +125,7 @@ export class Circle implements LearningWagtailPage {
public goals: CircleGoal[],
public job_situations: CircleJobSituation[],
) {
this.learningSequences = parseLearningSequences(this.children);
this.learningSequences = parseLearningSequences(this, this.children);
this.completed = false;
}
@ -154,6 +158,18 @@ export class Circle implements LearningWagtailPage {
return result;
}
public get flatLearningContents(): LearningContent[] {
const result: LearningContent[] = [];
this.learningSequences.forEach((learningSequence) => {
learningSequence.learningUnits.forEach((learningUnit) => {
learningUnit.learningContents.forEach((learningContent) => {
result.push(learningContent);
});
});
});
return result;
}
public someFinishedInLearningSequence(translationKey: string): boolean {
if (translationKey) {
return this.flatChildren.filter((lc) => {

View File

@ -1,10 +1,19 @@
import type { LearningPathChild, LearningWagtailPage, Topic } from '@/types'
import * as _ from 'lodash'
import type { CircleCompletion, LearningContent, LearningPathChild, LearningWagtailPage, Topic } from '@/types'
import { Circle } from '@/services/circle'
function getLastCompleted(learningPathKey: string, completionData: CircleCompletion[]) {
return _.orderBy(completionData, ['updated_at'], 'desc').find((c: CircleCompletion) => {
return c.completed && c.learning_path_key === learningPathKey && c.page_type === 'learnpath.LearningContent'
})
}
export class LearningPath implements LearningWagtailPage {
readonly type = 'learnpath.LearningPath'
public topics: Topic[]
public circles: Circle[]
public nextLearningContent?: LearningContent
public static fromJson(json: any, completionData: any): LearningPath {
return new LearningPath(json.id, json.slug, json.title, json.translation_key, json.children, completionData)
@ -37,6 +46,11 @@ export class LearningPath implements LearningWagtailPage {
if (topic) {
topic.circles.push(circle)
}
circle.previousCircle = this.circles[this.circles.length - 1]
if (circle.previousCircle) {
circle.previousCircle.nextCircle = circle
}
this.circles.push(circle)
}
})
@ -44,5 +58,26 @@ export class LearningPath implements LearningWagtailPage {
if (topic) {
this.topics.push(topic)
}
// find next learning content
const lastCompletedLearningContent = getLastCompleted(this.translation_key, completionData);
if (lastCompletedLearningContent) {
const lastCircle = this.circles.find(circle => circle.translation_key === lastCompletedLearningContent.circle_key);
if (lastCircle) {
const lastLearningContent = lastCircle.flatLearningContents.find(learningContent => learningContent.translation_key === lastCompletedLearningContent.page_key);
if (lastLearningContent && lastLearningContent.nextLearningContent) {
this.nextLearningContent = lastLearningContent.nextLearningContent;
} else {
if (lastCircle.nextCircle) {
this.nextLearningContent = lastCircle.nextCircle.flatLearningContents[0];
}
}
}
} else {
this.nextLearningContent = this.circles[0].flatLearningContents[0];
}
console.log('################# ', this.nextLearningContent);
}
}

View File

@ -3,67 +3,16 @@ import * as log from 'loglevel'
import { defineStore } from 'pinia'
import { itGet } from '@/fetchHelpers'
import { LearningPath } from '@/services/learningPath'
import {defineStore} from 'pinia'
import * as _ from 'lodash';
import type {LearningPath, Topic} from '@/types'
import {itGet} from '@/fetchHelpers';
import {Circle} from '@/services/circle';
import learningPathDiagram from "@/components/circle/LearningPathDiagram.vue";
export type LearningPathStoreState = {
learningPath: LearningPath | undefined
}
function getLastCompleted(completionData: any) {
return _.filter(_.orderBy(completionData, ['updated_at'], 'desc'), c =>{return c.completed && c.page_type === "learnpath.LearningContent" })[0]
}
function getFirstLearningContent(lastCopleted, learningPathData) {
const circles = _.filter(learningPathData.children, {'type': 'learnpath.Circle'})
let currentCircle = Circle.fromJson(circles[0])
const currentLearningUnit = currentCircle.flatChildren[0]
let currentLearningSequence = currentLearningUnit.parentLearningSequence
return [currentCircle, currentLearningSequence, currentLearningUnit]
}
function getNextLearningContent(lastCopleted, learningPathData) {
let currentCircle, currentLearningSequence, currentLearningUnit
currentLearningUnit = getFirstLearningContent(lastCopleted, learningPathData)
if (lastCopleted) {
const circles = _.filter(learningPathData.children, {'type': 'learnpath.Circle'})
_.forEach(circles, circle => {
_.forEach(Circle.fromJson(circle).learningSequences, learningSequence => {
_.forEach(learningSequence.learningUnits, learningUnit => {
_.forEach(learningUnit.learningContents, content => {
if (lastCopleted.page_key === content.translation_key) {
currentCircle = Circle.fromJson(circle)
currentLearningSequence = learningSequence
currentLearningUnit = content
}
})
})
})
})
currentLearningUnit = [currentCircle, currentLearningSequence, currentLearningUnit]
}
return currentLearningUnit
}
export const useLearningPathStore = defineStore({
id: 'learningPath',
state: () => {
return {
learningPath: undefined,
} as LearningPathStoreState;
},
getters: {},
@ -76,19 +25,7 @@ export const useLearningPathStore = defineStore({
const learningPathData = await itGet(`/learnpath/api/page/${slug}/`);
const completionData = await itGet(`/api/completion/learning_path/${learningPathData.translation_key}/`);
this.learningPath = learningPathData;
if (learningPathData) {
this.learningPath.lastCompleted = getLastCompleted(completionData)
const nextLearningContent = getNextLearningContent(this.learningPath.lastCompleted, learningPathData)
console.log('nextLearningContent', nextLearningContent)
this.learningPath.nextCircle = nextLearningContent[0]
this.learningPath.nextLearningSequence = nextLearningContent[1]
this.learningPath.nextLearningUnit = nextLearningContent[2]
this.learningPath = LearningPath.fromJson(learningPathData, completionData);
}
return this.learningPath;

View File

@ -59,6 +59,7 @@ export interface LearningContent extends LearningWagtailPage {
type: 'learnpath.LearningContent';
minutes: number;
contents: (LearningContentBlock | VideoBlock | PodcastBlock | DocumentBlock)[];
parentCircle: Circle;
parentLearningSequence?: LearningSequence;
parentLearningUnit?: LearningUnit;
nextLearningContent?: LearningContent;
@ -111,6 +112,7 @@ export interface CircleCompletion {
page_key: string;
page_type: string;
circle_key: string;
learning_path_key: string;
completed: boolean;
json_data: any;
}

View File

@ -2,7 +2,7 @@
import * as log from 'loglevel';
import {computed, onMounted} from 'vue'
import {onMounted} from 'vue'
import {useLearningPathStore} from '@/stores/learningPath';
import {useUserStore} from '@/stores/user';
@ -20,10 +20,6 @@ const learningPathStore = useLearningPathStore();
const userStore = useUserStore();
const continueRoute = computed(() => {
return "/circle/" + learningPathStore.learningPath.nextCircle.slug + "/";
})
onMounted(async () => {
log.info('LearningPathView mounted');
await learningPathStore.loadLearningPath(props.learningPathSlug);
@ -72,10 +68,14 @@ onMounted(async () => {
</p>
</div>
<div class="p-8 flex-2" v-if="learningPathStore.learningPath.nextCircle" translate>
<div class="p-8 flex-2" v-if="learningPathStore.learningPath.nextLearningContent" translate>
Nächster Schirtt
<h3>{{ learningPathStore.learningPath.nextCircle.title }}: {{ learningPathStore.learningPath.nextLearningSequence.title }}</h3>
<router-link class="mt-4 btn-blue" v-bind:to="this.continueRoute" translate>
<h3>{{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}: {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}</h3>
<router-link
class="mt-4 btn-blue"
:to="`/circle/${learningPathStore.learningPath.nextLearningContent.parentCircle.slug}/`"
translate
>
Los geht's
</router-link>
</div>

View File

@ -43,7 +43,7 @@ export default defineConfig(({ mode }) => {
},
test: {
globals: true,
environment: 'happy-dom',
environment: 'jsdom',
},
}
})