Merged in feature/completion-check-permissions (pull request #19)

Feature/completion check permissions

Approved-by: Christian Cueni
This commit is contained in:
Daniel Egger 2023-01-11 15:19:38 +00:00
commit dcf42934b1
22 changed files with 315 additions and 299 deletions

View File

@ -84,7 +84,7 @@ function showFileInformation() {
{{ $t("circlePage.documents.fileLabel") }} {{ $t("circlePage.documents.fileLabel") }}
</label> </label>
<div class="mb-4 btn-secondary mt-4 text-xl relative cursor-pointer"> <div class="mb-4 btn-secondary mt-4 text-xl relative cursor-pointer">
<input @change="fileChange" id="upload" type="file" class="absolute opacity-0" /> <input id="upload" type="file" class="absolute opacity-0" @change="fileChange" />
{{ $t("circlePage.documents.modalAction") }} {{ $t("circlePage.documents.modalAction") }}
</div> </div>
<div v-if="showFileInformation()" class="mb-4"> <div v-if="showFileInformation()" class="mb-4">
@ -100,7 +100,7 @@ function showFileInformation() {
<label class="block text-bold mb-4" for="name"> <label class="block text-bold mb-4" for="name">
{{ $t("circlePage.documents.modalFileName") }} {{ $t("circlePage.documents.modalFileName") }}
</label> </label>
<input v-model="formData.name" id="name" type="text" class="w-1/2 mb-2" /> <input id="name" v-model="formData.name" type="text" class="w-1/2 mb-2" />
<p>{{ $t("circlePage.documents.modalNameInformation") }}</p> <p>{{ $t("circlePage.documents.modalNameInformation") }}</p>
<div v-if="formErrors.name"> <div v-if="formErrors.name">
<p class="text-red-700">{{ $t("circlePage.documents.chooseName") }}</p> <p class="text-red-700">{{ $t("circlePage.documents.chooseName") }}</p>

View File

@ -7,7 +7,9 @@ import type { LearningPath } from "@/services/learningPath";
import { useCockpitStore } from "@/stores/cockpit"; import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence"; import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath"; import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import log from "loglevel"; import log from "loglevel";
import { computed } from "vue";
const props = defineProps<{ const props = defineProps<{
courseSlug: string; courseSlug: string;
@ -15,6 +17,7 @@ const props = defineProps<{
log.debug("CockpitIndexPage created", props.courseSlug); log.debug("CockpitIndexPage created", props.courseSlug);
const userStore = useUserStore();
const cockpitStore = useCockpitStore(); const cockpitStore = useCockpitStore();
const competenceStore = useCompetenceStore(); const competenceStore = useCompetenceStore();
const learningPathStore = useLearningPathStore(); const learningPathStore = useLearningPathStore();
@ -25,6 +28,33 @@ function userCountStatus(userId: number) {
); );
} }
const circles = computed(() => {
const learningPathCircles = learningPathStore
.learningPathForUser(props.courseSlug, userStore.id)
?.circles.map((c) => {
return {
id: c.id,
title: c.title,
slug: c.slug,
translation_key: c.translation_key,
};
});
if (cockpitStore.cockpitSessionUser?.circles?.length) {
return cockpitStore.cockpitSessionUser.circles;
} else if (learningPathCircles) {
return learningPathCircles;
} else {
return [];
}
});
const selectedCirclesTitles = computed(() => {
return circles.value
.filter((c) => cockpitStore.selectedCircles.includes(c.translation_key))
.map((c) => c.title);
});
const data = { const data = {
transferProgress: { transferProgress: {
fail: 0, fail: 0,
@ -47,7 +77,7 @@ function setActiveClasses(translationKey: string) {
<h1 class="heading-3">{{ $t("general.circles") }}:</h1> <h1 class="heading-3">{{ $t("general.circles") }}:</h1>
<ul class="flex flex-row leading-7 text-base font-bold ml-4"> <ul class="flex flex-row leading-7 text-base font-bold ml-4">
<li <li
v-for="circle in cockpitStore.circles" v-for="circle in circles"
:key="circle.translation_key" :key="circle.translation_key"
class="mr-4 last:mr-0" class="mr-4 last:mr-0"
> >
@ -132,10 +162,7 @@ function setActiveClasses(translationKey: string) {
></LearningPathDiagram> ></LearningPathDiagram>
</div> </div>
<div> <div>
<span <span v-for="title in selectedCirclesTitles" :key="title">
v-for="title in cockpitStore.selectedCirclesTitles"
:key="title"
>
{{ title }} {{ title }}
</span> </span>
</div> </div>

View File

@ -2,6 +2,7 @@
import { useCockpitStore } from "@/stores/cockpit"; import { useCockpitStore } from "@/stores/cockpit";
import { useCompetenceStore } from "@/stores/competence"; import { useCompetenceStore } from "@/stores/competence";
import { useLearningPathStore } from "@/stores/learningPath"; import { useLearningPathStore } from "@/stores/learningPath";
import { useUserStore } from "@/stores/user";
import * as log from "loglevel"; import * as log from "loglevel";
import { onMounted } from "vue"; import { onMounted } from "vue";
@ -28,6 +29,7 @@ onMounted(async () => {
learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id); learningPathStore.loadLearningPath(props.courseSlug + "-lp", csu.user_id);
}); });
learningPathStore.loadLearningPath(props.courseSlug + "-lp", useUserStore().id);
} catch (error) { } catch (error) {
log.error(error); log.error(error);
} }

View File

@ -276,6 +276,20 @@ async function uploadDocument(data: DocumentUploadData) {
}) })
}} }}
</div> </div>
<div
v-for="expert in courseSessionsStore.circleExperts"
:key="expert.user_id"
>
<div class="flex flex-row items-center mt-2 mb-2">
<img
class="h-[45px] rounded-full mr-2"
:src="expert.avatar_url"
/>
<p class="lg:leading-[45px]">
{{ expert.first_name }} {{ expert.last_name }}
</p>
</div>
</div>
<button class="btn-secondary mt-4 text-xl"> <button class="btn-secondary mt-4 text-xl">
{{ $t("circlePage.contactExpertButton") }} {{ $t("circlePage.contactExpertButton") }}
</button> </button>
@ -302,10 +316,10 @@ async function uploadDocument(data: DocumentUploadData) {
<template #title>{{ $t("circlePage.documents.action") }}</template> <template #title>{{ $t("circlePage.documents.action") }}</template>
<template #body> <template #body>
<DocumentUploadForm <DocumentUploadForm
@form-submit="uploadDocument"
:learning-sequences="dropdownLearningSequences" :learning-sequences="dropdownLearningSequences"
:show-upload-error-message="showUploadErrorMessage" :show-upload-error-message="showUploadErrorMessage"
:is-uploading="isUploading" :is-uploading="isUploading"
@form-submit="uploadDocument"
/> />
</template> </template>
</ItModal> </ItModal>

View File

@ -1,59 +1,59 @@
{ {
"id": 362, "id": 568,
"title": "Test Lernpfad", "title": "Test Lernpfad",
"slug": "test-lehrgang-lp", "slug": "test-lehrgang-lp",
"type": "learnpath.LearningPath", "type": "learnpath.LearningPath",
"translation_key": "8a230aa1-075e-4ac1-a8d6-87642c4f33ba", "translation_key": "25ddc136-af7d-4a74-8731-896281bfe20b",
"frontend_url": "/learn/test-lehrgang-lp", "frontend_url": "/course/test-lehrgang/learn",
"children": [ "children": [
{ {
"id": 363, "id": 569,
"title": "Basis", "title": "Basis",
"slug": "test-lehrgang-lp-topic-basis", "slug": "test-lehrgang-lp-topic-basis",
"type": "learnpath.Topic", "type": "learnpath.Topic",
"translation_key": "d6e14156-2fb9-4f1b-83ce-6879e364f9a2", "translation_key": "8cb75aee-0349-41e2-9d2b-2938c2b04891",
"frontend_url": "", "frontend_url": "",
"is_visible": false "is_visible": false
}, },
{ {
"id": 364, "id": 570,
"title": "Basis", "title": "Basis",
"slug": "test-lehrgang-lp-circle-basis", "slug": "test-lehrgang-lp-circle-basis",
"type": "learnpath.Circle", "type": "learnpath.Circle",
"translation_key": "8034e867-4b05-4509-a9bc-99f9f3619e88", "translation_key": "aaa04c2b-03bf-470e-b414-bd8203db529d",
"frontend_url": "/learn/test-lehrgang-lp/basis", "frontend_url": "/course/test-lehrgang/learn/basis",
"children": [ "children": [
{ {
"id": 365, "id": 571,
"title": "Starten", "title": "Starten",
"slug": "test-lehrgang-lp-circle-basis-ls-starten", "slug": "test-lehrgang-lp-circle-basis-ls-starten",
"type": "learnpath.LearningSequence", "type": "learnpath.LearningSequence",
"translation_key": "868bc4cb-c5b5-423e-a890-433184cd06e0", "translation_key": "ce16116f-f4ed-4fc0-a95a-cc75ba88958d",
"frontend_url": "/learn/test-lehrgang-lp/basis#ls-starten", "frontend_url": "/course/test-lehrgang/learn/basis#ls-starten",
"icon": "it-icon-ls-start" "icon": "it-icon-ls-start"
}, },
{ {
"id": 366, "id": 572,
"title": "Einf\u00fchrung", "title": "Einf\u00fchrung",
"slug": "test-lehrgang-lp-circle-basis-lu-einf\u00fchrung", "slug": "test-lehrgang-lp-circle-basis-lu-einf\u00fchrung",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "6b0a4794-9861-4ea4-b422-99261a4347a6", "translation_key": "33863daf-a503-425d-a1a2-9a711f8256a5",
"frontend_url": "/learn/test-lehrgang-lp/basis#lu-einf\u00fchrung", "frontend_url": "/course/test-lehrgang/learn/basis#lu-einf\u00fchrung",
"evaluate_url": "/learn/test-lehrgang-lp/basis/evaluate/einf\u00fchrung", "evaluate_url": "/course/test-lehrgang/learn/basis/evaluate/einf\u00fchrung",
"course_category": { "course_category": {
"id": 14, "id": 27,
"title": "Allgemein", "title": "Allgemein",
"general": true "general": true
}, },
"children": [] "children": []
}, },
{ {
"id": 367, "id": 573,
"title": "Einf\u00fchrung", "title": "Einf\u00fchrung",
"slug": "test-lehrgang-lp-circle-basis-lc-einf\u00fchrung", "slug": "test-lehrgang-lp-circle-basis-lc-einf\u00fchrung",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "d1d1b923-f597-4de7-ac44-d02c2f0a1a59", "translation_key": "ae19458c-9b08-4b9c-9e7c-62c3cce4a6cc",
"frontend_url": "/learn/test-lehrgang-lp/basis/einf\u00fchrung", "frontend_url": "/course/test-lehrgang/learn/basis/einf\u00fchrung",
"minutes": 15, "minutes": 15,
"contents": [ "contents": [
{ {
@ -62,41 +62,41 @@
"description": "Beispiel Dokument", "description": "Beispiel Dokument",
"url": null "url": null
}, },
"id": "9f22d0b7-643a-4e97-816a-a41141befc95" "id": "601ac5e1-b268-442e-b15b-26ead06bdf77"
} }
] ]
}, },
{ {
"id": 368, "id": 574,
"title": "Beenden", "title": "Beenden",
"slug": "test-lehrgang-lp-circle-basis-ls-beenden", "slug": "test-lehrgang-lp-circle-basis-ls-beenden",
"type": "learnpath.LearningSequence", "type": "learnpath.LearningSequence",
"translation_key": "338208db-7c85-470e-872f-850e34747873", "translation_key": "3c167825-a358-4c62-a632-9482e9fbfe5f",
"frontend_url": "/learn/test-lehrgang-lp/basis#ls-beenden", "frontend_url": "/course/test-lehrgang/learn/basis#ls-beenden",
"icon": "it-icon-ls-end" "icon": "it-icon-ls-end"
}, },
{ {
"id": 369, "id": 575,
"title": "Beenden", "title": "Beenden",
"slug": "test-lehrgang-lp-circle-basis-lu-beenden", "slug": "test-lehrgang-lp-circle-basis-lu-beenden",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "c14b63d6-3144-41fa-8a3c-2eada6ddd5ea", "translation_key": "9a70a164-0468-4681-a6ed-a1b636db1ae6",
"frontend_url": "/learn/test-lehrgang-lp/basis#lu-beenden", "frontend_url": "/course/test-lehrgang/learn/basis#lu-beenden",
"evaluate_url": "/learn/test-lehrgang-lp/basis/evaluate/beenden", "evaluate_url": "/course/test-lehrgang/learn/basis/evaluate/beenden",
"course_category": { "course_category": {
"id": 14, "id": 27,
"title": "Allgemein", "title": "Allgemein",
"general": true "general": true
}, },
"children": [] "children": []
}, },
{ {
"id": 370, "id": 576,
"title": "Jetzt kann es losgehen!", "title": "Jetzt kann es losgehen!",
"slug": "test-lehrgang-lp-circle-basis-lc-jetzt-kann-es-losgehen", "slug": "test-lehrgang-lp-circle-basis-lc-jetzt-kann-es-losgehen",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "6920bcac-597b-462a-9458-32aa5dc8d3f7", "translation_key": "f251beda-a4b8-4a49-89a6-8c52d335b140",
"frontend_url": "/learn/test-lehrgang-lp/basis/jetzt-kann-es-losgehen", "frontend_url": "/course/test-lehrgang/learn/basis/jetzt-kann-es-losgehen",
"minutes": 30, "minutes": 30,
"contents": [ "contents": [
{ {
@ -105,7 +105,7 @@
"description": "Beispiel Dokument", "description": "Beispiel Dokument",
"url": null "url": null
}, },
"id": "1422a7c3-0a9a-4321-88a0-d82d0ed26ba2" "id": "1075443f-e2e5-46eb-87c4-804f0227a858"
} }
] ]
} }
@ -116,17 +116,17 @@
{ {
"type": "goal", "type": "goal",
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 1", "value": "... hier ein Beispieltext f\u00fcr ein Ziel 1",
"id": "38afbda4-7b7e-4f5c-88e8-c595c43e1659" "id": "6c227ac1-42a7-4ca3-bde2-8531bf655481"
}, },
{ {
"type": "goal", "type": "goal",
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 2", "value": "... hier ein Beispieltext f\u00fcr ein Ziel 2",
"id": "4d00ac58-0499-4316-9af2-356c37dedc35" "id": "7c466321-abe3-4705-bbb1-f286a8e5c800"
}, },
{ {
"type": "goal", "type": "goal",
"value": "... hier ein Beispieltext f\u00fcr ein Ziel 3", "value": "... hier ein Beispieltext f\u00fcr ein Ziel 3",
"id": "945eb104-8cc1-45cd-a07a-a4d3ec4f39a3" "id": "1e491838-62a9-4185-b321-f64ca7c99f36"
} }
], ],
"job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:", "job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:",
@ -134,101 +134,88 @@
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 1", "value": "Job Situation 1",
"id": "02fb807a-0d07-4353-81ec-8b8b383954d7" "id": "0d4a66de-523c-4659-9b4b-bb81cef28961"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 2", "value": "Job Situation 2",
"id": "371952f6-5871-4bf1-b423-d3dab7371001" "id": "dc4d8157-1db4-4e64-bc0c-00a35159c32d"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 3", "value": "Job Situation 3",
"id": "116bfa7b-65e8-44a1-8c82-e8b05fd86a01" "id": "a270baca-1077-4535-9a9f-5562af9854d8"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 4", "value": "Job Situation 4",
"id": "08baf7dd-8801-4af9-8af8-714989775ddb" "id": "aa4b21f6-0bd3-40f3-8589-c43e4524f565"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 5", "value": "Job Situation 5",
"id": "93ade4b8-c4fb-4941-98c5-e58336fca4bb" "id": "8618d1e7-c628-42ef-aac3-201b2b65393f"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 6", "value": "Job Situation 6",
"id": "1fac4ee4-6d86-4e9e-9fa4-a99c6659bc8b" "id": "be7a2a59-2aef-4dd8-ad8d-17c4a46b064f"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Job Situation 7", "value": "Job Situation 7",
"id": "06d1e273-dec8-4a0b-ae2c-2baeb7a19ec7" "id": "a4b74338-abd7-4de8-82ab-d77664f6cbbf"
}
],
"experts": [
{
"type": "person",
"value": {
"first_name": "Patrizia",
"last_name": "Mustermann",
"email": "patrizia.mustermann@example.com",
"photo": null,
"biography": ""
},
"id": "83490f33-da54-4548-baac-af75ea36651e"
} }
] ]
}, },
{ {
"id": 371, "id": 577,
"title": "Beraten der Kunden", "title": "Beraten der Kunden",
"slug": "test-lehrgang-lp-topic-beraten-der-kunden", "slug": "test-lehrgang-lp-topic-beraten-der-kunden",
"type": "learnpath.Topic", "type": "learnpath.Topic",
"translation_key": "728a2578-a22c-41df-9079-43a5318c5030", "translation_key": "2ea6ea41-b839-4462-aea4-1796b0f9849d",
"frontend_url": "", "frontend_url": "",
"is_visible": true "is_visible": true
}, },
{ {
"id": 372, "id": 578,
"title": "Analyse", "title": "Analyse",
"slug": "test-lehrgang-lp-circle-analyse", "slug": "test-lehrgang-lp-circle-analyse",
"type": "learnpath.Circle", "type": "learnpath.Circle",
"translation_key": "e429adf5-dd5d-4699-b471-40c782fb507e", "translation_key": "dbc95ecd-0fdf-4b0a-ba91-7eec88a420f3",
"frontend_url": "/learn/test-lehrgang-lp/analyse", "frontend_url": "/course/test-lehrgang/learn/analyse",
"children": [ "children": [
{ {
"id": 373, "id": 579,
"title": "Starten", "title": "Starten",
"slug": "test-lehrgang-lp-circle-analyse-ls-starten", "slug": "test-lehrgang-lp-circle-analyse-ls-starten",
"type": "learnpath.LearningSequence", "type": "learnpath.LearningSequence",
"translation_key": "40e977e0-3668-418d-b838-d3774a5cbe7d", "translation_key": "9b10c6ec-e313-49fa-b09d-9639429254f9",
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-starten", "frontend_url": "/course/test-lehrgang/learn/analyse#ls-starten",
"icon": "it-icon-ls-start" "icon": "it-icon-ls-start"
}, },
{ {
"id": 374, "id": 580,
"title": "Einf\u00fchrung", "title": "Einf\u00fchrung",
"slug": "test-lehrgang-lp-circle-analyse-lu-einf\u00fchrung", "slug": "test-lehrgang-lp-circle-analyse-lu-einf\u00fchrung",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "badfd186-26c1-433e-90ad-8cac52eb599f", "translation_key": "f51ab860-bc23-4f59-bb88-0e8506ef42e5",
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-einf\u00fchrung", "frontend_url": "/course/test-lehrgang/learn/analyse#lu-einf\u00fchrung",
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/einf\u00fchrung", "evaluate_url": "/course/test-lehrgang/learn/analyse/evaluate/einf\u00fchrung",
"course_category": { "course_category": {
"id": 14, "id": 27,
"title": "Allgemein", "title": "Allgemein",
"general": true "general": true
}, },
"children": [] "children": []
}, },
{ {
"id": 375, "id": 581,
"title": "Einleitung Circle \"Analyse\"", "title": "Einleitung Circle \"Analyse\"",
"slug": "test-lehrgang-lp-circle-analyse-lc-einleitung-circle-analyse", "slug": "test-lehrgang-lp-circle-analyse-lc-einleitung-circle-analyse",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "5e8d6478-6287-4658-94c5-ecbd5d624962", "translation_key": "70363615-2a53-478d-82f7-0aa02744f559",
"frontend_url": "/learn/test-lehrgang-lp/analyse/einleitung-circle-analyse", "frontend_url": "/course/test-lehrgang/learn/analyse/einleitung-circle-analyse",
"minutes": 15, "minutes": 15,
"contents": [ "contents": [
{ {
@ -237,60 +224,60 @@
"description": "Beispiel Dokument", "description": "Beispiel Dokument",
"url": null "url": null
}, },
"id": "8b7f183e-1879-4391-953f-52d9a621f435" "id": "174d883b-1bd0-4bee-872d-d08c2544dc3a"
} }
] ]
}, },
{ {
"id": 376, "id": 582,
"title": "Beobachten", "title": "Beobachten",
"slug": "test-lehrgang-lp-circle-analyse-ls-beobachten", "slug": "test-lehrgang-lp-circle-analyse-ls-beobachten",
"type": "learnpath.LearningSequence", "type": "learnpath.LearningSequence",
"translation_key": "35df96df-2e8d-4f16-aee1-8d72990f63a0", "translation_key": "0a540c5a-25c8-42ab-8661-6c08c78d91c2",
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-beobachten", "frontend_url": "/course/test-lehrgang/learn/analyse#ls-beobachten",
"icon": "it-icon-ls-watch" "icon": "it-icon-ls-watch"
}, },
{ {
"id": 377, "id": 583,
"title": "Fahrzeug", "title": "Fahrzeug",
"slug": "test-lehrgang-lp-circle-analyse-lu-fahrzeug", "slug": "test-lehrgang-lp-circle-analyse-lu-fahrzeug",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "405d42e4-ee10-4453-8e5f-82e49bb4d597", "translation_key": "f072e5b2-c033-4d7d-9dae-28e6b856087c",
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-fahrzeug", "frontend_url": "/course/test-lehrgang/learn/analyse#lu-fahrzeug",
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/fahrzeug", "evaluate_url": "/course/test-lehrgang/learn/analyse/evaluate/fahrzeug",
"course_category": { "course_category": {
"id": 15, "id": 28,
"title": "Fahrzeug", "title": "Fahrzeug",
"general": false "general": false
}, },
"children": [ "children": [
{ {
"id": 391, "id": 597,
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).", "title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
"slug": "test-lehrgang-competence-crit-y13-fahrzeug", "slug": "test-lehrgang-competence-crit-y13-fahrzeug",
"type": "competence.PerformanceCriteria", "type": "competence.PerformanceCriteria",
"translation_key": "3b714984-afdb-4456-9c01-a59064724929", "translation_key": "7d76cdd3-57a0-4f82-bc33-fcc8a78b15e0",
"frontend_url": "", "frontend_url": "",
"competence_id": "Y1.3" "competence_id": "Y1.3"
}, },
{ {
"id": 392, "id": 598,
"title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die IST-Situation des Kunden mit der geeigneten Gespr\u00e4chs-/Fragetechnik zu erfassen.", "title": "Innerhalb des Handlungsfelds \u00abFahrzeug\u00bb bin ich f\u00e4hig, die IST-Situation des Kunden mit der geeigneten Gespr\u00e4chs-/Fragetechnik zu erfassen.",
"slug": "test-lehrgang-competence-crit-y21-fahrzeug", "slug": "test-lehrgang-competence-crit-y21-fahrzeug",
"type": "competence.PerformanceCriteria", "type": "competence.PerformanceCriteria",
"translation_key": "c2850a27-60c5-471b-9fec-ba0baf152e91", "translation_key": "5b3e1105-a12b-4d99-984c-28f01daeef54",
"frontend_url": "", "frontend_url": "",
"competence_id": "Y2.1" "competence_id": "Y2.1"
} }
] ]
}, },
{ {
"id": 378, "id": 584,
"title": "Rafael Fasel wechselt sein Auto", "title": "Rafael Fasel wechselt sein Auto",
"slug": "test-lehrgang-lp-circle-analyse-lc-rafael-fasel-wechselt-sein-auto", "slug": "test-lehrgang-lp-circle-analyse-lc-rafael-fasel-wechselt-sein-auto",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "b7779d45-adf4-41fc-a4a5-e95c732b2224", "translation_key": "ff254250-d010-4f57-9e57-987c343029a6",
"frontend_url": "/learn/test-lehrgang-lp/analyse/rafael-fasel-wechselt-sein-auto", "frontend_url": "/course/test-lehrgang/learn/analyse/rafael-fasel-wechselt-sein-auto",
"minutes": 30, "minutes": 30,
"contents": [ "contents": [
{ {
@ -299,17 +286,17 @@
"description": "In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.", "description": "In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.",
"url": "" "url": ""
}, },
"id": "c79d34cb-0e7e-403d-a672-03d94cf6bdc7" "id": "454fb0d8-c37d-4c8e-b15c-c50a32be9fd7"
} }
] ]
}, },
{ {
"id": 379, "id": 585,
"title": "Fachcheck Fahrzeug", "title": "Fachcheck Fahrzeug",
"slug": "test-lehrgang-lp-circle-analyse-lc-fachcheck-fahrzeug", "slug": "test-lehrgang-lp-circle-analyse-lc-fachcheck-fahrzeug",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "e395e05c-81bf-4bc6-98e8-3833bebb551c", "translation_key": "b0a95253-3a63-4d77-9ddc-961b711175a5",
"frontend_url": "/learn/test-lehrgang-lp/analyse/fachcheck-fahrzeug", "frontend_url": "/course/test-lehrgang/learn/analyse/fachcheck-fahrzeug",
"minutes": 30, "minutes": 30,
"contents": [ "contents": [
{ {
@ -318,42 +305,42 @@
"description": "Beispiel Test", "description": "Beispiel Test",
"url": null "url": null
}, },
"id": "ac4c67bc-7de9-4e5c-a35e-e13f5766d6cc" "id": "4b3e40c5-f5f9-4726-bc4c-0d914a029f83"
} }
] ]
}, },
{ {
"id": 380, "id": 586,
"title": "Reisen", "title": "Reisen",
"slug": "test-lehrgang-lp-circle-analyse-lu-reisen", "slug": "test-lehrgang-lp-circle-analyse-lu-reisen",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "d0c956cc-3c86-4e08-9990-ed4e85d03219", "translation_key": "22bcb754-d5c5-413b-b63e-76e6406afef9",
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-reisen", "frontend_url": "/course/test-lehrgang/learn/analyse#lu-reisen",
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/reisen", "evaluate_url": "/course/test-lehrgang/learn/analyse/evaluate/reisen",
"course_category": { "course_category": {
"id": 16, "id": 29,
"title": "Reisen", "title": "Reisen",
"general": false "general": false
}, },
"children": [ "children": [
{ {
"id": 393, "id": 599,
"title": "Innerhalb des Handlungsfelds \u00abReisen\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).", "title": "Innerhalb des Handlungsfelds \u00abReisen\u00bb bin ich f\u00e4hig, die Ziele und Pl\u00e4ne des Kunden zu ergr\u00fcnden (SOLL).",
"slug": "test-lehrgang-competence-crit-y13-reisen", "slug": "test-lehrgang-competence-crit-y13-reisen",
"type": "competence.PerformanceCriteria", "type": "competence.PerformanceCriteria",
"translation_key": "1df45a12-41f2-4ff5-8580-d5a7caf5dd56", "translation_key": "6a3ce4ac-0d38-4a02-81fa-0979290142e1",
"frontend_url": "", "frontend_url": "",
"competence_id": "Y1.3" "competence_id": "Y1.3"
} }
] ]
}, },
{ {
"id": 381, "id": 587,
"title": "Reiseversicherung", "title": "Reiseversicherung",
"slug": "test-lehrgang-lp-circle-analyse-lc-reiseversicherung", "slug": "test-lehrgang-lp-circle-analyse-lc-reiseversicherung",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "bad7439a-8b0c-4877-8d6c-78f292be83d4", "translation_key": "cb1c9c93-7f3b-42d6-9078-2d36655fdfd7",
"frontend_url": "/learn/test-lehrgang-lp/analyse/reiseversicherung", "frontend_url": "/course/test-lehrgang/learn/analyse/reiseversicherung",
"minutes": 240, "minutes": 240,
"contents": [ "contents": [
{ {
@ -362,60 +349,60 @@
"description": "Beispiel \u00dcbung", "description": "Beispiel \u00dcbung",
"url": null "url": null
}, },
"id": "7e1ee533-7f75-495b-a2bc-8bbd2b1311c9" "id": "b59bccb2-bcb2-48cc-8204-8df94723975b"
} }
] ]
}, },
{ {
"id": 382, "id": 588,
"title": "Emma und Ayla campen durch Amerika", "title": "Emma und Ayla campen durch Amerika",
"slug": "test-lehrgang-lp-circle-analyse-lc-emma-und-ayla-campen-durch-amerika", "slug": "test-lehrgang-lp-circle-analyse-lc-emma-und-ayla-campen-durch-amerika",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "27f9d8f3-209c-4d55-94d9-2e70fbfe163b", "translation_key": "fdf19afe-fe69-496d-a1d4-ecf18ed36b01",
"frontend_url": "/learn/test-lehrgang-lp/analyse/emma-und-ayla-campen-durch-amerika", "frontend_url": "/course/test-lehrgang/learn/analyse/emma-und-ayla-campen-durch-amerika",
"minutes": 120, "minutes": 120,
"contents": [ "contents": [
{ {
"type": "exercise", "type": "learningmodule",
"value": { "value": {
"description": "Beispiel \u00dcbung", "description": "Beispiel Lernmodul",
"url": "/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html" "url": "/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html"
}, },
"id": "b08e1851-8583-4428-b1bc-402c7095130b" "id": "c3a14f9f-3491-4695-9544-a3b2a2c3a82a"
} }
] ]
}, },
{ {
"id": 383, "id": 589,
"title": "Beenden", "title": "Beenden",
"slug": "test-lehrgang-lp-circle-analyse-ls-beenden", "slug": "test-lehrgang-lp-circle-analyse-ls-beenden",
"type": "learnpath.LearningSequence", "type": "learnpath.LearningSequence",
"translation_key": "68be244d-0e00-4700-834c-57b4db366fc1", "translation_key": "28ffad90-9333-452f-af18-108d4aff5ee4",
"frontend_url": "/learn/test-lehrgang-lp/analyse#ls-beenden", "frontend_url": "/course/test-lehrgang/learn/analyse#ls-beenden",
"icon": "it-icon-ls-end" "icon": "it-icon-ls-end"
}, },
{ {
"id": 384, "id": 590,
"title": "Beenden", "title": "Beenden",
"slug": "test-lehrgang-lp-circle-analyse-lu-beenden", "slug": "test-lehrgang-lp-circle-analyse-lu-beenden",
"type": "learnpath.LearningUnit", "type": "learnpath.LearningUnit",
"translation_key": "d594db87-ad78-491b-bf1b-410adfa3a0ba", "translation_key": "05a1cb34-e8d8-44e2-9635-5e33a852bfa2",
"frontend_url": "/learn/test-lehrgang-lp/analyse#lu-beenden", "frontend_url": "/course/test-lehrgang/learn/analyse#lu-beenden",
"evaluate_url": "/learn/test-lehrgang-lp/analyse/evaluate/beenden", "evaluate_url": "/course/test-lehrgang/learn/analyse/evaluate/beenden",
"course_category": { "course_category": {
"id": 14, "id": 27,
"title": "Allgemein", "title": "Allgemein",
"general": true "general": true
}, },
"children": [] "children": []
}, },
{ {
"id": 385, "id": 591,
"title": "KompetenzNavi anschauen", "title": "KompetenzNavi anschauen",
"slug": "test-lehrgang-lp-circle-analyse-lc-kompetenznavi-anschauen", "slug": "test-lehrgang-lp-circle-analyse-lc-kompetenznavi-anschauen",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "8ee57ba5-e09e-4058-937a-b733ea72b969", "translation_key": "b90e7ddc-fcde-47aa-91e1-abd67ed96e66",
"frontend_url": "/learn/test-lehrgang-lp/analyse/kompetenznavi-anschauen", "frontend_url": "/course/test-lehrgang/learn/analyse/kompetenznavi-anschauen",
"minutes": 30, "minutes": 30,
"contents": [ "contents": [
{ {
@ -424,17 +411,17 @@
"description": "Beispiel Dokument", "description": "Beispiel Dokument",
"url": null "url": null
}, },
"id": "3ef87e69-5e5c-415a-934c-ed47ad9fdd93" "id": "a7faefea-4cd1-471a-aed1-c780d8236301"
} }
] ]
}, },
{ {
"id": 386, "id": 592,
"title": "Circle \"Analyse\" abschliessen", "title": "Circle \"Analyse\" abschliessen",
"slug": "test-lehrgang-lp-circle-analyse-lc-circle-analyse-abschliessen", "slug": "test-lehrgang-lp-circle-analyse-lc-circle-analyse-abschliessen",
"type": "learnpath.LearningContent", "type": "learnpath.LearningContent",
"translation_key": "90d9ab63-cc0f-492f-aad1-f7d448ee5b2c", "translation_key": "5c17be62-2da6-413c-82a4-7754c00ef18a",
"frontend_url": "/learn/test-lehrgang-lp/analyse/circle-analyse-abschliessen", "frontend_url": "/course/test-lehrgang/learn/analyse/circle-analyse-abschliessen",
"minutes": 30, "minutes": 30,
"contents": [ "contents": [
{ {
@ -443,7 +430,7 @@
"description": "Beispiel Dokument", "description": "Beispiel Dokument",
"url": null "url": null
}, },
"id": "21415232-862b-488c-9987-4f4ee369a854" "id": "c39488aa-2ce4-4bd7-9a39-f9ace70353bd"
} }
] ]
} }
@ -454,12 +441,12 @@
{ {
"type": "goal", "type": "goal",
"value": "... die heutige Versicherungssituation von Privat- oder Gesch\u00e4ftskunden einzusch\u00e4tzen.", "value": "... die heutige Versicherungssituation von Privat- oder Gesch\u00e4ftskunden einzusch\u00e4tzen.",
"id": "d1f001fa-f7b8-41a3-90c7-632260ff7054" "id": "c6d1b496-14e7-4570-ba05-c6f408f6ecfb"
}, },
{ {
"type": "goal", "type": "goal",
"value": "... deinem Kunden seine optimale L\u00f6sung aufzuzeigen", "value": "... deinem Kunden seine optimale L\u00f6sung aufzuzeigen",
"id": "8f73bb0f-e898-4961-ab28-dd34caca2c0b" "id": "92e3ec0d-e90e-4d8e-a81a-43f0e56c504f"
} }
], ],
"job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:", "job_situation_description": "Du triffst in diesem Circle auf die folgenden berufstypischen Handlungsfelder:",
@ -467,25 +454,12 @@
{ {
"type": "job_situation", "type": "job_situation",
"value": "Autoversicherung", "value": "Autoversicherung",
"id": "df46930b-2911-4161-a677-75b4b156dff3" "id": "1a047ce6-8922-4bc7-b185-5b7d6d89c0f9"
}, },
{ {
"type": "job_situation", "type": "job_situation",
"value": "Autokauf", "value": "Autokauf",
"id": "17a6d252-e942-44cc-920f-015e38e727be" "id": "c1b5a26b-570a-4147-9338-753d87e67cf3"
}
],
"experts": [
{
"type": "person",
"value": {
"first_name": "Patrizia",
"last_name": "Huggel",
"email": "patrizia.huggel@eiger-versicherungen.ch",
"photo": null,
"biography": ""
},
"id": "b0633305-5e74-43eb-93b8-ebbcfb1b17d1"
} }
] ]
} }
@ -493,6 +467,7 @@
"course": { "course": {
"id": -1, "id": -1,
"title": "Test Lehrgang", "title": "Test Lehrgang",
"category_name": "Handlungsfeld" "category_name": "Handlungsfeld",
"slug": "test-lehrgang"
} }
} }

View File

@ -5,10 +5,10 @@ import requests
def main(): def main():
client = requests.session() client = requests.session()
client.get("http://localhost:8001/") client.get("http://localhost:8000/")
client.post( client.post(
"http://localhost:8001/api/core/login/", "http://localhost:8000/api/core/login/",
json={ json={
"username": "admin", "username": "admin",
"password": "test", "password": "test",
@ -16,7 +16,7 @@ def main():
) )
response = client.get( response = client.get(
"http://localhost:8001/api/course/page/test-lehrgang-lp/", "http://localhost:8000/api/course/page/test-lehrgang-lp/",
) )
print(response.status_code) print(response.status_code)
print(response.json()) print(response.json())

View File

@ -36,7 +36,7 @@ function directUpload(fileData: FileData, file: File) {
Accept: "application/json", Accept: "application/json",
} as HeadersInit; } as HeadersInit;
let options = { const options = {
method: "POST", method: "POST",
headers: headers, headers: headers,
body: formData, body: formData,

View File

@ -2,6 +2,7 @@ import { itGetCached } from "@/fetchHelpers";
import type { CourseSessionUser, ExpertSessionUser } from "@/types"; import type { CourseSessionUser, ExpertSessionUser } from "@/types";
import log from "loglevel"; import log from "loglevel";
import { useUserStore } from "@/stores/user";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
export type CockpitStoreState = { export type CockpitStoreState = {
@ -19,25 +20,21 @@ export const useCockpitStore = defineStore({
selectedCircles: [], selectedCircles: [],
} as CockpitStoreState; } as CockpitStoreState;
}, },
getters: {
circles: (state) => state.cockpitSessionUser?.circles,
selectedCirclesTitles: (state) =>
state.cockpitSessionUser?.circles
.filter((circle) => state.selectedCircles.indexOf(circle.translation_key) > -1)
.map((circle) => circle.title),
},
actions: { actions: {
async loadCourseSessionUsers(courseSlug: string, reload = false) { async loadCourseSessionUsers(courseSlug: string, reload = false) {
log.debug("loadCockpitData called"); log.debug("loadCockpitData called");
const { users, cockpit_user: cockpitUser } = await itGetCached( const users = (await itGetCached(`/api/course/sessions/${courseSlug}/users/`, {
`/api/course/sessions/${courseSlug}/users/`, reload: reload,
{ })) as CourseSessionUser[];
reload: reload,
}
);
this.courseSessionUsers = users; this.courseSessionUsers = users.filter((user) => user.role === "MEMBER");
this.cockpitSessionUser = cockpitUser;
const userStore = useUserStore();
const currentUser = users.find((user) => user.user_id === userStore.id);
if (currentUser && currentUser.role === "EXPERT") {
this.cockpitSessionUser = currentUser as ExpertSessionUser;
}
if (this.cockpitSessionUser && this.cockpitSessionUser.circles?.length > 0) { if (this.cockpitSessionUser && this.cockpitSessionUser.circles?.length > 0) {
this.selectedCircles = [this.cockpitSessionUser.circles[0].translation_key]; this.selectedCircles = [this.cockpitSessionUser.circles[0].translation_key];

View File

@ -1,9 +1,13 @@
import { itGetCached, itPost } from "@/fetchHelpers"; import { itGetCached, itPost } from "@/fetchHelpers";
import { deleteCircleDocument } from "@/services/files"; import { deleteCircleDocument } from "@/services/files";
import type { CircleDocument, CircleExpert, CourseSession } from "@/types"; import type {
CircleDocument,
CourseSession,
CourseSessionUser,
ExpertSessionUser,
} from "@/types";
import _ from "lodash"; import _ from "lodash";
import log from "loglevel"; import log from "loglevel";
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { computed, ref } from "vue"; import { computed, ref } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
@ -29,6 +33,27 @@ function loadCourseSessionsData(reload = false) {
reload: reload, reload: reload,
}); });
// TODO: refactor after implementing of Klassenkonzept
const uniqueCourses = _.uniqBy(courseSessions.value, "course.id");
await Promise.all(
uniqueCourses.map(async (courseSession) => {
const users = (await itGetCached(
`/api/course/sessions/${courseSession.course.slug}/users/`,
{
reload: reload,
}
)) as CourseSessionUser[];
courseSessions.value = courseSessions.value
.filter((cs) => {
return cs.course.slug === courseSession.course.slug;
})
.map((cs) => {
cs.users = users;
return cs;
});
})
);
const userStore = useUserStore(); const userStore = useUserStore();
if (!courseSessions.value && userStore.loggedIn) { if (!courseSessions.value && userStore.loggedIn) {
throw `No courseSessionData found for user`; throw `No courseSessionData found for user`;
@ -41,16 +66,6 @@ function loadCourseSessionsData(reload = false) {
return { courseSessions }; return { courseSessions };
} }
function userExpertCircles(
userId: number,
courseSessionForRoute: CourseSession | undefined
): CircleExpert[] {
if (!courseSessionForRoute) {
return [];
}
return courseSessionForRoute.experts.filter((expert) => expert.user_id === userId);
}
export const useCourseSessionsStore = defineStore("courseSessions", () => { export const useCourseSessionsStore = defineStore("courseSessions", () => {
// using setup function seems cleaner, see https://pinia.vuejs.org/core-concepts/#setup-stores // using setup function seems cleaner, see https://pinia.vuejs.org/core-concepts/#setup-stores
@ -59,10 +74,13 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
// store should do own setup, we don't want to have each component initialize it // store should do own setup, we don't want to have each component initialize it
// that's why we call the load function in here // that's why we call the load function in here
const { courseSessions } = loadCourseSessionsData(); const { courseSessions } = loadCourseSessionsData();
// these will become getters // these will become getters
const coursesFromCourseSessions = computed(() => const coursesFromCourseSessions = computed(() =>
// TODO: refactor after implementing of Klassenkonzept
_.uniqBy(courseSessions.value, "course.id") _.uniqBy(courseSessions.value, "course.id")
); );
const courseSessionForRoute = computed(() => { const courseSessionForRoute = computed(() => {
const route = useRoute(); const route = useRoute();
const routePath = decodeURI(route.path); const routePath = decodeURI(route.path);
@ -71,28 +89,40 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return routePath.startsWith(cs.course_url); return routePath.startsWith(cs.course_url);
}); });
}); });
const hasCockpit = computed(() => { const hasCockpit = computed(() => {
if (courseSessionForRoute.value) { if (courseSessionForRoute.value) {
const userStore = useUserStore(); const userStore = useUserStore();
return ( return (
courseSessionForRoute.value.experts.filter( userStore.course_session_experts.includes(courseSessionForRoute.value.id) ||
(expert) => expert.user_id === userStore.id userStore.is_superuser
).length > 0
); );
} }
return false; return false;
}); });
const circleExperts = computed(() => {
const circleStore = useCircleStore();
const circleTranslationKey = circleStore.circle?.translation_key;
if (courseSessionForRoute.value && circleTranslationKey) {
return courseSessionForRoute.value.users.filter((u) => {
if (u.role === "EXPERT") {
return (u as ExpertSessionUser).circles
.map((c) => c.translation_key)
.includes(circleTranslationKey);
}
return false;
}) as ExpertSessionUser[];
}
return [];
});
const canUploadCircleDocuments = computed(() => { const canUploadCircleDocuments = computed(() => {
const userStore = useUserStore(); const userStore = useUserStore();
const circleStore = useCircleStore();
const expertCircles = userExpertCircles(userStore.id, courseSessionForRoute.value);
return ( return (
expertCircles.filter( circleExperts.value.filter((expert) => expert.user_id === userStore.id).length > 0
(c) => c.circle_translation_key === circleStore.circle?.translation_key
).length > 0
); );
}); });
@ -106,7 +136,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
return ls; return ls;
} }
for (let document of courseSessionForRoute.value.documents) { for (const document of courseSessionForRoute.value.documents) {
if (document.learning_sequence === ls.id) { if (document.learning_sequence === ls.id) {
ls.documents.push(document); ls.documents.push(document);
} }
@ -146,6 +176,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
hasCockpit, hasCockpit,
canUploadCircleDocuments, canUploadCircleDocuments,
circleDocuments, circleDocuments,
circleExperts,
addDocument, addDocument,
startUpload, startUpload,
removeDocument, removeDocument,

View File

@ -14,6 +14,8 @@ export type UserState = {
email: string; email: string;
username: string; username: string;
avatar_url: string; avatar_url: string;
is_superuser: boolean;
course_session_experts: number[];
loggedIn: boolean; loggedIn: boolean;
}; };
@ -24,6 +26,8 @@ const initialUserState: UserState = {
last_name: "", last_name: "",
username: "", username: "",
avatar_url: "", avatar_url: "",
is_superuser: false,
course_session_experts: [],
loggedIn: false, loggedIn: false,
}; };
@ -76,9 +80,6 @@ export const useUserStore = defineStore({
this.$state = data; this.$state = data;
this.loggedIn = true; this.loggedIn = true;
appStore.userLoaded = true; appStore.userLoaded = true;
// todo: why?
// const courseSessionsStore = useCourseSessionsStore();
// await courseSessionsStore.loadCourseSessionsData();
}, },
}, },
}); });

View File

@ -341,8 +341,8 @@ export interface CourseSession {
course_url: string; course_url: string;
media_library_url: string; media_library_url: string;
additional_json_data: unknown; additional_json_data: unknown;
experts: CircleExpert[];
documents: CircleDocument[]; documents: CircleDocument[];
users: CourseSessionUser[];
} }
export type Role = "MEMBER" | "EXPERT" | "TUTOR"; export type Role = "MEMBER" | "EXPERT" | "TUTOR";

Binary file not shown.

View File

@ -32,7 +32,6 @@ from vbv_lernwelt.course.views import (
request_course_completion, request_course_completion,
request_course_completion_for_user, request_course_completion_for_user,
) )
from vbv_lernwelt.feedback.views import get_name from vbv_lernwelt.feedback.views import get_name
from wagtail import urls as wagtail_urls from wagtail import urls as wagtail_urls
from wagtail.admin import urls as wagtailadmin_urls from wagtail.admin import urls as wagtailadmin_urls
@ -78,7 +77,7 @@ urlpatterns = [
name="mark_course_completion"), name="mark_course_completion"),
path(r"api/course/completion/<course_id>/", request_course_completion, path(r"api/course/completion/<course_id>/", request_course_completion,
name="request_course_completion"), name="request_course_completion"),
path(r"api/course/completion/<course_id>/<user_id>/", path(r"api/course/completion/<course_id>/<int:user_id>/",
request_course_completion_for_user, request_course_completion_for_user,
name="request_course_completion_for_user"), name="request_course_completion_for_user"),

View File

@ -1,9 +1,12 @@
from rest_framework import serializers from rest_framework import serializers
from vbv_lernwelt.core.models import User from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.models import CourseSessionUser
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
course_session_experts = serializers.SerializerMethodField()
class Meta: class Meta:
model = User model = User
fields = [ fields = [
@ -13,4 +16,13 @@ class UserSerializer(serializers.ModelSerializer):
"email", "email",
"username", "username",
"avatar_url", "avatar_url",
"is_superuser",
"course_session_experts",
] ]
def get_course_session_experts(self, obj):
qs = CourseSessionUser.objects.filter(
role=CourseSessionUser.Role.EXPERT, user=obj
)
return [csu.course_session.id for csu in qs]

View File

@ -132,16 +132,6 @@ def create_test_learning_path(user=None, skip_locales=True):
), ),
("goal", "... deinem Kunden seine optimale Lösung aufzuzeigen"), ("goal", "... deinem Kunden seine optimale Lösung aufzuzeigen"),
], ],
experts=[
(
"person",
{
"last_name": "Huggel",
"first_name": "Patrizia",
"email": "patrizia.huggel@eiger-versicherungen.ch",
},
),
],
) )
LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start") LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")

View File

@ -236,6 +236,9 @@ class CourseSessionUser(models.Model):
"email": self.user.email, "email": self.user.email,
"avatar_url": self.user.avatar_url, "avatar_url": self.user.avatar_url,
"role": self.role, "role": self.role,
"circles": self.expert.all().values(
"id", "title", "slug", "translation_key"
),
} }

View File

@ -3,15 +3,29 @@ from vbv_lernwelt.learnpath.models import LearningSequence
def has_course_access_by_page_request(request, obj): def has_course_access_by_page_request(request, obj):
return has_course_access(request.user, obj.specific.get_course()) return has_course_access(request.user, obj.specific.get_course().id)
def has_course_access(user, course): def has_course_access(user, course_id):
if user.is_superuser: if user.is_superuser:
return True return True
if CourseSessionUser.objects.filter( if CourseSessionUser.objects.filter(
course_session__course_id=course.id, user=user course_session__course_id=course_id, user=user
).exists():
return True
return False
def is_course_expert(user, course_id: int):
if user.is_superuser:
return True
if CourseSessionUser.objects.filter(
course_session__course_id=course_id,
user=user,
role=CourseSessionUser.Role.EXPERT,
).exists(): ).exists():
return True return True
@ -27,25 +41,23 @@ def course_sessions_for_user_qs(user):
return course_sessions return course_sessions
def is_circle_expert(user, learning_sequence, course) -> bool: def is_circle_expert(user, course_session_id: int, learning_sequence_id: int) -> bool:
if user.is_superuser: if user.is_superuser:
return True return True
try: try:
learning_sequence = LearningSequence.objects.get(id=learning_sequence) learning_sequence = LearningSequence.objects.get(id=learning_sequence_id)
except LearningSequence.DoesNotExist: except LearningSequence.DoesNotExist:
return False return False
circle_id = learning_sequence.get_parent().circle.id circle_id = learning_sequence.get_parent().circle.id
try: if CourseSessionUser.objects.filter(
CourseSessionUser.objects.get( course_session_id=course_session_id,
course_session__id=course, user=user,
user_id=user.id, role=CourseSessionUser.Role.EXPERT,
role=CourseSessionUser.Role.EXPERT, expert__id=circle_id,
expert__id=circle_id, ).exists():
) return True
except CourseSessionUser.DoesNotExist:
return False
return True return False

View File

@ -6,9 +6,7 @@ from vbv_lernwelt.course.models import (
CourseCategory, CourseCategory,
CourseCompletion, CourseCompletion,
CourseSession, CourseSession,
CourseSessionUser,
) )
from vbv_lernwelt.learnpath.models import Circle
class CourseSerializer(serializers.ModelSerializer): class CourseSerializer(serializers.ModelSerializer):
@ -50,7 +48,6 @@ class CourseSessionSerializer(serializers.ModelSerializer):
learning_path_url = serializers.SerializerMethodField() learning_path_url = serializers.SerializerMethodField()
competence_url = serializers.SerializerMethodField() competence_url = serializers.SerializerMethodField()
media_library_url = serializers.SerializerMethodField() media_library_url = serializers.SerializerMethodField()
experts = serializers.SerializerMethodField()
documents = serializers.SerializerMethodField() documents = serializers.SerializerMethodField()
def get_course(self, obj): def get_course(self, obj):
@ -68,26 +65,6 @@ class CourseSessionSerializer(serializers.ModelSerializer):
def get_competence_url(self, obj): def get_competence_url(self, obj):
return obj.course.get_competence_url() return obj.course.get_competence_url()
def get_experts(self, obj):
expert_relations = CourseSessionUser.objects.filter(
expert__in=Circle.objects.descendant_of(obj.course.coursepage)
).distinct()
expert_result = []
for er in expert_relations:
for circle in er.expert.all():
expert_result.append(
{
"user_id": er.user.id,
"user_email": er.user.email,
"user_first_name": er.user.first_name,
"user_last_name": er.user.last_name,
"circle_id": circle.id,
"circle_slug": circle.slug,
"circle_translation_key": circle.translation_key,
}
)
return expert_result
def get_documents(self, obj): def get_documents(self, obj):
documents = CircleDocument.objects.filter( documents = CircleDocument.objects.filter(
course_session=obj, file__upload_finished_at__isnull=False course_session=obj, file__upload_finished_at__isnull=False
@ -109,7 +86,6 @@ class CourseSessionSerializer(serializers.ModelSerializer):
"competence_url", "competence_url",
"media_library_url", "media_library_url",
"course_url", "course_url",
"experts",
"documents", "documents",
] ]

View File

@ -13,8 +13,10 @@ from vbv_lernwelt.course.models import (
) )
from vbv_lernwelt.course.permissions import ( from vbv_lernwelt.course.permissions import (
course_sessions_for_user_qs, course_sessions_for_user_qs,
has_course_access,
has_course_access_by_page_request, has_course_access_by_page_request,
is_circle_expert, is_circle_expert,
is_course_expert,
) )
from vbv_lernwelt.course.serializers import ( from vbv_lernwelt.course.serializers import (
CourseCompletionSerializer, CourseCompletionSerializer,
@ -67,13 +69,16 @@ def _request_course_completion(course_id, user_id):
@api_view(["GET"]) @api_view(["GET"])
def request_course_completion(request, course_id): def request_course_completion(request, course_id):
return _request_course_completion(course_id, request.user.id) if has_course_access(request.user, course_id):
return _request_course_completion(course_id, request.user.id)
raise PermissionDenied()
@api_view(["GET"]) @api_view(["GET"])
def request_course_completion_for_user(request, course_id, user_id): def request_course_completion_for_user(request, course_id, user_id):
# TODO: check permissions to access this users data if request.user.id == user_id or is_course_expert(request.user, course_id):
return _request_course_completion(course_id, user_id) return _request_course_completion(course_id, user_id)
raise PermissionDenied()
@api_view(["POST"]) @api_view(["POST"])
@ -139,24 +144,9 @@ def get_course_session_users(request, course_slug):
course__slug=course_slug course__slug=course_slug
) )
qs = CourseSessionUser.objects.filter(course_session__in=course_sessions) qs = CourseSessionUser.objects.filter(course_session__in=course_sessions)
cockpit_user_csu = qs.filter(user_id=request.user.id)
if len(cockpit_user_csu) == 0: user_data = [csu.to_dict() for csu in qs]
return Response({"error": "User not found"}, status=404) return Response(status=200, data=user_data)
user_data = [csu.to_dict() for csu in qs.exclude(user_id=request.user.id)]
data = {
"cockpit_user": cockpit_user_csu[0].to_dict()
| {
"circles": cockpit_user_csu[0]
.expert.all()
.values("id", "title", "slug", "translation_key")
},
"users": user_data,
}
return Response(status=200, data=data)
except PermissionDenied as e: except PermissionDenied as e:
raise e raise e
except Exception as e: except Exception as e:
@ -171,8 +161,8 @@ def document_upload_start(request):
if not is_circle_expert( if not is_circle_expert(
request.user, request.user,
serializer.validated_data["learning_sequence"],
serializer.validated_data["course_session"], serializer.validated_data["course_session"],
serializer.validated_data["learning_sequence"],
): ):
raise PermissionDenied() raise PermissionDenied()
@ -227,7 +217,7 @@ def document_direct_upload(request, file_id):
def document_delete(request, document_id): def document_delete(request, document_id):
document = get_object_or_404(CircleDocument, id=document_id) document = get_object_or_404(CircleDocument, id=document_id)
if not is_circle_expert( if not is_circle_expert(
request.user, document.learning_sequence_id, document.course_session_id request.user, document.course_session.id, document.learning_sequence.id
): ):
raise PermissionDenied() raise PermissionDenied()

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.13 on 2023-01-11 09:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("learnpath", "0009_alter_learningcontent_contents"),
]
operations = [
migrations.RemoveField(
model_name="circle",
name="experts",
),
]

View File

@ -4,7 +4,6 @@ from wagtail import blocks
from wagtail.admin.panels import FieldPanel, StreamFieldPanel from wagtail.admin.panels import FieldPanel, StreamFieldPanel
from wagtail.blocks import StreamBlock from wagtail.blocks import StreamBlock
from wagtail.fields import StreamField from wagtail.fields import StreamField
from wagtail.images.blocks import ImageChooserBlock
from wagtail.models import Page from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.core.model_utils import find_available_slug
@ -73,17 +72,6 @@ class Topic(CourseBasePage):
return f"{self.title}" return f"{self.title}"
class PersonBlock(blocks.StructBlock):
first_name = blocks.CharBlock()
last_name = blocks.CharBlock()
email = blocks.EmailBlock()
photo = ImageChooserBlock(required=False)
biography = blocks.RichTextBlock(required=False)
class Meta:
icon = "user"
class Circle(CourseBasePage): class Circle(CourseBasePage):
parent_page_types = ["learnpath.LearningPath"] parent_page_types = ["learnpath.LearningPath"]
subpage_types = [ subpage_types = [
@ -99,7 +87,6 @@ class Circle(CourseBasePage):
"goals", "goals",
"job_situation_description", "job_situation_description",
"job_situations", "job_situations",
"experts",
] ]
description = models.TextField(default="", blank=True) description = models.TextField(default="", blank=True)
@ -119,18 +106,11 @@ class Circle(CourseBasePage):
], ],
use_json_field=True, use_json_field=True,
) )
experts = StreamField(
[
("person", PersonBlock()),
],
use_json_field=True,
)
content_panels = Page.content_panels + [ content_panels = Page.content_panels + [
FieldPanel("description"), FieldPanel("description"),
FieldPanel("goals"), FieldPanel("goals"),
FieldPanel("job_situations"), FieldPanel("job_situations"),
FieldPanel("experts"),
] ]
def get_frontend_url(self): def get_frontend_url(self):

View File

@ -135,16 +135,6 @@ pretium quis, sem. Nulla consequat massa quis enim. Donec.
goals = [ goals = [
("goal", f"... hier ein Beispieltext für ein Ziel {x + 1}") for x in range(3) ("goal", f"... hier ein Beispieltext für ein Ziel {x + 1}") for x in range(3)
] ]
experts = [
(
"person",
{
"last_name": "Mustermann",
"first_name": "Patrizia",
"email": "patrizia.mustermann@example.com",
},
),
]
class Meta: class Meta:
model = Circle model = Circle