Merge branch 'feature/course-permissions-backend' into develop
This commit is contained in:
commit
62db10ee00
|
|
@ -12,9 +12,6 @@ cd client && npm run dev
|
||||||
|
|
||||||
# reset db and run django dev server
|
# reset db and run django dev server
|
||||||
./prepare_server.sh
|
./prepare_server.sh
|
||||||
|
|
||||||
# run tailwind cli (for tailwind support on django templates)
|
|
||||||
cd client && npm run tailwind
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
@ -50,7 +47,7 @@ environment variables.
|
||||||
It will also setup the tables for django and run the django development server.
|
It will also setup the tables for django and run the django development server.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# will initial`migrate` and `runserver` etc...
|
# will run `migrate` and `runserver` etc...
|
||||||
./prepare_server.sh
|
./prepare_server.sh
|
||||||
|
|
||||||
# or async server
|
# or async server
|
||||||
|
|
|
||||||
|
|
@ -515,7 +515,7 @@ if "django_redis.cache.RedisCache" in env("IT_DJANGO_CACHE_BACKEND", default="")
|
||||||
|
|
||||||
CACHES["api_page_cache"] = {
|
CACHES["api_page_cache"] = {
|
||||||
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
|
||||||
"LOCATION": "django_cache_learning_path",
|
"LOCATION": "django_cache_table_api_page",
|
||||||
}
|
}
|
||||||
|
|
||||||
# OAuth/OpenId Connect
|
# OAuth/OpenId Connect
|
||||||
|
|
|
||||||
|
|
@ -20,8 +20,9 @@ from vbv_lernwelt.core.views import (
|
||||||
vue_logout,
|
vue_logout,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.course.views import (
|
from vbv_lernwelt.course.views import (
|
||||||
|
course_page_api_view,
|
||||||
|
get_course_sessions,
|
||||||
mark_course_completion,
|
mark_course_completion,
|
||||||
page_api_view,
|
|
||||||
request_course_completion,
|
request_course_completion,
|
||||||
)
|
)
|
||||||
from wagtail import urls as wagtail_urls
|
from wagtail import urls as wagtail_urls
|
||||||
|
|
@ -59,7 +60,9 @@ urlpatterns = [
|
||||||
name="generate_web_component_icons"),
|
name="generate_web_component_icons"),
|
||||||
|
|
||||||
# course
|
# course
|
||||||
path(r"api/course/page/<slug:slug>/", page_api_view, name="page_api_view"),
|
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||||
|
path(r"api/course/page/<slug>/", course_page_api_view,
|
||||||
|
name="course_page_api_view"),
|
||||||
path(r"api/course/completion/mark/", mark_course_completion,
|
path(r"api/course/completion/mark/", mark_course_completion,
|
||||||
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,
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,13 @@ from vbv_lernwelt.competence.factories import (
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.competence.models import CompetencePage
|
from vbv_lernwelt.competence.models import CompetencePage
|
||||||
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
|
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
|
||||||
from vbv_lernwelt.course.models import Course, CoursePage
|
from vbv_lernwelt.course.models import CoursePage
|
||||||
from vbv_lernwelt.learnpath.models import LearningUnit
|
from vbv_lernwelt.learnpath.models import LearningPath, LearningUnit
|
||||||
|
|
||||||
|
|
||||||
def create_default_competence_profile():
|
def create_default_competence_profile(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID):
|
||||||
course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
course_page = CoursePage.objects.get(course_id=course_id)
|
||||||
course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
slug_prefix = course_page.get_children().exact_type(LearningPath).first().slug
|
||||||
|
|
||||||
competence_profile_page = CompetenceProfilePageFactory(
|
competence_profile_page = CompetenceProfilePageFactory(
|
||||||
title="KompetenzNavi",
|
title="KompetenzNavi",
|
||||||
|
|
@ -133,380 +133,470 @@ def create_default_competence_profile():
|
||||||
# Daten anhand von WEVM_Version Oktober 2022
|
# Daten anhand von WEVM_Version Oktober 2022
|
||||||
# Einstieg/Beobachten – Selbsteinschätzung «Einkommenssicherung»
|
# Einstieg/Beobachten – Selbsteinschätzung «Einkommenssicherung»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.1",
|
competence_id="A2.1",
|
||||||
title="Ich bin fähig je nach (Neu-) Kunde Form und Ort für das Gespräch festzulegen.",
|
title="Ich bin fähig je nach (Neu-) Kunde Form und Ort für das Gespräch festzulegen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.2",
|
competence_id="A2.2",
|
||||||
title="Ich bin fähig mir intern und extern die nötigen Informationen über den (Neu-) Kunden zu beschaffen.",
|
title="Ich bin fähig mir intern und extern die nötigen Informationen über den (Neu-) Kunden zu beschaffen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.3",
|
competence_id="A2.3",
|
||||||
title="Ich bin fähig die Terminierung auf das Thema Einkommenssicherung auszurichten.",
|
title="Ich bin fähig die Terminierung auf das Thema Einkommenssicherung auszurichten.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.4",
|
competence_id="A2.4",
|
||||||
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.5",
|
competence_id="A2.5",
|
||||||
title="Ich bin fähig für das Handlungsfeld «Einkommenssicherung» geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
title="Ich bin fähig für das Handlungsfeld «Einkommenssicherung» geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B1"
|
||||||
|
),
|
||||||
competence_id="B1.1",
|
competence_id="B1.1",
|
||||||
title="Ich bin fähig dem Kunden den Gesprächsablauf und den Zeitrahmen (mittels Agenda) aufzuzeigen.",
|
title="Ich bin fähig dem Kunden den Gesprächsablauf und den Zeitrahmen (mittels Agenda) aufzuzeigen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B1"
|
||||||
|
),
|
||||||
competence_id="B1.2",
|
competence_id="B1.2",
|
||||||
title="Ich bin fähig mich beim Kunden korrekt zu identifizieren (VAG 45).",
|
title="Ich bin fähig mich beim Kunden korrekt zu identifizieren (VAG 45).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.3",
|
competence_id="B2.3",
|
||||||
title="Ich bin fähig alle erforderlichen Unterlagen einzufordern.",
|
title="Ich bin fähig alle erforderlichen Unterlagen einzufordern.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-einstieg-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Einstieg / Anwenden – Selbsteinschätzung «Fahrzeug»
|
# Einstieg / Anwenden – Selbsteinschätzung «Fahrzeug»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A1"
|
||||||
|
),
|
||||||
competence_id="A1.6",
|
competence_id="A1.6",
|
||||||
title="Ich bin fähig im täglichen Kontakt potenzielle Kundinnen und Kunden zu erkennen.",
|
title="Ich bin fähig im täglichen Kontakt potenzielle Kundinnen und Kunden zu erkennen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.1",
|
competence_id="A2.1",
|
||||||
title="Ich bin fähig je nach (Neu-) Kunde Form und Ort für das Gespräch festzulegen.",
|
title="Ich bin fähig je nach (Neu-) Kunde Form und Ort für das Gespräch festzulegen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.2",
|
competence_id="A2.2",
|
||||||
title="Ich bin fähig mir intern und extern die nötigen Informationen über den (Neu-) Kunden zu beschaffen.",
|
title="Ich bin fähig mir intern und extern die nötigen Informationen über den (Neu-) Kunden zu beschaffen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.3",
|
competence_id="A2.3",
|
||||||
title="Ich bin fähig die Terminierung auf das Thema Fahrzeug auszurichten.",
|
title="Ich bin fähig die Terminierung auf das Thema Fahrzeug auszurichten.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.4",
|
competence_id="A2.4",
|
||||||
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.5",
|
competence_id="A2.5",
|
||||||
title="Ich bin fähig für das zu führende Gespräch geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
title="Ich bin fähig für das zu führende Gespräch geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-einstieg-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Einstieg / Anwenden – Selbsteinschätzung «Reisen»
|
# Einstieg / Anwenden – Selbsteinschätzung «Reisen»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.3",
|
competence_id="A2.3",
|
||||||
title="Ich bin fähig die Terminierung auf das Thema Reisen auszurichten.",
|
title="Ich bin fähig die Terminierung auf das Thema Reisen auszurichten.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-reisen"
|
slug=f"{slug_prefix}-circle-einstieg-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.4",
|
competence_id="A2.4",
|
||||||
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
title="Ich bin fähig für das zu führende Gespräch eine Agenda zu erstellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-reisen"
|
slug=f"{slug_prefix}-circle-einstieg-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A2"
|
||||||
|
),
|
||||||
competence_id="A2.5",
|
competence_id="A2.5",
|
||||||
title="Ich bin fähig für das zu führende Gespräch geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
title="Ich bin fähig für das zu führende Gespräch geeignete Hilfsmittel und Unterlagen zusammenzustellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-einstieg-lu-reisen"
|
slug=f"{slug_prefix}-circle-einstieg-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Analyse / Beobachten – Selbsteinschätzung «Einkommenssicherung»
|
# Analyse / Beobachten – Selbsteinschätzung «Einkommenssicherung»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A1"
|
||||||
|
),
|
||||||
competence_id="A1.5",
|
competence_id="A1.5",
|
||||||
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, das Thema Risiko und Sicherheit in einem Gespräch gezielt und auf die Situation des jeweiligen Gesprächspartners bezogen, einfliessen zu lassen.",
|
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, das Thema Risiko und Sicherheit in einem Gespräch gezielt und auf die Situation des jeweiligen Gesprächspartners bezogen, einfliessen zu lassen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-analyse-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B1"
|
||||||
|
),
|
||||||
competence_id="B1.3",
|
competence_id="B1.3",
|
||||||
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-analyse-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.1",
|
competence_id="B2.1",
|
||||||
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-analyse-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.2",
|
competence_id="B2.2",
|
||||||
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die Risiken aufzuzeigen.",
|
title="Innerhalb des Handlungsfelds «Einkommenssicherung» bin ich fähig, die Risiken aufzuzeigen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-einkommenssicherung"
|
slug=f"{slug_prefix}-circle-analyse-lu-einkommenssicherung"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Analyse/Anwenden – Selbsteinschätzung «Fahrzeug»
|
# Analyse/Anwenden – Selbsteinschätzung «Fahrzeug»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B1"
|
||||||
|
),
|
||||||
competence_id="B1.3",
|
competence_id="B1.3",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-analyse-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.1",
|
competence_id="B2.1",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-analyse-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.2",
|
competence_id="B2.2",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Risiken aufzuzeigen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Risiken aufzuzeigen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-analyse-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Analyse/Anwenden – Selbsteinschätzung «Reisen»
|
# Analyse/Anwenden – Selbsteinschätzung «Reisen»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B1"
|
||||||
|
),
|
||||||
competence_id="B1.3",
|
competence_id="B1.3",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-reisen"
|
slug=f"{slug_prefix}-circle-analyse-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.1",
|
competence_id="B2.1",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die IST-Situation des Kunden mit der geeigneten Gesprächs-/Fragetechnik zu erfassen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-reisen"
|
slug=f"{slug_prefix}-circle-analyse-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B2"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B2"
|
||||||
|
),
|
||||||
competence_id="B2.2",
|
competence_id="B2.2",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die Risiken aufzuzeigen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, die Risiken aufzuzeigen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-reisen"
|
slug=f"{slug_prefix}-circle-analyse-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.1",
|
competence_id="C1.1",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» durch eine Bestandesaufnahme der aktuellen Policen zu prüfen, ob die Leistungen dem Bedarf des Kunden entsprechen.",
|
title="Innerhalb des Handlungsfelds «Reisen» durch eine Bestandesaufnahme der aktuellen Policen zu prüfen, ob die Leistungen dem Bedarf des Kunden entsprechen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-analyse-lu-reisen"
|
slug=f"{slug_prefix}-circle-analyse-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Lösung/Anwenden – Selbsteinschätzung «Fahrzeug»
|
# Lösung/Anwenden – Selbsteinschätzung «Fahrzeug»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.2",
|
competence_id="B4.2",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, dem Kunden die Vorschläge verständlich zu erläutern und die entsprechenden Informationspflichten zu erfüllen",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, dem Kunden die Vorschläge verständlich zu erläutern und die entsprechenden Informationspflichten zu erfüllen",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-lösung-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.3",
|
competence_id="B4.3",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, auf Vorbehalte und/oder Fragen sachlich korrekt und (verhandlungs-)sicher einzugehen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, auf Vorbehalte und/oder Fragen sachlich korrekt und (verhandlungs-)sicher einzugehen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-lösung-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.4",
|
competence_id="B4.4",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, nötige Anpassungen flexibel vorzunehmen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, nötige Anpassungen flexibel vorzunehmen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-lösung-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.1",
|
competence_id="C1.1",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig durch eine Bestandesaufnahme der aktuellen Policen zu prüfen, ob die Leistungen dem Bedarf des Kunden entsprechen",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig durch eine Bestandesaufnahme der aktuellen Policen zu prüfen, ob die Leistungen dem Bedarf des Kunden entsprechen",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-lösung-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.2",
|
competence_id="C1.2",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, den Kunden bedarfsgerechte Vorschläge für Anpassungen der Versicherungslösung zu unterbreiten (Up-Selling).",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, den Kunden bedarfsgerechte Vorschläge für Anpassungen der Versicherungslösung zu unterbreiten (Up-Selling).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-lösung-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Lösung/Anwenden – Selbsteinschätzung «Reisen»
|
# Lösung/Anwenden – Selbsteinschätzung «Reisen»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B3"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B3"
|
||||||
|
),
|
||||||
competence_id="B3.2",
|
competence_id="B3.2",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, eine Unterversicherung, eine Doppel- oder Überversicherung, einen fehlenden Versicherungsschutz und mögliches Optimierungspotential festzustellen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, eine Unterversicherung, eine Doppel- oder Überversicherung, einen fehlenden Versicherungsschutz und mögliches Optimierungspotential festzustellen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.2",
|
competence_id="B4.2",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, dem Kunden die Vorschläge verständlich zu erläutern und die entsprechenden Informationspflichten zu erfüllen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, dem Kunden die Vorschläge verständlich zu erläutern und die entsprechenden Informationspflichten zu erfüllen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.3",
|
competence_id="B4.3",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, auf Vorbehalte und/oder Fragen sachlich korrekt und (verhandlungs-)sicher einzugehen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, auf Vorbehalte und/oder Fragen sachlich korrekt und (verhandlungs-)sicher einzugehen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.4",
|
competence_id="B4.4",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, nötige Anpassungen flexibel vorzunehmen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, nötige Anpassungen flexibel vorzunehmen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.2",
|
competence_id="C1.2",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, den Kunden bedarfsgerechte Vorschläge für Anpassungen der Versicherungslösung zu unterbreiten (Up-Selling).",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, den Kunden bedarfsgerechte Vorschläge für Anpassungen der Versicherungslösung zu unterbreiten (Up-Selling).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.3",
|
competence_id="C1.3",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig aufgrund des Portfolios passende Zusatzprodukte anzubieten (Cross-Selling).",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig aufgrund des Portfolios passende Zusatzprodukte anzubieten (Cross-Selling).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-lösung-lu-reisen"
|
slug=f"{slug_prefix}-circle-lösung-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Abschluss/Anwenden – Selbsteinschätzung «Fahrzeug»
|
# Abschluss/Anwenden – Selbsteinschätzung «Fahrzeug»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A1"
|
||||||
|
),
|
||||||
competence_id="A1.2",
|
competence_id="A1.2",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, geeignete Personen wie z.B. Garagisten, Architekten, Treuhänder auf die Vermittlung/Zusammenarbeit anzusprechen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, geeignete Personen wie z.B. Garagisten, Architekten, Treuhänder auf die Vermittlung/Zusammenarbeit anzusprechen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-abschluss-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="A4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="A4"
|
||||||
|
),
|
||||||
competence_id="A4.1",
|
competence_id="A4.1",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, Kundendaten in Datenbanken (CRM) korrekt zu erfassen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, Kundendaten in Datenbanken (CRM) korrekt zu erfassen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-abschluss-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.6",
|
competence_id="B4.6",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, Anträge korrekt auszufüllen.",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, Anträge korrekt auszufüllen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-abschluss-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C1"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C1"
|
||||||
|
),
|
||||||
competence_id="C1.3",
|
competence_id="C1.3",
|
||||||
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, aufgrund des Portfolios passende Zusatzprodukte anzubieten (Cross-Selling).",
|
title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, aufgrund des Portfolios passende Zusatzprodukte anzubieten (Cross-Selling).",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-fahrzeug"
|
slug=f"{slug_prefix}-circle-abschluss-lu-fahrzeug"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Abschluss / Anwenden – Selbsteinschätzung «Reisen»
|
# Abschluss / Anwenden – Selbsteinschätzung «Reisen»
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="B4"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="B4"
|
||||||
|
),
|
||||||
competence_id="B4.6",
|
competence_id="B4.6",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, Anträge korrekt auszufüllen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, Anträge korrekt auszufüllen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-reisen"
|
slug=f"{slug_prefix}-circle-abschluss-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
PerformanceCriteriaFactory(
|
PerformanceCriteriaFactory(
|
||||||
parent=CompetencePage.objects.get(competence_id="C3"),
|
parent=CompetencePage.objects.get(
|
||||||
|
slug__startswith=slug_prefix.replace("-lp", ""), competence_id="C3"
|
||||||
|
),
|
||||||
competence_id="C3.1",
|
competence_id="C3.1",
|
||||||
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, Kunden die Vorgehensweise für die Meldung des Schadens nachvollziehbar zu erläutern und sie bei Bedarf zu unterstützen.",
|
title="Innerhalb des Handlungsfelds «Reisen» bin ich fähig, Kunden die Vorgehensweise für die Meldung des Schadens nachvollziehbar zu erläutern und sie bei Bedarf zu unterstützen.",
|
||||||
learning_unit=LearningUnit.objects.get(
|
learning_unit=LearningUnit.objects.get(
|
||||||
slug="versicherungsvermittlerin-lp-circle-abschluss-lu-reisen"
|
slug=f"{slug_prefix}-circle-abschluss-lu-reisen"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -6,10 +6,16 @@ from wagtail.fields import StreamField
|
||||||
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
|
||||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.course.models import CourseBasePage
|
||||||
|
|
||||||
|
|
||||||
class CompetenceProfilePage(Page):
|
class CompetenceProfilePage(CourseBasePage):
|
||||||
|
serialize_field_names = [
|
||||||
|
"course",
|
||||||
|
"circles",
|
||||||
|
"children",
|
||||||
|
]
|
||||||
|
|
||||||
parent_page_types = ["course.CoursePage"]
|
parent_page_types = ["course.CoursePage"]
|
||||||
subpage_types = ["competence.CompetencePage"]
|
subpage_types = ["competence.CompetencePage"]
|
||||||
|
|
||||||
|
|
@ -26,19 +32,13 @@ class CompetenceProfilePage(Page):
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
return f"/competence/{self.slug}"
|
return f"/competence/{self.slug}"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
class CompetencePage(CourseBasePage):
|
||||||
return get_it_serializer_class(
|
serialize_field_names = [
|
||||||
cls,
|
"competence_id",
|
||||||
[
|
|
||||||
"course",
|
|
||||||
"circles",
|
|
||||||
"children",
|
"children",
|
||||||
],
|
]
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CompetencePage(Page):
|
|
||||||
parent_page_types = ["competence.CompetenceProfilePage"]
|
parent_page_types = ["competence.CompetenceProfilePage"]
|
||||||
subpage_types = ["competence.PerformanceCriteria"]
|
subpage_types = ["competence.PerformanceCriteria"]
|
||||||
competence_id = models.TextField(default="A1")
|
competence_id = models.TextField(default="A1")
|
||||||
|
|
@ -63,18 +63,8 @@ class CompetencePage(Page):
|
||||||
)
|
)
|
||||||
super(CompetencePage, self).full_clean(*args, **kwargs)
|
super(CompetencePage, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
[
|
|
||||||
"competence_id",
|
|
||||||
"children",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
class PerformanceCriteria(CourseBasePage):
|
||||||
class PerformanceCriteria(Page):
|
|
||||||
parent_page_types = ["competence.CompetenceProfilePage"]
|
parent_page_types = ["competence.CompetenceProfilePage"]
|
||||||
competence_id = models.TextField(default="A1.1")
|
competence_id = models.TextField(default="A1.1")
|
||||||
learning_unit = models.ForeignKey(
|
learning_unit = models.ForeignKey(
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,14 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from vbv_lernwelt.competence.models import PerformanceCriteria
|
from vbv_lernwelt.competence.models import PerformanceCriteria
|
||||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
|
||||||
from vbv_lernwelt.course.serializers import CourseCategorySerializer
|
from vbv_lernwelt.course.serializers import CourseCategorySerializer
|
||||||
|
|
||||||
|
|
||||||
class PerformanceCriteriaSerializer(
|
class PerformanceCriteriaSerializer(
|
||||||
get_it_serializer_class(
|
get_course_serializer_class(
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"competence_id",
|
"competence_id",
|
||||||
"learning_unit",
|
"learning_unit",
|
||||||
"circle",
|
"circle",
|
||||||
|
|
@ -43,14 +38,9 @@ class PerformanceCriteriaSerializer(
|
||||||
|
|
||||||
|
|
||||||
class PerformanceCriteriaLearningPathSerializer(
|
class PerformanceCriteriaLearningPathSerializer(
|
||||||
get_it_serializer_class(
|
get_course_serializer_class(
|
||||||
PerformanceCriteria,
|
PerformanceCriteria,
|
||||||
[
|
field_names=[
|
||||||
"id",
|
|
||||||
"title",
|
|
||||||
"slug",
|
|
||||||
"type",
|
|
||||||
"translation_key",
|
|
||||||
"competence_id",
|
"competence_id",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ class CompetenceAPITestCase(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
create_default_users()
|
create_default_users()
|
||||||
create_test_course()
|
create_test_course()
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="admin")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="admin", password="test")
|
||||||
|
|
||||||
def test_get_compentence_page(self):
|
def test_get_compentence_page(self):
|
||||||
slug = "test-lehrgang-competence"
|
slug = "test-lehrgang-competence"
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,55 @@ from django.contrib.auth.models import Group
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
|
||||||
|
default_users = [
|
||||||
|
{
|
||||||
|
"email": "student",
|
||||||
|
"first_name": "Student",
|
||||||
|
"last_name": "Meier",
|
||||||
|
"avatar_url": "/static/avatars/avatar_iterativ.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "daniel.egger@iterativ.ch",
|
||||||
|
"first_name": "Daniel",
|
||||||
|
"last_name": "Egger",
|
||||||
|
"avatar_url": "/static/avatars/avatar_iterativ.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "axel.manderbach@lernetz.ch",
|
||||||
|
"first_name": "Axel",
|
||||||
|
"last_name": "Manderbach",
|
||||||
|
"avatar_url": "/static/avatars/avatar_axel.jpg",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "christoph.bosshard@vbv-afa.ch",
|
||||||
|
"first_name": "Christoph",
|
||||||
|
"last_name": "Bosshard",
|
||||||
|
"avatar_url": "/static/avatars/avatar_christoph.png",
|
||||||
|
"password": "myvbv1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "alexandra.vangelista@lernetz.ch",
|
||||||
|
"first_name": "Alexandra",
|
||||||
|
"last_name": "Vangelista",
|
||||||
|
"avatar_url": "/static/avatars/avatar_alexandra.png",
|
||||||
|
"password": "myvbv1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "chantal.rosenberg@vbv-afa.ch",
|
||||||
|
"first_name": "Chantal",
|
||||||
|
"last_name": "Rosenberg",
|
||||||
|
"avatar_url": "/static/avatars/avatar_chantal.png",
|
||||||
|
"password": "myvbv1234",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"email": "bianca.muster@eiger-versicherungen.ch",
|
||||||
|
"first_name": "Bianca",
|
||||||
|
"last_name": "Muster",
|
||||||
|
"avatar_url": "/static/avatars/avatar_bianca.png",
|
||||||
|
"password": "myvbv1234",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def create_default_users(user_model=User, group_model=Group, default_password=None):
|
def create_default_users(user_model=User, group_model=Group, default_password=None):
|
||||||
if default_password is None:
|
if default_password is None:
|
||||||
|
|
@ -54,57 +103,29 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
|
||||||
avatar_url="/static/avatars/avatar_iterativ.png",
|
avatar_url="/static/avatars/avatar_iterativ.png",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for user_data in default_users:
|
||||||
|
_create_student_user(**user_data)
|
||||||
|
|
||||||
_create_student_user(
|
_create_student_user(
|
||||||
email="student",
|
email="expertvv.analyse@vbv-afa.ch",
|
||||||
first_name="Student",
|
first_name="Expert",
|
||||||
last_name="Meier",
|
last_name="Analyse",
|
||||||
avatar_url="/static/avatars/avatar_iterativ.png",
|
)
|
||||||
|
_create_student_user(
|
||||||
|
email="expertvv.einstieg@vbv-afa.ch",
|
||||||
|
first_name="Expert",
|
||||||
|
last_name="Einstieg",
|
||||||
)
|
)
|
||||||
|
|
||||||
_create_student_user(
|
_create_student_user(
|
||||||
email="daniel.egger@iterativ.ch",
|
email="trainer-uk1.analyse@vbv-afa.ch",
|
||||||
first_name="Daniel",
|
first_name="Trainer",
|
||||||
last_name="Egger",
|
last_name="Analyse",
|
||||||
avatar_url="/static/avatars/avatar_iterativ.png",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
_create_student_user(
|
_create_student_user(
|
||||||
email="axel.manderbach@lernetz.ch",
|
email="trainer-uk1.einstieg@vbv-afa.ch",
|
||||||
first_name="Axel",
|
first_name="Trainer",
|
||||||
last_name="Manderbach",
|
last_name="Einstieg",
|
||||||
avatar_url="/static/avatars/avatar_axel.jpg",
|
|
||||||
)
|
|
||||||
|
|
||||||
_create_student_user(
|
|
||||||
email="christoph.bosshard@vbv-afa.ch",
|
|
||||||
first_name="Christoph",
|
|
||||||
last_name="Bosshard",
|
|
||||||
avatar_url="/static/avatars/avatar_christoph.png",
|
|
||||||
password="myvbv1234",
|
|
||||||
)
|
|
||||||
|
|
||||||
_create_student_user(
|
|
||||||
email="alexandra.vangelista@lernetz.ch",
|
|
||||||
first_name="Alexandra",
|
|
||||||
last_name="Vangelista",
|
|
||||||
avatar_url="/static/avatars/avatar_alexandra.png",
|
|
||||||
password="myvbv1234",
|
|
||||||
)
|
|
||||||
|
|
||||||
_create_student_user(
|
|
||||||
email="chantal.rosenberg@vbv-afa.ch",
|
|
||||||
first_name="Chantal",
|
|
||||||
last_name="Rosenberg",
|
|
||||||
avatar_url="/static/avatars/avatar_chantal.png",
|
|
||||||
password="myvbv1234",
|
|
||||||
)
|
|
||||||
|
|
||||||
_create_student_user(
|
|
||||||
email="bianca.muster@eiger-versicherungen.ch",
|
|
||||||
first_name="Bianca",
|
|
||||||
last_name="Muster",
|
|
||||||
avatar_url="/static/avatars/avatar_bianca.png",
|
|
||||||
password="myvbv1234",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
import wagtail.api.v2.serializers as wagtail_serializers
|
import wagtail.api.v2.serializers as wagtail_serializers
|
||||||
from rest_framework.fields import SerializerMethodField
|
from rest_framework.fields import SerializerMethodField
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import CoursePage
|
|
||||||
from vbv_lernwelt.course.serializers import CourseCategorySerializer, CourseSerializer
|
|
||||||
from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
||||||
|
|
||||||
|
|
||||||
def get_it_serializer_class(model, field_names):
|
def get_it_serializer_class(
|
||||||
|
model, field_names=None, base_field_names=None, base_class=None
|
||||||
|
):
|
||||||
|
if field_names is None:
|
||||||
|
field_names = []
|
||||||
|
|
||||||
|
if base_field_names is None:
|
||||||
base_field_names = [
|
base_field_names = [
|
||||||
"id",
|
"id",
|
||||||
"title",
|
"title",
|
||||||
|
|
@ -15,27 +19,28 @@ def get_it_serializer_class(model, field_names):
|
||||||
"translation_key",
|
"translation_key",
|
||||||
"frontend_url",
|
"frontend_url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if base_class is None:
|
||||||
|
base_class = ItWagtailBaseSerializer
|
||||||
|
|
||||||
return wagtail_serializers.get_serializer_class(
|
return wagtail_serializers.get_serializer_class(
|
||||||
model,
|
model,
|
||||||
field_names=base_field_names + field_names,
|
field_names=base_field_names + field_names,
|
||||||
meta_fields=[],
|
meta_fields=[],
|
||||||
base=ItBaseSerializer,
|
base=base_class,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ItTypeField(wagtail_serializers.TypeField):
|
class ItWagtailTypeField(wagtail_serializers.TypeField):
|
||||||
def to_representation(self, obj):
|
def to_representation(self, obj):
|
||||||
name = get_wagtail_type(obj)
|
name = get_wagtail_type(obj)
|
||||||
return name
|
return name
|
||||||
|
|
||||||
|
|
||||||
class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
class ItWagtailBaseSerializer(wagtail_serializers.BaseSerializer):
|
||||||
type = ItTypeField(read_only=True)
|
type = ItWagtailTypeField(read_only=True)
|
||||||
children = SerializerMethodField()
|
children = SerializerMethodField()
|
||||||
course = SerializerMethodField()
|
|
||||||
course_category = CourseCategorySerializer(read_only=True)
|
|
||||||
frontend_url = SerializerMethodField()
|
frontend_url = SerializerMethodField()
|
||||||
circles = SerializerMethodField()
|
|
||||||
|
|
||||||
meta_fields = []
|
meta_fields = []
|
||||||
|
|
||||||
|
|
@ -54,36 +59,6 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
|
||||||
for c in children
|
for c in children
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_course(self, obj):
|
|
||||||
if hasattr(obj, "course"):
|
|
||||||
return CourseSerializer(obj.course).data
|
|
||||||
else:
|
|
||||||
course_parent_page = obj.get_ancestors().exact_type(CoursePage).last()
|
|
||||||
if course_parent_page:
|
|
||||||
return CourseSerializer(course_parent_page.specific.course).data
|
|
||||||
return ""
|
|
||||||
|
|
||||||
def get_circles(self, obj):
|
|
||||||
course_parent_page = obj.get_ancestors().exact_type(CoursePage).last()
|
|
||||||
|
|
||||||
if course_parent_page:
|
|
||||||
from vbv_lernwelt.learnpath.models import Circle, LearningPath
|
|
||||||
|
|
||||||
circles = (
|
|
||||||
course_parent_page.get_children()
|
|
||||||
.exact_type(LearningPath)
|
|
||||||
.first()
|
|
||||||
.get_children()
|
|
||||||
.exact_type(Circle)
|
|
||||||
)
|
|
||||||
|
|
||||||
return [
|
|
||||||
{"id": c.id, "title": c.title, "translation_key": c.translation_key}
|
|
||||||
for c in circles
|
|
||||||
]
|
|
||||||
|
|
||||||
return []
|
|
||||||
|
|
||||||
def get_frontend_url(self, obj):
|
def get_frontend_url(self, obj):
|
||||||
if hasattr(obj, "get_frontend_url"):
|
if hasattr(obj, "get_frontend_url"):
|
||||||
return obj.get_frontend_url()
|
return obj.get_frontend_url()
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import logging
|
||||||
|
|
||||||
import structlog
|
import structlog
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.cache import caches
|
||||||
from rest_framework.throttling import UserRateThrottle
|
from rest_framework.throttling import UserRateThrottle
|
||||||
from structlog.types import EventDict
|
from structlog.types import EventDict
|
||||||
|
|
||||||
|
|
@ -50,3 +51,16 @@ def first_true(iterable, default=False, pred=None):
|
||||||
# first_true([a,b,c], x) --> a or b or c or x
|
# first_true([a,b,c], x) --> a or b or c or x
|
||||||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
||||||
return next(filter(pred, iterable), default)
|
return next(filter(pred, iterable), default)
|
||||||
|
|
||||||
|
|
||||||
|
def get_api_page_cache():
|
||||||
|
return caches["api_page_cache"]
|
||||||
|
|
||||||
|
|
||||||
|
def api_page_cache_get_or_set(key, func, timeout=60 * 60 * 8):
|
||||||
|
cache = get_api_page_cache()
|
||||||
|
value = cache.get(key)
|
||||||
|
if value is None:
|
||||||
|
value = func()
|
||||||
|
cache.set(key, value, timeout=timeout)
|
||||||
|
return value
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,61 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
# Register your models here.
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(CourseSession)
|
||||||
|
class CourseSessionAdmin(admin.ModelAdmin):
|
||||||
|
date_hierarchy = "created_at"
|
||||||
|
list_display = [
|
||||||
|
"title",
|
||||||
|
"course",
|
||||||
|
"start_date",
|
||||||
|
"end_date",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(CourseSessionUser)
|
||||||
|
class CourseSessionUserAdmin(admin.ModelAdmin):
|
||||||
|
date_hierarchy = "created_at"
|
||||||
|
list_display = [
|
||||||
|
"course_session",
|
||||||
|
"user",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
]
|
||||||
|
search_fields = [
|
||||||
|
"user__first_name",
|
||||||
|
"user__last_name",
|
||||||
|
"user__email",
|
||||||
|
"course_session__title",
|
||||||
|
]
|
||||||
|
list_filter = [
|
||||||
|
"course_session__course",
|
||||||
|
"course_session",
|
||||||
|
]
|
||||||
|
|
||||||
|
fieldsets = [
|
||||||
|
(None, {"fields": ("user", "course_session")}),
|
||||||
|
(
|
||||||
|
"Expert/Trainer",
|
||||||
|
{
|
||||||
|
"fields": ("expert",),
|
||||||
|
"description": "Expert/Trainer kann erst ausgewählt werden, wenn der Kurs ausgewählt und bereits einmal gespeichert wurde.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
def formfield_for_manytomany(self, db_field, request, **kwargs):
|
||||||
|
if db_field.name == "expert":
|
||||||
|
if request.resolver_match.kwargs.get("object_id"):
|
||||||
|
object_id = int(request.resolver_match.kwargs.get("object_id"))
|
||||||
|
csu = CourseSessionUser.objects.get(id=object_id)
|
||||||
|
kwargs["queryset"] = Circle.objects.descendant_of(
|
||||||
|
csu.course_session.course.coursepage
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
kwargs["queryset"] = Circle.objects.none()
|
||||||
|
return super().formfield_for_manytomany(db_field, request, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1,3 @@
|
||||||
COURSE_TEST_ID = -1
|
COURSE_TEST_ID = -1
|
||||||
COURSE_VERSICHERUNGSVERMITTLERIN_ID = -2
|
COURSE_VERSICHERUNGSVERMITTLERIN_ID = -2
|
||||||
|
COURSE_UK1 = -3
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,12 @@ from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
|
||||||
from vbv_lernwelt.course.factories import CoursePageFactory
|
from vbv_lernwelt.course.factories import CoursePageFactory
|
||||||
|
|
||||||
|
|
||||||
def create_versicherungsvermittlerin_with_categories(apps=None, schema_editor=None):
|
def create_versicherungsvermittlerin_with_categories(
|
||||||
|
apps=None,
|
||||||
|
schema_editor=None,
|
||||||
|
course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||||
|
title="Versicherungsvermittler/in",
|
||||||
|
):
|
||||||
if apps is not None:
|
if apps is not None:
|
||||||
Course = apps.get_model("course", "Course")
|
Course = apps.get_model("course", "Course")
|
||||||
CourseCategory = apps.get_model("course", "CourseCategory")
|
CourseCategory = apps.get_model("course", "CourseCategory")
|
||||||
|
|
@ -15,8 +20,8 @@ def create_versicherungsvermittlerin_with_categories(apps=None, schema_editor=No
|
||||||
from vbv_lernwelt.course.models import Course, CourseCategory
|
from vbv_lernwelt.course.models import Course, CourseCategory
|
||||||
|
|
||||||
course, _ = Course.objects.get_or_create(
|
course, _ = Course.objects.get_or_create(
|
||||||
id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
id=course_id,
|
||||||
title="Versicherungsvermittler/in",
|
title=title,
|
||||||
category_name="Handlungsfeld",
|
category_name="Handlungsfeld",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -48,7 +53,7 @@ def create_versicherungsvermittlerin_with_categories(apps=None, schema_editor=No
|
||||||
site.save()
|
site.save()
|
||||||
|
|
||||||
course_page = CoursePageFactory(
|
course_page = CoursePageFactory(
|
||||||
title="Versicherungsvermittler/in",
|
title=title,
|
||||||
parent=site.root_page,
|
parent=site.root_page,
|
||||||
course=course,
|
course=course,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,22 @@ import djclick as click
|
||||||
from vbv_lernwelt.competence.create_default_competence_profile import (
|
from vbv_lernwelt.competence.create_default_competence_profile import (
|
||||||
create_default_competence_profile,
|
create_default_competence_profile,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.core.create_default_users import default_users
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.consts import (
|
||||||
|
COURSE_TEST_ID,
|
||||||
|
COURSE_UK1,
|
||||||
|
COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||||
|
)
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
from vbv_lernwelt.course.creators.versicherungsvermittlerin import (
|
from vbv_lernwelt.course.creators.versicherungsvermittlerin import (
|
||||||
create_versicherungsvermittlerin_with_categories,
|
create_versicherungsvermittlerin_with_categories,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.learnpath.create_default_learning_path import (
|
from vbv_lernwelt.learnpath.create_default_learning_path import (
|
||||||
create_default_learning_path,
|
create_default_learning_path,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
from vbv_lernwelt.media_library.create_default_documents import (
|
from vbv_lernwelt.media_library.create_default_documents import (
|
||||||
create_default_collections,
|
create_default_collections,
|
||||||
create_default_documents,
|
create_default_documents,
|
||||||
|
|
@ -21,16 +30,79 @@ from vbv_lernwelt.media_library.create_default_media_library import (
|
||||||
|
|
||||||
@click.command()
|
@click.command()
|
||||||
def command():
|
def command():
|
||||||
|
# Versicherungsvermittler/in
|
||||||
create_versicherungsvermittlerin_with_categories()
|
create_versicherungsvermittlerin_with_categories()
|
||||||
|
|
||||||
create_default_learning_path()
|
create_default_learning_path()
|
||||||
|
|
||||||
create_default_competence_profile()
|
create_default_competence_profile()
|
||||||
|
|
||||||
# media library
|
# media library data
|
||||||
create_default_collections()
|
create_default_collections()
|
||||||
create_default_documents()
|
create_default_documents()
|
||||||
|
|
||||||
create_default_media_library()
|
create_default_media_library()
|
||||||
|
|
||||||
|
# Versicherungsvermittler/in
|
||||||
|
create_versicherungsvermittlerin_with_categories(
|
||||||
|
course_id=COURSE_UK1, title="Überbetriebliche Kurse"
|
||||||
|
)
|
||||||
|
create_default_learning_path(course_id=COURSE_UK1)
|
||||||
|
create_default_competence_profile(course_id=COURSE_UK1)
|
||||||
|
create_default_media_library(course_id=COURSE_UK1)
|
||||||
|
|
||||||
# test course
|
# test course
|
||||||
create_test_course()
|
create_test_course()
|
||||||
|
|
||||||
|
# course sessions
|
||||||
|
CourseSession.objects.create(
|
||||||
|
course_id=COURSE_TEST_ID,
|
||||||
|
title="Test Lehrgang Session",
|
||||||
|
)
|
||||||
|
|
||||||
|
# course session Versicherungsvermittler/in
|
||||||
|
cs = CourseSession.objects.create(
|
||||||
|
course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
|
||||||
|
title="Versicherungsvermittler/in",
|
||||||
|
)
|
||||||
|
for user_data in default_users:
|
||||||
|
CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username=user_data["email"]),
|
||||||
|
)
|
||||||
|
csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username="expertvv.einstieg@vbv-afa.ch"),
|
||||||
|
)
|
||||||
|
csu.expert.add(
|
||||||
|
Circle.objects.get(slug="versicherungsvermittlerin-lp-circle-einstieg")
|
||||||
|
)
|
||||||
|
|
||||||
|
csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username="expertvv.analyse@vbv-afa.ch"),
|
||||||
|
)
|
||||||
|
csu.expert.add(
|
||||||
|
Circle.objects.get(slug="versicherungsvermittlerin-lp-circle-analyse")
|
||||||
|
)
|
||||||
|
|
||||||
|
# course session Überbetriebliche Kurse Lehrjahr 1
|
||||||
|
cs = CourseSession.objects.create(
|
||||||
|
course_id=COURSE_UK1,
|
||||||
|
title="Überbetriebliche Kurse Region Bern Lehrjahr 1",
|
||||||
|
)
|
||||||
|
for user_data in default_users:
|
||||||
|
CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username=user_data["email"]),
|
||||||
|
)
|
||||||
|
|
||||||
|
csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username="trainer-uk1.einstieg@vbv-afa.ch"),
|
||||||
|
)
|
||||||
|
csu.expert.add(Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-einstieg"))
|
||||||
|
|
||||||
|
csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=cs,
|
||||||
|
user=User.objects.get(username="trainer-uk1.analyse@vbv-afa.ch"),
|
||||||
|
)
|
||||||
|
csu.expert.add(Circle.objects.get(slug="überbetriebliche-kurse-lp-circle-analyse"))
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,79 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-10-14 07:33
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("course", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CourseSession",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated_at", models.DateTimeField(auto_now=True)),
|
||||||
|
("title", models.TextField()),
|
||||||
|
("start_date", models.DateField(blank=True, null=True)),
|
||||||
|
("end_date", models.DateField(blank=True, null=True)),
|
||||||
|
("additional_json_data", models.JSONField(default=dict)),
|
||||||
|
(
|
||||||
|
"course",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="course.course"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CourseSessionUser",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||||
|
("updated_at", models.DateTimeField(auto_now=True)),
|
||||||
|
(
|
||||||
|
"course_session",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to="course.coursesession",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name="coursesessionuser",
|
||||||
|
constraint=models.UniqueConstraint(
|
||||||
|
fields=("course_session", "user"),
|
||||||
|
name="course_session_user_unique_course_session_user",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-11-07 13:30
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("course", "0002_auto_20221014_0933"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="coursepage",
|
||||||
|
name="course",
|
||||||
|
field=models.OneToOneField(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT, to="course.course"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-11-07 15:51
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("learnpath", "0008_alter_learningcontent_contents"),
|
||||||
|
("course", "0003_alter_coursepage_course"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="coursesessionuser",
|
||||||
|
name="expert",
|
||||||
|
field=models.ManyToManyField(related_name="expert", to="learnpath.Circle"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-11-07 15:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("learnpath", "0008_alter_learningcontent_contents"),
|
||||||
|
("course", "0004_coursesessionuser_expert"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="coursesessionuser",
|
||||||
|
name="expert",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
blank=True, related_name="expert", to="learnpath.Circle"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -6,6 +6,7 @@ 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
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
|
||||||
|
|
||||||
|
|
||||||
class Course(models.Model):
|
class Course(models.Model):
|
||||||
|
|
@ -17,6 +18,30 @@ class Course(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Lehrgang")
|
verbose_name = _("Lehrgang")
|
||||||
|
|
||||||
|
def get_learning_path_url(self):
|
||||||
|
from vbv_lernwelt.learnpath.models import LearningPath
|
||||||
|
|
||||||
|
learning_path_page = (
|
||||||
|
self.coursepage.get_children().exact_type(LearningPath).first()
|
||||||
|
)
|
||||||
|
return learning_path_page.specific.get_frontend_url()
|
||||||
|
|
||||||
|
def get_competence_url(self):
|
||||||
|
from vbv_lernwelt.competence.models import CompetenceProfilePage
|
||||||
|
|
||||||
|
competence_page = (
|
||||||
|
self.coursepage.get_children().exact_type(CompetenceProfilePage).first()
|
||||||
|
)
|
||||||
|
return competence_page.specific.get_frontend_url()
|
||||||
|
|
||||||
|
def get_media_library_url(self):
|
||||||
|
from vbv_lernwelt.media_library.models import MediaLibraryPage
|
||||||
|
|
||||||
|
media_library_page = (
|
||||||
|
self.coursepage.get_children().exact_type(MediaLibraryPage).first()
|
||||||
|
)
|
||||||
|
return media_library_page.specific.get_frontend_url()
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
|
|
@ -31,10 +56,66 @@ class CourseCategory(models.Model):
|
||||||
return f"{self.course} / {self.title}"
|
return f"{self.course} / {self.title}"
|
||||||
|
|
||||||
|
|
||||||
class CoursePage(Page):
|
class CourseBasePage(Page):
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
|
||||||
|
serialize_field_names = []
|
||||||
|
serialize_base_field_names = [
|
||||||
|
"id",
|
||||||
|
"title",
|
||||||
|
"slug",
|
||||||
|
"type",
|
||||||
|
"translation_key",
|
||||||
|
"frontend_url",
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_course_parent(self):
|
||||||
|
return self.get_ancestors(inclusive=True).exact_type(CoursePage).last()
|
||||||
|
|
||||||
|
def get_course(self):
|
||||||
|
course_parent_page = self.get_course_parent()
|
||||||
|
if course_parent_page:
|
||||||
|
return course_parent_page.specific.course
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_circles(self):
|
||||||
|
course_parent_page = self.get_course_parent()
|
||||||
|
|
||||||
|
if course_parent_page:
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle, LearningPath
|
||||||
|
|
||||||
|
circles = (
|
||||||
|
course_parent_page.get_children()
|
||||||
|
.exact_type(LearningPath)
|
||||||
|
.first()
|
||||||
|
.get_children()
|
||||||
|
.exact_type(Circle)
|
||||||
|
)
|
||||||
|
return circles
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_serializer_class(cls):
|
||||||
|
return get_course_serializer_class(
|
||||||
|
cls,
|
||||||
|
field_names=cls.serialize_field_names,
|
||||||
|
base_field_names=cls.serialize_base_field_names,
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class CoursePage(CourseBasePage):
|
||||||
content_panels = Page.content_panels
|
content_panels = Page.content_panels
|
||||||
subpage_types = ["learnpath.LearningPath", "media_library.MediaLibraryPage"]
|
subpage_types = [
|
||||||
course = models.ForeignKey("course.Course", on_delete=models.PROTECT)
|
"learnpath.LearningPath",
|
||||||
|
"competence.CompetenceProfilePage",
|
||||||
|
"media_library.MediaLibraryPage",
|
||||||
|
]
|
||||||
|
course = models.OneToOneField("course.Course", on_delete=models.PROTECT)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Lehrgang-Seite")
|
verbose_name = _("Lehrgang-Seite")
|
||||||
|
|
@ -81,3 +162,52 @@ class CourseCompletion(models.Model):
|
||||||
name="course_completion_unique_user_page_key",
|
name="course_completion_unique_user_page_key",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CourseSession(models.Model):
|
||||||
|
"""
|
||||||
|
Die Durchführung eines Kurses
|
||||||
|
Benutzer die an eine CourseSession gehängt sind können diesen Lehrgang sehen
|
||||||
|
Das anhängen kann via CourseSessionUser oder "Schulklasse (TODO)" geschehen
|
||||||
|
"""
|
||||||
|
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
course = models.ForeignKey("course.Course", on_delete=models.CASCADE)
|
||||||
|
title = models.TextField()
|
||||||
|
|
||||||
|
start_date = models.DateField(null=True, blank=True)
|
||||||
|
end_date = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
|
additional_json_data = models.JSONField(default=dict)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.title}"
|
||||||
|
|
||||||
|
|
||||||
|
class CourseSessionUser(models.Model):
|
||||||
|
"""
|
||||||
|
Ein Benutzer der an einer CourseSession teilnimmt
|
||||||
|
"""
|
||||||
|
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
course_session = models.ForeignKey("course.CourseSession", on_delete=models.CASCADE)
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
expert = models.ManyToManyField(
|
||||||
|
"learnpath.Circle", related_name="expert", blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
UniqueConstraint(
|
||||||
|
fields=[
|
||||||
|
"course_session",
|
||||||
|
"user",
|
||||||
|
],
|
||||||
|
name="course_session_user_unique_course_session_user",
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
|
|
||||||
|
|
||||||
|
def has_course_access_by_page_request(request, obj):
|
||||||
|
return has_course_access(request.user, obj.specific.get_course())
|
||||||
|
|
||||||
|
|
||||||
|
def has_course_access(user, course):
|
||||||
|
if user.is_superuser:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if CourseSessionUser.objects.filter(
|
||||||
|
course_session__course_id=course.id, user=user
|
||||||
|
).exists():
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def course_sessions_for_user_qs(user):
|
||||||
|
if user.is_superuser:
|
||||||
|
return CourseSession.objects.all()
|
||||||
|
|
||||||
|
course_sessions = CourseSession.objects.filter(coursesessionuser__user=user)
|
||||||
|
|
||||||
|
return course_sessions
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
from rest_framework.fields import SerializerMethodField
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.serializer_helpers import (
|
||||||
|
get_it_serializer_class,
|
||||||
|
ItWagtailBaseSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CourseBaseSerializer(ItWagtailBaseSerializer):
|
||||||
|
course = SerializerMethodField()
|
||||||
|
course_category = SerializerMethodField()
|
||||||
|
circles = SerializerMethodField()
|
||||||
|
|
||||||
|
meta_fields = []
|
||||||
|
|
||||||
|
def get_course(self, obj):
|
||||||
|
course = obj.get_course()
|
||||||
|
if course:
|
||||||
|
from vbv_lernwelt.course.serializers import CourseSerializer
|
||||||
|
|
||||||
|
return CourseSerializer(course).data
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def get_course_category(self, obj):
|
||||||
|
from vbv_lernwelt.course.serializers import CourseCategorySerializer
|
||||||
|
|
||||||
|
return CourseCategorySerializer(obj.course_category).data
|
||||||
|
|
||||||
|
def get_circles(self, obj):
|
||||||
|
circles = obj.get_circles()
|
||||||
|
|
||||||
|
if circles:
|
||||||
|
return [
|
||||||
|
{"id": c.id, "title": c.title, "translation_key": c.translation_key}
|
||||||
|
for c in circles
|
||||||
|
]
|
||||||
|
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def get_course_serializer_class(model, field_names=None, base_field_names=None):
|
||||||
|
return get_it_serializer_class(
|
||||||
|
model, field_names, base_field_names, CourseBaseSerializer
|
||||||
|
)
|
||||||
|
|
@ -1,6 +1,13 @@
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import Course, CourseCategory, CourseCompletion
|
from vbv_lernwelt.course.models import (
|
||||||
|
Course,
|
||||||
|
CourseCategory,
|
||||||
|
CourseCompletion,
|
||||||
|
CourseSession,
|
||||||
|
CourseSessionUser,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
|
|
||||||
class CourseSerializer(serializers.ModelSerializer):
|
class CourseSerializer(serializers.ModelSerializer):
|
||||||
|
|
@ -34,3 +41,60 @@ class CourseCompletionSerializer(serializers.ModelSerializer):
|
||||||
"completion_status",
|
"completion_status",
|
||||||
"additional_json_data",
|
"additional_json_data",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CourseSessionSerializer(serializers.ModelSerializer):
|
||||||
|
learning_path_url = serializers.SerializerMethodField()
|
||||||
|
competence_url = serializers.SerializerMethodField()
|
||||||
|
media_library_url = serializers.SerializerMethodField()
|
||||||
|
course = serializers.SerializerMethodField()
|
||||||
|
experts = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
def get_course(self, obj):
|
||||||
|
return CourseSerializer(obj.course).data
|
||||||
|
|
||||||
|
def get_learning_path_url(self, obj):
|
||||||
|
return obj.course.get_learning_path_url()
|
||||||
|
|
||||||
|
def get_media_library_url(self, obj):
|
||||||
|
return obj.course.get_media_library_url()
|
||||||
|
|
||||||
|
def get_competence_url(self, obj):
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
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
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CourseSession
|
||||||
|
fields = [
|
||||||
|
"id",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
"course",
|
||||||
|
"title",
|
||||||
|
"start_date",
|
||||||
|
"end_date",
|
||||||
|
"additional_json_data",
|
||||||
|
"learning_path_url",
|
||||||
|
"competence_url",
|
||||||
|
"media_library_url",
|
||||||
|
"experts",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,8 @@ class CourseCompletionApiTestCase(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
create_default_users()
|
create_default_users()
|
||||||
create_test_course()
|
create_test_course()
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="admin")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="admin", password="test")
|
||||||
|
|
||||||
def test_completeLearningContent_works(self):
|
def test_completeLearningContent_works(self):
|
||||||
learning_content = LearningContent.objects.get(title="Fachcheck Fahrzeug")
|
learning_content = LearningContent.objects.get(title="Fachcheck Fahrzeug")
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import json
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
|
||||||
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
|
from vbv_lernwelt.core.models import User
|
||||||
|
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
||||||
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
|
|
||||||
|
|
||||||
|
class CourseCompletionApiTestCase(APITestCase):
|
||||||
|
def setUp(self) -> None:
|
||||||
|
create_default_users()
|
||||||
|
create_test_course()
|
||||||
|
|
||||||
|
self.user = User.objects.get(username="student")
|
||||||
|
|
||||||
|
self.course_session = CourseSession.objects.create(
|
||||||
|
course_id=COURSE_TEST_ID,
|
||||||
|
title="Test Lehrgang Session",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
|
def test_api_noCourseSession_withoutCourseSessionUser(self):
|
||||||
|
response = self.client.get(f"/api/course/sessions/")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(response.json()), 0)
|
||||||
|
|
||||||
|
def test_api_courseSession_withCourseSessionUser(self):
|
||||||
|
csu = CourseSessionUser.objects.create(
|
||||||
|
course_session=self.course_session,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
response = self.client.get(f"/api/course/sessions/")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(response.json()), 1)
|
||||||
|
|
||||||
|
print(json.dumps(response.json(), indent=4))
|
||||||
|
self.assertEqual(response.json()[0]["id"], self.course_session.id)
|
||||||
|
|
||||||
|
def test_api_superUser_canAccessEveryCourseSession(self):
|
||||||
|
self.client.login(username="admin", password="test")
|
||||||
|
response = self.client.get(f"/api/course/sessions/")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertEqual(len(response.json()), 1)
|
||||||
|
|
||||||
|
print(json.dumps(response.json(), indent=4))
|
||||||
|
self.assertEqual(response.json()[0]["id"], self.course_session.id)
|
||||||
|
|
@ -1,23 +1,39 @@
|
||||||
import structlog
|
import structlog
|
||||||
from django.views.decorators.cache import cache_page
|
|
||||||
from rest_framework.decorators import api_view
|
from rest_framework.decorators import api_view
|
||||||
|
from rest_framework.exceptions import PermissionDenied
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.course.models import CourseCompletion, CoursePage
|
from vbv_lernwelt.core.utils import api_page_cache_get_or_set
|
||||||
from vbv_lernwelt.course.serializers import CourseCompletionSerializer
|
from vbv_lernwelt.course.models import CourseCompletion
|
||||||
|
from vbv_lernwelt.course.permissions import (
|
||||||
|
course_sessions_for_user_qs,
|
||||||
|
has_course_access_by_page_request,
|
||||||
|
)
|
||||||
|
from vbv_lernwelt.course.serializers import (
|
||||||
|
CourseCompletionSerializer,
|
||||||
|
CourseSessionSerializer,
|
||||||
|
)
|
||||||
from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
from vbv_lernwelt.learnpath.utils import get_wagtail_type
|
||||||
|
|
||||||
logger = structlog.get_logger(__name__)
|
logger = structlog.get_logger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
@cache_page(60 * 60 * 8, cache="api_page_cache")
|
def course_page_api_view(request, slug):
|
||||||
def page_api_view(request, slug):
|
|
||||||
try:
|
try:
|
||||||
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
||||||
serializer = page.specific.get_serializer_class()(page.specific)
|
if not has_course_access_by_page_request(request, page):
|
||||||
return Response(serializer.data)
|
raise PermissionDenied()
|
||||||
|
|
||||||
|
data = api_page_cache_get_or_set(
|
||||||
|
key=request.get_full_path(),
|
||||||
|
func=lambda: page.specific.get_serializer_class()(page.specific).data,
|
||||||
|
)
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
except PermissionDenied as e:
|
||||||
|
raise e
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
return Response({"error": str(e)}, status=404)
|
return Response({"error": str(e)}, status=404)
|
||||||
|
|
@ -25,22 +41,32 @@ def page_api_view(request, slug):
|
||||||
|
|
||||||
@api_view(["GET"])
|
@api_view(["GET"])
|
||||||
def request_course_completion(request, course_id):
|
def request_course_completion(request, course_id):
|
||||||
|
try:
|
||||||
response_data = CourseCompletionSerializer(
|
response_data = CourseCompletionSerializer(
|
||||||
CourseCompletion.objects.filter(user=request.user, course_id=course_id),
|
CourseCompletion.objects.filter(user=request.user, course_id=course_id),
|
||||||
many=True,
|
many=True,
|
||||||
).data
|
).data
|
||||||
|
|
||||||
return Response(status=200, data=response_data)
|
return Response(status=200, data=response_data)
|
||||||
|
except PermissionDenied as e:
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return Response({"error": str(e)}, status=404)
|
||||||
|
|
||||||
|
|
||||||
@api_view(["POST"])
|
@api_view(["POST"])
|
||||||
def mark_course_completion(request):
|
def mark_course_completion(request):
|
||||||
|
try:
|
||||||
page_key = request.data.get("page_key")
|
page_key = request.data.get("page_key")
|
||||||
completion_status = request.data.get("completion_status", "success")
|
completion_status = request.data.get("completion_status", "success")
|
||||||
|
|
||||||
page = Page.objects.get(translation_key=page_key, locale__language_code="de-CH")
|
page = Page.objects.get(translation_key=page_key, locale__language_code="de-CH")
|
||||||
|
if not has_course_access_by_page_request(request, page):
|
||||||
|
raise PermissionDenied()
|
||||||
|
|
||||||
page_type = get_wagtail_type(page.specific)
|
page_type = get_wagtail_type(page.specific)
|
||||||
course = CoursePage.objects.ancestor_of(page).first().specific.course
|
course = page.specific.get_course()
|
||||||
|
|
||||||
cc, created = CourseCompletion.objects.get_or_create(
|
cc, created = CourseCompletion.objects.get_or_create(
|
||||||
user=request.user,
|
user=request.user,
|
||||||
|
|
@ -70,3 +96,22 @@ def mark_course_completion(request):
|
||||||
)
|
)
|
||||||
|
|
||||||
return Response(status=200, data=response_data)
|
return Response(status=200, data=response_data)
|
||||||
|
except PermissionDenied as e:
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return Response({"error": str(e)}, status=404)
|
||||||
|
|
||||||
|
|
||||||
|
@api_view(["GET"])
|
||||||
|
def get_course_sessions(request):
|
||||||
|
try:
|
||||||
|
course_sessions = course_sessions_for_user_qs(request.user)
|
||||||
|
return Response(
|
||||||
|
status=200, data=CourseSessionSerializer(course_sessions, many=True).data
|
||||||
|
)
|
||||||
|
except PermissionDenied as e:
|
||||||
|
raise e
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(e)
|
||||||
|
return Response({"error": str(e)}, status=404)
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,9 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||||
READY_HF = ["Fahrzeug", "Reisen"]
|
READY_HF = ["Fahrzeug", "Reisen"]
|
||||||
|
|
||||||
|
|
||||||
def create_default_learning_path(user=None, skip_locales=True):
|
def create_default_learning_path(
|
||||||
|
course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, user=None, skip_locales=True
|
||||||
|
):
|
||||||
if user is None:
|
if user is None:
|
||||||
user = User.objects.get(username="info@iterativ.ch")
|
user = User.objects.get(username="info@iterativ.ch")
|
||||||
|
|
||||||
|
|
@ -41,7 +43,7 @@ def create_default_learning_path(user=None, skip_locales=True):
|
||||||
site.port = 8000
|
site.port = 8000
|
||||||
site.save()
|
site.save()
|
||||||
|
|
||||||
course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
course_page = CoursePage.objects.get(course_id=course_id)
|
||||||
lp = LearningPathFactory(
|
lp = LearningPathFactory(
|
||||||
title="Lernpfad",
|
title="Lernpfad",
|
||||||
parent=course_page,
|
parent=course_page,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
# Generated by Django 3.2.13 on 2022-11-07 13:30
|
||||||
|
|
||||||
|
import wagtail.blocks
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("learnpath", "0007_alter_learningcontent_contents"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="learningcontent",
|
||||||
|
name="contents",
|
||||||
|
field=wagtail.fields.StreamField(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"video",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"resource",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
("text", wagtail.blocks.RichTextBlock(required=False)),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"exercise",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"learningmodule",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"online_training",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"media_library",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"document",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"test",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"book",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"assignment",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
("text", wagtail.blocks.RichTextBlock(required=False)),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"placeholder",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("description", wagtail.blocks.TextBlock()),
|
||||||
|
("url", wagtail.blocks.TextBlock()),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
use_json_field=None,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -8,8 +8,7 @@ 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
|
||||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.course.models import CourseBasePage, CoursePage
|
||||||
from vbv_lernwelt.course.models import CoursePage
|
|
||||||
from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
||||||
AssignmentBlock,
|
AssignmentBlock,
|
||||||
BookBlock,
|
BookBlock,
|
||||||
|
|
@ -25,7 +24,8 @@ from vbv_lernwelt.learnpath.models_learning_unit_content import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class LearningPath(Page):
|
class LearningPath(CourseBasePage):
|
||||||
|
serialize_field_names = ["children", "course"]
|
||||||
content_panels = Page.content_panels
|
content_panels = Page.content_panels
|
||||||
subpage_types = ["learnpath.Circle", "learnpath.Topic"]
|
subpage_types = ["learnpath.Circle", "learnpath.Topic"]
|
||||||
parent_page_types = ["course.CoursePage"]
|
parent_page_types = ["course.CoursePage"]
|
||||||
|
|
@ -45,19 +45,10 @@ class LearningPath(Page):
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
return f"/learn/{self.slug}"
|
return f"/learn/{self.slug}"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
[
|
|
||||||
"children",
|
|
||||||
"course",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
class Topic(CourseBasePage):
|
||||||
|
serialize_field_names = ["is_visible"]
|
||||||
|
|
||||||
class Topic(Page):
|
|
||||||
# title = models.TextField(default='')
|
|
||||||
is_visible = models.BooleanField(default=True)
|
is_visible = models.BooleanField(default=True)
|
||||||
|
|
||||||
parent_page_types = ["learnpath.LearningPath"]
|
parent_page_types = ["learnpath.LearningPath"]
|
||||||
|
|
@ -67,27 +58,10 @@ class Topic(Page):
|
||||||
FieldPanel("is_visible"),
|
FieldPanel("is_visible"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# content_panels = Page.content_panels + [
|
|
||||||
# FieldPanel('is_visible', classname="full"),
|
|
||||||
# PageChooserPanel('learning_path', 'learnpath.LearningPath'),
|
|
||||||
# ]
|
|
||||||
|
|
||||||
# parent_page_types = ['learnpath.LearningPath']
|
|
||||||
# subpage_types = ['learnpath.Circle']
|
|
||||||
|
|
||||||
def full_clean(self, *args, **kwargs):
|
def full_clean(self, *args, **kwargs):
|
||||||
self.slug = find_slug_with_parent_prefix(self, "topic")
|
self.slug = find_slug_with_parent_prefix(self, "topic")
|
||||||
super(Topic, self).full_clean(*args, **kwargs)
|
super(Topic, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
field_names=[
|
|
||||||
"is_visible",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_admin_display_title(self):
|
def get_admin_display_title(self):
|
||||||
return f"Thema: {self.draft_title}"
|
return f"Thema: {self.draft_title}"
|
||||||
|
|
||||||
|
|
@ -109,7 +83,7 @@ class PersonBlock(blocks.StructBlock):
|
||||||
icon = "user"
|
icon = "user"
|
||||||
|
|
||||||
|
|
||||||
class Circle(Page):
|
class Circle(CourseBasePage):
|
||||||
parent_page_types = ["learnpath.LearningPath"]
|
parent_page_types = ["learnpath.LearningPath"]
|
||||||
subpage_types = [
|
subpage_types = [
|
||||||
"learnpath.LearningSequence",
|
"learnpath.LearningSequence",
|
||||||
|
|
@ -117,6 +91,16 @@ class Circle(Page):
|
||||||
"learnpath.LearningContent",
|
"learnpath.LearningContent",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
serialize_field_names = [
|
||||||
|
"children",
|
||||||
|
"description",
|
||||||
|
"goal_description",
|
||||||
|
"goals",
|
||||||
|
"job_situation_description",
|
||||||
|
"job_situations",
|
||||||
|
"experts",
|
||||||
|
]
|
||||||
|
|
||||||
description = models.TextField(default="", blank=True)
|
description = models.TextField(default="", blank=True)
|
||||||
|
|
||||||
goal_description = models.TextField(default="", blank=True)
|
goal_description = models.TextField(default="", blank=True)
|
||||||
|
|
@ -148,21 +132,6 @@ class Circle(Page):
|
||||||
FieldPanel("experts"),
|
FieldPanel("experts"),
|
||||||
]
|
]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
field_names=[
|
|
||||||
"children",
|
|
||||||
"description",
|
|
||||||
"goal_description",
|
|
||||||
"goals",
|
|
||||||
"job_situation_description",
|
|
||||||
"job_situations",
|
|
||||||
"experts",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "")
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-circle-", "")
|
||||||
return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}"
|
return f"{self.get_parent().specific.get_frontend_url()}/{short_slug}"
|
||||||
|
|
@ -178,7 +147,9 @@ class Circle(Page):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
|
|
||||||
class LearningSequence(Page):
|
class LearningSequence(CourseBasePage):
|
||||||
|
serialize_field_names = ["icon"]
|
||||||
|
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
|
|
||||||
|
|
@ -194,10 +165,6 @@ class LearningSequence(Page):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(cls, field_names=["icon"])
|
|
||||||
|
|
||||||
def get_admin_display_title(self):
|
def get_admin_display_title(self):
|
||||||
return f"{self.icon} {self.draft_title}"
|
return f"{self.icon} {self.draft_title}"
|
||||||
|
|
||||||
|
|
@ -220,7 +187,7 @@ class LearningSequence(Page):
|
||||||
return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}"
|
return f"{self.get_parent().specific.get_frontend_url()}#{short_slug}"
|
||||||
|
|
||||||
|
|
||||||
class LearningUnit(Page):
|
class LearningUnit(CourseBasePage):
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
course_category = models.ForeignKey(
|
course_category = models.ForeignKey(
|
||||||
|
|
@ -277,7 +244,12 @@ class LearningUnit(Page):
|
||||||
return f'<span style="font-weight: 700; font-size: 20px;">{self.draft_title}</span>'
|
return f'<span style="font-weight: 700; font-size: 20px;">{self.draft_title}</span>'
|
||||||
|
|
||||||
|
|
||||||
class LearningContent(Page):
|
class LearningContent(CourseBasePage):
|
||||||
|
serialize_field_names = [
|
||||||
|
"minutes",
|
||||||
|
"contents",
|
||||||
|
]
|
||||||
|
|
||||||
parent_page_types = ["learnpath.Circle"]
|
parent_page_types = ["learnpath.Circle"]
|
||||||
subpage_types = []
|
subpage_types = []
|
||||||
|
|
||||||
|
|
@ -335,19 +307,6 @@ class LearningContent(Page):
|
||||||
self.slug = find_slug_with_parent_prefix(self, "lc")
|
self.slug = find_slug_with_parent_prefix(self, "lc")
|
||||||
super(LearningContent, self).full_clean(*args, **kwargs)
|
super(LearningContent, self).full_clean(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
field_names=[
|
|
||||||
"minutes",
|
|
||||||
"contents",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"{self.title}"
|
|
||||||
|
|
||||||
|
|
||||||
def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None):
|
def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None):
|
||||||
parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug
|
parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@ from rest_framework.fields import SerializerMethodField
|
||||||
from vbv_lernwelt.competence.serializers import (
|
from vbv_lernwelt.competence.serializers import (
|
||||||
PerformanceCriteriaLearningPathSerializer,
|
PerformanceCriteriaLearningPathSerializer,
|
||||||
)
|
)
|
||||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.course.serializer_helpers import get_course_serializer_class
|
||||||
from vbv_lernwelt.learnpath.models import LearningUnit
|
from vbv_lernwelt.learnpath.models import LearningUnit
|
||||||
|
|
||||||
|
|
||||||
class LearningUnitSerializer(
|
class LearningUnitSerializer(
|
||||||
get_it_serializer_class(
|
get_course_serializer_class(
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
[
|
field_names=[
|
||||||
"evaluate_url",
|
"evaluate_url",
|
||||||
"course_category",
|
"course_category",
|
||||||
"children",
|
"children",
|
||||||
|
|
@ -30,9 +30,9 @@ class LearningUnitSerializer(
|
||||||
|
|
||||||
|
|
||||||
class LearningUnitPerformanceCriteriaSerializer(
|
class LearningUnitPerformanceCriteriaSerializer(
|
||||||
get_it_serializer_class(
|
get_course_serializer_class(
|
||||||
LearningUnit,
|
LearningUnit,
|
||||||
[
|
field_names=[
|
||||||
"evaluate_url",
|
"evaluate_url",
|
||||||
"course_category",
|
"course_category",
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,9 @@ from rest_framework.test import APITestCase
|
||||||
|
|
||||||
from vbv_lernwelt.core.admin import User
|
from vbv_lernwelt.core.admin import User
|
||||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||||
|
from vbv_lernwelt.course.consts import COURSE_TEST_ID
|
||||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||||
|
from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
|
||||||
from vbv_lernwelt.learnpath.models import LearningPath
|
from vbv_lernwelt.learnpath.models import LearningPath
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -10,19 +12,44 @@ class TestRetrieveLearingPathContents(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
create_default_users()
|
create_default_users()
|
||||||
create_test_course()
|
create_test_course()
|
||||||
self.user = User.objects.get(username="student")
|
self.slug = "test-lehrgang-lp"
|
||||||
self.client.login(username="student", password="test")
|
self.learning_path = LearningPath.objects.get(slug=self.slug)
|
||||||
|
|
||||||
def test_get_learnpath_page(self):
|
def test_get_learnpath_page(self):
|
||||||
slug = "test-lehrgang-lp"
|
self.user = User.objects.get(username="admin")
|
||||||
learning_path = LearningPath.objects.get(slug=slug)
|
self.client.login(username="admin", password="test")
|
||||||
response = self.client.get(f"/api/course/page/{slug}/")
|
|
||||||
|
response = self.client.get(f"/api/course/page/{self.slug}/")
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
data = response.json()
|
data = response.json()
|
||||||
|
|
||||||
self.assertEqual(learning_path.title, data["title"])
|
self.assertEqual(self.learning_path.title, data["title"])
|
||||||
# topics and circles
|
# topics and circles
|
||||||
self.assertEqual(4, len(data["children"]))
|
self.assertEqual(4, len(data["children"]))
|
||||||
# circle "analyse" contents
|
# circle "analyse" contents
|
||||||
self.assertEqual(14, len(data["children"][3]["children"]))
|
self.assertEqual(14, len(data["children"][3]["children"]))
|
||||||
|
|
||||||
|
def test_normalUser_withoutCourseSession_cannotAccess(self):
|
||||||
|
self.user = User.objects.get(username="student")
|
||||||
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
|
response = self.client.get(f"/api/course/page/{self.slug}/")
|
||||||
|
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
def test_normalUser_withCourseSession_canAccess(self):
|
||||||
|
self.user = User.objects.get(username="student")
|
||||||
|
self.client.login(username="student", password="test")
|
||||||
|
|
||||||
|
course_session = CourseSession.objects.create(
|
||||||
|
course_id=COURSE_TEST_ID,
|
||||||
|
title="Test Lehrgang Session",
|
||||||
|
)
|
||||||
|
CourseSessionUser.objects.create(
|
||||||
|
course_session=course_session,
|
||||||
|
user=self.user,
|
||||||
|
)
|
||||||
|
|
||||||
|
response = self.client.get(f"/api/course/page/{self.slug}/")
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,9 @@ from vbv_lernwelt.media_library.tests.media_library_factories import (
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_default_media_library():
|
def create_default_media_library(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID):
|
||||||
course = Course.objects.get(id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
course = Course.objects.get(id=course_id)
|
||||||
course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
|
course_page = CoursePage.objects.get(course_id=course_id)
|
||||||
|
|
||||||
media_lib_page = MediaLibraryPageFactory(
|
media_lib_page = MediaLibraryPageFactory(
|
||||||
title="Mediathek",
|
title="Mediathek",
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,13 @@ from wagtail.fields import StreamField
|
||||||
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
|
||||||
from vbv_lernwelt.core.serializer_helpers import get_it_serializer_class
|
from vbv_lernwelt.course.models import CourseBasePage
|
||||||
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
|
from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
|
||||||
|
|
||||||
|
|
||||||
class MediaLibraryPage(Page):
|
class MediaLibraryPage(CourseBasePage):
|
||||||
|
serialize_field_names = ["course", "children"]
|
||||||
|
|
||||||
parent_page_types = ["course.CoursePage"]
|
parent_page_types = ["course.CoursePage"]
|
||||||
subpage_types = ["media_library.MediaCategoryPage"]
|
subpage_types = ["media_library.MediaCategoryPage"]
|
||||||
|
|
||||||
|
|
@ -28,22 +30,23 @@ class MediaLibraryPage(Page):
|
||||||
def get_frontend_url(self):
|
def get_frontend_url(self):
|
||||||
return f"/media/{self.slug}"
|
return f"/media/{self.slug}"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
[
|
|
||||||
"course",
|
|
||||||
"children",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
class MediaCategoryPage(CourseBasePage):
|
||||||
class MediaCategoryPage(Page):
|
|
||||||
"""
|
"""
|
||||||
Handlungsfeld. zB. Fahrzeug
|
Handlungsfeld. zB. Fahrzeug
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
serialize_field_names = [
|
||||||
|
"course_category",
|
||||||
|
"introduction_text",
|
||||||
|
"overview_icon",
|
||||||
|
"detail_image",
|
||||||
|
"description_title",
|
||||||
|
"description_text",
|
||||||
|
"items",
|
||||||
|
"body",
|
||||||
|
]
|
||||||
|
|
||||||
course_category = models.ForeignKey(
|
course_category = models.ForeignKey(
|
||||||
"course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True
|
"course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True
|
||||||
)
|
)
|
||||||
|
|
@ -89,22 +92,6 @@ class MediaCategoryPage(Page):
|
||||||
short_slug = self.slug.replace(f"{self.get_parent().slug}-cat-", "")
|
short_slug = self.slug.replace(f"{self.get_parent().slug}-cat-", "")
|
||||||
return f"{self.get_parent().specific.get_frontend_url()}/category/{short_slug}"
|
return f"{self.get_parent().specific.get_frontend_url()}/category/{short_slug}"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_serializer_class(cls):
|
|
||||||
return get_it_serializer_class(
|
|
||||||
cls,
|
|
||||||
field_names=[
|
|
||||||
"course_category",
|
|
||||||
"introduction_text",
|
|
||||||
"overview_icon",
|
|
||||||
"detail_image",
|
|
||||||
"description_title",
|
|
||||||
"description_text",
|
|
||||||
"items",
|
|
||||||
"body",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class LibraryDocument(AbstractDocument):
|
class LibraryDocument(AbstractDocument):
|
||||||
# Todo: check https://filepreviews.io/
|
# Todo: check https://filepreviews.io/
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ class MediaLibraryAPITestCase(APITestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
create_default_users()
|
create_default_users()
|
||||||
create_test_course()
|
create_test_course()
|
||||||
self.user = User.objects.get(username="student")
|
self.user = User.objects.get(username="admin")
|
||||||
self.client.login(username="student", password="test")
|
self.client.login(username="admin", password="test")
|
||||||
|
|
||||||
def test_get_media_library_page(self):
|
def test_get_media_library_page(self):
|
||||||
slug = "test-lehrgang-media"
|
slug = "test-lehrgang-media"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue