From eaa06719d6e656edd8ecf81493d2f3153ef1ae6f Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 11:15:03 +0200
Subject: [PATCH 01/11] Update styleguide colors
---
client/src/assets/circle-analyse.svg | 47 ----------------------------
client/src/assets/learning_path.svg | 0
client/src/views/StyelGuideView.vue | 4 +--
client/tailwind.config.js | 8 ++---
4 files changed, 4 insertions(+), 55 deletions(-)
delete mode 100644 client/src/assets/circle-analyse.svg
delete mode 100644 client/src/assets/learning_path.svg
diff --git a/client/src/assets/circle-analyse.svg b/client/src/assets/circle-analyse.svg
deleted file mode 100644
index be722b24..00000000
--- a/client/src/assets/circle-analyse.svg
+++ /dev/null
@@ -1,47 +0,0 @@
-
diff --git a/client/src/assets/learning_path.svg b/client/src/assets/learning_path.svg
deleted file mode 100644
index e69de29b..00000000
diff --git a/client/src/views/StyelGuideView.vue b/client/src/views/StyelGuideView.vue
index 3257d3a7..db299bff 100644
--- a/client/src/views/StyelGuideView.vue
+++ b/client/src/views/StyelGuideView.vue
@@ -54,8 +54,8 @@ const dropdownData = [
// TODO: die CSS-Klasse für die Farben wird hier in der StyleGuideView.vue generiert.
// deshalb muss man diese CSS-Klassen in tailwind.config.js "safelist"en, wenn diese sonst
// noch nirgendwo verwendet werden.
-const colors = ['blue', 'sky', 'orange', 'green', 'red', 'gray',];
-const colorValues = [100, 200, 300, 400, 500, 600, 700, 800, 900,];
+const colors = ['blue', 'sky', 'green', 'red', 'orange', 'yellow', 'stone', 'gray', 'slate'];
+const colorValues = [200, 300, 400, 500, 600, 700, 800, 900,];
function colorBgClass(color: string, value: number) {
return `bg-${color}-${value}`;
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index f5ca7ec5..920c7b2b 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -20,12 +20,8 @@ module.exports = {
},
colors: colors,
},
- safelist: [{
- pattern: /bg-(blue|sky|orange|green|red)-(400|500|600|700)/,
- }, {
- pattern: /bg-gray-(100|300|500|700|900)/,
- },
- 'bg-blue-900',
+ safelist: [
+ { pattern: /bg-(blue|sky|green|red|orange|yellow|stone|gray|slate)-(200|300|400|500|600|700|800|900)/, },
'it-icon',
],
plugins: [
From 26139304a3f535f1404093c92052e02b5518659c Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 13:17:50 +0200
Subject: [PATCH 02/11] Generate additional simple learningPath for development
---
.../components/circle/SimpleCircleDiagram.vue | 106 ------------------
.../commands/create_default_learning_path.py | 3 +-
.../tests/create_simple_test_learning_path.py | 34 +++++-
.../vbv_lernwelt/learnpath/tests/test_api.py | 4 +-
4 files changed, 37 insertions(+), 110 deletions(-)
delete mode 100644 client/src/components/circle/SimpleCircleDiagram.vue
diff --git a/client/src/components/circle/SimpleCircleDiagram.vue b/client/src/components/circle/SimpleCircleDiagram.vue
deleted file mode 100644
index 89034d4b..00000000
--- a/client/src/components/circle/SimpleCircleDiagram.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
index ba20d5fb..99b2b7ee 100644
--- a/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
+++ b/server/vbv_lernwelt/learnpath/management/commands/create_default_learning_path.py
@@ -1,9 +1,10 @@
import djclick as click
from vbv_lernwelt.learnpath.create_default_learning_path import create_default_learning_path
+from vbv_lernwelt.learnpath.tests.create_simple_test_learning_path import create_simple_test_learning_path
@click.command()
def command():
create_default_learning_path(skip_locales=True)
- # create_simple_test_learning_path(skip_locales=True)
+ create_simple_test_learning_path(skip_locales=True)
diff --git a/server/vbv_lernwelt/learnpath/tests/create_simple_test_learning_path.py b/server/vbv_lernwelt/learnpath/tests/create_simple_test_learning_path.py
index f3c8c153..e71d1f65 100644
--- a/server/vbv_lernwelt/learnpath/tests/create_simple_test_learning_path.py
+++ b/server/vbv_lernwelt/learnpath/tests/create_simple_test_learning_path.py
@@ -125,7 +125,39 @@ def create_simple_test_learning_path(user=None, skip_locales=True):
site.save()
lp = LearningPathFactory(title="Unit-Test Lernpfad", parent=site.root_page)
- TopicFactory(title="Unit-Test Topic", is_visible=False, parent=lp)
+
+ TopicFactory(title="Basis", is_visible=False, parent=lp)
+
+ circle_basis = CircleFactory(
+ title="Basis",
+ parent=lp,
+ description="Basis von Unit-Test Lernpfad",
+ )
+ LearningSequenceFactory(title='Starten', parent=circle_basis, icon='it-icon-ls-start')
+ LearningContentFactory(
+ title='Einleitung Circle "Basis"',
+ parent=circle_basis,
+ minutes=15,
+ contents=[('video', VideoBlockFactory(
+ url='https://www.youtube.com/embed/qhPIfxS2hvI',
+ description='Basis Video'
+ ))]
+ )
+ LearningSequenceFactory(title='Beenden', parent=circle_basis, icon='it-icon-ls-end')
+ LearningContentFactory(
+ title='Kompetenzprofil anschauen',
+ parent=circle_basis,
+ minutes=30,
+ contents=[('document', CompetenceBlockFactory())]
+ )
+ LearningContentFactory(
+ title='Circle "Analyse" abschliessen',
+ parent=circle_basis,
+ minutes=30,
+ contents=[('document', CompetenceBlockFactory())]
+ )
+
+ TopicFactory(title="Gewinnen von Kunden", parent=lp)
circle_analyse = create_circle('Unit-Test Circle', lp)
create_circle_children(circle_analyse, 'Unit-Test Circle')
diff --git a/server/vbv_lernwelt/learnpath/tests/test_api.py b/server/vbv_lernwelt/learnpath/tests/test_api.py
index dad7a7e5..1bfd83eb 100644
--- a/server/vbv_lernwelt/learnpath/tests/test_api.py
+++ b/server/vbv_lernwelt/learnpath/tests/test_api.py
@@ -26,6 +26,6 @@ class TestRetrieveLearingPathContents(APITestCase):
self.assertEqual(learning_path.title, data['title'])
# topic and circle
- self.assertEqual(2, len(data['children']))
+ self.assertEqual(4, len(data['children']))
# circle "unit-test-circle" contents
- self.assertEqual(13, len(data['children'][1]['children']))
+ self.assertEqual(13, len(data['children'][3]['children']))
From b4045789267e20892e028d46b520aa33cb47e512 Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 13:45:10 +0200
Subject: [PATCH 03/11] Upgrade client libraries
---
client/package.json | 52 ++++++++----------
.../src/components/circle/CircleDiagram.vue | 6 +-
.../components/circle/LearningPathDiagram.vue | 14 ++---
env_secrets/local_chrigu.env | Bin 1805 -> 1805 bytes
env_secrets/local_daniel.env | Bin 794 -> 55 bytes
env_secrets/local_lorenz.env | Bin 787 -> 330 bytes
server/config/settings/base.py | 6 +-
7 files changed, 37 insertions(+), 41 deletions(-)
diff --git a/client/package.json b/client/package.json
index 433ab715..6ab172f1 100644
--- a/client/package.json
+++ b/client/package.json
@@ -12,47 +12,43 @@
"tailwind": "tailwindcss -i tailwind.css -o ../server/vbv_lernwelt/static/css/tailwind.css --watch"
},
"dependencies": {
- "@headlessui/vue": "^1.6.4",
+ "@headlessui/vue": "^1.6.7",
"axios": "^0.26.1",
- "d3": "^7.4.4",
+ "d3": "^7.6.1",
"loglevel": "^1.8.0",
- "pinia": "^2.0.13",
- "underscore": "^1.13.4",
- "vue": "^3.2.31",
- "vue-i18n": "^9.1.9",
- "vue-router": "^4.0.14"
+ "pinia": "^2.0.21",
+ "vue": "^3.2.38",
+ "vue-i18n": "^9.2.2",
+ "vue-router": "^4.1.5"
},
"devDependencies": {
- "@intlify/vite-plugin-vue-i18n": "^3.4.0",
"@rollup/plugin-alias": "^3.1.9",
- "@rushstack/eslint-patch": "^1.1.0",
+ "@rushstack/eslint-patch": "^1.1.4",
"@tailwindcss/forms": "^0.5.2",
"@tailwindcss/typography": "^0.5.4",
- "@testing-library/vue": "^6.6.0",
+ "@testing-library/vue": "^6.6.1",
"@types/d3": "^7.4.0",
- "@types/jsdom": "^16.2.14",
- "@types/node": "^16.11.26",
- "@vitejs/plugin-vue": "^2.3.1",
+ "@types/jsdom": "^20.0.0",
+ "@types/node": "^18.7.14",
+ "@vitejs/plugin-vue": "^3.0.3",
"@vue/eslint-config-prettier": "^7.0.0",
- "@vue/eslint-config-typescript": "^10.0.0",
- "@vue/test-utils": "^2.0.0-rc.18",
+ "@vue/eslint-config-typescript": "^11.0.0",
+ "@vue/test-utils": "^2.0.2",
"@vue/tsconfig": "^0.1.3",
- "autoprefixer": "^10.4.7",
- "cypress": "^9.5.3",
- "eslint": "^8.5.0",
- "eslint-plugin-cypress": "^2.12.1",
- "eslint-plugin-vue": "^8.2.0",
- "happy-dom": "^5.3.1",
+ "autoprefixer": "^10.4.8",
+ "eslint": "^8.23.0",
+ "eslint-plugin-vue": "^9.4.0",
+ "jsdom": "^20.0.0",
"postcss": "^8.4.14",
"postcss-import": "^14.1.0",
- "prettier": "^2.5.1",
- "sass": "^1.50.1",
+ "prettier": "^2.7.1",
+ "sass": "^1.54.6",
"sass-loader": "^12.6.0",
"start-server-and-test": "^1.14.0",
- "tailwindcss": "^3.1.4",
- "typescript": "~4.6.3",
- "vite": "^2.9.1",
- "vitest": "^0.15.1",
- "vue-tsc": "^0.33.9"
+ "tailwindcss": "^3.1.8",
+ "typescript": "^4.8.2",
+ "vite": "^3.0.9",
+ "vitest": "^0.22.1",
+ "vue-tsc": "^0.40.4"
}
}
diff --git a/client/src/components/circle/CircleDiagram.vue b/client/src/components/circle/CircleDiagram.vue
index f9d4b8b5..49e80cba 100644
--- a/client/src/components/circle/CircleDiagram.vue
+++ b/client/src/components/circle/CircleDiagram.vue
@@ -36,8 +36,8 @@ const pieData = computed(() => {
if (circle) {
console.log('initial of compute pie data ', circle)
- let pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1)
- let pieGenerator = d3.pie()
+ const pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1)
+ const pieGenerator = d3.pie()
let angles = pieGenerator(pieWeights)
_.forEach(angles, (pie) => {
const thisLearningSequence = circle.learningSequences[parseInt(pie.index)]
@@ -214,7 +214,7 @@ function render() {
// remove last arrow
d3.selection.prototype.last = function () {
- let last = this.size() - 1;
+ const last = this.size() - 1;
return d3.select(this.nodes()[last]);
};
diff --git a/client/src/components/circle/LearningPathDiagram.vue b/client/src/components/circle/LearningPathDiagram.vue
index 9d70576f..8f83ed31 100644
--- a/client/src/components/circle/LearningPathDiagram.vue
+++ b/client/src/components/circle/LearningPathDiagram.vue
@@ -49,11 +49,11 @@ export default {
}
if (this.learningPathStore.learningPath) {
- let internalCircles = []
+ const internalCircles = []
this.learningPathStore.learningPath.circles.forEach((circle) => {
const pieWeights = new Array(Math.max(circle.learningSequences.length, 1)).fill(1)
const pieGenerator = d3.pie()
- let pieData = pieGenerator(pieWeights)
+ const pieData = pieGenerator(pieWeights)
pieData.forEach((pie) => {
const thisLearningSequence = circle.learningSequences[parseInt(pie.index)]
pie.startAngle = pie.startAngle + Math.PI
@@ -62,7 +62,7 @@ export default {
pie.someFinished = someFinished(circle, thisLearningSequence)
pie.allFinished = allFinished(circle, thisLearningSequence)
});
- let newCircle = {}
+ const newCircle = {}
newCircle.pieData = pieData.reverse()
newCircle.title = circle.title
newCircle.slug = circle.slug
@@ -109,7 +109,7 @@ export default {
return color
}
- let vueRouter = this.$router
+ const vueRouter = this.$router
// Create append pie charts to the main svg
@@ -201,7 +201,7 @@ export default {
let pos = topicHeightOffset
for (let index = 0; index < i; index++) {
- let topic = topics[index]
+ const topic = topics[index]
if (topic.is_visible) {
pos += topicHeight
}
@@ -218,7 +218,7 @@ export default {
y += topicHeight
}
for (let circle_index = 0; circle_index < topic.circles.length; circle_index++) {
- let circle = topic.circles[circle_index]
+ const circle = topic.circles[circle_index]
if (circle.id === d.id) {
return y
}
@@ -283,7 +283,7 @@ export default {
} else {
circle_groups
.attr('transform', (d, i) => {
- let x_coord = (i + 1) * circleWidth - radius
+ const x_coord = (i + 1) * circleWidth - radius
return 'translate(' + x_coord + ', 200)'
})
diff --git a/env_secrets/local_chrigu.env b/env_secrets/local_chrigu.env
index 2e1036893898e20ab230f1700ba48439b916bdc1..5012ac7d6c506b7058fc2d2cb14bfcf6e75b44b1 100644
GIT binary patch
literal 1805
zcmV+o2lDs;M@dveQdv+`0P?V7F4yWdvCu0m%PHq$+lUsmhU%U1H{!83J1Ej}x35)3
zA3^h*5nB$fRjP7W8V`$iDzcA`du$hjR}RAwC`WBA8
zk1{~6ZjHgu
zADqwtSkVb$kp;)@-c2m}(^%*HDdc~n)hI(-xNfH=%!IZQD
z-7|(jGjOQ6mU5x{j!7Ap$5<&
zP*Siiuh>eN&607w%tR%ZZjdr8(8z_2^}(xvLuL*c54P1td6k@7@CW`b8_U$>Nwz2+
zlOW@`%DEq!6X8DPfZ8~8hYM}fxvmy$$+;Bhv0znfDfjcFP2%oRrg_uBq6FPZA=&WwHXe^e
z9qgl9>PVBn81My8=Vn^VTjZi?q*LSMIMYx3Wq%Ej#c7(zn#&Kb6Ur8SH?MpBr%HnC%QhJ$!vq7L*hf?0D>fhV
z@s&T(IxZT#*=+{+;2>eTR;oj~$!$9fnSx-P!8=Y$Xn$>X5zIAhzh>=ck7w#BJ0L<_
zVy5&VT8Pl4n1dN&S{!Eg_yt49z#A<;PYr(zVbodPD~rwM45eS%^{;=OYl
zY>1NhfkMV_2%Nw-PjSFib5XL9osEN>2S?GUK>Et?U)&)Mr_sHExg>8-x;p@{!KLJ+
zG>Y_qqcdxYGdJ^1^9Fgp=RwT?!J30R$Tuvf}tjt1!0^pAI
zw}0;Zs*PCEv9F_z9xUh4N-D1dMPzF(+efj66SVXe{MfmhlfxjiJ@CZVw3m8@yEtm!
zTGs*^PaF9rYIPp2+deA=u@1;jF7qvmGl!1oKE4Gl*`1iyNmq|UD$JolOxXI&=dVK~
z23c@2qw?jTJkm~<9W*ACOmN~`;*=)_Y78PQiyrA9C^VUEw$$nWGmgQvM%?BvW{+;d
z03=LHLuVGn#hX(u2G$s?bwfS^VxX`#6$qM2=+X5!Z-rIa5akvD21(M=yYcSwl2GVs
zL&Ak1uk1Q{u`<1tFI?7dKH@RzeAA!&n!V;87#ps;#T_8$(i-^38ntCDmTEj+>g&~p
zkg&x9GS}BiT3xIzrsPU61}Lv+pJjvAt51CCrvkYHO&^qb2vHW=wX}?cezSu*-V!(c
zHC3iZhU}U7I%0uC?#CDL_o$6+4Sdmj&nn3&%O=9MBP?ozX&oF6)hX`uc>6PMS4&Ot
zn-rgM80mkl;oa^NsBJ1AkM=Vb76&1wK)kKcqEt!-@I(rJPZqS?zs$Lwsy&-A#riF+
zO{8WiYySTemb44lM>C>0l?~8urRD#=MJW+Ywmy`@e~}?_pV(PP(B%Pw>c{41w4
zvT?}hN)(4!_qUsX+#;(Je+0g=F9|L#lc(65jTQA8!XQ2T$PgB-CN+&r@t!;1!gknn
ze8F^Bak+@~6iv6xMtnEG%)Q6h^N}f3$fAGaJapAGvC7q>^0Xu0lOjTYebcMw_&D#B
zOpz`a%g9(InaxF)4xJ5OzBKcxwQs!kHY-a?{=8EZjt_J&HdudE0=4eet$Ze)4tdj+
zNi2|F-w3BWJYQ}&y^gCp7{h%r*^PMI9h?xE6XlC;C+UpvN`J3W>yl~9L9s;Txhk5$
zD%{POaB4gM^-~=h=DndmIkR+zqFsg}F_9E4Hp#IEWav?6RZ`GY=5vVh)p}ph=e1jl
z^U88P#YC=H9@kf!SEe?Aq!%KaTCz$Oj7Apmo%w~b&$!xEtz$wjn*$&)|
zL>W9mle4*AR)%SHzeZk|o5Y0?cAu+xMY2?nied`!aC%7^Rg5cjl=uIivWzy3GDK+c
veno{{+0~)^{)jV4J4KFx(;T2%xypfNob|Io@l!ed4YL~2-BWAzq3&em!-0YX
literal 1805
zcmV+o2lDs;M@dveQdv+`0JpNKPl?Kanf;b@HfwJkeb;u>Nv|ewxV#w&M!0l*hM9Er
zR)aBK{3A0&14T5LB`ciE`1!K*!~_Pghqq-{j84oIvL0%et~DCOR&A;Y|7%s*b4I{z
z%7{#{ot9L{Zz5Cz=YuEs!C1*f)PJf2wO=>_Z3PQ-)-%0Uth737T7wZS!S=-&L&Ouvydcf$)-*Scss+YFUp@KQZVhY1zTrogG!`7$Ln$I-qy<(j!rXL0{5Aq0uZfq%@1uA_wh
zGVhaKaafdpm9Q|fpEZjmsmF-?UaL180_BOI_;;M(dY_a^;ax5d&S9K?v6T#w{v
zsQ9!r`7SWNNw8wKqD;~NIs*Oy5R_|9>?F(3B>POO5rDe{uuwCvyn6<9x=3OIgJHMN8Ztt9Ox(>D)AX>qB6GDafafde}i9|WL7Z|4YVEhy7aI|e>
z#8fCw0mgLbrJ<1j#YtVmgw>eTx?qx=#24%)HlcAQ4<>(24QKb1YJ4!^3GlR2aS1ff
zfI>4hoKsR;Qtr9%oA%Mo&;n-}^(Vtz|J_9~Ov5*ezuJnZC9o)H4w_34NG9$gjuVT_
z-%X#iia`}0eG^7Yvc=1*MElu}uPjB{y2(MMiTVH8`OgQ7_AMK{m}-Qk30W?Ty(XRP
z+F-=5O4-{Up!gc6X?G-~+f|NFb-f%)Br&E$jEpK!+H3C8I`-Ms{~2qFC|s+z&T_rqYfi#iKq`e=
zH!oIZqjBEDp-H{xCt-l4OEFF~c4(y8zV7_e*CBAKD5gWuRB5{XI#n^23^3!?H+jtj
zXu_9I1`Q2DJ8aecBj;#q9RQ!xA{J(Txi<(T6onE$YTO*@f1fhB1wQCv9
zr1M?plZK-&W78a6rPZg7>YG{_a)9ZZS&Jwvi>59nWnrT7CX|8I_g~0mZyTPT5wp@R
z)R?)hSX=BSIz%!89-%^;IXYz90{6@2-xe>VnuDExDe|3zC>0o`rqFp0hJF1TrJj
z&=7ua9F&cF9ok2Nqu^eSJEhXk9?pNQ_dExvs@8IV)d9?$orO4mYsQ9SN@xsC`1I5|
z@V)$6XCI&ZVf0(XZ%%-_^X@gfX+#E&HEmoEwV9nU=LYAlc8*uBPIVN)$3L&S)<4DT
zqK<12yVg|U-A^|DYcqVa(}?5zM0em8_IV3vZx4GJX70MTId<*
zs=cCPU9Nl-R6~2_YR-}yW7f=!AjL;^c#~WviS13(8wFi6Nlw6V4hnWT
vlLywgA@k`x?6(QmwtJ`lu!QW~JEvi&Joc`k{aPk5u4>YZ0`B;YKZ8_&bJCcJlC)qAiaB_3ug7Eo<^5Ekg66sPX+E%S5aJUCt!e?%sCNL-eq
zz~CP(^v|8?B&jo?+zDdB=c9463G8WVsNaNgez!Yl^K7p%EROFuFl?+mjvtybiRKT6
zrLh=#j(Spn@cbF|)HP*p>|n)l?HaAGqU4<|sAvE(Gk9u;xKSUYZGkUOS-q_}QvpiS
zc_Xne8GoQr$*OB}I5G{#FWrTCPhN&zi45o;5V`7)_S)gAo1B%&ySw%ku_E!_2;9ec_NG02wrHb}x+=-K@
z{I356e2c;37c;*f-Krl*+@ntj&Y_1gFKKgu_kg6!e*Df0jFfB#HIx}R&SgZqt~@{T
zn1QN{j7k>Yd#kuAltT(6WIc85+@h5V#*INYdG6^NkHL7e{_Ss8fDk4?ZYAxkkD0;4
z^rAOp*JWTODvYuN&!P$svR-~YH{)32X0B$vW^Fe>M*SHG4%^SgfBiU*+NfY#Aj95}
z4(RiK-(GUHoe849Zg5NChHEEA^&~r2qf`
diff --git a/env_secrets/local_lorenz.env b/env_secrets/local_lorenz.env
index 4104dedb2937de9ddf6a6c07adffd05f9c004b5f..f51a11700ea2f1950d0d607d75e4c2db414b90aa 100644
GIT binary patch
literal 330
zcmV-Q0k!@BM@dveQdv+`04IYefdblBGA+;Ro{)l&p|jgZj0&4kPn%XVb@Vf;pK8j0
z?yUAruotk!=dV1012D~i0YX5)x6S5!zFw}AK~fHvI#AE+4OI76ylGzOQQLC2xzdz0
zIsk_8w&AAKuMn$+)YttM|6ZDaiufR3yrlmQMG6~aM8C#+!@clz{Jj$bAq(raop07c
z2I8+(D$OIuP(scqxv`VfiN^}BCV~e#5_mZ6L&j*BmihU>@^eMqD7=LQ6nK|;zVN_O
zzw-_RvSP{olD22+Y~Z{Qsmj+iCj*-dyK|oGl3J90Pc-}Mu)Af9inF1vqkuy08XH>E
zs<_djZ)*bsjs9ZN98IgQY3eAi9=hZxgg^Lo5dP+FYDc*oPKCg?w+_su4>|irrGpNG
cHwEAQw($StxxeX!uGHxmdrlEYC#O)wXOBRtYybcN
literal 787
zcmV+u1MK_&M@dveQdv+`0QZc{k-CkU9ZHpzn~zvP(zlyoh-c?#vUt>ATy%a|N!nV`
ztN59*7PoFjgJ+mRgQ;2v$O~xGRaCqCGX7pG&cyq4dr1Fvn>FTl8^1!R!vdHpG$>Cv?|%Sn}wLmzoy?Y7$0(P*|K*qvMWB>w?C+|`3tH5M%jv^US%
z4kxzz<0QW_{WA3^f0jk(5az$vijJ^oQ?)C|JuFw9&?>ozRG7THy5
zFZDAq>eHP1vG-)Pwa3ICx!*K4I1)O(l&TI~N>MBd$1(q7HDCnO6lQge%{QQscXdyd
ze)`LZsThD>8?k4BeqNTc_ty#8TlFtyXgVf+%*Tn4(nItPuGSR&Rof4jm2%nXD=H
z9@0zMx*EJY0
z!s1_P-X#?$Q#2U#>&{K*vWJCy!#Q;6bC-P-O%*lRSyB`X7TSp^@qr{2{Wr2Az0p7#
z9^Zz=QSha5o7YbA^LIpLQ<;{#{-R#=eq7aCLVfFzfFB22&z@U_OZY&!Kl)Q@>e?2T
zYkg_h(U*TBf@Pz_>Gzbi-#lByk=@$dtkVs27XXi`gFvNMKvcck{cBUd@0BXH$O=W8
z0y3_xKcW@h6hMHnerQgFBbxrIS8U5h*5!=LF>#;S9wDhKD+~gX4OL!YSix1iX~VE-
zlJki;>2$%CcfsZ)q5vI)CKQ+^(7GeF&KC$JM}WugIaSxo|F;4oUxmC1w7TC|psTxC
zCNO@Y#z>!Cc;%p~^AigRpMLbV`-oZ4F$%4v9=2mLW3rF*FxWM2^?%_gyH=$Z;|l-b
z&VVqJa{=L=u^`<9AzM29=fN07rLq`>h&+YW-${-G3)Q?`Cb>Wv=pPp!#8!d94fAVD
zZz4c~u}H@WIXiHwKXXc-dg#G{k
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index e6b7b7da..8be507d0 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -133,7 +133,7 @@ AUTH_USER_MODEL = "core.User"
LOGIN_URL = "/login"
LOGIN_REDIRECT_URL = "/"
-ALLOW_LOCAL_LOGIN = env.bool("IT_ALLOW_LOCAL_LOGIN", default=False)
+ALLOW_LOCAL_LOGIN = env.bool("IT_ALLOW_LOCAL_LOGIN", default=DEBUG)
# PASSWORDS
# ------------------------------------------------------------------------------
@@ -200,7 +200,7 @@ MEDIA_ROOT = str(APPS_DIR / "media")
MEDIA_URL = "/media/"
IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG)
-IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", 'http://localhost:3000')
+IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", 'http://localhost:5173')
# WAGTAIL
# ------------------------------------------------------------------------------
@@ -454,7 +454,7 @@ REST_FRAMEWORK = {
CORS_URLS_REGEX = r"^/api/.*$"
# django-csp
-CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", 'ws://localhost:3000', 'localhost:8000', 'blob:', 'data:', 'http://*')
+CSP_DEFAULT_SRC = ("'self'", "'unsafe-inline'", 'ws://localhost:5173', 'localhost:8000', 'blob:', 'data:', 'http://*')
CSP_FRAME_ANCESTORS = ("'self'",)
# By Default swagger ui is available only to admin user. You can change permission classs to change that
From 97f01e0a089394ee2c7395cc1d3b9851db1b923b Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 15:10:57 +0200
Subject: [PATCH 04/11] Refactor LearningPath class
---
client/package.json | 2 +-
.../__tests__/request_learning_path_data.py | 29 +++++++++++
client/src/services/circle.ts | 7 ++-
client/src/services/learningPath.ts | 48 +++++++++++++++++++
client/src/stores/learningPath.ts | 33 +++----------
client/src/types.ts | 22 ++-------
6 files changed, 93 insertions(+), 48 deletions(-)
create mode 100644 client/src/services/__tests__/request_learning_path_data.py
create mode 100644 client/src/services/learningPath.ts
diff --git a/client/package.json b/client/package.json
index 6ab172f1..09806947 100644
--- a/client/package.json
+++ b/client/package.json
@@ -36,7 +36,7 @@
"@vue/test-utils": "^2.0.2",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.8",
- "eslint": "^8.23.0",
+ "eslint": "8.22.0",
"eslint-plugin-vue": "^9.4.0",
"jsdom": "^20.0.0",
"postcss": "^8.4.14",
diff --git a/client/src/services/__tests__/request_learning_path_data.py b/client/src/services/__tests__/request_learning_path_data.py
new file mode 100644
index 00000000..e1da14c9
--- /dev/null
+++ b/client/src/services/__tests__/request_learning_path_data.py
@@ -0,0 +1,29 @@
+import json
+
+import requests
+
+
+def main():
+ client = requests.session()
+ client.get('http://localhost:8000/')
+
+ client.post(
+ 'http://localhost:8000/core/login/',
+ json={
+ 'username': 'admin',
+ 'password': 'test',
+ }
+ )
+
+ response = client.get(
+ 'http://localhost:8000/learnpath/api/page/unit-test-lernpfad/',
+ )
+ print(response.status_code)
+ print(response.json())
+
+ with open('unit-test-lernpfad.json', 'w') as f:
+ f.write(json.dumps(response.json(), indent=4))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/client/src/services/circle.ts b/client/src/services/circle.ts
index e467b1b9..09cd0e69 100644
--- a/client/src/services/circle.ts
+++ b/client/src/services/circle.ts
@@ -6,9 +6,8 @@ import type {
LearningContent,
LearningSequence,
LearningUnit,
- LearningWagtailPage
-} from '@/types';
-
+ LearningWagtailPage,
+} from '@/types'
function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): LearningUnit {
return {
@@ -22,7 +21,7 @@ function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): Lea
parentLearningSequence: parentLearningSequence,
children: [],
last: true,
- };
+ }
}
export function parseLearningSequences (children: CircleChild[]): LearningSequence[] {
diff --git a/client/src/services/learningPath.ts b/client/src/services/learningPath.ts
new file mode 100644
index 00000000..b902c71e
--- /dev/null
+++ b/client/src/services/learningPath.ts
@@ -0,0 +1,48 @@
+import type { LearningPathChild, LearningWagtailPage, Topic } from '@/types'
+import { Circle } from '@/services/circle'
+
+export class LearningPath implements LearningWagtailPage {
+ readonly type = 'learnpath.LearningPath'
+ public topics: Topic[]
+ public circles: Circle[]
+
+ public static fromJson(json: any, completionData: any): LearningPath {
+ return new LearningPath(json.id, json.slug, json.title, json.translation_key, json.children, completionData)
+ }
+
+ constructor(
+ public readonly id: number,
+ public readonly slug: string,
+ public readonly title: string,
+ public readonly translation_key: string,
+ public children: LearningPathChild[],
+ completionData?: any
+ ) {
+ // parse children
+ this.topics = []
+ this.circles = []
+
+ let topic: Topic | undefined
+
+ this.children.forEach((page) => {
+ if (page.type === 'learnpath.Topic') {
+ if (topic) {
+ this.topics.push(topic)
+ }
+ topic = Object.assign(page, { circles: [] })
+ }
+ if (page.type === 'learnpath.Circle') {
+ const circle = Circle.fromJson(page)
+ circle.parseCompletionData(completionData)
+ if (topic) {
+ topic.circles.push(circle)
+ }
+ this.circles.push(circle)
+ }
+ })
+
+ if (topic) {
+ this.topics.push(topic)
+ }
+ }
+}
diff --git a/client/src/stores/learningPath.ts b/client/src/stores/learningPath.ts
index 0f3c5668..7b6ad0d4 100644
--- a/client/src/stores/learningPath.ts
+++ b/client/src/stores/learningPath.ts
@@ -1,5 +1,8 @@
-import * as log from 'loglevel';
+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';
@@ -9,7 +12,7 @@ import {Circle} from '@/services/circle';
import learningPathDiagram from "@/components/circle/LearningPathDiagram.vue";
export type LearningPathStoreState = {
- learningPath: LearningPath | undefined;
+ learningPath: LearningPath | undefined
}
@@ -76,7 +79,7 @@ export const useLearningPathStore = defineStore({
this.learningPath = learningPathData;
- if (this.learningPath) {
+ if (learningPathData) {
this.learningPath.lastCompleted = getLastCompleted(completionData)
const nextLearningContent = getNextLearningContent(this.learningPath.lastCompleted, learningPathData)
@@ -86,29 +89,7 @@ export const useLearningPathStore = defineStore({
this.learningPath.nextLearningUnit = nextLearningContent[2]
- this.learningPath.topics = [];
- this.learningPath.circles = [];
-
- let topic: Topic | undefined;
-
- this.learningPath.children.forEach((page) => {
- if (page.type === 'learnpath.Topic') {
- if (topic) {
- this.learningPath.topics.push(topic);
- }
- topic = Object.assign(page, {circles: []});
- }
- if (page.type === 'learnpath.Circle') {
- const circle = Circle.fromJson(page);
- circle.parseCompletionData(completionData);
- if (topic) {
- topic.circles.push(circle);
- }
- this.learningPath.circles.push(circle);
- }
-
- })
- this.learningPath.topics.push(topic);
+ this.learningPath = LearningPath.fromJson(learningPathData, completionData);
}
return this.learningPath;
} catch (error) {
diff --git a/client/src/types.ts b/client/src/types.ts
index 3a8ff19b..b349af30 100644
--- a/client/src/types.ts
+++ b/client/src/types.ts
@@ -1,11 +1,11 @@
-import type {Circle} from '@/services/circle';
+import type { Circle } from '@/services/circle'
export interface LearningContentBlock {
- type: 'web-based-training' | 'competence' | 'exercise' | 'knowledge';
+ type: 'web-based-training' | 'competence' | 'exercise' | 'knowledge'
value: {
- description: string;
- },
- id: string;
+ description: string
+ }
+ id: string
}
export interface VideoBlock {
@@ -103,18 +103,6 @@ export interface Topic extends LearningWagtailPage {
export type LearningPathChild = Topic | WagtailCircle;
-export interface LearningPath extends LearningWagtailPage {
- type: 'learnpath.LearningPath';
- children: LearningPathChild[];
- topics: Topic[];
- circles: Circle[];
- lastCompleted: CircleCompletion;
- nextCircle: Circle;
- nextLearningSequence: LearningSequence;
- nextLearningUnit: LearningContent;
-
-}
-
export interface CircleCompletion {
id: number;
created_at: string;
From a32c8ccbff71374c34e28ea0f8daec8fb7e4eda6 Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 16:34:08 +0200
Subject: [PATCH 05/11] VBV-80: Refactor next learning content
---
client/package.json | 1 +
client/src/services/circle.ts | 20 ++++++++-
client/src/services/learningPath.ts | 37 +++++++++++++++-
client/src/stores/learningPath.ts | 63 ---------------------------
client/src/types.ts | 2 +
client/src/views/LearningPathView.vue | 16 +++----
client/vite.config.ts | 2 +-
7 files changed, 66 insertions(+), 75 deletions(-)
diff --git a/client/package.json b/client/package.json
index 09806947..91a7c6e4 100644
--- a/client/package.json
+++ b/client/package.json
@@ -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",
diff --git a/client/src/services/circle.ts b/client/src/services/circle.ts
index 09cd0e69..54e8eb46 100644
--- a/client/src/services/circle.ts
+++ b/client/src/services/circle.ts
@@ -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) => {
diff --git a/client/src/services/learningPath.ts b/client/src/services/learningPath.ts
index b902c71e..1c7c45ae 100644
--- a/client/src/services/learningPath.ts
+++ b/client/src/services/learningPath.ts
@@ -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);
}
}
diff --git a/client/src/stores/learningPath.ts b/client/src/stores/learningPath.ts
index 7b6ad0d4..9dcfb1a9 100644
--- a/client/src/stores/learningPath.ts
+++ b/client/src/stores/learningPath.ts
@@ -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;
diff --git a/client/src/types.ts b/client/src/types.ts
index b349af30..420c1984 100644
--- a/client/src/types.ts
+++ b/client/src/types.ts
@@ -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;
}
diff --git a/client/src/views/LearningPathView.vue b/client/src/views/LearningPathView.vue
index d77d7470..ee0c80ec 100644
--- a/client/src/views/LearningPathView.vue
+++ b/client/src/views/LearningPathView.vue
@@ -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 () => {
-
+
Nächster Schirtt
-
{{ learningPathStore.learningPath.nextCircle.title }}: {{ learningPathStore.learningPath.nextLearningSequence.title }}
-
+ {{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}: {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}
+
Los geht's
diff --git a/client/vite.config.ts b/client/vite.config.ts
index 8fc64adf..36825c0d 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -43,7 +43,7 @@ export default defineConfig(({ mode }) => {
},
test: {
globals: true,
- environment: 'happy-dom',
+ environment: 'jsdom',
},
}
})
From 231615037540c60b34a979621d32826b8fe586b5 Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Tue, 30 Aug 2022 17:47:32 +0200
Subject: [PATCH 06/11] Update `nextLearningContent` dynamically
---
client/src/components/ui/ItFullScreenModal.vue | 1 -
client/src/services/circle.ts | 9 ++++++++-
client/src/services/learningPath.ts | 17 +++++++++++------
3 files changed, 19 insertions(+), 8 deletions(-)
diff --git a/client/src/components/ui/ItFullScreenModal.vue b/client/src/components/ui/ItFullScreenModal.vue
index 22fbbf96..4aa10be1 100644
--- a/client/src/components/ui/ItFullScreenModal.vue
+++ b/client/src/components/ui/ItFullScreenModal.vue
@@ -2,7 +2,6 @@
// inspiration https://vuejs.org/examples/#modal
import {onMounted, watch} from "vue";
-import {HTMLElement} from "happy-dom";
const props = defineProps<{
show: boolean
diff --git a/client/src/services/circle.ts b/client/src/services/circle.ts
index 54e8eb46..eb6dba57 100644
--- a/client/src/services/circle.ts
+++ b/client/src/services/circle.ts
@@ -8,6 +8,7 @@ import type {
LearningUnit,
LearningWagtailPage,
} from '@/types'
+import type { LearningPath } from '@/services/learningPath'
function _createEmptyLearningUnit(parentLearningSequence: LearningSequence): LearningUnit {
return {
@@ -124,12 +125,13 @@ export class Circle implements LearningWagtailPage {
public children: CircleChild[],
public goals: CircleGoal[],
public job_situations: CircleJobSituation[],
+ public readonly parentLearningPath?: LearningPath,
) {
this.learningSequences = parseLearningSequences(this, this.children);
this.completed = false;
}
- public static fromJson(json: any): Circle {
+ public static fromJson(json: any, learningPath?: LearningPath): Circle {
// TODO add error checking when the data does not conform to the schema
return new Circle(
json.id,
@@ -140,6 +142,7 @@ export class Circle implements LearningWagtailPage {
json.children,
json.goals,
json.job_situations,
+ learningPath,
)
}
@@ -206,5 +209,9 @@ export class Circle implements LearningWagtailPage {
page.completed = undefined;
}
});
+
+ if (this.parentLearningPath) {
+ this.parentLearningPath.calcNextLearningContent(completionData);
+ }
}
}
diff --git a/client/src/services/learningPath.ts b/client/src/services/learningPath.ts
index 1c7c45ae..0b7d2439 100644
--- a/client/src/services/learningPath.ts
+++ b/client/src/services/learningPath.ts
@@ -15,7 +15,7 @@ export class LearningPath implements LearningWagtailPage {
public circles: Circle[]
public nextLearningContent?: LearningContent
- public static fromJson(json: any, completionData: any): LearningPath {
+ public static fromJson(json: any, completionData: CircleCompletion[]): LearningPath {
return new LearningPath(json.id, json.slug, json.title, json.translation_key, json.children, completionData)
}
@@ -41,7 +41,7 @@ export class LearningPath implements LearningWagtailPage {
topic = Object.assign(page, { circles: [] })
}
if (page.type === 'learnpath.Circle') {
- const circle = Circle.fromJson(page)
+ const circle = Circle.fromJson(page, this)
circle.parseCompletionData(completionData)
if (topic) {
topic.circles.push(circle)
@@ -59,7 +59,12 @@ export class LearningPath implements LearningWagtailPage {
this.topics.push(topic)
}
- // find next learning content
+ this.calcNextLearningContent(completionData);
+ }
+
+ public calcNextLearningContent(completionData: CircleCompletion[]): void {
+ this.nextLearningContent = undefined;
+
const lastCompletedLearningContent = getLastCompleted(this.translation_key, completionData);
if (lastCompletedLearningContent) {
@@ -75,9 +80,9 @@ export class LearningPath implements LearningWagtailPage {
}
}
} else {
- this.nextLearningContent = this.circles[0].flatLearningContents[0];
+ if (this.circles[0]) {
+ this.nextLearningContent = this.circles[0].flatLearningContents[0];
+ }
}
-
- console.log('################# ', this.nextLearningContent);
}
}
From 911b974360b585294a0937aa9203409762e78adf Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Wed, 31 Aug 2022 12:23:05 +0200
Subject: [PATCH 07/11] Fix some typescript warnings
---
.tool-versions | 2 +-
client/package.json | 3 +-
.../src/components/circle/CircleOverview.vue | 23 +++++---------
.../src/components/circle/LearningContent.vue | 2 +-
client/src/services/circle.ts | 5 +--
client/src/services/learningPath.ts | 20 +++++++-----
client/src/views/LoginView.vue | 31 +++++++------------
client/tsconfig.app.json | 1 +
8 files changed, 39 insertions(+), 48 deletions(-)
diff --git a/.tool-versions b/.tool-versions
index a6a8eac6..8c4ec728 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,2 +1,2 @@
-nodejs 16.10.0
+nodejs 16.17.0
python 3.10.5
diff --git a/client/package.json b/client/package.json
index 91a7c6e4..07906114 100644
--- a/client/package.json
+++ b/client/package.json
@@ -7,7 +7,7 @@
"build:tailwind": "tailwindcss -i tailwind.css -o ../server/vbv_lernwelt/static/css/tailwind.css --minify",
"test": "vitest run",
"coverage": "vitest run --coverage",
- "typecheck": "vue-tsc --noEmit -p tsconfig.vitest.json --composite false",
+ "typecheck": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"tailwind": "tailwindcss -i tailwind.css -o ../server/vbv_lernwelt/static/css/tailwind.css --watch"
},
@@ -30,6 +30,7 @@
"@testing-library/vue": "^6.6.1",
"@types/d3": "^7.4.0",
"@types/jsdom": "^20.0.0",
+ "@types/lodash": "^4.14.184",
"@types/node": "^18.7.14",
"@vitejs/plugin-vue": "^3.0.3",
"@vue/eslint-config-prettier": "^7.0.0",
diff --git a/client/src/components/circle/CircleOverview.vue b/client/src/components/circle/CircleOverview.vue
index 59776904..ce95474a 100644
--- a/client/src/components/circle/CircleOverview.vue
+++ b/client/src/components/circle/CircleOverview.vue
@@ -1,27 +1,23 @@
-
+
Überblick: Circle "{{ circle.title }}"
Hier zeigen wir dir, was du in diesem Circle lernen wirst.
-
Du wirst in der Lage sein, ...
+
Du wirst in der Lage sein, ...
-
@@ -31,9 +27,7 @@ const emits = defineEmits(['closemodal'])
-
- Du wirst dein neu erworbenes Wissen auf folgenden berufstypischen Situation anwenden können:
-
+ Du wirst dein neu erworbenes Wissen auf folgenden berufstypischen Situation anwenden können:
-
- {{jobSituation.value}}
+ {{ jobSituation.value }}
-
+
diff --git a/client/src/components/circle/LearningContent.vue b/client/src/components/circle/LearningContent.vue
index 44e6ca56..6644ba26 100644
--- a/client/src/components/circle/LearningContent.vue
+++ b/client/src/components/circle/LearningContent.vue
@@ -37,7 +37,7 @@ const block = computed(() => {
zurück zum Circle
- {{ learningContent.title }}
+ {{ learningContent?.title }}
@@ -117,15 +114,8 @@ onMounted(async () => {
diff --git a/client/src/views/CockpitView.vue b/client/src/views/CockpitView.vue
index ff53dac3..a4774bff 100644
--- a/client/src/views/CockpitView.vue
+++ b/client/src/views/CockpitView.vue
@@ -1,31 +1,25 @@
- Willkommen, {{userStore.first_name}}
+ Willkommen, {{ userStore.first_name }}
Deine Kurse
Versicherungsvermittler/in
-
- Weiter gehts
-
+ Weiter gehts
-
-
-
+
diff --git a/client/src/views/HomeView.vue b/client/src/views/HomeView.vue
deleted file mode 100644
index 828fd2a3..00000000
--- a/client/src/views/HomeView.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-
-
- myVBV Start Page
-
-
-
Styelguide
-
Login
-
-
Lernpfad "Versicherungsvermittlerin"
-
Circle "Analyse"
-
-
-
-
-
diff --git a/client/src/views/LearningPathView.vue b/client/src/views/LearningPathView.vue
index ee0c80ec..fcc892e8 100644
--- a/client/src/views/LearningPathView.vue
+++ b/client/src/views/LearningPathView.vue
@@ -1,31 +1,31 @@
@@ -42,12 +42,8 @@ onMounted(async () => {
-
@@ -61,19 +57,21 @@ onMounted(async () => {
{{ learningPathStore.learningPath.title }}
+ class="bg-white m-12 p-8 flex flex-col lg:flex-row divide-y lg:divide-y-0 lg:divide-x divide-gray-500 justify-start"
+ >
Willkommmen zurück, {{ userStore.first_name }}
-
-
-
+
Nächster Schirtt
-
{{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}: {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}
+
+ {{ learningPathStore.learningPath.nextLearningContent.parentCircle.title }}:
+ {{ learningPathStore.learningPath.nextLearningContent.parentLearningSequence.title }}
+
Los geht's
@@ -86,5 +84,4 @@ onMounted(async () => {
-
+
diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py
index 78688654..71cc5528 100644
--- a/server/vbv_lernwelt/learnpath/models.py
+++ b/server/vbv_lernwelt/learnpath/models.py
@@ -22,7 +22,7 @@ class LearningPath(Page):
verbose_name = "Learning Path"
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(Page, slugify(self.title, allow_unicode=True))
+ self.slug = find_available_slug(slugify(self.title, allow_unicode=True))
super(LearningPath, self).full_clean(*args, **kwargs)
def __str__(self):
@@ -54,8 +54,7 @@ class Topic(Page):
# subpage_types = ['learnpath.Circle']
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(Topic, slugify(self.title, allow_unicode=True))
- print(self.slug)
+ self.slug = find_available_slug(slugify(f'topic-{self.title}', allow_unicode=True))
super(Topic, self).full_clean(*args, **kwargs)
@classmethod
@@ -115,8 +114,7 @@ class Circle(Page):
)
def full_clean(self, *args, **kwargs):
- # TODO: why own slug function?
- self.slug = find_available_slug(Page, slugify(self.title, allow_unicode=True))
+ self.slug = find_available_slug(slugify(self.title, allow_unicode=True))
super(Circle, self).full_clean(*args, **kwargs)
class Meta:
@@ -157,6 +155,7 @@ class LearningSequence(Page):
'''
def full_clean(self, *args, **kwargs):
+ self.slug = find_available_slug(slugify(f'ls-{self.title}', allow_unicode=True))
super(LearningSequence, self).full_clean(*args, **kwargs)
@@ -170,6 +169,10 @@ class LearningUnit(Page):
def __str__(self):
return f"{self.title}"
+ def full_clean(self, *args, **kwargs):
+ self.slug = find_available_slug(slugify(f'lu-{self.title}', allow_unicode=True))
+ super(LearningUnit, self).full_clean(*args, **kwargs)
+
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'children'])
@@ -188,6 +191,10 @@ class LearningUnitQuestion(Page):
def __str__(self):
return f"{self.title}"
+ def full_clean(self, *args, **kwargs):
+ self.slug = find_available_slug(slugify(f'luq-{self.title}', allow_unicode=True))
+ super(LearningUnitQuestion, self).full_clean(*args, **kwargs)
+
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', ])
@@ -240,7 +247,8 @@ class LearningContent(Page):
verbose_name = "Learning Content"
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(LearningContent, slugify(self.title, allow_unicode=True))
+ self.slug = find_available_slug(slugify(f'lc-{self.title}', allow_unicode=True))
+ print(self.slug)
super(LearningContent, self).full_clean(*args, **kwargs)
@classmethod
@@ -253,7 +261,7 @@ class LearningContent(Page):
return f"{self.title}"
-def find_available_slug(model, requested_slug, ignore_page_id=None):
+def find_available_slug(requested_slug, ignore_page_id=None):
"""
Finds an available slug within the specified parent.
@@ -270,8 +278,7 @@ def find_available_slug(model, requested_slug, ignore_page_id=None):
treated as in use by another page.
"""
- # TODO: In comparison ot wagtails own function, I look for the same model instead of the parent
- pages = model.objects.filter(slug__startswith=requested_slug)
+ pages = Page.objects.filter(slug__startswith=requested_slug)
if ignore_page_id:
pages = pages.exclude(id=ignore_page_id)
From a457a1fff314b2f86deb4b796889cbd070baf6d2 Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Wed, 31 Aug 2022 15:32:35 +0200
Subject: [PATCH 10/11] Fix cypress tests
---
cypress/e2e/circle.cy.js | 2 +-
cypress/e2e/learningPath.cy.js | 10 +++++-----
cypress/e2e/login.cy.js | 2 +-
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/cypress/e2e/circle.cy.js b/cypress/e2e/circle.cy.js
index 788f5174..2d6cd7fd 100644
--- a/cypress/e2e/circle.cy.js
+++ b/cypress/e2e/circle.cy.js
@@ -5,7 +5,7 @@ describe("circle page", () => {
cy.manageCommand("cypress_reset");
login("admin", "test");
- cy.visit("/circle/analyse");
+ cy.visit("/learn/versicherungsvermittlerin/analyse");
});
it("can open circle page", () => {
diff --git a/cypress/e2e/learningPath.cy.js b/cypress/e2e/learningPath.cy.js
index 306d8c86..7319d285 100644
--- a/cypress/e2e/learningPath.cy.js
+++ b/cypress/e2e/learningPath.cy.js
@@ -7,7 +7,7 @@ describe("learningPath page", () => {
it("can open learningPath page", () => {
login("admin", "test");
- cy.visit("/learningpath/versicherungsvermittlerin");
+ cy.visit("/learn/versicherungsvermittlerin");
cy.get('[data-cy="learning-path-title"]').should(
"contain",
@@ -17,24 +17,24 @@ describe("learningPath page", () => {
it("click on circle on learningPath page will open circle", () => {
login("admin", "test");
- cy.visit("/learningpath/versicherungsvermittlerin");
+ cy.visit("/learn/versicherungsvermittlerin");
cy.get('[data-cy="circle-analyse"]').click({ force: true });
- cy.url().should("include", "/circle/analyse");
+ cy.url().should("include", "/learn/versicherungsvermittlerin/analyse");
cy.get('[data-cy="circle-title"]').should("contain", "Analyse");
});
it("open listView and click on cirle will open circle", () => {
login("admin", "test");
- cy.visit("/learningpath/versicherungsvermittlerin");
+ cy.visit("/learn/versicherungsvermittlerin");
cy.get('[data-cy="show-list-view"]').click();
cy.get('[data-cy="full-screen-modal"]').should("be.visible");
cy.get('[data-cy="circle-analyse-vertical"]').click({ force: true });
- cy.url().should("include", "/circle/analyse");
+ cy.url().should("include", "/learn/versicherungsvermittlerin/analyse");
cy.get('[data-cy="circle-title"]').should("contain", "Analyse");
});
});
diff --git a/cypress/e2e/login.cy.js b/cypress/e2e/login.cy.js
index 20d073f9..0b56b3cc 100644
--- a/cypress/e2e/login.cy.js
+++ b/cypress/e2e/login.cy.js
@@ -31,7 +31,7 @@ describe("login", () => {
});
it("login will redirect to requestet page", () => {
- cy.visit("/learningpath/versicherungsvermittlerin");
+ cy.visit("/learn/versicherungsvermittlerin");
cy.get("h1").should("contain", "Login");
cy.get("#username").type("admin");
From 26f373afe24b36119273aba6fc039aa576368b2f Mon Sep 17 00:00:00 2001
From: Daniel Egger
Date: Wed, 31 Aug 2022 18:36:10 +0200
Subject: [PATCH 11/11] Add e2e test for "weiter gehts"-button
---
.../components/circle/LearningSequence.vue | 74 +++++++------------
client/src/views/CircleView.vue | 1 +
client/src/views/LearningPathView.vue | 1 +
cypress/e2e/learningPath.cy.js | 19 +++++
4 files changed, 48 insertions(+), 47 deletions(-)
diff --git a/client/src/components/circle/LearningSequence.vue b/client/src/components/circle/LearningSequence.vue
index dcbd0cd0..91ccf59b 100644
--- a/client/src/components/circle/LearningSequence.vue
+++ b/client/src/components/circle/LearningSequence.vue
@@ -1,37 +1,37 @@
@@ -57,15 +56,8 @@ const learningSequenceBorderClass = computed(() => {
{{ learningSequence.minutes }} Minuten
-
-
+
+
{{ learningUnit.title }}
{{ learningUnit.minutes }} Minuten
@@ -79,48 +71,36 @@ const learningSequenceBorderClass = computed(() => {
- {{ learningContent.contents[0].type }}: {{ learningContent.title }}
+ {{ learningContent.contents[0].type }}: {{ learningContent.title }}
-
-
-
+
+
+
Selbsteinschätzung: Ich kann das.
-
+
Selbsteinschätzung: Muss ich nochmals anschauen
-
-
-
+
-
-
+
diff --git a/client/src/views/CircleView.vue b/client/src/views/CircleView.vue
index 31e9c206..358301c0 100644
--- a/client/src/views/CircleView.vue
+++ b/client/src/views/CircleView.vue
@@ -53,6 +53,7 @@ onMounted(async () => {
zurück zum Lernpfad
diff --git a/client/src/views/LearningPathView.vue b/client/src/views/LearningPathView.vue
index fcc892e8..5c24eeee 100644
--- a/client/src/views/LearningPathView.vue
+++ b/client/src/views/LearningPathView.vue
@@ -72,6 +72,7 @@ onMounted(async () => {
Los geht's
diff --git a/cypress/e2e/learningPath.cy.js b/cypress/e2e/learningPath.cy.js
index 7319d285..280d5ce6 100644
--- a/cypress/e2e/learningPath.cy.js
+++ b/cypress/e2e/learningPath.cy.js
@@ -37,4 +37,23 @@ describe("learningPath page", () => {
cy.url().should("include", "/learn/versicherungsvermittlerin/analyse");
cy.get('[data-cy="circle-title"]').should("contain", "Analyse");
});
+
+ it("weiter gehts button will open next circle", () => {
+ login("admin", "test");
+ cy.visit("/learn/unit-test-lernpfad");
+
+ // first click will open first circle
+ cy.get('[data-cy="continue-button"]').click();
+ cy.get('[data-cy="circle-title"]').should("contain", "Basis");
+ cy.get('[data-cy="back-to-learning-path-button"]').click();
+
+ // mark a learning content in second circle
+ cy.get('[data-cy="circle-unit-test-circle"]').click({ force: true });
+ cy.get('[data-cy="lc-reiseversicherung-7"] > .cy-checkbox').click();
+ cy.get('[data-cy="back-to-learning-path-button"]').click();
+
+ // click on continue should go to unit-test-circle
+ cy.get('[data-cy="continue-button"]').click();
+ cy.get('[data-cy="circle-title"]').should("contain", "Unit-Test Circle");
+ });
});