+
@@ -336,19 +356,26 @@ function log(data: any) {
Dropdown (Work-in-progress)
-
+
+
{{ state.dropdownSelected }}
Checkbox
-
Label
+
Label
Disabled
Dropdown
- Click Me
diff --git a/client/tailwind.config.js b/client/tailwind.config.js
index 75d9b72c..256768e8 100644
--- a/client/tailwind.config.js
+++ b/client/tailwind.config.js
@@ -1,40 +1,40 @@
-const colors = require('./src/colors.json');
+const colors = require("./src/colors.json");
module.exports = {
content: [
- './index.html',
- './src/**/*.{vue,js,ts,jsx,tsx}',
+ "./index.html",
+ "./src/**/*.{vue,js,ts,jsx,tsx}",
// TODO: wenn man den server-pfad auch angibt wird Tailwind langsamer?! (Startzeit erhöht sich stark...)
// '../server/vbv_lernwelt/**/*.{html,js,py}',
],
theme: {
fontFamily: {
- sans: ['Buenos Aires', 'sans-serif'],
+ sans: ["Buenos Aires", "sans-serif"],
},
extend: {
spacing: {
- '128': '32rem',
+ 128: "32rem",
},
maxWidth: {
- '8xl': '88rem',
- '9xl': '96rem',
+ "8xl": "88rem",
+ "9xl": "96rem",
},
backgroundImage: {
- 'handlungsfelder-overview': "url('/static/icons/icon-handlungsfelder-overview.svg')",
- 'lernmedien-overview': "url('/static/icons/icon-lernmedien-overview.svg')",
- }
+ "handlungsfelder-overview":
+ "url('/static/icons/icon-handlungsfelder-overview.svg')",
+ "lernmedien-overview": "url('/static/icons/icon-lernmedien-overview.svg')",
+ },
},
colors: colors,
},
safelist: [
- { pattern: /bg-(blue|sky|green|red|orange|yellow|stone|gray|slate)-(200|300|400|500|600|700|800|900)/, },
- 'it-icon',
- 'bg-handlungsfelder-overview',
- 'bg-lernmedien-overview',
-
+ {
+ pattern:
+ /bg-(blue|sky|green|red|orange|yellow|stone|gray|slate)-(200|300|400|500|600|700|800|900)/,
+ },
+ "it-icon",
+ "bg-handlungsfelder-overview",
+ "bg-lernmedien-overview",
],
- plugins: [
- require('@tailwindcss/typography'),
- require('@tailwindcss/forms'),
- ],
-}
+ plugins: [require("@tailwindcss/typography"), require("@tailwindcss/forms")],
+};
diff --git a/client/tailwind.css b/client/tailwind.css
index b369767a..7ff15ab8 100644
--- a/client/tailwind.css
+++ b/client/tailwind.css
@@ -3,7 +3,7 @@
@tailwind utilities;
html {
- @apply text-black
+ @apply text-black;
}
body {
@@ -11,94 +11,93 @@ body {
}
svg {
- @apply fill-current
+ @apply fill-current;
}
@layer base {
.it-icon {
- @apply w-8 h-8 inline-block
+ @apply w-8 h-8 inline-block;
}
h1 {
- @apply text-4xl md:text-5xl xl:text-7xl font-bold
+ @apply text-4xl md:text-5xl xl:text-7xl font-bold;
}
.heading-1 {
- @apply text-4xl md:text-5xl xl:text-7xl font-bold
+ @apply text-4xl md:text-5xl xl:text-7xl font-bold;
}
h2 {
- @apply text-2xl md:text-3xl xl:text-4xl font-bold
+ @apply text-2xl md:text-3xl xl:text-4xl font-bold;
}
.heading-2 {
- @apply text-2xl md:text-3xl xl:text-4xl font-bold
+ @apply text-2xl md:text-3xl xl:text-4xl font-bold;
}
h3 {
- @apply text-xl xl:text-2xl font-bold
+ @apply text-xl xl:text-2xl font-bold;
}
.heading-3 {
- @apply text-xl xl:text-2xl font-bold
+ @apply text-xl xl:text-2xl font-bold;
}
.link {
- @apply underline underline-offset-2
+ @apply underline underline-offset-2;
}
.link-large {
- @apply text-lg underline xl:text-xl
+ @apply text-lg underline xl:text-xl;
}
.text-large {
- @apply text-lg xl:text-xl
+ @apply text-lg xl:text-xl;
}
.text-bold {
- @apply text-base font-bold
+ @apply text-base font-bold;
}
.container-medium {
- @apply mx-auto max-w-5xl px-4 lg:px-8 py-4
+ @apply mx-auto max-w-5xl px-4 lg:px-8 py-4;
}
.container-large {
- @apply mx-auto max-w-9xl px-4 lg:px-8 py-4
+ @apply mx-auto max-w-9xl px-4 lg:px-8 py-4;
}
-
}
@layer components {
.circle-title {
- @apply text-9xl font-bold
+ @apply text-9xl font-bold;
}
.btn-primary {
@apply font-semibold py-2 px-4 align-middle inline-block
bg-blue-900 text-white border-2 border-blue-900
hover:bg-blue-700 hover:border-blue-700
- disabled:opacity-50 disabled:cursor-not-allowed
+ disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-secondary {
@apply font-semibold py-2 px-4 align-middle inline-block
bg-white text-blue-900 border-2 border-blue-900
hover:bg-gray-200
- disabled:opacity-50 disabled:cursor-not-allowed
+ disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-blue {
@apply font-semibold py-2 px-4 align-middle inline-block
bg-sky-500 text-blue-900 border-2 border-sky-500
hover:bg-sky-400 hover:border-sky-400
- disabled:opacity-50 disabled:cursor-not-allowed
+ disabled:opacity-50 disabled:cursor-not-allowed;
}
.btn-text {
@apply font-semibold py-2 px-4 align-middle inline-block
hover:text-gray-700
- disabled:opacity-50 disabled:cursor-not-allowed
+ disabled:opacity-50 disabled:cursor-not-allowed;
}
}
@@ -108,4 +107,3 @@ svg {
overflow: hidden;
}
}
-
diff --git a/client/tsconfig.vitest.json b/client/tsconfig.vitest.json
index ee2193e4..67705fd2 100644
--- a/client/tsconfig.vitest.json
+++ b/client/tsconfig.vitest.json
@@ -4,10 +4,6 @@
"compilerOptions": {
"composite": true,
"lib": [],
- "types": [
- "node",
- "jsdom",
- "vitest/globals"
- ]
+ "types": ["node", "jsdom", "vitest/globals"]
}
}
diff --git a/client/versionize.js b/client/versionize.js
index ac4a8633..454ff70d 100644
--- a/client/versionize.js
+++ b/client/versionize.js
@@ -1,12 +1,13 @@
const replace = require("replace-in-file");
-const gitHash = require('child_process')
+const gitHash = require("child_process")
.execSync("git rev-parse --short HEAD")
- .toString().trim()
+ .toString()
+ .trim();
const options = {
files: "dist/static/vue/*.js",
from: /VBV_VERSION_BUILD_NUMBER_VBV/g,
- to: new Date().toISOString().replace("T", " ").substring(0, 19) + ' ' + gitHash,
+ to: new Date().toISOString().replace("T", " ").substring(0, 19) + " " + gitHash,
};
replace(options, (error, results) => {
diff --git a/client/vite.config.ts b/client/vite.config.ts
index 5edc7109..5c8a83a0 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -1,20 +1,20 @@
import { fileURLToPath, URL } from "url";
-import { defineConfig, loadEnv } from "vite";
import vue from "@vitejs/plugin-vue";
+import { defineConfig, loadEnv } from "vite";
// import vueI18n from '@intlify/vite-plugin-vue-i18n'
import alias from "@rollup/plugin-alias";
// https://vitejs.dev/config/
export default defineConfig(({ mode }) => {
- process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }
+ process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
return {
plugins: [
vue({
template: {
compilerOptions: {
// treat all tags which start with '
tag.startsWith('it-'),
+ isCustomElement: (tag) => tag.startsWith("it-"),
},
},
}),
@@ -35,19 +35,19 @@ export default defineConfig(({ mode }) => {
],
server: {
port: 5173,
- hmr: { port: 5173 }
+ hmr: { port: 5173 },
},
resolve: {
alias: {
- '@': fileURLToPath(new URL('./src', import.meta.url)),
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
build: {
- assetsDir: 'static/vue',
+ assetsDir: "static/vue",
},
test: {
globals: true,
- environment: 'jsdom',
+ environment: "jsdom",
},
- }
-})
+ };
+});
diff --git a/format_code.sh b/format_code.sh
new file mode 100755
index 00000000..0c0e7ddd
--- /dev/null
+++ b/format_code.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# script should fail when any process returns non zero code
+set -e
+
+echo 'format client code'
+npm run prettier
+
+echo 'format python code'
+ufmt format server
diff --git a/git-pre-commit.sh b/git-pre-commit.sh
new file mode 100755
index 00000000..c81f387c
--- /dev/null
+++ b/git-pre-commit.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# script should fail when any process returns non zero code
+set -e
+
+echo 'prettier:check'
+(cd client && npm run prettier:check)
+
+echo 'lint'
+(cd client && npm run lint)
+
+echo 'python ufmt check'
+ufmt check server
diff --git a/git-pre-push.sh b/git-pre-push.sh
new file mode 100755
index 00000000..0cb1fccd
--- /dev/null
+++ b/git-pre-push.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# script should fail when any process returns non zero code
+set -e
+
+echo 'check git-crypt files diff'
+git-crypt status -e | sort > git-crypt-encrypted-files-check.txt && diff git-crypt-encrypted-files.txt git-crypt-encrypted-files-check.txt
+
+echo 'check for secrets with truffleHog'
+trufflehog --exclude_paths trufflehog-exclude-patterns.txt --allow trufflehog-allow.json --max_depth=3 .
diff --git a/package.json b/package.json
index d8e6babe..ed702d91 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,8 @@
"build": "npm install --prefix client && npm run build --prefix client && npm run build:tailwind --prefix client",
"test": "echo \"Error: no test specified\" && exit 1",
"cypress:open": "cypress open",
- "cypress:ci": "cypress run"
+ "cypress:ci": "cypress run",
+ "prettier": "npm run prettier --prefix client"
},
"devDependencies": {
"cypress": "^10.6.0"
diff --git a/server/config/settings/base.py b/server/config/settings/base.py
index aa407615..cfb29b4a 100644
--- a/server/config/settings/base.py
+++ b/server/config/settings/base.py
@@ -78,26 +78,24 @@ THIRD_PARTY_APPS = [
"rest_framework.authtoken",
"corsheaders",
"drf_spectacular",
-
- 'wagtail.contrib.forms',
- 'wagtail.contrib.redirects',
- 'wagtail.contrib.styleguide',
- 'wagtail.embeds',
- 'wagtail.sites',
- 'wagtail.users',
- 'wagtail.snippets',
- 'wagtail.documents',
- 'wagtail.images',
- 'wagtail.search',
- 'wagtail.admin',
- 'wagtail',
+ "wagtail.contrib.forms",
+ "wagtail.contrib.redirects",
+ "wagtail.contrib.styleguide",
+ "wagtail.embeds",
+ "wagtail.sites",
+ "wagtail.users",
+ "wagtail.snippets",
+ "wagtail.documents",
+ "wagtail.images",
+ "wagtail.search",
+ "wagtail.admin",
+ "wagtail",
# 'wagtail.locales',
"wagtail_localize",
"wagtail_localize.locales",
- 'wagtail.api.v2',
-
- 'modelcluster',
- 'taggit',
+ "wagtail.api.v2",
+ "modelcluster",
+ "taggit",
]
LOCAL_APPS = [
@@ -199,32 +197,32 @@ MEDIA_ROOT = str(APPS_DIR / "media")
MEDIA_URL = "/media/"
IT_SERVE_VUE = env.bool("IT_SERVE_VUE", DEBUG)
-IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", 'http://localhost:5173')
+IT_SERVE_VUE_URL = env("IT_SERVE_VUE_URL", "http://localhost:5173")
# WAGTAIL
# ------------------------------------------------------------------------------
-WAGTAIL_SITE_NAME = 'VBV Lernwelt'
+WAGTAIL_SITE_NAME = "VBV Lernwelt"
WAGTAIL_I18N_ENABLED = True
LANGUAGES = [
- ('en-US', "English (American)"),
- ('fr-CH', "Swiss French"),
- ('de-CH', "Swiss German"),
- ('it-CH', "Swiss Italian")
+ ("en-US", "English (American)"),
+ ("fr-CH", "Swiss French"),
+ ("de-CH", "Swiss German"),
+ ("it-CH", "Swiss Italian"),
]
-WAGTAILDOCS_DOCUMENT_MODEL = 'media_library.LibraryDocument'
+WAGTAILDOCS_DOCUMENT_MODEL = "media_library.LibraryDocument"
WAGTAIL_CONTENT_LANGUAGES = [
- ('fr-CH', "Swiss French"),
- ('de-CH', "Swiss German"),
- ('it-CH', "Swiss Italian")
+ ("fr-CH", "Swiss French"),
+ ("de-CH", "Swiss German"),
+ ("it-CH", "Swiss Italian"),
]
WAGTAILSEARCH_BACKENDS = {
- 'default': {
- 'BACKEND': 'wagtail.search.backends.database',
+ "default": {
+ "BACKEND": "wagtail.search.backends.database",
}
}
@@ -456,13 +454,13 @@ CORS_URLS_REGEX = r"^/api/.*$"
CSP_DEFAULT_SRC = [
"'self'",
"'unsafe-inline'",
- 'ws://localhost:5173',
- 'ws://127.0.0.1:5173',
- 'localhost:8000',
- 'localhost:8001',
- 'blob:',
- 'data:',
- 'http://*'
+ "ws://localhost:5173",
+ "ws://127.0.0.1:5173",
+ "localhost:8000",
+ "localhost:8001",
+ "blob:",
+ "data:",
+ "http://*",
]
CSP_FRAME_ANCESTORS = ("'self'",)
@@ -498,7 +496,10 @@ ALLOWED_HOSTS = env.list(
# CACHES
CACHES = {
"default": {
- "BACKEND": env("IT_DJANGO_CACHE_BACKEND", default="django.core.cache.backends.db.DatabaseCache"),
+ "BACKEND": env(
+ "IT_DJANGO_CACHE_BACKEND",
+ default="django.core.cache.backends.db.DatabaseCache",
+ ),
"LOCATION": env("IT_DJANGO_CACHE_LOCATION", default="django_cache_table"),
},
}
@@ -524,9 +525,7 @@ CACHES["api_page_cache"] = {
IT_OAUTH_TENANT_ID = env.str("IT_OAUTH_TENANT_ID", default=None)
if IT_OAUTH_TENANT_ID:
- IT_OAUTH_AUTHORIZE_PARAMS = {
- 'tenant_id': IT_OAUTH_TENANT_ID
- }
+ IT_OAUTH_AUTHORIZE_PARAMS = {"tenant_id": IT_OAUTH_TENANT_ID}
else:
IT_OAUTH_AUTHORIZE_PARAMS = {}
@@ -538,14 +537,22 @@ OAUTH = {
# "authorize_url": env("IT_OAUTH_AUTHORIZE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/auth"),
"authorize_params": IT_OAUTH_AUTHORIZE_PARAMS,
"access_token_params": IT_OAUTH_AUTHORIZE_PARAMS,
- "api_base_url": env("IT_OAUTH_API_BASE_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/"),
- "local_redirect_uri": env("IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/sso/callback/"),
- "server_metadata_url": env("IT_OAUTH_SERVER_METADATA_URL", default="https://sso.test.b.lernetz.host/auth/realms/vbv/.well-known/openid-configuration"),
+ "api_base_url": env(
+ "IT_OAUTH_API_BASE_URL",
+ default="https://sso.test.b.lernetz.host/auth/realms/vbv/protocol/openid-connect/",
+ ),
+ "local_redirect_uri": env(
+ "IT_OAUTH_LOCAL_DIRECT_URI", default="http://localhost:8000/sso/callback/"
+ ),
+ "server_metadata_url": env(
+ "IT_OAUTH_SERVER_METADATA_URL",
+ default="https://sso.test.b.lernetz.host/auth/realms/vbv/.well-known/openid-configuration",
+ ),
"client_kwargs": {
- 'scope': env("IT_OAUTH_SCOPE", default=''),
- 'token_endpoint_auth_method': 'client_secret_post',
- 'token_placement': 'body',
- }
+ "scope": env("IT_OAUTH_SCOPE", default=""),
+ "token_endpoint_auth_method": "client_secret_post",
+ "token_placement": "body",
+ },
}
if APP_ENVIRONMENT == "development":
@@ -555,7 +562,7 @@ if APP_ENVIRONMENT == "development":
# django-debug-toolbar
# ------------------------------------------------------------------------------
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#prerequisites
- #INSTALLED_APPS += ["debug_toolbar"] # noqa F405
+ # INSTALLED_APPS += ["debug_toolbar"] # noqa F405
# https://django-debug-toolbar.readthedocs.io/en/latest/installation.html#middleware
# MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware"] # noqa F405
# https://django-debug-toolbar.readthedocs.io/en/latest/configuration.html#debug-toolbar-config
@@ -582,7 +589,9 @@ if APP_ENVIRONMENT == "development":
# https://django-extensions.readthedocs.io/en/latest/installation_instructions.html#configuration
INSTALLED_APPS += ["django_extensions"] # noqa F405
-if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith("caprover"):
+if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith(
+ "caprover"
+):
# SECURITY
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#secure-proxy-ssl-header
@@ -632,7 +641,7 @@ if APP_ENVIRONMENT in ["production", "caprover"] or APP_ENVIRONMENT.startswith("
# ADMIN
# ------------------------------------------------------------------------------
# Django Admin URL regex.
- ADMIN_URL = env("IT_DJANGO_ADMIN_URL", 'admin/')
+ ADMIN_URL = env("IT_DJANGO_ADMIN_URL", "admin/")
# Anymail
# ------------------------------------------------------------------------------
diff --git a/server/config/settings/test.py b/server/config/settings/test.py
index b1d2b21a..5651c045 100644
--- a/server/config/settings/test.py
+++ b/server/config/settings/test.py
@@ -1,7 +1,7 @@
# pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position
import os
-os.environ['IT_APP_ENVIRONMENT'] = 'development'
+os.environ["IT_APP_ENVIRONMENT"] = "development"
from .base import * # noqa
diff --git a/server/config/settings/test_cypress.py b/server/config/settings/test_cypress.py
index 339a15d8..b974bad4 100644
--- a/server/config/settings/test_cypress.py
+++ b/server/config/settings/test_cypress.py
@@ -1,14 +1,14 @@
# pylint: disable=unused-wildcard-import,wildcard-import,wrong-import-position
import os
-os.environ['IT_APP_ENVIRONMENT'] = 'development'
+os.environ["IT_APP_ENVIRONMENT"] = "development"
from .base import * # noqa
# GENERAL
# ------------------------------------------------------------------------------
# https://docs.djangoproject.com/en/dev/ref/settings/#secret-key
-DATABASES['default']['NAME'] = 'vbv_lernwelt_cypress'
+DATABASES["default"]["NAME"] = "vbv_lernwelt_cypress"
# EMAIL
# ------------------------------------------------------------------------------
diff --git a/server/config/urls.py b/server/config/urls.py
index e978128c..7cc07c28 100644
--- a/server/config/urls.py
+++ b/server/config/urls.py
@@ -6,16 +6,27 @@ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path, re_path
from django.views import defaults as default_views
from ratelimit.exceptions import Ratelimited
-from wagtail import urls as wagtail_urls
-from wagtail.admin import urls as wagtailadmin_urls
-from wagtail.documents import urls as wagtaildocs_urls
from vbv_lernwelt.core.middleware.auth import django_view_authentication_exempt
from vbv_lernwelt.core.views import (
- rate_limit_exceeded_view,
+ check_rate_limit,
+ cypress_reset_view,
+ generate_web_component_icons,
+ me_user_view,
permission_denied_view,
- check_rate_limit, cypress_reset_view, vue_home, vue_login, me_user_view, vue_logout, generate_web_component_icons, )
-from vbv_lernwelt.course.views import page_api_view, request_course_completion, mark_course_completion
+ rate_limit_exceeded_view,
+ vue_home,
+ vue_login,
+ vue_logout,
+)
+from vbv_lernwelt.course.views import (
+ mark_course_completion,
+ page_api_view,
+ request_course_completion,
+)
+from wagtail import urls as wagtail_urls
+from wagtail.admin import urls as wagtailadmin_urls
+from wagtail.documents import urls as wagtaildocs_urls
def raise_example_error(request):
@@ -106,4 +117,4 @@ if settings.DEBUG:
# serve everything else via the vue app
-urlpatterns += [re_path(r'^(?!.*(server/|api/|sso/)).*$', vue_home, name='home')]
+urlpatterns += [re_path(r"^(?!.*(server/|api/|sso/)).*$", vue_home, name="home")]
diff --git a/server/integration_tests/ratelimit/test_ratelimit.py b/server/integration_tests/ratelimit/test_ratelimit.py
index 66cffadc..a3723a31 100644
--- a/server/integration_tests/ratelimit/test_ratelimit.py
+++ b/server/integration_tests/ratelimit/test_ratelimit.py
@@ -1,4 +1,4 @@
-from django.test import TestCase, override_settings
+from django.test import override_settings, TestCase
class RateLimitTest(TestCase):
diff --git a/server/requirements/requirements-dev.in b/server/requirements/requirements-dev.in
index ee33d498..9cdfe6b5 100644
--- a/server/requirements/requirements-dev.in
+++ b/server/requirements/requirements-dev.in
@@ -19,9 +19,10 @@ djangorestframework-stubs # https://github.com/typeddjango/djangorestframework-
flake8 # https://github.com/PyCQA/flake8
flake8-isort # https://github.com/gforcada/flake8-isort
coverage # https://github.com/nedbat/coveragepy
-black # https://github.com/psf/black
+black>=22.8.0 # https://github.com/psf/black
pylint-django # https://github.com/PyCQA/pylint-django
pre-commit # https://github.com/pre-commit/pre-commit
+ufmt
# Django
# ------------------------------------------------------------------------------
diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt
index d8dcb9d5..4784184c 100644
--- a/server/requirements/requirements-dev.txt
+++ b/server/requirements/requirements-dev.txt
@@ -28,14 +28,17 @@ attrs==21.4.0
# via
# jsonschema
# pytest
+ # usort
authlib==1.0.0
# via -r requirements.in
backcall==0.2.0
# via ipython
beautifulsoup4==4.9.3
# via wagtail
-black==22.3.0
- # via -r requirements-dev.in
+black==22.8.0
+ # via
+ # -r requirements-dev.in
+ # ufmt
certifi==2021.10.8
# via
# requests
@@ -52,7 +55,10 @@ click==8.1.1
# via
# black
# django-click
+ # moreorless
# pip-tools
+ # ufmt
+ # usort
# uvicorn
concurrent-log-handler==0.9.20
# via -r requirements.in
@@ -86,7 +92,6 @@ django==3.2.13
# django-debug-toolbar
# django-extensions
# django-filter
- # django-htmx
# django-model-utils
# django-modelcluster
# django-permissionedforms
@@ -212,8 +217,14 @@ l18n==2021.3
# via wagtail
lazy-object-proxy==1.7.1
# via astroid
+libcst==0.4.7
+ # via
+ # ufmt
+ # usort
markupsafe==2.1.1
- # via jinja2
+ # via
+ # jinja2
+ # werkzeug
marshmallow==3.15.0
# via environs
matplotlib-inline==0.1.3
@@ -222,6 +233,10 @@ mccabe==0.6.1
# via
# flake8
# pylint
+moreorless==0.4.0
+ # via
+ # ufmt
+ # usort
mypy==0.942
# via
# -r requirements-dev.in
@@ -231,6 +246,7 @@ mypy-extensions==0.4.3
# via
# black
# mypy
+ # typing-inspect
nodeenv==1.6.0
# via pre-commit
openpyxl==3.0.9
@@ -244,7 +260,9 @@ packaging==21.3
parso==0.8.3
# via jedi
pathspec==0.9.0
- # via black
+ # via
+ # black
+ # trailrunner
pep517==0.12.0
# via pip-tools
pexpect==4.8.0
@@ -329,6 +347,7 @@ pytz==2022.1
pyyaml==6.0
# via
# drf-spectacular
+ # libcst
# pre-commit
# uvicorn
redis==4.2.1
@@ -362,6 +381,8 @@ sqlparse==0.4.2
# django-debug-toolbar
stack-data==0.2.0
# via ipython
+stdlibs==2022.6.8
+ # via usort
structlog==21.5.0
# via -r requirements.in
tablib[xls,xlsx]==3.2.1
@@ -378,6 +399,7 @@ toml==0.10.2
# via
# ipdb
# pre-commit
+ # usort
tomli==2.0.1
# via
# black
@@ -386,6 +408,12 @@ tomli==2.0.1
# pep517
# pylint
# pytest
+tomlkit==0.11.5
+ # via ufmt
+trailrunner==1.2.1
+ # via
+ # ufmt
+ # usort
traitlets==5.1.1
# via
# ipython
@@ -403,8 +431,15 @@ typing-extensions==4.1.1
# django-stubs
# django-stubs-ext
# djangorestframework-stubs
+ # libcst
# mypy
+ # typing-inspect
+ # ufmt
# wagtail-localize
+typing-inspect==0.8.0
+ # via libcst
+ufmt==2.0.1
+ # via -r requirements-dev.in
uritemplate==4.1.1
# via
# coreapi
@@ -413,6 +448,8 @@ urllib3==1.26.9
# via
# requests
# sentry-sdk
+usort==1.0.5
+ # via ufmt
uvicorn[standard]==0.17.6
# via -r requirements.in
uvloop==0.16.0
diff --git a/server/vbv_lernwelt/competence/apps.py b/server/vbv_lernwelt/competence/apps.py
index a8310128..c1eb5350 100644
--- a/server/vbv_lernwelt/competence/apps.py
+++ b/server/vbv_lernwelt/competence/apps.py
@@ -2,5 +2,5 @@ from django.apps import AppConfig
class MediaLibraryConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'vbv_lernwelt.competence'
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "vbv_lernwelt.competence"
diff --git a/server/vbv_lernwelt/competence/create_default_competence_profile.py b/server/vbv_lernwelt/competence/create_default_competence_profile.py
index fafcbc6c..feb410dc 100644
--- a/server/vbv_lernwelt/competence/create_default_competence_profile.py
+++ b/server/vbv_lernwelt/competence/create_default_competence_profile.py
@@ -1,8 +1,11 @@
-from vbv_lernwelt.competence.factories import CompetenceProfilePageFactory, PerformanceCriteriaFactory, \
- CompetencePageFactory
+from vbv_lernwelt.competence.factories import (
+ CompetencePageFactory,
+ CompetenceProfilePageFactory,
+ PerformanceCriteriaFactory,
+)
from vbv_lernwelt.competence.models import CompetencePage
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
-from vbv_lernwelt.course.models import CoursePage, Course
+from vbv_lernwelt.course.models import Course, CoursePage
from vbv_lernwelt.learnpath.models import LearningUnit
@@ -11,135 +14,143 @@ def create_default_competence_profile():
course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
competence_profile_page = CompetenceProfilePageFactory(
- title='Kompetenzprofil',
+ title="Kompetenzprofil",
parent=course_page,
)
- competences = [{
- 'competence_id': 'A1',
- 'title': 'Weiterempfehlung für Neukunden generieren',
- 'items': [
- 'Verhandlungsgeschick',
- 'Überzeugtes Auftreten',
- ],
- }, {
- 'competence_id': 'A2',
- 'title': 'Kundengespräche vereinbaren',
- 'items': [
- 'Gesprächsführung / Fragetechniken',
- 'Selbstorganisation',
- 'Arbeitstechniken',
- 'Psychologische Kenntnisse / Kommunikations-psychologie',
- ],
- }, {
- 'competence_id': 'A3',
- 'title': 'Auftritt in den sozialen Medien zeitgemäss halten',
- 'items': [
- 'Gesetzliche und Compliance-Anforderungen der Versicherer',
- 'Datenschutzgesetz',
- 'Kommunikation in den sozialen Medien',
- ]
- }, {
- 'competence_id': 'A4',
- 'title': 'Kundendaten erfassen',
- 'items': []
- }, {
- 'competence_id': 'B1',
- 'title': 'Wünsche, Ziele und Bedürfnisse der Kunden im Gespräch ermitteln',
- 'items': [
- 'Gesprächsführung',
- 'Fragetechniken',
- 'Kundenpsychologie',
- ]
- }, {
- 'competence_id': 'B2',
- 'title': 'Analyse des Kundenbedarfs und des Kundenbedürfnisses durchführen',
- 'items': [
- 'Fragetechniken',
- 'Visuelle Hilfsmittel / Visualisierungstechniken',
- ]
- }, {
- 'competence_id': 'B3',
- 'title': 'Individuelle Lösungsvorschläge erarbeiten',
- 'items': [
- 'Fundierte Produktekenntnisse',
- 'Regulatorische Vorschriften',
- ]
- }, {
- 'competence_id': 'B4',
- 'title': 'Lösungsvorschläge präsentieren und umsetzen',
- 'items': [
- 'Verhandlungsstrategien',
- 'Fundierte Produktkenntnisse',
- 'Visuelle Hilfsmittel / Visualisierungstechniken',
- ]
- }, {
- 'competence_id': 'C1',
- 'title': 'Cross- und Upselling; bestehende fremdverwaltete Versicherungspolicen prüfen und in das Portfolio aufnehmen',
- 'items': [
- 'Produktkenntnisse',
- 'Gesprächsführung',
- 'Kommunikation',
- 'Fragetechnik',
- 'Verhandlungsgeschick',
- 'Vertragsrecht',
- 'Regulatorische Vorgaben',
- 'UVG, BVG, KVG, VVG',
- ]
- }, {
- 'competence_id': 'C2',
- 'title': 'Änderungswünsche entgegennehmen und bestehende Verträge anpassen',
- 'items': [
- 'Produktkenntnisse',
- 'Gesprächsführung',
- 'Kommunikation',
- 'Fragetechnik',
- 'Verhandlungsgeschick',
- 'Vertragsrecht',
- 'Regulatorische Vorgaben',
- 'UVG, BVG, KVG, VVG',
- ]
- }, {
- 'competence_id': 'C3',
- 'title': 'Kunden im Schadenfall unterstützen',
- 'items': []
- }, {
- 'competence_id': 'C4',
- 'title': 'Bestehende Kunden pflegen',
- 'items': []
- }, {
- 'competence_id': 'C5',
- 'title': 'Versicherungsanträge nachbearbeiten',
- 'items': []
- }]
+ competences = [
+ {
+ "competence_id": "A1",
+ "title": "Weiterempfehlung für Neukunden generieren",
+ "items": [
+ "Verhandlungsgeschick",
+ "Überzeugtes Auftreten",
+ ],
+ },
+ {
+ "competence_id": "A2",
+ "title": "Kundengespräche vereinbaren",
+ "items": [
+ "Gesprächsführung / Fragetechniken",
+ "Selbstorganisation",
+ "Arbeitstechniken",
+ "Psychologische Kenntnisse / Kommunikations-psychologie",
+ ],
+ },
+ {
+ "competence_id": "A3",
+ "title": "Auftritt in den sozialen Medien zeitgemäss halten",
+ "items": [
+ "Gesetzliche und Compliance-Anforderungen der Versicherer",
+ "Datenschutzgesetz",
+ "Kommunikation in den sozialen Medien",
+ ],
+ },
+ {"competence_id": "A4", "title": "Kundendaten erfassen", "items": []},
+ {
+ "competence_id": "B1",
+ "title": "Wünsche, Ziele und Bedürfnisse der Kunden im Gespräch ermitteln",
+ "items": [
+ "Gesprächsführung",
+ "Fragetechniken",
+ "Kundenpsychologie",
+ ],
+ },
+ {
+ "competence_id": "B2",
+ "title": "Analyse des Kundenbedarfs und des Kundenbedürfnisses durchführen",
+ "items": [
+ "Fragetechniken",
+ "Visuelle Hilfsmittel / Visualisierungstechniken",
+ ],
+ },
+ {
+ "competence_id": "B3",
+ "title": "Individuelle Lösungsvorschläge erarbeiten",
+ "items": [
+ "Fundierte Produktekenntnisse",
+ "Regulatorische Vorschriften",
+ ],
+ },
+ {
+ "competence_id": "B4",
+ "title": "Lösungsvorschläge präsentieren und umsetzen",
+ "items": [
+ "Verhandlungsstrategien",
+ "Fundierte Produktkenntnisse",
+ "Visuelle Hilfsmittel / Visualisierungstechniken",
+ ],
+ },
+ {
+ "competence_id": "C1",
+ "title": "Cross- und Upselling; bestehende fremdverwaltete Versicherungspolicen prüfen und in das Portfolio aufnehmen",
+ "items": [
+ "Produktkenntnisse",
+ "Gesprächsführung",
+ "Kommunikation",
+ "Fragetechnik",
+ "Verhandlungsgeschick",
+ "Vertragsrecht",
+ "Regulatorische Vorgaben",
+ "UVG, BVG, KVG, VVG",
+ ],
+ },
+ {
+ "competence_id": "C2",
+ "title": "Änderungswünsche entgegennehmen und bestehende Verträge anpassen",
+ "items": [
+ "Produktkenntnisse",
+ "Gesprächsführung",
+ "Kommunikation",
+ "Fragetechnik",
+ "Verhandlungsgeschick",
+ "Vertragsrecht",
+ "Regulatorische Vorgaben",
+ "UVG, BVG, KVG, VVG",
+ ],
+ },
+ {
+ "competence_id": "C3",
+ "title": "Kunden im Schadenfall unterstützen",
+ "items": [],
+ },
+ {"competence_id": "C4", "title": "Bestehende Kunden pflegen", "items": []},
+ {
+ "competence_id": "C5",
+ "title": "Versicherungsanträge nachbearbeiten",
+ "items": [],
+ },
+ ]
for c in competences:
CompetencePageFactory(
parent=competence_profile_page,
- competence_id=c['competence_id'],
- title=c['title'],
- items=[
- ('item', i) for i in c['items']
- ]
+ competence_id=c["competence_id"],
+ title=c["title"],
+ items=[("item", i) for i in c["items"]],
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='B1'),
- competence_id='B1.3',
- 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(slug='versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug'),
+ parent=CompetencePage.objects.get(competence_id="B1"),
+ competence_id="B1.3",
+ 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(
+ slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
+ ),
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='B2'),
- 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.',
- learning_unit=LearningUnit.objects.get(slug='versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug'),
+ parent=CompetencePage.objects.get(competence_id="B2"),
+ 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.",
+ learning_unit=LearningUnit.objects.get(
+ slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
+ ),
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='B2'),
- competence_id='B2.2',
- title='Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Risiken aufzuzeigen.',
- learning_unit=LearningUnit.objects.get(slug='versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug'),
+ parent=CompetencePage.objects.get(competence_id="B2"),
+ competence_id="B2.2",
+ title="Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Risiken aufzuzeigen.",
+ learning_unit=LearningUnit.objects.get(
+ slug="versicherungsvermittlerin-lp-circle-analyse-lu-fahrzeug"
+ ),
)
-
-
diff --git a/server/vbv_lernwelt/competence/factories.py b/server/vbv_lernwelt/competence/factories.py
index 1e0e21ed..d4ba2a17 100644
--- a/server/vbv_lernwelt/competence/factories.py
+++ b/server/vbv_lernwelt/competence/factories.py
@@ -1,26 +1,32 @@
import wagtail_factories
-from vbv_lernwelt.competence.models import CompetenceProfilePage, PerformanceCriteria, CompetencePage
+from vbv_lernwelt.competence.models import (
+ CompetencePage,
+ CompetenceProfilePage,
+ PerformanceCriteria,
+)
class CompetenceProfilePageFactory(wagtail_factories.PageFactory):
- title = 'Kompetenzprofil'
+ title = "Kompetenzprofil"
class Meta:
model = CompetenceProfilePage
class CompetencePageFactory(wagtail_factories.PageFactory):
- competence_id = 'A1'
- title = 'Weiterempfehlung für Neukunden generieren'
+ competence_id = "A1"
+ title = "Weiterempfehlung für Neukunden generieren"
class Meta:
model = CompetencePage
class PerformanceCriteriaFactory(wagtail_factories.PageFactory):
- competence_id = 'A1.1'
- title = 'Bestehende Kunden so zu beraten, dass sie von diesen weiterempfohlen werden'
+ competence_id = "A1.1"
+ title = (
+ "Bestehende Kunden so zu beraten, dass sie von diesen weiterempfohlen werden"
+ )
class Meta:
model = PerformanceCriteria
diff --git a/server/vbv_lernwelt/competence/migrations/0001_initial.py b/server/vbv_lernwelt/competence/migrations/0001_initial.py
index b2b8c3ff..60aae385 100644
--- a/server/vbv_lernwelt/competence/migrations/0001_initial.py
+++ b/server/vbv_lernwelt/competence/migrations/0001_initial.py
@@ -1,9 +1,9 @@
# Generated by Django 3.2.13 on 2022-09-28 12:51
-from django.db import migrations, models
import django.db.models.deletion
import wagtail.blocks
import wagtail.fields
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -11,41 +11,76 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('wagtailcore', '0069_log_entry_jsonfield'),
+ ("wagtailcore", "0069_log_entry_jsonfield"),
]
operations = [
migrations.CreateModel(
- name='CompetencePage',
+ name="CompetencePage",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('competence_id', models.TextField(default='A1')),
- ('items', wagtail.fields.StreamField([('item', wagtail.blocks.TextBlock())], use_json_field=True)),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("competence_id", models.TextField(default="A1")),
+ (
+ "items",
+ wagtail.fields.StreamField(
+ [("item", wagtail.blocks.TextBlock())], use_json_field=True
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='CompetenceProfilePage',
+ name="CompetenceProfilePage",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='PerformanceCriteria',
+ name="PerformanceCriteria",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('competence_id', models.TextField(default='A1.1')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("competence_id", models.TextField(default="A1.1")),
],
options={
- 'abstract': False,
+ "abstract": False,
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
]
diff --git a/server/vbv_lernwelt/competence/migrations/0002_performancecriteria_learning_unit.py b/server/vbv_lernwelt/competence/migrations/0002_performancecriteria_learning_unit.py
index bdaadc78..f48ab76d 100644
--- a/server/vbv_lernwelt/competence/migrations/0002_performancecriteria_learning_unit.py
+++ b/server/vbv_lernwelt/competence/migrations/0002_performancecriteria_learning_unit.py
@@ -1,7 +1,7 @@
# Generated by Django 3.2.13 on 2022-09-28 12:51
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -9,14 +9,19 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('learnpath', '0001_initial'),
- ('competence', '0001_initial'),
+ ("learnpath", "0001_initial"),
+ ("competence", "0001_initial"),
]
operations = [
migrations.AddField(
- model_name='performancecriteria',
- name='learning_unit',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='learnpath.learningunit'),
+ model_name="performancecriteria",
+ name="learning_unit",
+ field=models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="learnpath.learningunit",
+ ),
),
]
diff --git a/server/vbv_lernwelt/competence/models.py b/server/vbv_lernwelt/competence/models.py
index 4174c675..5286613e 100644
--- a/server/vbv_lernwelt/competence/models.py
+++ b/server/vbv_lernwelt/competence/models.py
@@ -10,86 +10,117 @@ from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
class CompetenceProfilePage(Page):
- parent_page_types = ['course.CoursePage']
- subpage_types = ['competence.CompetencePage']
+ parent_page_types = ["course.CoursePage"]
+ subpage_types = ["competence.CompetencePage"]
content_panels = [
- FieldPanel('title', classname="full title"),
+ FieldPanel("title", classname="full title"),
]
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(slugify(f"{self.get_parent().slug}-competence", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(f"{self.get_parent().slug}-competence", allow_unicode=True)
+ )
super(CompetenceProfilePage, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'course',
- 'children',
- ]
+ cls,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "course",
+ "children",
+ ],
)
class CompetencePage(Page):
- parent_page_types = ['competence.CompetenceProfilePage']
- subpage_types = ['competence.PerformanceCriteria']
- competence_id = models.TextField(default='A1')
- items = StreamField([
- ('item', blocks.TextBlock()),
- ], use_json_field=True)
+ parent_page_types = ["competence.CompetenceProfilePage"]
+ subpage_types = ["competence.PerformanceCriteria"]
+ competence_id = models.TextField(default="A1")
+ items = StreamField(
+ [
+ ("item", blocks.TextBlock()),
+ ],
+ use_json_field=True,
+ )
content_panels = [
- FieldPanel('title'),
- FieldPanel('competence_id'),
+ FieldPanel("title"),
+ FieldPanel("competence_id"),
]
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(slugify(f"{self.get_parent().slug}-competence-{self.competence_id}", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(
+ f"{self.get_parent().slug}-competence-{self.competence_id}",
+ allow_unicode=True,
+ )
+ )
super(CompetencePage, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'children',
- ]
+ cls,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "children",
+ ],
)
class PerformanceCriteria(Page):
- parent_page_types = ['competence.CompetenceProfilePage']
- competence_id = models.TextField(default='A1.1')
+ parent_page_types = ["competence.CompetenceProfilePage"]
+ competence_id = models.TextField(default="A1.1")
learning_unit = models.ForeignKey(
- 'learnpath.LearningUnit',
+ "learnpath.LearningUnit",
null=True,
blank=True,
on_delete=models.SET_NULL,
)
content_panels = [
- FieldPanel('title'),
- FieldPanel('competence_id'),
- FieldPanel('learning_unit'),
+ FieldPanel("title"),
+ FieldPanel("competence_id"),
+ FieldPanel("learning_unit"),
]
def full_clean(self, *args, **kwargs):
profile_parent = self.get_ancestors().exact_type(CompetenceProfilePage).last()
if self.learning_unit and self.learning_unit.course_category:
- self.slug = find_available_slug(slugify(f"{profile_parent.slug}-crit-{self.competence_id}-{self.learning_unit.course_category.title}", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(
+ f"{profile_parent.slug}-crit-{self.competence_id}-{self.learning_unit.course_category.title}",
+ allow_unicode=True,
+ )
+ )
else:
- self.slug = find_available_slug(slugify(f"{profile_parent.slug}-crit-{self.competence_id}", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(
+ f"{profile_parent.slug}-crit-{self.competence_id}",
+ allow_unicode=True,
+ )
+ )
super(PerformanceCriteria, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
from vbv_lernwelt.competence.serializers import PerformanceCriteriaSerializer
+
return PerformanceCriteriaSerializer
def get_admin_display_title(self):
if self.learning_unit and self.learning_unit.course_category:
- return f'{self.competence_id} ({self.learning_unit.course_category.title}) {self.draft_title[:30]}'
+ return f"{self.competence_id} ({self.learning_unit.course_category.title}) {self.draft_title[:30]}"
else:
- return f'{self.competence_id} {self.draft_title[:30]}'
+ return f"{self.competence_id} {self.draft_title[:30]}"
diff --git a/server/vbv_lernwelt/competence/serializers.py b/server/vbv_lernwelt/competence/serializers.py
index 33c294ab..5b42311d 100644
--- a/server/vbv_lernwelt/competence/serializers.py
+++ b/server/vbv_lernwelt/competence/serializers.py
@@ -6,18 +6,37 @@ from vbv_lernwelt.learnpath.models import LearningUnit
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
-class PerformanceCriteriaSerializer(get_it_serializer_class(PerformanceCriteria, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'competence_id', 'learning_unit', 'circle', 'course_category',
-])):
+class PerformanceCriteriaSerializer(
+ get_it_serializer_class(
+ PerformanceCriteria,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "competence_id",
+ "learning_unit",
+ "circle",
+ "course_category",
+ ],
+ )
+):
learning_unit = serializers.SerializerMethodField()
circle = serializers.SerializerMethodField()
course_category = serializers.SerializerMethodField()
def get_learning_unit(self, obj):
- learning_unit_serializer = get_it_serializer_class(LearningUnit, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- ])
+ learning_unit_serializer = get_it_serializer_class(
+ LearningUnit,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ ],
+ )
return learning_unit_serializer(obj.learning_unit).data
def get_circle(self, obj):
@@ -29,7 +48,17 @@ class PerformanceCriteriaSerializer(get_it_serializer_class(PerformanceCriteria,
return None
-class PerformanceCriteriaLearningPathSerializer(get_it_serializer_class(PerformanceCriteria, [
- 'id', 'title', 'slug', 'type', 'translation_key', 'competence_id',
-])):
+class PerformanceCriteriaLearningPathSerializer(
+ get_it_serializer_class(
+ PerformanceCriteria,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "competence_id",
+ ],
+ )
+):
pass
diff --git a/server/vbv_lernwelt/competence/tests/test_api.py b/server/vbv_lernwelt/competence/tests/test_api.py
index 10d68783..c8ab53f9 100644
--- a/server/vbv_lernwelt/competence/tests/test_api.py
+++ b/server/vbv_lernwelt/competence/tests/test_api.py
@@ -10,16 +10,19 @@ class CompetenceAPITestCase(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course()
- self.user = User.objects.get(username='student')
- self.client.login(username='student', password='test')
+ self.user = User.objects.get(username="student")
+ self.client.login(username="student", password="test")
def test_get_learnpathPage(self):
- slug = 'test-lehrgang-competence'
+ slug = "test-lehrgang-competence"
competence_profile = CompetenceProfilePage.objects.get(slug=slug)
- response = self.client.get(f'/api/course/page/{slug}/')
+ response = self.client.get(f"/api/course/page/{slug}/")
self.assertEqual(response.status_code, 200)
data = response.json()
- self.assertEqual(competence_profile.title, data['title'])
- self.assertEqual('Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).', data['children'][1]['children'][0]['title'])
+ self.assertEqual(competence_profile.title, data["title"])
+ self.assertEqual(
+ "Innerhalb des Handlungsfelds «Fahrzeug» bin ich fähig, die Ziele und Pläne des Kunden zu ergründen (SOLL).",
+ data["children"][1]["children"][0]["title"],
+ )
diff --git a/server/vbv_lernwelt/core/admin.py b/server/vbv_lernwelt/core/admin.py
index fef21a6f..9e0be577 100644
--- a/server/vbv_lernwelt/core/admin.py
+++ b/server/vbv_lernwelt/core/admin.py
@@ -1,6 +1,5 @@
from django.contrib import admin
-from django.contrib.auth import admin as auth_admin
-from django.contrib.auth import get_user_model
+from django.contrib.auth import admin as auth_admin, get_user_model
from django.utils.translation import gettext_lazy as _
User = get_user_model()
diff --git a/server/vbv_lernwelt/core/create_default_users.py b/server/vbv_lernwelt/core/create_default_users.py
index 99a93c28..7e969e22 100644
--- a/server/vbv_lernwelt/core/create_default_users.py
+++ b/server/vbv_lernwelt/core/create_default_users.py
@@ -6,15 +6,21 @@ from vbv_lernwelt.core.models import User
def create_default_users(user_model=User, group_model=Group, default_password=None):
if default_password is None:
- default_password = 'test'
+ default_password = "test"
- admin_group, created = group_model.objects.get_or_create(name='admin_group')
- _content_creator_grop, _created = group_model.objects.get_or_create(name='content_creator_grop')
- student_group, created = group_model.objects.get_or_create(name='student_group')
+ admin_group, created = group_model.objects.get_or_create(name="admin_group")
+ _content_creator_grop, _created = group_model.objects.get_or_create(
+ name="content_creator_grop"
+ )
+ student_group, created = group_model.objects.get_or_create(name="student_group")
- def _create_student_user(email, first_name, last_name, avatar_url='', password=default_password):
+ def _create_student_user(
+ email, first_name, last_name, avatar_url="", password=default_password
+ ):
student_user, created = _get_or_create_user(
- user_model=user_model, username=email, password=password,
+ user_model=user_model,
+ username=email,
+ password=password,
)
student_user.first_name = first_name
student_user.last_name = last_name
@@ -22,7 +28,7 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
student_user.groups.add(student_group)
student_user.save()
- def _create_admin_user(email, first_name, last_name, avatar_url=''):
+ def _create_admin_user(email, first_name, last_name, avatar_url=""):
admin_user, created = _get_or_create_user(
user_model=user_model, username=email, password=default_password
)
@@ -35,68 +41,68 @@ def create_default_users(user_model=User, group_model=Group, default_password=No
admin_user.save()
_create_admin_user(
- email='info@iterativ.ch',
- first_name='Info',
- last_name='Iterativ',
- avatar_url='/static/avatars/avatar_iterativ.png'
+ email="info@iterativ.ch",
+ first_name="Info",
+ last_name="Iterativ",
+ avatar_url="/static/avatars/avatar_iterativ.png",
)
_create_admin_user(
- email='admin',
- first_name='Peter',
- last_name='Adminson',
- avatar_url='/static/avatars/avatar_iterativ.png'
+ email="admin",
+ first_name="Peter",
+ last_name="Adminson",
+ avatar_url="/static/avatars/avatar_iterativ.png",
)
_create_student_user(
- email='student',
- first_name='Student',
- last_name='Meier',
- avatar_url='/static/avatars/avatar_iterativ.png'
+ email="student",
+ first_name="Student",
+ last_name="Meier",
+ avatar_url="/static/avatars/avatar_iterativ.png",
)
_create_student_user(
- email='daniel.egger@iterativ.ch',
- first_name='Daniel',
- last_name='Egger',
- 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",
)
_create_student_user(
- email='axel.manderbach@lernetz.ch',
- first_name='Axel',
- last_name='Manderbach',
- avatar_url='/static/avatars/avatar_axel.jpg'
+ email="axel.manderbach@lernetz.ch",
+ first_name="Axel",
+ last_name="Manderbach",
+ 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'
+ 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'
+ 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'
+ email="chantal.rosenberg@vbv-afa.ch",
+ first_name="Chantal",
+ last_name="Rosenberg",
+ avatar_url="/static/avatars/avatar_chantal.png",
+ password="myvbv1234",
)
def _get_or_create_user(user_model, *args, **kwargs):
- username = kwargs.get('username', None)
- password = kwargs.get('password', None)
+ username = kwargs.get("username", None)
+ password = kwargs.get("password", None)
created = False
user = user_model.objects.filter(username=username).first()
diff --git a/server/vbv_lernwelt/core/management/commands/reset_schema.py b/server/vbv_lernwelt/core/management/commands/reset_schema.py
index bad46f70..7217e319 100644
--- a/server/vbv_lernwelt/core/management/commands/reset_schema.py
+++ b/server/vbv_lernwelt/core/management/commands/reset_schema.py
@@ -1,15 +1,15 @@
import djclick as click
from django.conf import settings
from django.core.management import call_command
-from django.db import transaction, connection
+from django.db import connection, transaction
def reset_schema(db_config_user):
sql_list = (
- 'DROP SCHEMA public CASCADE',
- f'CREATE SCHEMA public AUTHORIZATION {db_config_user}',
- 'GRANT ALL ON SCHEMA public TO postgres',
- 'GRANT ALL ON SCHEMA public TO public',
+ "DROP SCHEMA public CASCADE",
+ f"CREATE SCHEMA public AUTHORIZATION {db_config_user}",
+ "GRANT ALL ON SCHEMA public TO postgres",
+ "GRANT ALL ON SCHEMA public TO public",
"COMMENT ON SCHEMA public IS 'standard public schema';",
)
@@ -21,11 +21,11 @@ def reset_schema(db_config_user):
@click.command()
def command():
- user = settings.DATABASES['default']['USER']
+ user = settings.DATABASES["default"]["USER"]
print(user)
reset_schema(db_config_user=user)
- call_command('createcachetable')
- call_command('migrate')
- call_command('create_default_users')
- call_command('create_default_learning_path')
+ call_command("createcachetable")
+ call_command("migrate")
+ call_command("create_default_users")
+ call_command("create_default_learning_path")
diff --git a/server/vbv_lernwelt/core/managers.py b/server/vbv_lernwelt/core/managers.py
index b1e53efe..e2b4aeb2 100644
--- a/server/vbv_lernwelt/core/managers.py
+++ b/server/vbv_lernwelt/core/managers.py
@@ -3,13 +3,17 @@ from django.contrib.auth.models import AbstractUser
class UserManager(BaseUserManager):
- def create_or_update_by_email(self, email: str, first_name: str, last_name: str, username: str) -> tuple[
- AbstractUser, bool]:
+ def create_or_update_by_email(
+ self, email: str, first_name: str, last_name: str, username: str
+ ) -> tuple[AbstractUser, bool]:
# create or sync user with OpenID Data
- user, created = self.model.objects.get_or_create(email=email, defaults={
- "first_name": first_name,
- "last_name": last_name,
- "username": username
- })
+ user, created = self.model.objects.get_or_create(
+ email=email,
+ defaults={
+ "first_name": first_name,
+ "last_name": last_name,
+ "username": username,
+ },
+ )
return user, created
diff --git a/server/vbv_lernwelt/core/middleware/auth.py b/server/vbv_lernwelt/core/middleware/auth.py
index 69c2b0c4..1c245765 100644
--- a/server/vbv_lernwelt/core/middleware/auth.py
+++ b/server/vbv_lernwelt/core/middleware/auth.py
@@ -55,13 +55,15 @@ class UserLoggedInCookieMiddleWare(MiddlewareMixin):
If the user is not authenticated and the cookie remains, delete it
"""
- cookie_name = 'loginStatus'
+ cookie_name = "loginStatus"
def process_response(self, request, response):
# if user and no cookie, set cookie
if request.user.is_authenticated and not request.COOKIES.get(self.cookie_name):
- response.set_cookie(self.cookie_name, 'true')
- elif not request.user.is_authenticated and request.COOKIES.get(self.cookie_name):
+ response.set_cookie(self.cookie_name, "true")
+ elif not request.user.is_authenticated and request.COOKIES.get(
+ self.cookie_name
+ ):
# else if no user and cookie remove user cookie, logout
response.delete_cookie(self.cookie_name)
return response
diff --git a/server/vbv_lernwelt/core/migrations/0001_initial.py b/server/vbv_lernwelt/core/migrations/0001_initial.py
index 2ff9698f..de29190a 100644
--- a/server/vbv_lernwelt/core/migrations/0001_initial.py
+++ b/server/vbv_lernwelt/core/migrations/0001_initial.py
@@ -2,8 +2,8 @@
import django.contrib.auth.models
import django.contrib.auth.validators
-from django.db import migrations, models
import django.utils.timezone
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/server/vbv_lernwelt/core/migrations/0002_user_model.py b/server/vbv_lernwelt/core/migrations/0002_user_model.py
index 2733511e..837d3c0a 100644
--- a/server/vbv_lernwelt/core/migrations/0002_user_model.py
+++ b/server/vbv_lernwelt/core/migrations/0002_user_model.py
@@ -6,18 +6,20 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
- ('core', '0001_initial'),
+ ("core", "0001_initial"),
]
operations = [
migrations.AddField(
- model_name='user',
- name='avatar_url',
- field=models.CharField(blank=True, default='', max_length=254),
+ model_name="user",
+ name="avatar_url",
+ field=models.CharField(blank=True, default="", max_length=254),
),
migrations.AlterField(
- model_name='user',
- name='email',
- field=models.EmailField(max_length=254, unique=True, verbose_name='email address'),
+ model_name="user",
+ name="email",
+ field=models.EmailField(
+ max_length=254, unique=True, verbose_name="email address"
+ ),
),
]
diff --git a/server/vbv_lernwelt/core/migrations/0003_create_users.py b/server/vbv_lernwelt/core/migrations/0003_create_users.py
index faf79e86..043919a3 100644
--- a/server/vbv_lernwelt/core/migrations/0003_create_users.py
+++ b/server/vbv_lernwelt/core/migrations/0003_create_users.py
@@ -7,10 +7,12 @@ from vbv_lernwelt.core.models import User
def create_users(apps, schema_editor):
- default_password = 'ACEEs0DCmNaPxdoNV8vhccuCTRl9b'
- if settings.APP_ENVIRONMENT == 'development':
+ default_password = "ACEEs0DCmNaPxdoNV8vhccuCTRl9b"
+ if settings.APP_ENVIRONMENT == "development":
default_password = None
- create_default_users(user_model=User, group_model=Group, default_password=default_password)
+ create_default_users(
+ user_model=User, group_model=Group, default_password=default_password
+ )
class Migration(migrations.Migration):
diff --git a/server/vbv_lernwelt/core/migrations/0004_alter_user_managers.py b/server/vbv_lernwelt/core/migrations/0004_alter_user_managers.py
index 820b5223..ea675910 100644
--- a/server/vbv_lernwelt/core/migrations/0004_alter_user_managers.py
+++ b/server/vbv_lernwelt/core/migrations/0004_alter_user_managers.py
@@ -6,13 +6,12 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
- ('core', '0003_create_users'),
+ ("core", "0003_create_users"),
]
operations = [
migrations.AlterModelManagers(
- name='user',
- managers=[
- ],
+ name="user",
+ managers=[],
),
]
diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py
index 432e0608..36a21ee3 100644
--- a/server/vbv_lernwelt/core/models.py
+++ b/server/vbv_lernwelt/core/models.py
@@ -10,10 +10,11 @@ class User(AbstractUser):
Default custom user model for VBV Lernwelt.
If adding fields that need to be filled at user signup,
"""
+
# FIXME: look into it...
# objects = UserManager()
- avatar_url = models.CharField(max_length=254, blank=True, default='')
- email = models.EmailField('email address', unique=True)
+ avatar_url = models.CharField(max_length=254, blank=True, default="")
+ email = models.EmailField("email address", unique=True)
objects = UserManager()
diff --git a/server/vbv_lernwelt/core/serializers.py b/server/vbv_lernwelt/core/serializers.py
index ddc5ef24..f6dfe04b 100644
--- a/server/vbv_lernwelt/core/serializers.py
+++ b/server/vbv_lernwelt/core/serializers.py
@@ -7,5 +7,10 @@ class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
- 'id', 'first_name', 'last_name', 'email', 'username', 'avatar_url',
+ "id",
+ "first_name",
+ "last_name",
+ "email",
+ "username",
+ "avatar_url",
]
diff --git a/server/vbv_lernwelt/core/views.py b/server/vbv_lernwelt/core/views.py
index dd647446..ad1f2ec9 100644
--- a/server/vbv_lernwelt/core/views.py
+++ b/server/vbv_lernwelt/core/views.py
@@ -7,13 +7,17 @@ import structlog
from django.conf import settings
from django.contrib.auth import authenticate, login, logout
from django.core.management import call_command
-from django.http import JsonResponse, HttpResponse, HttpResponseRedirect
+from django.http import HttpResponse, HttpResponseRedirect, JsonResponse
from django.shortcuts import render
from django.template import loader
from django.views.decorators.csrf import ensure_csrf_cookie
from ratelimit.decorators import ratelimit
from rest_framework import authentication
-from rest_framework.decorators import api_view, authentication_classes, permission_classes
+from rest_framework.decorators import (
+ api_view,
+ authentication_classes,
+ permission_classes,
+)
from rest_framework.permissions import IsAdminUser
from rest_framework.response import Response
@@ -28,56 +32,67 @@ logger = structlog.get_logger(__name__)
def vue_home(request, *args):
if settings.IT_SERVE_VUE:
try:
- res = requests.get(f'{settings.IT_SERVE_VUE_URL}{request.get_full_path()}')
+ res = requests.get(f"{settings.IT_SERVE_VUE_URL}{request.get_full_path()}")
content = res.text
- content = content.replace('https://vbv-lernwelt.control.iterativ.ch/static/', '/static/')
+ content = content.replace(
+ "https://vbv-lernwelt.control.iterativ.ch/static/", "/static/"
+ )
headers = res.headers
- content_type = headers.get('content-type', 'text/html')
+ content_type = headers.get("content-type", "text/html")
return HttpResponse(content, content_type=content_type)
except Exception as e:
return HttpResponse(
- f'Can not connect to vue dev server at {settings.IT_SERVE_VUE_URL}: {e}'
+ f"Can not connect to vue dev server at {settings.IT_SERVE_VUE_URL}: {e}"
)
# render index.html from `npm run build`
- content = loader.render_to_string('vue/index.html', context={}, request=request)
- content = content.replace('https://vbv-lernwelt.control.iterativ.ch/static/', '/static/')
+ content = loader.render_to_string("vue/index.html", context={}, request=request)
+ content = content.replace(
+ "https://vbv-lernwelt.control.iterativ.ch/static/", "/static/"
+ )
return HttpResponse(content)
-@api_view(['POST'])
+@api_view(["POST"])
@ensure_csrf_cookie
def vue_login(request):
if settings.ALLOW_LOCAL_LOGIN:
try:
- username = request.data.get('username')
- password = request.data.get('password')
+ username = request.data.get("username")
+ password = request.data.get("password")
if username and password:
user = authenticate(request, username=username, password=password)
if user:
login(request, user)
- logger.debug('login successful', username=username, email=user.email, label='login')
+ logger.debug(
+ "login successful",
+ username=username,
+ email=user.email,
+ label="login",
+ )
return Response(UserSerializer(user).data)
except Exception as e:
logger.exception(e)
- logger.debug('login failed', username=username, label='login')
- return Response({'success': False}, status=401)
+ logger.debug("login failed", username=username, label="login")
+ return Response({"success": False}, status=401)
else:
- return Response({'success': False, 'message': 'ALLOW_LOCAL_LOGIN=false'}, status=403)
+ return Response(
+ {"success": False, "message": "ALLOW_LOCAL_LOGIN=false"}, status=403
+ )
-@api_view(['GET'])
+@api_view(["GET"])
def me_user_view(request):
if request.user.is_authenticated:
return Response(UserSerializer(request.user).data)
return Response(status=403)
-@api_view(['POST'])
+@api_view(["POST"])
def vue_logout(request):
logout(request)
- return Response({'success': True}, 200)
+ return Response({"success": True}, 200)
def permission_denied_view(request, exception):
@@ -106,32 +121,35 @@ def check_rate_limit(request):
return HttpResponse(content=b"Hello")
-@api_view(['POST'])
+@api_view(["POST"])
@authentication_classes((authentication.SessionAuthentication,))
@permission_classes((IsAdminUser,))
def cypress_reset_view(request):
- if settings.APP_ENVIRONMENT != 'production':
- call_command('cypress_reset')
+ if settings.APP_ENVIRONMENT != "production":
+ call_command("cypress_reset")
- return HttpResponseRedirect('/admin/')
+ return HttpResponseRedirect("/admin/")
@django_view_authentication_exempt
def generate_web_component_icons(request):
svg_files = []
- for filepath in glob.iglob(f'{settings.APPS_DIR}/static/icons/*.svg'):
- with open(filepath, 'r') as f:
+ for filepath in glob.iglob(f"{settings.APPS_DIR}/static/icons/*.svg"):
+ with open(filepath, "r") as f:
filename = Path(filepath).stem
- elementname = 'it-' + filename
- svg_files.append({
- 'filepath': filepath,
- 'content': f.read(),
- 'filename': filename,
- 'elementname': elementname,
- 'classname': filename.replace('-', '_'),
- })
+ elementname = "it-" + filename
+ svg_files.append(
+ {
+ "filepath": filepath,
+ "content": f.read(),
+ "filename": filename,
+ "elementname": elementname,
+ "classname": filename.replace("-", "_"),
+ }
+ )
return render(
- request, "core/icons.html",
- context={'svg_files': svg_files},
- content_type="application/javascript"
+ request,
+ "core/icons.html",
+ context={"svg_files": svg_files},
+ content_type="application/javascript",
)
diff --git a/server/vbv_lernwelt/course/apps.py b/server/vbv_lernwelt/course/apps.py
index 9453e526..9b82af02 100644
--- a/server/vbv_lernwelt/course/apps.py
+++ b/server/vbv_lernwelt/course/apps.py
@@ -2,8 +2,8 @@ from django.apps import AppConfig
class CourseConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'vbv_lernwelt.course'
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "vbv_lernwelt.course"
def ready(self):
try:
diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py
index 79ab3624..6a8bdeb2 100644
--- a/server/vbv_lernwelt/course/creators/test_course.py
+++ b/server/vbv_lernwelt/course/creators/test_course.py
@@ -4,20 +4,37 @@ import wagtail_factories
from django.conf import settings
from wagtail.models import Site
-from vbv_lernwelt.competence.factories import CompetenceProfilePageFactory, CompetencePageFactory, \
- PerformanceCriteriaFactory
+from vbv_lernwelt.competence.factories import (
+ CompetencePageFactory,
+ CompetenceProfilePageFactory,
+ PerformanceCriteriaFactory,
+)
from vbv_lernwelt.competence.models import CompetencePage
from vbv_lernwelt.core.tests.helpers import create_locales_for_wagtail
from vbv_lernwelt.course.consts import COURSE_TEST_ID
from vbv_lernwelt.course.factories import CoursePageFactory
-from vbv_lernwelt.course.models import CoursePage, CourseCategory, Course
+from vbv_lernwelt.course.models import Course, CourseCategory, CoursePage
from vbv_lernwelt.learnpath.models import LearningUnit
-from vbv_lernwelt.learnpath.tests.learning_path_factories import CircleFactory, LearningSequenceFactory, \
- LearningContentFactory, DocumentBlockFactory, LearningUnitFactory, TestBlockFactory, ExerciseBlockFactory, \
- LearningPathFactory, TopicFactory, OnlineTrainingBlockFactory
-from vbv_lernwelt.media_library.tests.media_library_factories import MediaLibraryPageFactory, \
- create_document_collection, create_link_collection, create_media_content_link, LinkBlockFactory, \
- MediaCategoryPageFactory
+from vbv_lernwelt.learnpath.tests.learning_path_factories import (
+ CircleFactory,
+ DocumentBlockFactory,
+ ExerciseBlockFactory,
+ LearningContentFactory,
+ LearningPathFactory,
+ LearningSequenceFactory,
+ LearningUnitFactory,
+ OnlineTrainingBlockFactory,
+ TestBlockFactory,
+ TopicFactory,
+)
+from vbv_lernwelt.media_library.tests.media_library_factories import (
+ create_document_collection,
+ create_link_collection,
+ create_media_content_link,
+ LinkBlockFactory,
+ MediaCategoryPageFactory,
+ MediaLibraryPageFactory,
+)
def create_test_course():
@@ -30,22 +47,21 @@ def create_test_course():
def create_test_course_with_categories(apps=None, schema_editor=None):
if apps is not None:
- Course = apps.get_model('course', 'Course')
- CourseCategory = apps.get_model('course', 'CourseCategory')
+ Course = apps.get_model("course", "Course")
+ CourseCategory = apps.get_model("course", "CourseCategory")
else:
# pylint: disable=import-outside-toplevel
from vbv_lernwelt.course.models import Course, CourseCategory
course, _ = Course.objects.get_or_create(
- id=COURSE_TEST_ID,
- title='Test Lehrgang',
- category_name='Handlungsfeld'
+ id=COURSE_TEST_ID, title="Test Lehrgang", category_name="Handlungsfeld"
)
- CourseCategory.objects.get_or_create(course=course, title='Allgemein', general=True)
+ CourseCategory.objects.get_or_create(course=course, title="Allgemein", general=True)
for cat in [
- 'Fahrzeug', 'Reisen',
+ "Fahrzeug",
+ "Reisen",
]:
CourseCategory.objects.get_or_create(course=course, title=cat)
@@ -54,7 +70,7 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
if not site:
site = wagtail_factories.SiteFactory(is_default_site=True)
- if settings.APP_ENVIRONMENT == 'development':
+ if settings.APP_ENVIRONMENT == "development":
site.port = 8000
site.save()
@@ -67,9 +83,7 @@ def create_test_course_with_categories(apps=None, schema_editor=None):
def create_test_learning_path(user=None, skip_locales=True):
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
- lp = LearningPathFactory(
- title="Test Lernpfad", parent=course_page
- )
+ lp = LearningPathFactory(title="Test Lernpfad", parent=course_page)
TopicFactory(title="Basis", is_visible=False, parent=lp)
@@ -78,19 +92,21 @@ def create_test_learning_path(user=None, skip_locales=True):
parent=lp,
description="Basis",
)
- LearningSequenceFactory(title='Starten', parent=circle_basis, icon='it-icon-ls-start')
+ LearningSequenceFactory(
+ title="Starten", parent=circle_basis, icon="it-icon-ls-start"
+ )
LearningContentFactory(
- title='Einführung',
+ title="Einführung",
parent=circle_basis,
minutes=15,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
- LearningSequenceFactory(title='Beenden', parent=circle_basis, icon='it-icon-ls-end')
+ LearningSequenceFactory(title="Beenden", parent=circle_basis, icon="it-icon-ls-end")
LearningContentFactory(
- title='Jetzt kann es losgehen!',
+ title="Jetzt kann es losgehen!",
parent=circle_basis,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
TopicFactory(title="Beraten der Kunden", parent=lp)
@@ -100,79 +116,104 @@ def create_test_learning_path(user=None, skip_locales=True):
parent=lp,
description="Unit-Test Circle",
job_situations=[
- ('job_situation', 'Autoversicherung'),
- ('job_situation', 'Autokauf'),
+ ("job_situation", "Autoversicherung"),
+ ("job_situation", "Autokauf"),
],
goals=[
- ('goal', '... die heutige Versicherungssituation von Privat- oder Geschäftskunden einzuschätzen.'),
- ('goal', '... deinem Kunden seine optimale Lösung aufzuzeigen'),
+ (
+ "goal",
+ "... die heutige Versicherungssituation von Privat- oder Geschäftskunden einzuschätzen.",
+ ),
+ ("goal", "... deinem Kunden seine optimale Lösung aufzuzeigen"),
],
experts=[
- ('person', {'last_name': 'Huggel', 'first_name': 'Patrizia', 'email': 'patrizia.huggel@example.com'}),
- ]
+ (
+ "person",
+ {
+ "last_name": "Huggel",
+ "first_name": "Patrizia",
+ "email": "patrizia.huggel@example.com",
+ },
+ ),
+ ],
)
- LearningSequenceFactory(title='Starten', parent=circle, icon='it-icon-ls-start')
+ LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
LearningContentFactory(
title=f'Einleitung Circle "Analyse"',
parent=circle,
minutes=15,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
- LearningSequenceFactory(title='Beobachten', parent=circle, icon='it-icon-ls-watch')
+ LearningSequenceFactory(title="Beobachten", parent=circle, icon="it-icon-ls-watch")
lu = LearningUnitFactory(
- title='Fahrzeug',
+ title="Fahrzeug",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_TEST_ID, title='Fahrzeug'),
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_TEST_ID, title="Fahrzeug"
+ ),
)
LearningContentFactory(
- title='Rafael Fasel wechselt sein Auto',
+ title="Rafael Fasel wechselt sein Auto",
parent=circle,
minutes=30,
- contents=[('online_training', OnlineTrainingBlockFactory(
- description='In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.',
- url='',
- ))]
+ contents=[
+ (
+ "online_training",
+ OnlineTrainingBlockFactory(
+ description="In diesem Online-Training lernst du, wie du den Kundenbedarf ermittelst.",
+ url="",
+ ),
+ )
+ ],
)
LearningContentFactory(
- title='Fachcheck Fahrzeug',
+ title="Fachcheck Fahrzeug",
parent=circle,
minutes=30,
- contents=[('test', TestBlockFactory())]
+ contents=[("test", TestBlockFactory())],
)
lu = LearningUnitFactory(
- title='Reisen',
+ title="Reisen",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_TEST_ID, title='Reisen'),
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_TEST_ID, title="Reisen"
+ ),
)
LearningContentFactory(
- title='Reiseversicherung',
+ title="Reiseversicherung",
parent=circle,
minutes=240,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
LearningContentFactory(
- title='Emma und Ayla campen durch Amerika',
+ title="Emma und Ayla campen durch Amerika",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory(
- url='/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html'))]
+ contents=[
+ (
+ "exercise",
+ ExerciseBlockFactory(
+ url="/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html"
+ ),
+ )
+ ],
)
- LearningSequenceFactory(title='Beenden', parent=circle, icon='it-icon-ls-end')
+ LearningSequenceFactory(title="Beenden", parent=circle, icon="it-icon-ls-end")
LearningContentFactory(
- title='Kompetenzprofil anschauen',
+ title="Kompetenzprofil anschauen",
parent=circle,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
LearningContentFactory(
title='Circle "Analyse" abschliessen',
parent=circle,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
@@ -181,62 +222,70 @@ def create_test_competence_profile():
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
competence_profile_page = CompetenceProfilePageFactory(
- title='Kompetenzprofil',
+ title="Kompetenzprofil",
parent=course_page,
)
- competences = [{
- 'competence_id': 'X1',
- 'title': 'Weiterempfehlung für Neukunden generieren',
- 'items': [
- 'Verhandlungsgeschick',
- 'Überzeugtes Auftreten',
- ],
- }, {
- 'competence_id': 'Y1',
- 'title': 'Wünsche, Ziele und Bedürfnisse der Kunden im Gespräch ermitteln',
- 'items': [
- 'Gesprächsführung',
- 'Fragetechniken',
- 'Kundenpsychologie',
- ]
- }, {
- 'competence_id': 'Y2',
- 'title': 'Analyse des Kundenbedarfs und des Kundenbedürfnisses durchführen',
- 'items': [
- 'Fragetechniken',
- 'Visuelle Hilfsmittel / Visualisierungstechniken',
- ]
- }]
+ competences = [
+ {
+ "competence_id": "X1",
+ "title": "Weiterempfehlung für Neukunden generieren",
+ "items": [
+ "Verhandlungsgeschick",
+ "Überzeugtes Auftreten",
+ ],
+ },
+ {
+ "competence_id": "Y1",
+ "title": "Wünsche, Ziele und Bedürfnisse der Kunden im Gespräch ermitteln",
+ "items": [
+ "Gesprächsführung",
+ "Fragetechniken",
+ "Kundenpsychologie",
+ ],
+ },
+ {
+ "competence_id": "Y2",
+ "title": "Analyse des Kundenbedarfs und des Kundenbedürfnisses durchführen",
+ "items": [
+ "Fragetechniken",
+ "Visuelle Hilfsmittel / Visualisierungstechniken",
+ ],
+ },
+ ]
for c in competences:
CompetencePageFactory(
parent=competence_profile_page,
- competence_id=c['competence_id'],
- title=c['title'],
- items=[
- ('item', i) for i in c['items']
- ]
+ competence_id=c["competence_id"],
+ title=c["title"],
+ items=[("item", i) for i in c["items"]],
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='Y1'),
- competence_id='Y1.3',
- 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(slug='test-lehrgang-lp-circle-analyse-lu-fahrzeug'),
+ parent=CompetencePage.objects.get(competence_id="Y1"),
+ competence_id="Y1.3",
+ 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(
+ slug="test-lehrgang-lp-circle-analyse-lu-fahrzeug"
+ ),
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='Y2'),
- competence_id='Y2.1',
- 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(slug='test-lehrgang-lp-circle-analyse-lu-fahrzeug'),
+ parent=CompetencePage.objects.get(competence_id="Y2"),
+ competence_id="Y2.1",
+ 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(
+ slug="test-lehrgang-lp-circle-analyse-lu-fahrzeug"
+ ),
)
PerformanceCriteriaFactory(
- parent=CompetencePage.objects.get(competence_id='Y1'),
- competence_id='Y1.3',
- 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(slug='test-lehrgang-lp-circle-analyse-lu-reisen'),
+ parent=CompetencePage.objects.get(competence_id="Y1"),
+ competence_id="Y1.3",
+ 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(
+ slug="test-lehrgang-lp-circle-analyse-lu-reisen"
+ ),
)
@@ -245,38 +294,52 @@ def create_test_media_library():
course_page = CoursePage.objects.get(course_id=COURSE_TEST_ID)
media_lib_page = MediaLibraryPageFactory(
- title='Mediathek',
+ title="Mediathek",
parent=course_page,
)
- icons = ['icon-hf-fahrzeug', 'icon-hf-reisen', 'icon-hf-einkommenssicherung', ]
+ icons = [
+ "icon-hf-fahrzeug",
+ "icon-hf-reisen",
+ "icon-hf-einkommenssicherung",
+ ]
for idx, cat in enumerate(course.coursecategory_set.all()):
overview_icon = icons[(idx + 2) % len(icons)]
- introduction_text = '''
+ introduction_text = """
Das Auto ist für viele der grösste Stolz! Es birgt aber auch ein grosses Gefahrenpotenzial.
Dabei geht es bei den heutigen Fahrzeugpreisen und Reparaturkosten rasch um namhafte Summen,
-die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.'''.strip()
- description_title = 'Das erwartet dich in diesem Handlungsfeld'
- description_text = '''
+die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.""".strip()
+ description_title = "Das erwartet dich in diesem Handlungsfeld"
+ description_text = """
In diesem berufstypischem Handlungsfeld lernst du alles rund um Motorfahrzeugversicherungen,
wie man sein Auto optimal schützen kann, wie du vorgehst bei einem Fahrzeugwechsel,
-welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.'''.strip()
+welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.""".strip()
items = [
- ('item', 'Motorfahrzeughaftpflichtversicherung'),
- ('item', 'Motorfahrzeugkaskoversicherung'),
- ('item', 'Insassenunfallversicherung'),
+ ("item", "Motorfahrzeughaftpflichtversicherung"),
+ ("item", "Motorfahrzeugkaskoversicherung"),
+ ("item", "Insassenunfallversicherung"),
]
- body_data = json.dumps([
- create_document_collection(),
- create_link_collection(
- links_dict=[
- create_media_content_link(
- LinkBlockFactory(title='Nationales Versicherungsbüro', url='https://www.vbv.ch/')),
- create_media_content_link(
- LinkBlockFactory(title='Adressen der Strassenverkehrsämter', url='https://www.vbv.ch/')),
- ]
- )
- ])
+ body_data = json.dumps(
+ [
+ create_document_collection(),
+ create_link_collection(
+ links_dict=[
+ create_media_content_link(
+ LinkBlockFactory(
+ title="Nationales Versicherungsbüro",
+ url="https://www.vbv.ch/",
+ )
+ ),
+ create_media_content_link(
+ LinkBlockFactory(
+ title="Adressen der Strassenverkehrsämter",
+ url="https://www.vbv.ch/",
+ )
+ ),
+ ]
+ ),
+ ]
+ )
media_category = MediaCategoryPageFactory(
overview_icon=overview_icon,
title=cat.title,
diff --git a/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py b/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py
index 67a8bda9..a23109b2 100644
--- a/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py
+++ b/server/vbv_lernwelt/course/creators/versicherungsvermittlerin.py
@@ -8,24 +8,33 @@ from vbv_lernwelt.course.factories import CoursePageFactory
def create_versicherungsvermittlerin_with_categories(apps=None, schema_editor=None):
if apps is not None:
- Course = apps.get_model('course', 'Course')
- CourseCategory = apps.get_model('course', 'CourseCategory')
+ Course = apps.get_model("course", "Course")
+ CourseCategory = apps.get_model("course", "CourseCategory")
else:
# pylint: disable=import-outside-toplevel
from vbv_lernwelt.course.models import Course, CourseCategory
course, _ = Course.objects.get_or_create(
id=COURSE_VERSICHERUNGSVERMITTLERIN_ID,
- title='Versicherungsvermittler/in',
- category_name='Handlungsfeld'
+ title="Versicherungsvermittler/in",
+ category_name="Handlungsfeld",
)
- CourseCategory.objects.get_or_create(course=course, title='Allgemein', general=True)
+ CourseCategory.objects.get_or_create(course=course, title="Allgemein", general=True)
for cat in [
- 'Fahrzeug', 'Reisen', 'Einkommenssicherung', 'Gesundheit', 'Haushalt', 'Sparen',
- 'Pensionierung', 'KMU', 'Wohneigentum', 'Rechtsstreitigkeiten', 'Erben / Vererben',
- 'Selbständigkeit',
+ "Fahrzeug",
+ "Reisen",
+ "Einkommenssicherung",
+ "Gesundheit",
+ "Haushalt",
+ "Sparen",
+ "Pensionierung",
+ "KMU",
+ "Wohneigentum",
+ "Rechtsstreitigkeiten",
+ "Erben / Vererben",
+ "Selbständigkeit",
]:
CourseCategory.objects.get_or_create(course=course, title=cat)
@@ -34,7 +43,7 @@ def create_versicherungsvermittlerin_with_categories(apps=None, schema_editor=No
if not site:
site = wagtail_factories.SiteFactory(is_default_site=True)
- if settings.APP_ENVIRONMENT == 'development':
+ if settings.APP_ENVIRONMENT == "development":
site.port = 8000
site.save()
diff --git a/server/vbv_lernwelt/course/factories.py b/server/vbv_lernwelt/course/factories.py
index 766f5c31..2931a070 100644
--- a/server/vbv_lernwelt/course/factories.py
+++ b/server/vbv_lernwelt/course/factories.py
@@ -1,15 +1,15 @@
import wagtail_factories
from factory.django import DjangoModelFactory
-from vbv_lernwelt.course.models import CoursePage, Course
+from vbv_lernwelt.course.models import Course, CoursePage
class CourseFactory(DjangoModelFactory):
class Meta:
model = Course
- title = 'Versicherungsvermittler/in'
- category_name = 'Handlungsfeld'
+ title = "Versicherungsvermittler/in"
+ category_name = "Handlungsfeld"
class CoursePageFactory(wagtail_factories.PageFactory):
diff --git a/server/vbv_lernwelt/course/management/commands/create_default_courses.py b/server/vbv_lernwelt/course/management/commands/create_default_courses.py
index 22550740..66b42e80 100644
--- a/server/vbv_lernwelt/course/management/commands/create_default_courses.py
+++ b/server/vbv_lernwelt/course/management/commands/create_default_courses.py
@@ -1,11 +1,22 @@
import djclick as click
-from vbv_lernwelt.competence.create_default_competence_profile import create_default_competence_profile
+from vbv_lernwelt.competence.create_default_competence_profile import (
+ create_default_competence_profile,
+)
from vbv_lernwelt.course.creators.test_course import create_test_course
-from vbv_lernwelt.course.creators.versicherungsvermittlerin import create_versicherungsvermittlerin_with_categories
-from vbv_lernwelt.learnpath.create_default_learning_path import create_default_learning_path
-from vbv_lernwelt.media_library.create_default_documents import create_default_collections, create_default_documents
-from vbv_lernwelt.media_library.create_default_media_library import create_default_media_library
+from vbv_lernwelt.course.creators.versicherungsvermittlerin import (
+ create_versicherungsvermittlerin_with_categories,
+)
+from vbv_lernwelt.learnpath.create_default_learning_path import (
+ create_default_learning_path,
+)
+from vbv_lernwelt.media_library.create_default_documents import (
+ create_default_collections,
+ create_default_documents,
+)
+from vbv_lernwelt.media_library.create_default_media_library import (
+ create_default_media_library,
+)
@click.command()
diff --git a/server/vbv_lernwelt/course/migrations/0001_initial.py b/server/vbv_lernwelt/course/migrations/0001_initial.py
index 40fe9d3e..742fa60f 100644
--- a/server/vbv_lernwelt/course/migrations/0001_initial.py
+++ b/server/vbv_lernwelt/course/migrations/0001_initial.py
@@ -11,58 +11,140 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('wagtailcore', '0069_log_entry_jsonfield'),
+ ("wagtailcore", "0069_log_entry_jsonfield"),
]
operations = [
migrations.CreateModel(
- name='Course',
+ name="Course",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=255, verbose_name='Titel')),
- ('category_name', models.CharField(default='Kategorie', max_length=255, verbose_name='Kategorie-Name')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("title", models.CharField(max_length=255, verbose_name="Titel")),
+ (
+ "category_name",
+ models.CharField(
+ default="Kategorie",
+ max_length=255,
+ verbose_name="Kategorie-Name",
+ ),
+ ),
],
options={
- 'verbose_name': 'Lehrgang',
+ "verbose_name": "Lehrgang",
},
),
migrations.CreateModel(
- name='CoursePage',
+ name="CoursePage",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('course', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='course.course')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ (
+ "course",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.PROTECT, to="course.course"
+ ),
+ ),
],
options={
- 'verbose_name': 'Lehrgang-Seite',
+ "verbose_name": "Lehrgang-Seite",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='CourseCompletion',
+ name="CourseCompletion",
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)),
- ('page_key', models.UUIDField()),
- ('page_type', models.CharField(blank=True, default='', max_length=255)),
- ('page_slug', models.CharField(blank=True, default='', max_length=255)),
- ('completion_status', models.CharField(choices=[('unknown', 'unknown'), ('success', 'success'), ('fail', 'fail')], default='unknown', max_length=255)),
- ('additional_json_data', models.JSONField(default=dict)),
- ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.course')),
- ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ (
+ "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)),
+ ("page_key", models.UUIDField()),
+ ("page_type", models.CharField(blank=True, default="", max_length=255)),
+ ("page_slug", models.CharField(blank=True, default="", max_length=255)),
+ (
+ "completion_status",
+ models.CharField(
+ choices=[
+ ("unknown", "unknown"),
+ ("success", "success"),
+ ("fail", "fail"),
+ ],
+ default="unknown",
+ max_length=255,
+ ),
+ ),
+ ("additional_json_data", models.JSONField(default=dict)),
+ (
+ "course",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="course.course"
+ ),
+ ),
+ (
+ "user",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ to=settings.AUTH_USER_MODEL,
+ ),
+ ),
],
),
migrations.CreateModel(
- name='CourseCategory',
+ name="CourseCategory",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(blank=True, max_length=255, verbose_name='Titel')),
- ('general', models.BooleanField(default=False, verbose_name='Allgemein')),
- ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.course')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ (
+ "title",
+ models.CharField(blank=True, max_length=255, verbose_name="Titel"),
+ ),
+ (
+ "general",
+ models.BooleanField(default=False, verbose_name="Allgemein"),
+ ),
+ (
+ "course",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE, to="course.course"
+ ),
+ ),
],
),
migrations.AddConstraint(
- model_name='coursecompletion',
- constraint=models.UniqueConstraint(fields=('user', 'page_key'), name='course_completion_unique_user_page_key'),
+ model_name="coursecompletion",
+ constraint=models.UniqueConstraint(
+ fields=("user", "page_key"),
+ name="course_completion_unique_user_page_key",
+ ),
),
]
diff --git a/server/vbv_lernwelt/course/models.py b/server/vbv_lernwelt/course/models.py
index 1422b1eb..e2d94596 100644
--- a/server/vbv_lernwelt/course/models.py
+++ b/server/vbv_lernwelt/course/models.py
@@ -9,8 +9,10 @@ from vbv_lernwelt.core.models import User
class Course(models.Model):
- title = models.CharField(_('Titel'), max_length=255)
- category_name = models.CharField(_('Kategorie-Name'), max_length=255, default='Kategorie')
+ title = models.CharField(_("Titel"), max_length=255)
+ category_name = models.CharField(
+ _("Kategorie-Name"), max_length=255, default="Kategorie"
+ )
class Meta:
verbose_name = _("Lehrgang")
@@ -21,9 +23,9 @@ class Course(models.Model):
class CourseCategory(models.Model):
# Die Handlungsfelder im "Versicherungsvermittler/in"
- title = models.CharField(_('Titel'), max_length=255, blank=True)
- course = models.ForeignKey('course.Course', on_delete=models.CASCADE)
- general = models.BooleanField(_('Allgemein'), default=False)
+ title = models.CharField(_("Titel"), max_length=255, blank=True)
+ course = models.ForeignKey("course.Course", on_delete=models.CASCADE)
+ general = models.BooleanField(_("Allgemein"), default=False)
def __str__(self):
return f"{self.course} / {self.title}"
@@ -31,8 +33,8 @@ class CourseCategory(models.Model):
class CoursePage(Page):
content_panels = Page.content_panels
- subpage_types = ['learnpath.LearningPath', 'media_library.MediaLibraryPage']
- course = models.ForeignKey('course.Course', on_delete=models.PROTECT)
+ subpage_types = ["learnpath.LearningPath", "media_library.MediaLibraryPage"]
+ course = models.ForeignKey("course.Course", on_delete=models.PROTECT)
class Meta:
verbose_name = _("Lehrgang-Seite")
@@ -53,26 +55,29 @@ class CourseCompletion(models.Model):
# page can logically be a LearningContent or a PerformanceCriteria for now
page_key = models.UUIDField()
- page_type = models.CharField(max_length=255, default='', blank=True)
- page_slug = models.CharField(max_length=255, default='', blank=True)
+ page_type = models.CharField(max_length=255, default="", blank=True)
+ page_slug = models.CharField(max_length=255, default="", blank=True)
- course = models.ForeignKey('course.Course', on_delete=models.CASCADE)
+ course = models.ForeignKey("course.Course", on_delete=models.CASCADE)
completion_status = models.CharField(
max_length=255,
choices=[
- ('unknown', 'unknown'),
- ('success', 'success'),
- ('fail', 'fail'),
+ ("unknown", "unknown"),
+ ("success", "success"),
+ ("fail", "fail"),
],
- default='unknown',
+ default="unknown",
)
additional_json_data = models.JSONField(default=dict)
class Meta:
constraints = [
UniqueConstraint(
- fields=['user', 'page_key', ],
- name='course_completion_unique_user_page_key'
+ fields=[
+ "user",
+ "page_key",
+ ],
+ name="course_completion_unique_user_page_key",
)
]
diff --git a/server/vbv_lernwelt/course/serializers.py b/server/vbv_lernwelt/course/serializers.py
index c2899c1d..49bf8b6d 100644
--- a/server/vbv_lernwelt/course/serializers.py
+++ b/server/vbv_lernwelt/course/serializers.py
@@ -1,25 +1,36 @@
from rest_framework import serializers
-from vbv_lernwelt.course.models import CourseCategory, Course, CourseCompletion
+from vbv_lernwelt.course.models import Course, CourseCategory, CourseCompletion
class CourseSerializer(serializers.ModelSerializer):
class Meta:
model = Course
- fields = ['id', 'title', 'category_name']
+ fields = ["id", "title", "category_name"]
class CourseCategorySerializer(serializers.ModelSerializer):
class Meta:
model = CourseCategory
- fields = ['id', 'title', 'general',]
+ fields = [
+ "id",
+ "title",
+ "general",
+ ]
class CourseCompletionSerializer(serializers.ModelSerializer):
class Meta:
model = CourseCompletion
fields = [
- 'id', 'created_at', 'updated_at', 'user',
- 'page_key', 'page_type', 'page_slug',
- 'course', 'completion_status', 'additional_json_data',
+ "id",
+ "created_at",
+ "updated_at",
+ "user",
+ "page_key",
+ "page_type",
+ "page_slug",
+ "course",
+ "completion_status",
+ "additional_json_data",
]
diff --git a/server/vbv_lernwelt/course/signals.py b/server/vbv_lernwelt/course/signals.py
index d7e767d9..90780f1c 100644
--- a/server/vbv_lernwelt/course/signals.py
+++ b/server/vbv_lernwelt/course/signals.py
@@ -7,8 +7,8 @@ logger = structlog.get_logger(__name__)
def invalidate_api_page_cache(sender, **kwargs):
- logger.debug('invalidate api_page_cache', label='api_page_cache')
- caches['api_page_cache'].clear()
+ logger.debug("invalidate api_page_cache", label="api_page_cache")
+ caches["api_page_cache"].clear()
for subclass in Page.__subclasses__():
diff --git a/server/vbv_lernwelt/course/tests/test_completion_api.py b/server/vbv_lernwelt/course/tests/test_completion_api.py
index af738b70..6de3072b 100644
--- a/server/vbv_lernwelt/course/tests/test_completion_api.py
+++ b/server/vbv_lernwelt/course/tests/test_completion_api.py
@@ -14,52 +14,61 @@ class CourseCompletionApiTestCase(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course()
- self.user = User.objects.get(username='student')
- self.client.login(username='student', password='test')
+ self.user = User.objects.get(username="student")
+ self.client.login(username="student", password="test")
def test_completeLearningContent_works(self):
- learning_content = LearningContent.objects.get(title='Fachcheck Fahrzeug')
+ learning_content = LearningContent.objects.get(title="Fachcheck Fahrzeug")
learning_content_key = str(learning_content.translation_key)
- mark_url = f'/api/course/completion/mark/'
+ mark_url = f"/api/course/completion/mark/"
- response = self.client.post(mark_url, {
- 'page_key': learning_content_key,
- })
+ response = self.client.post(
+ mark_url,
+ {
+ "page_key": learning_content_key,
+ },
+ )
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response_json), 1)
- self.assertEqual(response_json[0]['page_key'], learning_content_key)
- self.assertEqual(response_json[0]['completion_status'], 'success')
+ self.assertEqual(response_json[0]["page_key"], learning_content_key)
+ self.assertEqual(response_json[0]["completion_status"], "success")
- db_entry = CourseCompletion.objects.get(user=self.user, course_id=COURSE_TEST_ID, page_key=learning_content_key)
- self.assertEqual(db_entry.completion_status, 'success')
+ db_entry = CourseCompletion.objects.get(
+ user=self.user, course_id=COURSE_TEST_ID, page_key=learning_content_key
+ )
+ self.assertEqual(db_entry.completion_status, "success")
# test getting the circle data
- response = self.client.get(f'/api/course/completion/{COURSE_TEST_ID}/')
+ response = self.client.get(f"/api/course/completion/{COURSE_TEST_ID}/")
print(response.status_code)
response_json = response.json()
print(json.dumps(response.json(), indent=2))
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response_json), 1)
- self.assertEqual(response_json[0]['page_key'], learning_content_key)
- self.assertTrue(response_json[0]['completion_status'], 'success')
+ self.assertEqual(response_json[0]["page_key"], learning_content_key)
+ self.assertTrue(response_json[0]["completion_status"], "success")
# test with "fail"
- response = self.client.post(mark_url, {
- 'page_key': learning_content_key,
- 'completion_status': 'fail',
- })
+ response = self.client.post(
+ mark_url,
+ {
+ "page_key": learning_content_key,
+ "completion_status": "fail",
+ },
+ )
response_json = response.json()
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response_json), 1)
- self.assertEqual(response_json[0]['page_key'], learning_content_key)
- self.assertEqual(response_json[0]['completion_status'], 'fail')
-
- db_entry = CourseCompletion.objects.get(user=self.user, course_id=COURSE_TEST_ID, page_key=learning_content_key)
- self.assertEqual(db_entry.completion_status, 'fail')
+ self.assertEqual(response_json[0]["page_key"], learning_content_key)
+ self.assertEqual(response_json[0]["completion_status"], "fail")
+ db_entry = CourseCompletion.objects.get(
+ user=self.user, course_id=COURSE_TEST_ID, page_key=learning_content_key
+ )
+ self.assertEqual(db_entry.completion_status, "fail")
diff --git a/server/vbv_lernwelt/course/views.py b/server/vbv_lernwelt/course/views.py
index 16e4e5da..2a0ddace 100644
--- a/server/vbv_lernwelt/course/views.py
+++ b/server/vbv_lernwelt/course/views.py
@@ -10,11 +10,11 @@ from vbv_lernwelt.learnpath.utils import get_wagtail_type
logger = structlog.get_logger(__name__)
-@api_view(['GET'])
+@api_view(["GET"])
# @cache_page(60 * 60 * 8, cache="api_page_cache")
def page_api_view(request, slug):
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)
return Response(serializer.data)
except Exception as e:
@@ -22,7 +22,7 @@ def page_api_view(request, slug):
return Response({"error": str(e)}, status=404)
-@api_view(['GET'])
+@api_view(["GET"])
def request_course_completion(request, course_id):
response_data = CourseCompletionSerializer(
CourseCompletion.objects.filter(user=request.user, course_id=course_id),
@@ -32,12 +32,12 @@ def request_course_completion(request, course_id):
return Response(status=200, data=response_data)
-@api_view(['POST'])
+@api_view(["POST"])
def mark_course_completion(request):
- page_key = request.data.get('page_key')
- completion_status = request.data.get('completion_status', 'success')
+ page_key = request.data.get("page_key")
+ 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")
page_type = get_wagtail_type(page.specific)
course = CoursePage.objects.ancestor_of(page).first().specific.course
@@ -57,8 +57,8 @@ def mark_course_completion(request):
).data
logger.debug(
- 'mark_course_completion successful',
- label='completion_api',
+ "mark_course_completion successful",
+ label="completion_api",
page_key=page_key,
page_type=page_type,
page_slug=page.slug,
diff --git a/server/vbv_lernwelt/learnpath/apps.py b/server/vbv_lernwelt/learnpath/apps.py
index 384ba36a..568e9dff 100644
--- a/server/vbv_lernwelt/learnpath/apps.py
+++ b/server/vbv_lernwelt/learnpath/apps.py
@@ -2,8 +2,8 @@ from django.apps import AppConfig
class LearnpathConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'vbv_lernwelt.learnpath'
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "vbv_lernwelt.learnpath"
def ready(self):
try:
diff --git a/server/vbv_lernwelt/learnpath/create_default_learning_path.py b/server/vbv_lernwelt/learnpath/create_default_learning_path.py
index 8cf427fa..b5a13bf8 100644
--- a/server/vbv_lernwelt/learnpath/create_default_learning_path.py
+++ b/server/vbv_lernwelt/learnpath/create_default_learning_path.py
@@ -1,16 +1,29 @@
import wagtail_factories
from django.conf import settings
from django.core.management import call_command
-from wagtail.models import Site, Page, Locale
+from wagtail.models import Locale, Page, Site
from wagtail_localize.models import LocaleSynchronization
from vbv_lernwelt.core.admin import User
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
-from vbv_lernwelt.course.models import CoursePage, CourseCategory
-from vbv_lernwelt.learnpath.tests.learning_path_factories import LearningPathFactory, TopicFactory, CircleFactory, \
- LearningSequenceFactory, LearningContentFactory, VideoBlockFactory, ResourceBlockFactory, \
- ExerciseBlockFactory, DocumentBlockFactory, LearningUnitFactory, AssignmentBlockFactory, BookBlockFactory, \
- MediaLibraryBlockFactory, OnlineTrainingBlockFactory, TestBlockFactory
+from vbv_lernwelt.course.models import CourseCategory, CoursePage
+from vbv_lernwelt.learnpath.tests.learning_path_factories import (
+ AssignmentBlockFactory,
+ BookBlockFactory,
+ CircleFactory,
+ DocumentBlockFactory,
+ ExerciseBlockFactory,
+ LearningContentFactory,
+ LearningPathFactory,
+ LearningSequenceFactory,
+ LearningUnitFactory,
+ MediaLibraryBlockFactory,
+ OnlineTrainingBlockFactory,
+ ResourceBlockFactory,
+ TestBlockFactory,
+ TopicFactory,
+ VideoBlockFactory,
+)
def create_circle(title, learning_path):
@@ -24,310 +37,379 @@ def create_circle(title, learning_path):
Fachspezialisten bei.
""",
job_situations=[
- ('job_situation', 'Absicherung der Familie'),
- ('job_situation', 'Prämien einsparen'),
- ('job_situation', 'Deckung optimieren'),
- ('job_situation', 'Auto kaufen'),
- ('job_situation', 'Fahrzeugwechsel'),
- ('job_situation', 'Pensionerung inklusive Variante Frühpensionierung'),
- ('job_situation', 'Reisen'),
+ ("job_situation", "Absicherung der Familie"),
+ ("job_situation", "Prämien einsparen"),
+ ("job_situation", "Deckung optimieren"),
+ ("job_situation", "Auto kaufen"),
+ ("job_situation", "Fahrzeugwechsel"),
+ ("job_situation", "Pensionerung inklusive Variante Frühpensionierung"),
+ ("job_situation", "Reisen"),
],
goals=[
- ('goal', '... die heutige Versicherungssituation von Privat- oder Geschäftskunden einzuschätzen.'),
- ('goal', '... deinem Kunden einen ungenügenden oder übermässigen Versicherungsschutz aufzuzeigen.'),
- ('goal', '... deinem Kunden zu helfen, sein Optimierungspotential voll auszuschöpfen.'),
- ('goal', '... deinem Kunden seine optimale Lösung aufzuzeigen'),
+ (
+ "goal",
+ "... die heutige Versicherungssituation von Privat- oder Geschäftskunden einzuschätzen.",
+ ),
+ (
+ "goal",
+ "... deinem Kunden einen ungenügenden oder übermässigen Versicherungsschutz aufzuzeigen.",
+ ),
+ (
+ "goal",
+ "... deinem Kunden zu helfen, sein Optimierungspotential voll auszuschöpfen.",
+ ),
+ ("goal", "... deinem Kunden seine optimale Lösung aufzuzeigen"),
],
experts=[
- ('person', {'last_name': 'Huggel', 'first_name': 'Patrizia', 'email': 'patrizia.huggel@example.com'}),
- ]
+ (
+ "person",
+ {
+ "last_name": "Huggel",
+ "first_name": "Patrizia",
+ "email": "patrizia.huggel@example.com",
+ },
+ ),
+ ],
)
def create_circle_children(circle, title):
- LearningSequenceFactory(title='Starten', parent=circle, icon='it-icon-ls-start')
+ LearningSequenceFactory(title="Starten", parent=circle, icon="it-icon-ls-start")
LearningContentFactory(
title=f'Einleitung Circle "{title}"',
parent=circle,
minutes=15,
- contents=[('video', VideoBlockFactory(
- url='https://www.youtube.com/embed/qhPIfxS2hvI',
- description='In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich'
- 'den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.'
- ))]
+ contents=[
+ (
+ "video",
+ VideoBlockFactory(
+ url="https://www.youtube.com/embed/qhPIfxS2hvI",
+ description="In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich"
+ "den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.",
+ ),
+ )
+ ],
)
- LearningSequenceFactory(title='Beobachten', parent=circle, icon='it-icon-ls-watch')
+ LearningSequenceFactory(title="Beobachten", parent=circle, icon="it-icon-ls-watch")
lu = LearningUnitFactory(
- title='Absicherung der Familie',
+ title="Absicherung der Familie",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Einkommenssicherung')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Einkommenssicherung"
+ ),
)
LearningContentFactory(
- title='Ermittlung des Kundenbedarfs',
+ title="Ermittlung des Kundenbedarfs",
parent=circle,
minutes=30,
- contents=[('video', VideoBlockFactory(
- description='Die Ermittlung des Kundenbedarfs muss in einem eingehenden Gespräch herausgefunden werden. Höre dazu auch diesen Podcast an.',
- url='https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/325190984&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true',
- ))]
+ contents=[
+ (
+ "video",
+ VideoBlockFactory(
+ description="Die Ermittlung des Kundenbedarfs muss in einem eingehenden Gespräch herausgefunden werden. Höre dazu auch diesen Podcast an.",
+ url="https://w.soundcloud.com/player/?url=https%3A//api.soundcloud.com/tracks/325190984&color=%23ff5500&auto_play=false&hide_related=false&show_comments=true&show_user=true&show_reposts=false&show_teaser=true&visual=true",
+ ),
+ )
+ ],
)
- if circle.title == 'Einstieg':
+ if circle.title == "Einstieg":
LearningContentFactory(
- title='Einstieg ins Gespräch',
+ title="Einstieg ins Gespräch",
parent=circle,
minutes=15,
- contents=[('video', VideoBlockFactory(
- url='https://onedrive.live.com/embed?cid=26E4A934B79DCE5E&resid=26E4A934B79DCE5E%2153350&authkey=AId6i7z_X8l2fHw',
- description='In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich'
- 'den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.'
- ))]
+ contents=[
+ (
+ "video",
+ VideoBlockFactory(
+ url="https://onedrive.live.com/embed?cid=26E4A934B79DCE5E&resid=26E4A934B79DCE5E%2153350&authkey=AId6i7z_X8l2fHw",
+ description="In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich"
+ "den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.",
+ ),
+ )
+ ],
)
- if circle.title == 'Analyse':
+ if circle.title == "Analyse":
LearningContentFactory(
- title='Kundenanalyse',
+ title="Kundenanalyse",
parent=circle,
minutes=15,
- contents=[('video', VideoBlockFactory(
- url= "https://onedrive.live.com/embed?cid=26E4A934B79DCE5E&resid=26E4A934B79DCE5E%2153352&authkey=ABAdnARrD5VsIG8",
- description=''
- ))]
+ contents=[
+ (
+ "video",
+ VideoBlockFactory(
+ url="https://onedrive.live.com/embed?cid=26E4A934B79DCE5E&resid=26E4A934B79DCE5E%2153352&authkey=ABAdnARrD5VsIG8",
+ description="",
+ ),
+ )
+ ],
)
-
LearningContentFactory(
- title='Kundenbedürfnisse erkennen',
+ title="Kundenbedürfnisse erkennen",
parent=circle,
minutes=30,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
LearningContentFactory(
- title='Was braucht eine Familie?',
+ title="Was braucht eine Familie?",
parent=circle,
minutes=60,
- contents=[('exercise', ExerciseBlockFactory(url='/static/media/web_based_trainings/story-01-a-01-patrizia-marco-sichern-sich-ab-einstieg/scormcontent/index.html'
- ))]
-
+ contents=[
+ (
+ "exercise",
+ ExerciseBlockFactory(
+ url="/static/media/web_based_trainings/story-01-a-01-patrizia-marco-sichern-sich-ab-einstieg/scormcontent/index.html"
+ ),
+ )
+ ],
)
- LearningSequenceFactory(title='Anwenden', parent=circle, icon='it-icon-ls-apply')
+ LearningSequenceFactory(title="Anwenden", parent=circle, icon="it-icon-ls-apply")
lu = LearningUnitFactory(
- title='Prämien einsparen',
+ title="Prämien einsparen",
parent=circle,
)
LearningContentFactory(
- title='Versicherungsbedarf für Familien',
+ title="Versicherungsbedarf für Familien",
parent=circle,
minutes=60,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
LearningContentFactory(
- title='Alles klar?',
+ title="Alles klar?",
parent=circle,
minutes=60,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
lu = LearningUnitFactory(
- title='Sich selbständig machen',
+ title="Sich selbständig machen",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Selbständigkeit')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Selbständigkeit"
+ ),
)
LearningContentFactory(
- title='GmbH oder AG',
+ title="GmbH oder AG",
parent=circle,
minutes=120,
- contents=[('video', VideoBlockFactory())]
+ contents=[("video", VideoBlockFactory())],
)
LearningContentFactory(
- title='Tiertherapie Patrizia Feller',
+ title="Tiertherapie Patrizia Feller",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
lu = LearningUnitFactory(
- title='Auto verkaufen',
+ title="Auto verkaufen",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Fahrzeug')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Fahrzeug"
+ ),
)
LearningContentFactory(
- title='Motorfahrzeugversicherung',
+ title="Motorfahrzeugversicherung",
parent=circle,
minutes=240,
- contents=[('assignment', AssignmentBlockFactory())]
+ contents=[("assignment", AssignmentBlockFactory())],
)
LearningContentFactory(
- title='Nora kauft sich ein neues Auto',
+ title="Nora kauft sich ein neues Auto",
parent=circle,
minutes=60,
- contents=[('resource', ResourceBlockFactory())]
+ contents=[("resource", ResourceBlockFactory())],
)
LearningContentFactory(
- title='Ermittlung des Kundenbedarfs',
+ title="Ermittlung des Kundenbedarfs",
parent=circle,
minutes=120,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
LearningContentFactory(
- title='Motorfahrzeug kaufen',
+ title="Motorfahrzeug kaufen",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory(url='/static/media/web_based_trainings/training-04-a-01-rafael-fasel-wechselt-sein-auto-einstieg/scormcontent/index.html'))]
+ contents=[
+ (
+ "exercise",
+ ExerciseBlockFactory(
+ url="/static/media/web_based_trainings/training-04-a-01-rafael-fasel-wechselt-sein-auto-einstieg/scormcontent/index.html"
+ ),
+ )
+ ],
)
lu = LearningUnitFactory(
- title='Pensionierung',
+ title="Pensionierung",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Pensionierung')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Pensionierung"
+ ),
)
LearningContentFactory(
- title='3-Säulen-Prinzip',
+ title="3-Säulen-Prinzip",
parent=circle,
minutes=240,
- contents=[('test', TestBlockFactory())]
+ contents=[("test", TestBlockFactory())],
)
LearningContentFactory(
- title='Altersvorsorge',
+ title="Altersvorsorge",
parent=circle,
minutes=240,
- contents=[('test', TestBlockFactory())]
+ contents=[("test", TestBlockFactory())],
)
LearningContentFactory(
- title='AHV',
+ title="AHV",
parent=circle,
minutes=120,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
LearningContentFactory(
- title='Altersvorsorge planen',
+ title="Altersvorsorge planen",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
lu = LearningUnitFactory(
- title='Reisen',
+ title="Reisen",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Reisen')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Reisen"
+ ),
)
LearningContentFactory(
- title='Reiseversicherung',
+ title="Reiseversicherung",
parent=circle,
minutes=240,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
LearningContentFactory(
- title='Sorgenfrei reisen',
+ title="Sorgenfrei reisen",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory(
- url='/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html'))]
+ contents=[
+ (
+ "exercise",
+ ExerciseBlockFactory(
+ url="/static/media/web_based_trainings/story-06-a-01-emma-und-ayla-campen-durch-amerika-einstieg/scormcontent/index.html"
+ ),
+ )
+ ],
)
lu = LearningUnitFactory(
- title='Haushalt',
+ title="Haushalt",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Haushalt')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Haushalt"
+ ),
)
LearningContentFactory(
- title='Privathaftpflicht',
+ title="Privathaftpflicht",
parent=circle,
minutes=240,
- contents=[('media_library', MediaLibraryBlockFactory())]
+ contents=[("media_library", MediaLibraryBlockFactory())],
)
LearningContentFactory(
- title='Zusatzversicherung',
+ title="Zusatzversicherung",
parent=circle,
minutes=120,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
LearningContentFactory(
- title='Einen eigenen Haushalt führen',
+ title="Einen eigenen Haushalt führen",
parent=circle,
minutes=120,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
- LearningSequenceFactory(title='Üben', parent=circle, icon='it-icon-ls-practice')
+ LearningSequenceFactory(title="Üben", parent=circle, icon="it-icon-ls-practice")
lu = LearningUnitFactory(
- title='Kind zieht von zu Hause aus',
+ title="Kind zieht von zu Hause aus",
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Einkommenssicherung')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Einkommenssicherung"
+ ),
)
LearningContentFactory(
- title='Hausrat',
+ title="Hausrat",
parent=circle,
minutes=120,
- contents=[('online_training', OnlineTrainingBlockFactory())]
+ contents=[("online_training", OnlineTrainingBlockFactory())],
)
LearningContentFactory(
- title='Privathaftpflicht',
+ title="Privathaftpflicht",
parent=circle,
minutes=60,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
LearningContentFactory(
- title='Kind zieht von zu Hause wirklich aus',
+ title="Kind zieht von zu Hause wirklich aus",
parent=circle,
minutes=60,
- contents=[('exercise', ExerciseBlockFactory())]
+ contents=[("exercise", ExerciseBlockFactory())],
)
- LearningSequenceFactory(title='Testen', parent=circle, icon='it-icon-ls-test')
+ LearningSequenceFactory(title="Testen", parent=circle, icon="it-icon-ls-test")
lu = LearningUnitFactory(
title='Kind zieht von zu Hause aus "Testen"',
parent=circle,
- course_category=CourseCategory.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title='Einkommenssicherung')
+ course_category=CourseCategory.objects.get(
+ course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Einkommenssicherung"
+ ),
)
LearningContentFactory(
- title='Das erwartet dich im Test',
+ title="Das erwartet dich im Test",
parent=circle,
minutes=30,
- contents=[('test', TestBlockFactory())]
+ contents=[("test", TestBlockFactory())],
)
LearningContentFactory(
- title='Test durchführen',
+ title="Test durchführen",
parent=circle,
minutes=30,
- contents=[('test', TestBlockFactory())]
+ contents=[("test", TestBlockFactory())],
)
- LearningSequenceFactory(title='Vernetzen', parent=circle, icon='it-icon-ls-network')
+ LearningSequenceFactory(title="Vernetzen", parent=circle, icon="it-icon-ls-network")
LearningContentFactory(
- title='Online Training',
+ title="Online Training",
parent=circle,
minutes=60,
- contents=[('online_training', OnlineTrainingBlockFactory())]
+ contents=[("online_training", OnlineTrainingBlockFactory())],
)
- LearningSequenceFactory(title='Beenden', parent=circle, icon='it-icon-ls-end')
+ LearningSequenceFactory(title="Beenden", parent=circle, icon="it-icon-ls-end")
LearningContentFactory(
- title='Mehr darüber erfahren',
+ title="Mehr darüber erfahren",
parent=circle,
minutes=30,
- contents=[('book', BookBlockFactory())]
+ contents=[("book", BookBlockFactory())],
)
LearningContentFactory(
title='Circle "Analyse" abschliessen',
parent=circle,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
def create_default_learning_path(user=None, skip_locales=True):
if user is None:
- user = User.objects.get(username='info@iterativ.ch')
+ user = User.objects.get(username="info@iterativ.ch")
site = Site.objects.filter(is_default_site=True).first()
if not site:
site = wagtail_factories.SiteFactory(is_default_site=True)
- if settings.APP_ENVIRONMENT == 'development':
+ if settings.APP_ENVIRONMENT == "development":
site.port = 8000
site.save()
@@ -350,29 +432,36 @@ Versicherungsvermittler / in " aufgebaut ist. Zudem vermitteln wir dir die wicht
damit erfolgreich mit deinem Lernpfad starten kannst.
""",
)
- LearningSequenceFactory(title='Starten', parent=circle_basis, icon='it-icon-ls-start')
+ LearningSequenceFactory(
+ title="Starten", parent=circle_basis, icon="it-icon-ls-start"
+ )
LearningContentFactory(
title='Einleitung Circle "Basis"',
parent=circle_basis,
minutes=15,
- contents=[('video', VideoBlockFactory(
- url='https://www.youtube.com/embed/qhPIfxS2hvI',
- description='In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich'
- 'den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.'
- ))]
+ contents=[
+ (
+ "video",
+ VideoBlockFactory(
+ url="https://www.youtube.com/embed/qhPIfxS2hvI",
+ description="In dieser Circle zeigt dir ein Fachexperte anhand von Kundensituationen, wie du erfolgreich"
+ "den Kundenbedarf ermitteln, analysieren, priorisieren und anschliessend zusammenfassen kannst.",
+ ),
+ )
+ ],
)
- LearningSequenceFactory(title='Beenden', parent=circle_basis, icon='it-icon-ls-end')
+ LearningSequenceFactory(title="Beenden", parent=circle_basis, icon="it-icon-ls-end")
LearningContentFactory(
- title='Kompetenzprofil anschauen',
+ title="Kompetenzprofil anschauen",
parent=circle_basis,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
LearningContentFactory(
title='Circle "Analyse" abschliessen',
parent=circle_basis,
minutes=30,
- contents=[('document', DocumentBlockFactory())]
+ contents=[("document", DocumentBlockFactory())],
)
TopicFactory(title="Gewinnen von Kunden", parent=lp)
@@ -388,57 +477,69 @@ und Kunden auf Weiterempfehlung an. So nutzen sie ihre
bestehenden Kontakte geschickt für das Anwerben von
Neukundinnen und -kunden.""",
goals=[
- ('goal', '... Bestehende Kunden so zu beraten, dass sie von diesen weiterempfohlen werden'),
- ('goal',
- '... Geeignete Personen wie z.B. Garagisten, Architekten, Treuhänder auf die Vermittlung / Zusammenarbeit anzusprechen'),
- ('goal',
- '... Verschiedene Datenquellen wie Internet, Telefonbuch, Handelszeitung, Baugesuche etc. Gezielt für die Gewinnung von Neukunden zu benützen'),
- ('goal',
- '... Ein beliebiges Gespräch resp. Einen bestehenden Kontakt in die Richtung «Versicherung» zu lenken'),
- ('goal',
- '... Das Thema Risiko und Sicherheit in einem Gespräch gezielt und auf die Situation des jeweiligen Gesprächspartners bezogen einfliessen zu lassen'),
- ('goal', '... Im täglichen Kontakt potenzielle Kundinnen und Kunden zu erkennen'),
+ (
+ "goal",
+ "... Bestehende Kunden so zu beraten, dass sie von diesen weiterempfohlen werden",
+ ),
+ (
+ "goal",
+ "... Geeignete Personen wie z.B. Garagisten, Architekten, Treuhänder auf die Vermittlung / Zusammenarbeit anzusprechen",
+ ),
+ (
+ "goal",
+ "... Verschiedene Datenquellen wie Internet, Telefonbuch, Handelszeitung, Baugesuche etc. Gezielt für die Gewinnung von Neukunden zu benützen",
+ ),
+ (
+ "goal",
+ "... Ein beliebiges Gespräch resp. Einen bestehenden Kontakt in die Richtung «Versicherung» zu lenken",
+ ),
+ (
+ "goal",
+ "... Das Thema Risiko und Sicherheit in einem Gespräch gezielt und auf die Situation des jeweiligen Gesprächspartners bezogen einfliessen zu lassen",
+ ),
+ (
+ "goal",
+ "... Im täglichen Kontakt potenzielle Kundinnen und Kunden zu erkennen",
+ ),
],
)
- create_circle_children(circle_gewinnen, 'Gewinnen')
+ create_circle_children(circle_gewinnen, "Gewinnen")
TopicFactory(title="Beraten der Kunden", parent=lp)
- circle_einstieg = create_circle('Einstieg', lp)
- create_circle_children(circle_einstieg, 'Einstieg')
+ circle_einstieg = create_circle("Einstieg", lp)
+ create_circle_children(circle_einstieg, "Einstieg")
- circle_analyse = create_circle('Analyse', lp)
- create_circle_children(circle_analyse, 'Analyse')
+ circle_analyse = create_circle("Analyse", lp)
+ create_circle_children(circle_analyse, "Analyse")
- circle_analyse = create_circle('Lösung', lp)
- create_circle_children(circle_analyse, 'Lösung')
+ circle_analyse = create_circle("Lösung", lp)
+ create_circle_children(circle_analyse, "Lösung")
- circle_analyse = create_circle('Abschluss', lp)
- create_circle_children(circle_analyse, 'Abschluss')
+ circle_analyse = create_circle("Abschluss", lp)
+ create_circle_children(circle_analyse, "Abschluss")
TopicFactory(title="Betreuen und Ausbauen des Kundenstamms", parent=lp)
- circle_analyse = create_circle('Betreuen', lp)
- create_circle_children(circle_analyse, 'Betreuen')
+ circle_analyse = create_circle("Betreuen", lp)
+ create_circle_children(circle_analyse, "Betreuen")
TopicFactory(title="Prüfung", is_visible=True, parent=lp)
- circle_analyse = create_circle('Prüfungsvorbereitung', lp)
- create_circle_children(circle_analyse, 'Prüfungsvorbereitung')
+ circle_analyse = create_circle("Prüfungsvorbereitung", lp)
+ create_circle_children(circle_analyse, "Prüfungsvorbereitung")
# locales
if not skip_locales:
- locale_de = Locale.objects.get(language_code='de-CH')
- locale_fr, _ = Locale.objects.get_or_create(language_code='fr-CH')
+ locale_de = Locale.objects.get(language_code="de-CH")
+ locale_fr, _ = Locale.objects.get_or_create(language_code="fr-CH")
LocaleSynchronization.objects.get_or_create(
- locale_id=locale_fr.id,
- sync_from_id=locale_de.id
+ locale_id=locale_fr.id, sync_from_id=locale_de.id
)
- locale_it, _ = Locale.objects.get_or_create(language_code='it-CH')
+ locale_it, _ = Locale.objects.get_or_create(language_code="it-CH")
LocaleSynchronization.objects.get_or_create(
- locale_id=locale_it.id,
- sync_from_id=locale_de.id
+ locale_id=locale_it.id, sync_from_id=locale_de.id
)
- call_command('sync_locale_trees')
+ call_command("sync_locale_trees")
# all pages belong to 'admin' by default
Page.objects.update(owner=user)
diff --git a/server/vbv_lernwelt/learnpath/migrations/0001_initial.py b/server/vbv_lernwelt/learnpath/migrations/0001_initial.py
index 26a8d3d6..4d031bed 100644
--- a/server/vbv_lernwelt/learnpath/migrations/0001_initial.py
+++ b/server/vbv_lernwelt/learnpath/migrations/0001_initial.py
@@ -1,10 +1,10 @@
# Generated by Django 3.2.13 on 2022-09-28 12:51
-from django.db import migrations, models
import django.db.models.deletion
import wagtail.blocks
import wagtail.fields
import wagtail.images.blocks
+from django.db import migrations, models
class Migration(migrations.Migration):
@@ -12,78 +12,274 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('course', '0001_initial'),
- ('wagtailcore', '0069_log_entry_jsonfield'),
+ ("course", "0001_initial"),
+ ("wagtailcore", "0069_log_entry_jsonfield"),
]
operations = [
migrations.CreateModel(
- name='Circle',
+ name="Circle",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('description', models.TextField(blank=True, default='')),
- ('goals', wagtail.fields.StreamField([('goal', wagtail.blocks.TextBlock())], use_json_field=True)),
- ('job_situations', wagtail.fields.StreamField([('job_situation', wagtail.blocks.CharBlock())], use_json_field=True)),
- ('experts', wagtail.fields.StreamField([('person', wagtail.blocks.StructBlock([('first_name', wagtail.blocks.CharBlock()), ('last_name', wagtail.blocks.CharBlock()), ('email', wagtail.blocks.EmailBlock()), ('photo', wagtail.images.blocks.ImageChooserBlock(required=False)), ('biography', wagtail.blocks.RichTextBlock(required=False))]))], use_json_field=True)),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("description", models.TextField(blank=True, default="")),
+ (
+ "goals",
+ wagtail.fields.StreamField(
+ [("goal", wagtail.blocks.TextBlock())], use_json_field=True
+ ),
+ ),
+ (
+ "job_situations",
+ wagtail.fields.StreamField(
+ [("job_situation", wagtail.blocks.CharBlock())],
+ use_json_field=True,
+ ),
+ ),
+ (
+ "experts",
+ wagtail.fields.StreamField(
+ [
+ (
+ "person",
+ wagtail.blocks.StructBlock(
+ [
+ ("first_name", wagtail.blocks.CharBlock()),
+ ("last_name", wagtail.blocks.CharBlock()),
+ ("email", wagtail.blocks.EmailBlock()),
+ (
+ "photo",
+ wagtail.images.blocks.ImageChooserBlock(
+ required=False
+ ),
+ ),
+ (
+ "biography",
+ wagtail.blocks.RichTextBlock(
+ required=False
+ ),
+ ),
+ ]
+ ),
+ )
+ ],
+ use_json_field=True,
+ ),
+ ),
],
options={
- 'verbose_name': 'Circle',
+ "verbose_name": "Circle",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='LearningContent',
+ name="LearningContent",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('minutes', models.PositiveIntegerField(default=15)),
- ('contents', wagtail.fields.StreamField([('video', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('resource', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('exercise', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('online_training', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('media_library', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('document', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('test', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('book', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())])), ('assignment', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock()), ('url', wagtail.blocks.URLBlock())]))], use_json_field=None)),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("minutes", models.PositiveIntegerField(default=15)),
+ (
+ "contents",
+ wagtail.fields.StreamField(
+ [
+ (
+ "video",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "resource",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "exercise",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "online_training",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "media_library",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "document",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "test",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "book",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ (
+ "assignment",
+ wagtail.blocks.StructBlock(
+ [
+ ("description", wagtail.blocks.TextBlock()),
+ ("url", wagtail.blocks.URLBlock()),
+ ]
+ ),
+ ),
+ ],
+ use_json_field=None,
+ ),
+ ),
],
options={
- 'verbose_name': 'Learning Content',
+ "verbose_name": "Learning Content",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='LearningPath',
+ name="LearningPath",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
],
options={
- 'verbose_name': 'Learning Path',
+ "verbose_name": "Learning Path",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='LearningSequence',
+ name="LearningSequence",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('icon', models.CharField(default='it-icon-ls-start', max_length=255)),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("icon", models.CharField(default="it-icon-ls-start", max_length=255)),
],
options={
- 'verbose_name': 'Learning Sequence',
+ "verbose_name": "Learning Sequence",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='Topic',
+ name="Topic",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('is_visible', models.BooleanField(default=True)),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("is_visible", models.BooleanField(default=True)),
],
options={
- 'verbose_name': 'Topic',
+ "verbose_name": "Topic",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='LearningUnit',
+ name="LearningUnit",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('course_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='course.coursecategory')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ (
+ "course_category",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="course.coursecategory",
+ ),
+ ),
],
options={
- 'verbose_name': 'Learning Unit',
+ "verbose_name": "Learning Unit",
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
]
diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py
index 0944d7a9..ac9e50fa 100644
--- a/server/vbv_lernwelt/learnpath/models.py
+++ b/server/vbv_lernwelt/learnpath/models.py
@@ -9,22 +9,32 @@ from wagtail.models import Page
from vbv_lernwelt.core.model_utils import find_available_slug
from vbv_lernwelt.course.models import CoursePage
-from vbv_lernwelt.learnpath.models_learning_unit_content import VideoBlock, \
- ExerciseBlock, DocumentBlock, AssignmentBlock, BookBlock, MediaLibraryBlock, \
- OnlineTrainingBlock, ResourceBlock, TestBlock
+from vbv_lernwelt.learnpath.models_learning_unit_content import (
+ AssignmentBlock,
+ BookBlock,
+ DocumentBlock,
+ ExerciseBlock,
+ MediaLibraryBlock,
+ OnlineTrainingBlock,
+ ResourceBlock,
+ TestBlock,
+ VideoBlock,
+)
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
class LearningPath(Page):
content_panels = Page.content_panels
- subpage_types = ['learnpath.Circle', 'learnpath.Topic']
- parent_page_types = ['course.CoursePage']
+ subpage_types = ["learnpath.Circle", "learnpath.Topic"]
+ parent_page_types = ["course.CoursePage"]
class Meta:
verbose_name = "Learning Path"
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(slugify(f"{self.get_parent().slug}-lp", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(f"{self.get_parent().slug}-lp", allow_unicode=True)
+ )
super(LearningPath, self).full_clean(*args, **kwargs)
def __str__(self):
@@ -33,7 +43,8 @@ class LearningPath(Page):
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, ['id', 'title', 'slug', 'type', 'translation_key', 'children', 'course']
+ cls,
+ ["id", "title", "slug", "type", "translation_key", "children", "course"],
)
@@ -41,11 +52,12 @@ class Topic(Page):
# title = models.TextField(default='')
is_visible = models.BooleanField(default=True)
- parent_page_types = ['learnpath.LearningPath']
+ parent_page_types = ["learnpath.LearningPath"]
- panels = [FieldPanel('title'),
- FieldPanel('is_visible'),
- ]
+ panels = [
+ FieldPanel("title"),
+ FieldPanel("is_visible"),
+ ]
# content_panels = Page.content_panels + [
# FieldPanel('is_visible', classname="full"),
@@ -56,13 +68,21 @@ class Topic(Page):
# subpage_types = ['learnpath.Circle']
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)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'is_visible', ]
+ cls,
+ field_names=[
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "is_visible",
+ ],
)
class Meta:
@@ -80,29 +100,42 @@ class PersonBlock(blocks.StructBlock):
biography = blocks.RichTextBlock(required=False)
class Meta:
- icon = 'user'
+ icon = "user"
class Circle(Page):
description = models.TextField(default="", blank=True)
- goals = StreamField([
- ('goal', blocks.TextBlock()),
- ], use_json_field=True)
- job_situations = StreamField([
- ('job_situation', blocks.CharBlock()),
- ], use_json_field=True)
- experts = StreamField([
- ('person', PersonBlock()),
- ], use_json_field=True)
+ goals = StreamField(
+ [
+ ("goal", blocks.TextBlock()),
+ ],
+ use_json_field=True,
+ )
+ job_situations = StreamField(
+ [
+ ("job_situation", blocks.CharBlock()),
+ ],
+ use_json_field=True,
+ )
+ experts = StreamField(
+ [
+ ("person", PersonBlock()),
+ ],
+ use_json_field=True,
+ )
- parent_page_types = ['learnpath.LearningPath']
- subpage_types = ['learnpath.LearningSequence', 'learnpath.LearningUnit', 'learnpath.LearningContent']
+ parent_page_types = ["learnpath.LearningPath"]
+ subpage_types = [
+ "learnpath.LearningSequence",
+ "learnpath.LearningUnit",
+ "learnpath.LearningContent",
+ ]
content_panels = Page.content_panels + [
- FieldPanel('description'),
- FieldPanel('goals'),
- FieldPanel('job_situations'),
- FieldPanel('experts'),
+ FieldPanel("description"),
+ FieldPanel("goals"),
+ FieldPanel("job_situations"),
+ FieldPanel("experts"),
]
@classmethod
@@ -110,13 +143,21 @@ class Circle(Page):
return get_it_serializer_class(
cls,
field_names=[
- 'id', 'title', 'slug', 'type', 'translation_key', 'children',
- 'description', 'job_situations', 'goals', 'experts',
- ]
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "children",
+ "description",
+ "job_situations",
+ "goals",
+ "experts",
+ ],
)
def full_clean(self, *args, **kwargs):
- self.slug = find_slug_with_parent_prefix(self, 'circle')
+ self.slug = find_slug_with_parent_prefix(self, "circle")
super(Circle, self).full_clean(*args, **kwargs)
class Meta:
@@ -127,13 +168,13 @@ class Circle(Page):
class LearningSequence(Page):
- parent_page_types = ['learnpath.Circle']
+ parent_page_types = ["learnpath.Circle"]
subpage_types = []
icon = models.CharField(max_length=255, default="it-icon-ls-start")
content_panels = Page.content_panels + [
- FieldPanel('icon'),
+ FieldPanel("icon"),
]
class Meta:
@@ -144,30 +185,34 @@ class LearningSequence(Page):
@classmethod
def get_serializer_class(cls):
- return get_it_serializer_class(cls, field_names=['id', 'title', 'slug', 'type', 'translation_key', 'icon'])
+ return get_it_serializer_class(
+ cls, field_names=["id", "title", "slug", "type", "translation_key", "icon"]
+ )
def get_admin_display_title(self):
- return f'{self.icon} {self.draft_title}'
+ return f"{self.icon} {self.draft_title}"
def get_admin_display_title_html(self):
- return f'''
+ return f"""
<{self.icon} style="height: 32px; width: 32px;">{self.icon}>
{self.draft_title}
- '''
+ """
def full_clean(self, *args, **kwargs):
- self.slug = find_slug_with_parent_prefix(self, 'ls')
+ self.slug = find_slug_with_parent_prefix(self, "ls")
super(LearningSequence, self).full_clean(*args, **kwargs)
class LearningUnit(Page):
- parent_page_types = ['learnpath.Circle']
+ parent_page_types = ["learnpath.Circle"]
subpage_types = []
- course_category = models.ForeignKey('course.CourseCategory', on_delete=models.SET_NULL, null=True, blank=True)
+ course_category = models.ForeignKey(
+ "course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True
+ )
content_panels = Page.content_panels + [
- FieldPanel('course_category'),
+ FieldPanel("course_category"),
]
class Meta:
@@ -183,17 +228,22 @@ class LearningUnit(Page):
course = course_parent_page.specific.course
if self.course_category is None and course:
- self.course_category = course.coursecategory_set.filter(general=True).first()
+ self.course_category = course.coursecategory_set.filter(
+ general=True
+ ).first()
if self.course_category.general:
- self.slug = find_slug_with_parent_prefix(self, 'lu')
+ self.slug = find_slug_with_parent_prefix(self, "lu")
else:
- self.slug = find_slug_with_parent_prefix(self, 'lu', self.course_category.title)
+ self.slug = find_slug_with_parent_prefix(
+ self, "lu", self.course_category.title
+ )
super(LearningUnit, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
from vbv_lernwelt.learnpath.serializers import LearningUnitSerializer
+
return LearningUnitSerializer
def get_admin_display_title_html(self):
@@ -201,21 +251,21 @@ class LearningUnit(Page):
class LearningContent(Page):
- parent_page_types = ['learnpath.Circle']
+ parent_page_types = ["learnpath.Circle"]
subpage_types = []
minutes = models.PositiveIntegerField(default=15)
content_blocks = [
- ('video', VideoBlock()),
- ('resource', ResourceBlock()),
- ('exercise', ExerciseBlock()),
- ('online_training', OnlineTrainingBlock()),
- ('media_library', MediaLibraryBlock()),
- ('document', DocumentBlock()),
- ('test', TestBlock()),
- ('book', BookBlock()),
- ('assignment', AssignmentBlock()),
+ ("video", VideoBlock()),
+ ("resource", ResourceBlock()),
+ ("exercise", ExerciseBlock()),
+ ("online_training", OnlineTrainingBlock()),
+ ("media_library", MediaLibraryBlock()),
+ ("document", DocumentBlock()),
+ ("test", TestBlock()),
+ ("book", BookBlock()),
+ ("assignment", AssignmentBlock()),
]
contents = StreamField(
@@ -223,40 +273,49 @@ class LearningContent(Page):
)
content_panels = [
- FieldPanel('title', classname="full title"),
- FieldPanel('minutes'),
- StreamFieldPanel('contents'),
+ FieldPanel("title", classname="full title"),
+ FieldPanel("minutes"),
+ StreamFieldPanel("contents"),
]
def get_admin_display_title(self):
- display_title = ''
+ display_title = ""
if len(self.contents) > 0:
- display_title += f'{self.contents[0].block_type.capitalize()}: '
+ display_title += f"{self.contents[0].block_type.capitalize()}: "
display_title += self.draft_title
return display_title
def get_admin_display_title_html(self):
- return f'''
+ return f"""
{self.get_admin_display_title()}
- '''
+ """
class Meta:
verbose_name = "Learning Content"
def full_clean(self, *args, **kwargs):
- 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)
@classmethod
def get_serializer_class(cls):
- return get_it_serializer_class(cls,
- field_names=['id', 'title', 'slug', 'type',
- 'translation_key', 'minutes', 'contents'])
+ return get_it_serializer_class(
+ cls,
+ field_names=[
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "minutes",
+ "contents",
+ ],
+ )
def __str__(self):
return f"{self.title}"
@@ -272,4 +331,6 @@ def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None):
if slug_postfix is None:
slug_postfix = page.title
- return find_available_slug(slugify(f'{slug_prefix}-{slug_postfix}', allow_unicode=True))
+ return find_available_slug(
+ slugify(f"{slug_prefix}-{slug_postfix}", allow_unicode=True)
+ )
diff --git a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py
index 3fd0f6d1..cb3148f7 100644
--- a/server/vbv_lernwelt/learnpath/models_learning_unit_content.py
+++ b/server/vbv_lernwelt/learnpath/models_learning_unit_content.py
@@ -1,60 +1,69 @@
from wagtail import blocks
+
class AssignmentBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class BookBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class DocumentBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class ExerciseBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class MediaLibraryBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class OnlineTrainingBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class ResourceBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class TestBlock(blocks.StructBlock):
description = blocks.TextBlock()
url = blocks.URLBlock()
class Meta:
- icon = 'media'
+ icon = "media"
+
class VideoBlock(blocks.StructBlock):
# TODO: Possible video Types for the user, upload file, add URL
@@ -62,4 +71,4 @@ class VideoBlock(blocks.StructBlock):
url = blocks.URLBlock()
class Meta:
- icon = 'media'
\ No newline at end of file
+ icon = "media"
diff --git a/server/vbv_lernwelt/learnpath/serializer_helpers.py b/server/vbv_lernwelt/learnpath/serializer_helpers.py
index 428135b5..e59b644d 100644
--- a/server/vbv_lernwelt/learnpath/serializer_helpers.py
+++ b/server/vbv_lernwelt/learnpath/serializer_helpers.py
@@ -7,7 +7,9 @@ from vbv_lernwelt.learnpath.utils import get_wagtail_type
def get_it_serializer_class(model, field_names):
- return wagtail_serializers.get_serializer_class(model, field_names=field_names, meta_fields=[], base=ItBaseSerializer)
+ return wagtail_serializers.get_serializer_class(
+ model, field_names=field_names, meta_fields=[], base=ItBaseSerializer
+ )
class ItTypeField(wagtail_serializers.TypeField):
@@ -25,23 +27,28 @@ class ItBaseSerializer(wagtail_serializers.BaseSerializer):
meta_fields = []
def __init__(self, *args, **kwargs):
- self.descendants = kwargs.pop('descendants', None)
+ self.descendants = kwargs.pop("descendants", None)
super().__init__(*args, **kwargs)
def get_children(self, obj):
if not self.descendants:
self.descendants = [p for p in obj.get_descendants().specific()]
children = _get_children(self.descendants, obj)
- return [c.specific.get_serializer_class()(c.specific, descendants=self.descendants).data for c in children]
+ return [
+ c.specific.get_serializer_class()(
+ c.specific, descendants=self.descendants
+ ).data
+ for c in children
+ ]
def get_course(self, obj):
- if hasattr(obj, 'course'):
+ 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 ''
+ return ""
def _get_descendants(pages, obj):
@@ -49,4 +56,6 @@ def _get_descendants(pages, obj):
def _get_children(pages, obj):
- return [c for c in pages if c.path.startswith(obj.path) and obj.depth + 1 == c.depth]
+ return [
+ c for c in pages if c.path.startswith(obj.path) and obj.depth + 1 == c.depth
+ ]
diff --git a/server/vbv_lernwelt/learnpath/serializers.py b/server/vbv_lernwelt/learnpath/serializers.py
index 7225b990..58eb2673 100644
--- a/server/vbv_lernwelt/learnpath/serializers.py
+++ b/server/vbv_lernwelt/learnpath/serializers.py
@@ -1,12 +1,24 @@
-from vbv_lernwelt.competence.serializers import PerformanceCriteriaLearningPathSerializer
+from vbv_lernwelt.competence.serializers import (
+ PerformanceCriteriaLearningPathSerializer,
+)
from vbv_lernwelt.learnpath.models import LearningUnit
from vbv_lernwelt.learnpath.serializer_helpers import get_it_serializer_class
-class LearningUnitSerializer(get_it_serializer_class(LearningUnit, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'course_category', 'children',
-])):
+class LearningUnitSerializer(
+ get_it_serializer_class(
+ LearningUnit,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "course_category",
+ "children",
+ ],
+ )
+):
def get_children(self, obj):
return [
PerformanceCriteriaLearningPathSerializer(child).data
diff --git a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
index 2a817a22..ff3beb3f 100644
--- a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
+++ b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py
@@ -1,8 +1,24 @@
import wagtail_factories
-from vbv_lernwelt.learnpath.models import LearningPath, Topic, Circle, LearningSequence, LearningContent, LearningUnit
-from vbv_lernwelt.learnpath.models_learning_unit_content import VideoBlock, OnlineTrainingBlock, \
- ExerciseBlock, DocumentBlock, ResourceBlock, TestBlock, BookBlock, MediaLibraryBlock, AssignmentBlock
+from vbv_lernwelt.learnpath.models import (
+ Circle,
+ LearningContent,
+ LearningPath,
+ LearningSequence,
+ LearningUnit,
+ Topic,
+)
+from vbv_lernwelt.learnpath.models_learning_unit_content import (
+ AssignmentBlock,
+ BookBlock,
+ DocumentBlock,
+ ExerciseBlock,
+ MediaLibraryBlock,
+ OnlineTrainingBlock,
+ ResourceBlock,
+ TestBlock,
+ VideoBlock,
+)
class LearningPathFactory(wagtail_factories.PageFactory):
@@ -35,14 +51,14 @@ class LearningSequenceFactory(wagtail_factories.PageFactory):
class LearningUnitFactory(wagtail_factories.PageFactory):
- title = 'Lerneinheit'
+ title = "Lerneinheit"
class Meta:
model = LearningUnit
class LearningContentFactory(wagtail_factories.PageFactory):
- title = 'Lerninhalt'
+ title = "Lerninhalt"
class Meta:
model = LearningContent
diff --git a/server/vbv_lernwelt/learnpath/tests/test_api.py b/server/vbv_lernwelt/learnpath/tests/test_api.py
index 41b5821b..a6f053d5 100644
--- a/server/vbv_lernwelt/learnpath/tests/test_api.py
+++ b/server/vbv_lernwelt/learnpath/tests/test_api.py
@@ -10,19 +10,19 @@ class TestRetrieveLearingPathContents(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course()
- self.user = User.objects.get(username='student')
- self.client.login(username='student', password='test')
+ self.user = User.objects.get(username="student")
+ self.client.login(username="student", password="test")
def test_get_learnpathPage(self):
- slug = 'test-lehrgang-lp'
+ slug = "test-lehrgang-lp"
learning_path = LearningPath.objects.get(slug=slug)
- response = self.client.get(f'/api/course/page/{slug}/')
+ response = self.client.get(f"/api/course/page/{slug}/")
self.assertEqual(response.status_code, 200)
data = response.json()
- self.assertEqual(learning_path.title, data['title'])
+ self.assertEqual(learning_path.title, data["title"])
# topics and circles
- self.assertEqual(4, len(data['children']))
+ self.assertEqual(4, len(data["children"]))
# circle "analyse" contents
- self.assertEqual(12, len(data['children'][3]['children']))
+ self.assertEqual(12, len(data["children"][3]["children"]))
diff --git a/server/vbv_lernwelt/learnpath/utils.py b/server/vbv_lernwelt/learnpath/utils.py
index a4e77eee..f5cbf0ad 100644
--- a/server/vbv_lernwelt/learnpath/utils.py
+++ b/server/vbv_lernwelt/learnpath/utils.py
@@ -1,2 +1,2 @@
def get_wagtail_type(obj):
- return obj._meta.app_label + '.' + type(obj).__name__
+ return obj._meta.app_label + "." + type(obj).__name__
diff --git a/server/vbv_lernwelt/media_library/apps.py b/server/vbv_lernwelt/media_library/apps.py
index 8aa00356..35304242 100644
--- a/server/vbv_lernwelt/media_library/apps.py
+++ b/server/vbv_lernwelt/media_library/apps.py
@@ -2,9 +2,8 @@ from django.apps import AppConfig
class MediaLibraryConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'vbv_lernwelt.media_library'
-
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "vbv_lernwelt.media_library"
#
# default_auto_field = 'django.db.models.BigAutoField'
diff --git a/server/vbv_lernwelt/media_library/content_blocks.py b/server/vbv_lernwelt/media_library/content_blocks.py
index 7b39272a..5e250259 100644
--- a/server/vbv_lernwelt/media_library/content_blocks.py
+++ b/server/vbv_lernwelt/media_library/content_blocks.py
@@ -14,9 +14,9 @@ class MediaLibraryContent(models.Model):
# _revisions = GenericRelation("wagtailcore.Revision", related_query_name="media_library_content")
panels = [
- FieldPanel('title'),
- FieldPanel('description'),
- FieldPanel('link_display_text'),
+ FieldPanel("title"),
+ FieldPanel("description"),
+ FieldPanel("link_display_text"),
]
@property
@@ -28,34 +28,38 @@ class AnchorBlock(blocks.PageChooserBlock):
"""
Verankerung im Lernpfad. Link to a Learning Content.
"""
- page_type = 'learnpath.LearningUnit'
+
+ page_type = "learnpath.LearningUnit"
class LinkBlock(blocks.StructBlock):
title = blocks.TextBlock(blank=False, null=False)
- description = blocks.TextBlock(default='')
- link_display_text = blocks.CharBlock(max_length=255, default='Link öffnen')
+ description = blocks.TextBlock(default="")
+ link_display_text = blocks.CharBlock(max_length=255, default="Link öffnen")
url = blocks.URLBlock()
class CrossReferenceBlock(blocks.StructBlock):
title = models.TextField(blank=False, null=False)
- description = blocks.TextBlock(default='')
- link_display_text = blocks.CharBlock(max_length=255, default='Link öffnen')
- category = blocks.PageChooserBlock(page_type='media_library.MediaCategoryPage')
+ description = blocks.TextBlock(default="")
+ link_display_text = blocks.CharBlock(max_length=255, default="Link öffnen")
+ category = blocks.PageChooserBlock(page_type="media_library.MediaCategoryPage")
class MediaContentCollection(blocks.StructBlock):
"""
Lernmedien, Links, Querverweise, Verankerung
"""
+
title = blocks.TextBlock()
- contents = blocks.StreamBlock([
- ('Links', LinkBlock()),
- ('Documents', DocumentChooserBlock()),
- ('Ankers', AnchorBlock()),
- ('CrossReference', CrossReferenceBlock())
- ])
+ contents = blocks.StreamBlock(
+ [
+ ("Links", LinkBlock()),
+ ("Documents", DocumentChooserBlock()),
+ ("Ankers", AnchorBlock()),
+ ("CrossReference", CrossReferenceBlock()),
+ ]
+ )
class Meta:
- icon = 'link'
+ icon = "link"
diff --git a/server/vbv_lernwelt/media_library/create_default_documents.py b/server/vbv_lernwelt/media_library/create_default_documents.py
index 93ee8b02..a8380206 100644
--- a/server/vbv_lernwelt/media_library/create_default_documents.py
+++ b/server/vbv_lernwelt/media_library/create_default_documents.py
@@ -5,13 +5,15 @@ from wagtail.core.models import Collection
from vbv_lernwelt.course.models import Course
from vbv_lernwelt.media_library.models import LibraryDocument
-from vbv_lernwelt.media_library.tests.media_library_factories import LibraryDocumentFactory
+from vbv_lernwelt.media_library.tests.media_library_factories import (
+ LibraryDocumentFactory,
+)
def create_default_collections():
c = Collection.objects.all().delete()
- root, created = Collection.objects.get_or_create(name='Root', depth=0)
+ root, created = Collection.objects.get_or_create(name="Root", depth=0)
for course in Course.objects.all():
course_collection = root.add_child(name=course.title)
@@ -22,26 +24,32 @@ def create_default_collections():
def create_default_documents():
LibraryDocument.objects.all().delete()
- path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '../static/media/documents/')
-
- collection = Collection.objects.get(name='Fahrzeug')
-
- filename = 'SchweizerischesZivilgesetzbuch.pdf'
- document = LibraryDocumentFactory(
- title='V1 C25 ZGB CH',
- display_text='Schweizerisches Zivilgesetzbuch',
- description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.',
- link_display_text='Dokument laden',
- file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
- collection=collection
+ path = os.path.join(
+ os.path.dirname(os.path.abspath(__file__)), "../static/media/documents/"
)
- filename = 'SmallPDF.pdf'
+ collection = Collection.objects.get(name="Fahrzeug")
+
+ filename = "SchweizerischesZivilgesetzbuch.pdf"
document = LibraryDocumentFactory(
- title='V1 C25 ',
- display_text='Pdf showcase ',
- description='Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.',
- link_display_text='Dokument laden',
- file=factory.django.FileField(from_path=os.path.join(path, filename), filename=filename),
- collection=collection
+ title="V1 C25 ZGB CH",
+ display_text="Schweizerisches Zivilgesetzbuch",
+ description="Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.",
+ link_display_text="Dokument laden",
+ file=factory.django.FileField(
+ from_path=os.path.join(path, filename), filename=filename
+ ),
+ collection=collection,
+ )
+
+ filename = "SmallPDF.pdf"
+ document = LibraryDocumentFactory(
+ title="V1 C25 ",
+ display_text="Pdf showcase ",
+ description="Ein wundervolles Dokument, Bachblüten für Leseratten und metaphysisches Wohlbefinden für Handyvekäufer.",
+ link_display_text="Dokument laden",
+ file=factory.django.FileField(
+ from_path=os.path.join(path, filename), filename=filename
+ ),
+ collection=collection,
)
diff --git a/server/vbv_lernwelt/media_library/create_default_media_library.py b/server/vbv_lernwelt/media_library/create_default_media_library.py
index 9d7ad336..5c74443e 100644
--- a/server/vbv_lernwelt/media_library/create_default_media_library.py
+++ b/server/vbv_lernwelt/media_library/create_default_media_library.py
@@ -1,9 +1,15 @@
import json
from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_ID
-from vbv_lernwelt.course.models import CoursePage, Course
-from vbv_lernwelt.media_library.tests.media_library_factories import MediaLibraryPageFactory, MediaCategoryPageFactory, \
- create_media_content_link, LinkBlockFactory, create_link_collection, create_document_collection
+from vbv_lernwelt.course.models import Course, CoursePage
+from vbv_lernwelt.media_library.tests.media_library_factories import (
+ create_document_collection,
+ create_link_collection,
+ create_media_content_link,
+ LinkBlockFactory,
+ MediaCategoryPageFactory,
+ MediaLibraryPageFactory,
+)
def create_default_media_library():
@@ -11,36 +17,52 @@ def create_default_media_library():
course_page = CoursePage.objects.get(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID)
media_lib_page = MediaLibraryPageFactory(
- title='Mediathek',
+ title="Mediathek",
parent=course_page,
)
- icons = ['icon-hf-fahrzeug', 'icon-hf-reisen', 'icon-hf-einkommenssicherung', ]
+ icons = [
+ "icon-hf-fahrzeug",
+ "icon-hf-reisen",
+ "icon-hf-einkommenssicherung",
+ ]
for idx, cat in enumerate(course.coursecategory_set.all()):
overview_icon = icons[(idx + 2) % len(icons)]
- introduction_text = '''
+ introduction_text = """
Das Auto ist für viele der grösste Stolz! Es birgt aber auch ein grosses Gefahrenpotenzial.
Dabei geht es bei den heutigen Fahrzeugpreisen und Reparaturkosten rasch um namhafte Summen,
-die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.'''.strip()
- description_title = 'Das erwartet dich in diesem Handlungsfeld'
- description_text = '''
+die der Fahrzeugbesitzer und die Fahrzeugbesitzerin in einem grösseren Schadenfall oft nur schwer selbst aufbringen kann.""".strip()
+ description_title = "Das erwartet dich in diesem Handlungsfeld"
+ description_text = """
In diesem berufstypischem Handlungsfeld lernst du alles rund um Motorfahrzeugversicherungen,
wie man sein Auto optimal schützen kann, wie du vorgehst bei einem Fahrzeugwechsel,
-welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.'''.strip()
+welche Aspekte du bei einer Offerte beachten musst und wie du dem Kunden die Lösung präsentierst.""".strip()
items = [
- ('item', 'Motorfahrzeughaftpflichtversicherung'),
- ('item', 'Motorfahrzeugkaskoversicherung'),
- ('item', 'Insassenunfallversicherung'),
+ ("item", "Motorfahrzeughaftpflichtversicherung"),
+ ("item", "Motorfahrzeugkaskoversicherung"),
+ ("item", "Insassenunfallversicherung"),
]
- body_data = json.dumps([
- create_document_collection(),
- create_link_collection(
- links_dict=[
- create_media_content_link(LinkBlockFactory(title='Nationales Versicherungsbüro', url='https://www.vbv.ch/')),
- create_media_content_link(LinkBlockFactory(title='Adressen der Strassenverkehrsämter', url='https://www.vbv.ch/')),
- ]
- )
- ])
+ body_data = json.dumps(
+ [
+ create_document_collection(),
+ create_link_collection(
+ links_dict=[
+ create_media_content_link(
+ LinkBlockFactory(
+ title="Nationales Versicherungsbüro",
+ url="https://www.vbv.ch/",
+ )
+ ),
+ create_media_content_link(
+ LinkBlockFactory(
+ title="Adressen der Strassenverkehrsämter",
+ url="https://www.vbv.ch/",
+ )
+ ),
+ ]
+ ),
+ ]
+ )
media_category = MediaCategoryPageFactory(
overview_icon=overview_icon,
title=cat.title,
diff --git a/server/vbv_lernwelt/media_library/migrations/0001_initial.py b/server/vbv_lernwelt/media_library/migrations/0001_initial.py
index 17122e3b..9f567f5b 100644
--- a/server/vbv_lernwelt/media_library/migrations/0001_initial.py
+++ b/server/vbv_lernwelt/media_library/migrations/0001_initial.py
@@ -1,15 +1,16 @@
# Generated by Django 3.2.13 on 2022-09-28 12:51
-from django.conf import settings
-from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
-import vbv_lernwelt.media_library.content_blocks
import wagtail.blocks
import wagtail.documents.blocks
import wagtail.fields
import wagtail.models.collections
import wagtail.search.index
+from django.conf import settings
+from django.db import migrations, models
+
+import vbv_lernwelt.media_library.content_blocks
class Migration(migrations.Migration):
@@ -17,70 +18,251 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
- ('course', '0001_initial'),
- ('wagtailcore', '0069_log_entry_jsonfield'),
+ ("course", "0001_initial"),
+ ("wagtailcore", "0069_log_entry_jsonfield"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('taggit', '0004_alter_taggeditem_content_type_alter_taggeditem_tag'),
+ ("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"),
]
operations = [
migrations.CreateModel(
- name='MediaLibraryContent',
+ name="MediaLibraryContent",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.TextField()),
- ('description', models.TextField()),
- ('link_display_text', models.CharField(max_length=255)),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("title", models.TextField()),
+ ("description", models.TextField()),
+ ("link_display_text", models.CharField(max_length=255)),
],
),
migrations.CreateModel(
- name='MediaLibraryPage',
+ name="MediaLibraryPage",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='MediaCategoryPage',
+ name="MediaCategoryPage",
fields=[
- ('page_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='wagtailcore.page')),
- ('introduction_text', models.TextField(default='')),
- ('description_title', models.TextField(default='Das erwartet dich in diesem Handlungsfeld')),
- ('description_text', models.TextField(default='')),
- ('items', wagtail.fields.StreamField([('item', wagtail.blocks.TextBlock())], use_json_field=True)),
- ('overview_icon', models.CharField(default='icon-hf-fahrzeug', max_length=255)),
- ('body', wagtail.fields.StreamField([('content_collection', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock()), ('contents', wagtail.blocks.StreamBlock([('Links', wagtail.blocks.StructBlock([('title', wagtail.blocks.TextBlock(blank=False, null=False)), ('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('url', wagtail.blocks.URLBlock())])), ('Documents', wagtail.documents.blocks.DocumentChooserBlock()), ('Ankers', vbv_lernwelt.media_library.content_blocks.AnchorBlock()), ('CrossReference', wagtail.blocks.StructBlock([('description', wagtail.blocks.TextBlock(default='')), ('link_display_text', wagtail.blocks.CharBlock(default='Link öffnen', max_length=255)), ('category', wagtail.blocks.PageChooserBlock(page_type=['media_library.MediaCategoryPage']))]))]))]))], null=True, use_json_field=True)),
- ('course_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='course.coursecategory')),
+ (
+ "page_ptr",
+ models.OneToOneField(
+ auto_created=True,
+ on_delete=django.db.models.deletion.CASCADE,
+ parent_link=True,
+ primary_key=True,
+ serialize=False,
+ to="wagtailcore.page",
+ ),
+ ),
+ ("introduction_text", models.TextField(default="")),
+ (
+ "description_title",
+ models.TextField(
+ default="Das erwartet dich in diesem Handlungsfeld"
+ ),
+ ),
+ ("description_text", models.TextField(default="")),
+ (
+ "items",
+ wagtail.fields.StreamField(
+ [("item", wagtail.blocks.TextBlock())], use_json_field=True
+ ),
+ ),
+ (
+ "overview_icon",
+ models.CharField(default="icon-hf-fahrzeug", max_length=255),
+ ),
+ (
+ "body",
+ wagtail.fields.StreamField(
+ [
+ (
+ "content_collection",
+ wagtail.blocks.StructBlock(
+ [
+ ("title", wagtail.blocks.TextBlock()),
+ (
+ "contents",
+ wagtail.blocks.StreamBlock(
+ [
+ (
+ "Links",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "title",
+ wagtail.blocks.TextBlock(
+ blank=False,
+ null=False,
+ ),
+ ),
+ (
+ "description",
+ wagtail.blocks.TextBlock(
+ default=""
+ ),
+ ),
+ (
+ "link_display_text",
+ wagtail.blocks.CharBlock(
+ default="Link öffnen",
+ max_length=255,
+ ),
+ ),
+ (
+ "url",
+ wagtail.blocks.URLBlock(),
+ ),
+ ]
+ ),
+ ),
+ (
+ "Documents",
+ wagtail.documents.blocks.DocumentChooserBlock(),
+ ),
+ (
+ "Ankers",
+ vbv_lernwelt.media_library.content_blocks.AnchorBlock(),
+ ),
+ (
+ "CrossReference",
+ wagtail.blocks.StructBlock(
+ [
+ (
+ "description",
+ wagtail.blocks.TextBlock(
+ default=""
+ ),
+ ),
+ (
+ "link_display_text",
+ wagtail.blocks.CharBlock(
+ default="Link öffnen",
+ max_length=255,
+ ),
+ ),
+ (
+ "category",
+ wagtail.blocks.PageChooserBlock(
+ page_type=[
+ "media_library.MediaCategoryPage"
+ ]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ]
+ ),
+ ),
+ ]
+ ),
+ )
+ ],
+ null=True,
+ use_json_field=True,
+ ),
+ ),
+ (
+ "course_category",
+ models.ForeignKey(
+ blank=True,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to="course.coursecategory",
+ ),
+ ),
],
options={
- 'abstract': False,
+ "abstract": False,
},
- bases=('wagtailcore.page',),
+ bases=("wagtailcore.page",),
),
migrations.CreateModel(
- name='LibraryDocument',
+ name="LibraryDocument",
fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=255, verbose_name='title')),
- ('file', models.FileField(upload_to='documents', verbose_name='file')),
- ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
- ('file_size', models.PositiveIntegerField(editable=False, null=True)),
- ('file_hash', models.CharField(blank=True, editable=False, max_length=40)),
- ('display_text', models.CharField(default='', max_length=1024)),
- ('description', models.TextField(default='')),
- ('link_display_text', models.CharField(default='', max_length=1024)),
- ('thumbnail', models.URLField()),
- ('collection', models.ForeignKey(default=wagtail.models.collections.get_root_collection_id, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='wagtailcore.collection', verbose_name='collection')),
- ('tags', taggit.managers.TaggableManager(blank=True, help_text=None, through='taggit.TaggedItem', to='taggit.Tag', verbose_name='tags')),
- ('uploaded_by_user', models.ForeignKey(blank=True, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='uploaded by user')),
+ (
+ "id",
+ models.BigAutoField(
+ auto_created=True,
+ primary_key=True,
+ serialize=False,
+ verbose_name="ID",
+ ),
+ ),
+ ("title", models.CharField(max_length=255, verbose_name="title")),
+ ("file", models.FileField(upload_to="documents", verbose_name="file")),
+ (
+ "created_at",
+ models.DateTimeField(auto_now_add=True, verbose_name="created at"),
+ ),
+ ("file_size", models.PositiveIntegerField(editable=False, null=True)),
+ (
+ "file_hash",
+ models.CharField(blank=True, editable=False, max_length=40),
+ ),
+ ("display_text", models.CharField(default="", max_length=1024)),
+ ("description", models.TextField(default="")),
+ ("link_display_text", models.CharField(default="", max_length=1024)),
+ ("thumbnail", models.URLField()),
+ (
+ "collection",
+ models.ForeignKey(
+ default=wagtail.models.collections.get_root_collection_id,
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="+",
+ to="wagtailcore.collection",
+ verbose_name="collection",
+ ),
+ ),
+ (
+ "tags",
+ taggit.managers.TaggableManager(
+ blank=True,
+ help_text=None,
+ through="taggit.TaggedItem",
+ to="taggit.Tag",
+ verbose_name="tags",
+ ),
+ ),
+ (
+ "uploaded_by_user",
+ models.ForeignKey(
+ blank=True,
+ editable=False,
+ null=True,
+ on_delete=django.db.models.deletion.SET_NULL,
+ to=settings.AUTH_USER_MODEL,
+ verbose_name="uploaded by user",
+ ),
+ ),
],
options={
- 'verbose_name': 'document',
- 'verbose_name_plural': 'documents',
- 'abstract': False,
+ "verbose_name": "document",
+ "verbose_name_plural": "documents",
+ "abstract": False,
},
bases=(wagtail.search.index.Indexed, models.Model),
),
diff --git a/server/vbv_lernwelt/media_library/models.py b/server/vbv_lernwelt/media_library/models.py
index dd21ab49..47e6f02f 100644
--- a/server/vbv_lernwelt/media_library/models.py
+++ b/server/vbv_lernwelt/media_library/models.py
@@ -1,6 +1,6 @@
from django.db import models
from django.utils.text import slugify
-from wagtail import fields, blocks
+from wagtail import blocks, fields
from wagtail.admin.panels import FieldPanel, StreamFieldPanel
from wagtail.documents.models import AbstractDocument, Document
from wagtail.fields import StreamField
@@ -12,25 +12,32 @@ from vbv_lernwelt.media_library.content_blocks import MediaContentCollection
class MediaLibraryPage(Page):
- parent_page_types = ['course.CoursePage']
- subpage_types = ['media_library.MediaCategoryPage']
+ parent_page_types = ["course.CoursePage"]
+ subpage_types = ["media_library.MediaCategoryPage"]
content_panels = [
- FieldPanel('title', classname="full title"),
+ FieldPanel("title", classname="full title"),
]
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(slugify(f"{self.get_parent().slug}-media", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(f"{self.get_parent().slug}-media", allow_unicode=True)
+ )
super(MediaLibraryPage, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, [
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'course',
- 'children',
- ]
+ cls,
+ [
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "course",
+ "children",
+ ],
)
@@ -38,45 +45,65 @@ class MediaCategoryPage(Page):
"""
Handlungsfeld. zB. Fahrzeug
"""
- course_category = models.ForeignKey('course.CourseCategory', on_delete=models.SET_NULL, null=True, blank=True)
- parent_page_types = ['media_library.MediaLibraryPage']
- introduction_text = models.TextField(default='')
- description_title = models.TextField(default='Das erwartet dich in diesem Handlungsfeld')
- description_text = models.TextField(default='')
- items = StreamField([
- ('item', blocks.TextBlock()),
- ], use_json_field=True)
+
+ course_category = models.ForeignKey(
+ "course.CourseCategory", on_delete=models.SET_NULL, null=True, blank=True
+ )
+ parent_page_types = ["media_library.MediaLibraryPage"]
+ introduction_text = models.TextField(default="")
+ description_title = models.TextField(
+ default="Das erwartet dich in diesem Handlungsfeld"
+ )
+ description_text = models.TextField(default="")
+ items = StreamField(
+ [
+ ("item", blocks.TextBlock()),
+ ],
+ use_json_field=True,
+ )
overview_icon = models.CharField(max_length=255, default="icon-hf-fahrzeug")
body = fields.StreamField(
- [('content_collection', MediaContentCollection())],
+ [("content_collection", MediaContentCollection())],
use_json_field=True,
- null=True
+ null=True,
)
content_panels = [
- FieldPanel('title'),
- FieldPanel('course_category'),
- FieldPanel('introduction_text'),
- FieldPanel('description_title'),
- FieldPanel('description_text'),
- FieldPanel('items'),
- StreamFieldPanel('body')
+ FieldPanel("title"),
+ FieldPanel("course_category"),
+ FieldPanel("introduction_text"),
+ FieldPanel("description_title"),
+ FieldPanel("description_text"),
+ FieldPanel("items"),
+ StreamFieldPanel("body"),
]
def full_clean(self, *args, **kwargs):
- self.slug = find_available_slug(slugify(f"{self.get_parent()}-cat-{self.title}", allow_unicode=True))
+ self.slug = find_available_slug(
+ slugify(f"{self.get_parent()}-cat-{self.title}", allow_unicode=True)
+ )
super(MediaCategoryPage, self).full_clean(*args, **kwargs)
@classmethod
def get_serializer_class(cls):
return get_it_serializer_class(
- cls, field_names=[
- 'id', 'title', 'slug', 'type', 'translation_key',
- 'course_category',
- 'introduction_text', 'overview_icon', 'description_title', 'description_text', 'items', 'body',
- ]
+ cls,
+ field_names=[
+ "id",
+ "title",
+ "slug",
+ "type",
+ "translation_key",
+ "course_category",
+ "introduction_text",
+ "overview_icon",
+ "description_title",
+ "description_text",
+ "items",
+ "body",
+ ],
)
@@ -84,11 +111,14 @@ class LibraryDocument(AbstractDocument):
# Todo: check https://filepreviews.io/
# Custom field example:
- display_text = models.CharField(max_length=1024, default='')
- description = models.TextField(default='')
- link_display_text = models.CharField(max_length=1024, default='')
+ display_text = models.CharField(max_length=1024, default="")
+ description = models.TextField(default="")
+ link_display_text = models.CharField(max_length=1024, default="")
thumbnail = models.URLField()
admin_form_fields = Document.admin_form_fields + (
- 'display_text', 'description', 'link_display_text', 'thumbnail'
+ "display_text",
+ "description",
+ "link_display_text",
+ "thumbnail",
)
diff --git a/server/vbv_lernwelt/media_library/test_data.py b/server/vbv_lernwelt/media_library/test_data.py
index 9e6e0951..ae29b5e3 100644
--- a/server/vbv_lernwelt/media_library/test_data.py
+++ b/server/vbv_lernwelt/media_library/test_data.py
@@ -7,6 +7,3 @@
#
# Created on 2022-08-16
# @author: lorenz.padberg@iterativ.ch
-
-
-
diff --git a/server/vbv_lernwelt/media_library/tests/media_library_factories.py b/server/vbv_lernwelt/media_library/tests/media_library_factories.py
index b10c168e..7819b6cb 100644
--- a/server/vbv_lernwelt/media_library/tests/media_library_factories.py
+++ b/server/vbv_lernwelt/media_library/tests/media_library_factories.py
@@ -2,21 +2,29 @@ import uuid
import wagtail_factories
-from vbv_lernwelt.media_library.content_blocks import MediaContentCollection, AnchorBlock, LinkBlock, \
- CrossReferenceBlock
-from vbv_lernwelt.media_library.models import LibraryDocument, MediaLibraryPage, MediaCategoryPage
+from vbv_lernwelt.media_library.content_blocks import (
+ AnchorBlock,
+ CrossReferenceBlock,
+ LinkBlock,
+ MediaContentCollection,
+)
+from vbv_lernwelt.media_library.models import (
+ LibraryDocument,
+ MediaCategoryPage,
+ MediaLibraryPage,
+)
class LibraryDocumentFactory(wagtail_factories.DocumentFactory):
- link_display_text = 'Dokument herunter laden'
- thumbnail = 'https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/thumbnails/image/file.jpg'
+ link_display_text = "Dokument herunter laden"
+ thumbnail = "https://d9-wret.s3.us-west-2.amazonaws.com/assets/palladium/production/s3fs-public/thumbnails/image/file.jpg"
class Meta:
model = LibraryDocument
class MediaLibraryPageFactory(wagtail_factories.PageFactory):
- title = 'Mediathek'
+ title = "Mediathek"
class Meta:
model = MediaLibraryPage
@@ -28,9 +36,9 @@ class AnchorBlockFactory(wagtail_factories.StructBlockFactory):
class LinkBlockFactory(wagtail_factories.StructBlockFactory):
- title = 'Interesting link'
- description = 'This link is really interesting...'
- url = 'https://www.vbv.ch/'
+ title = "Interesting link"
+ description = "This link is really interesting..."
+ url = "https://www.vbv.ch/"
class Meta:
model = LinkBlock
@@ -42,20 +50,19 @@ class CrossReferenceBlockFactory(wagtail_factories.StructBlockFactory):
class MediaContentCollectionFactory(wagtail_factories.StructBlockFactory):
- title = 'Links'
- contents = wagtail_factories.StreamFieldFactory({
- 'Links': LinkBlockFactory,
- 'Documents': LibraryDocumentFactory
- })
+ title = "Links"
+ contents = wagtail_factories.StreamFieldFactory(
+ {"Links": LinkBlockFactory, "Documents": LibraryDocumentFactory}
+ )
class Meta:
model = MediaContentCollection
class MediaCategoryPageFactory(wagtail_factories.PageFactory):
- title = 'Fahrzeug'
- introduction_text = 'Das Auto ist für viele der grösste Stolz! Es birgt aber ...'
- description_title = 'Das erwartet dich in diesem Handlungsfeld'
+ title = "Fahrzeug"
+ introduction_text = "Das Auto ist für viele der grösste Stolz! Es birgt aber ..."
+ description_title = "Das erwartet dich in diesem Handlungsfeld"
class Meta:
model = MediaCategoryPage
@@ -64,11 +71,7 @@ class MediaCategoryPageFactory(wagtail_factories.PageFactory):
def create_media_content_link(link_block=None):
if link_block is None:
link_block = LinkBlockFactory()
- return {
- "id": str(uuid.uuid4()),
- "type": "Links",
- "value": dict(link_block.items())
- }
+ return {"id": str(uuid.uuid4()), "type": "Links", "value": dict(link_block.items())}
def create_link_collection(links_dict=None):
@@ -77,8 +80,8 @@ def create_link_collection(links_dict=None):
"type": "content_collection",
"value": {
"title": "Links",
- "contents": [link_dict for link_dict in links_dict]
- }
+ "contents": [link_dict for link_dict in links_dict],
+ },
}
@@ -91,10 +94,9 @@ def create_document_collection(document_ids=None):
"type": "content_collection",
"value": {
"title": "Lernmedien",
- "contents": [{
- "id": str(uuid.uuid4()),
- "type": "Documents",
- "value": doc_id
- } for doc_id in document_ids]
- }
+ "contents": [
+ {"id": str(uuid.uuid4()), "type": "Documents", "value": doc_id}
+ for doc_id in document_ids
+ ],
+ },
}
diff --git a/server/vbv_lernwelt/media_library/tests/test_api.py b/server/vbv_lernwelt/media_library/tests/test_api.py
index f8b65482..5763ffdf 100644
--- a/server/vbv_lernwelt/media_library/tests/test_api.py
+++ b/server/vbv_lernwelt/media_library/tests/test_api.py
@@ -10,16 +10,16 @@ class MediaCenterAPITestCase(APITestCase):
def setUp(self) -> None:
create_default_users()
create_test_course()
- self.user = User.objects.get(username='student')
- self.client.login(username='student', password='test')
+ self.user = User.objects.get(username="student")
+ self.client.login(username="student", password="test")
def test_get_learnpathPage(self):
- slug = 'test-lehrgang-media'
+ slug = "test-lehrgang-media"
media_library = MediaLibraryPage.objects.get(slug=slug)
- response = self.client.get(f'/api/course/page/{slug}/')
+ response = self.client.get(f"/api/course/page/{slug}/")
self.assertEqual(response.status_code, 200)
data = response.json()
- self.assertEqual(media_library.title, data['title'])
- self.assertEqual('Fahrzeug', data['children'][1]['title'])
+ self.assertEqual(media_library.title, data["title"])
+ self.assertEqual("Fahrzeug", data["children"][1]["title"])
diff --git a/server/vbv_lernwelt/sso/apps.py b/server/vbv_lernwelt/sso/apps.py
index c14f09ef..dac416ca 100644
--- a/server/vbv_lernwelt/sso/apps.py
+++ b/server/vbv_lernwelt/sso/apps.py
@@ -2,5 +2,5 @@ from django.apps import AppConfig
class SsoConfig(AppConfig):
- default_auto_field = 'django.db.models.BigAutoField'
- name = 'vbv_lernwelt.sso'
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "vbv_lernwelt.sso"
diff --git a/server/vbv_lernwelt/sso/jwt.py b/server/vbv_lernwelt/sso/jwt.py
index 625022dd..c31462a6 100644
--- a/server/vbv_lernwelt/sso/jwt.py
+++ b/server/vbv_lernwelt/sso/jwt.py
@@ -1,17 +1,17 @@
-import logging
import base64
import json
+import logging
logger = logging.getLogger(__name__)
def decode_jwt(jwt: str):
- jwt_parts = jwt.split('.')
+ jwt_parts = jwt.split(".")
try:
payload_bytes = base64.urlsafe_b64decode(_correct_padding(jwt_parts[1]))
payload = json.loads(payload_bytes.decode("UTF-8"))
except Exception as e:
- logger.warning(f'OAuthToken error: Could not decode jwt: {e}')
+ logger.warning(f"OAuthToken error: Could not decode jwt: {e}")
return None
return payload
diff --git a/server/vbv_lernwelt/sso/urls.py b/server/vbv_lernwelt/sso/urls.py
index 56785481..d3bea976 100644
--- a/server/vbv_lernwelt/sso/urls.py
+++ b/server/vbv_lernwelt/sso/urls.py
@@ -1,10 +1,15 @@
from django.urls import path
-from . import views
from ..core.middleware.auth import django_view_authentication_exempt
-app_name = 'sso'
+from . import views
+
+app_name = "sso"
urlpatterns = [
- path(r'login/', django_view_authentication_exempt(views.login), name='login'),
- path(r'callback/', django_view_authentication_exempt(views.authorize), name='authorize'),
+ path(r"login/", django_view_authentication_exempt(views.login), name="login"),
+ path(
+ r"callback/",
+ django_view_authentication_exempt(views.authorize),
+ name="authorize",
+ ),
]
diff --git a/server/vbv_lernwelt/sso/views.py b/server/vbv_lernwelt/sso/views.py
index b37b41c2..275d34d2 100644
--- a/server/vbv_lernwelt/sso/views.py
+++ b/server/vbv_lernwelt/sso/views.py
@@ -1,18 +1,18 @@
import structlog as structlog
from authlib.integrations.base_client import OAuthError
from django.conf import settings
-from django.shortcuts import redirect
+from django.contrib.auth import get_user_model, login as dj_login
from django.http import HttpResponse
+from django.shortcuts import redirect
from sentry_sdk import capture_exception
-from django.contrib.auth import login as dj_login, get_user_model
from vbv_lernwelt.sso.client import oauth
from vbv_lernwelt.sso.jwt import decode_jwt
logger = structlog.get_logger(__name__)
-OAUTH_SUCCESS_REDIRECT = 'profile'
-OAUTH_FAIL_REDIRECT = 'login-error'
+OAUTH_SUCCESS_REDIRECT = "profile"
+OAUTH_FAIL_REDIRECT = "login-error"
def login(request):
@@ -24,21 +24,27 @@ def login(request):
def authorize(request):
try:
logger.debug(request)
- token = getattr(oauth, settings.OAUTH["client_name"]).authorize_access_token(request)
+ token = getattr(oauth, settings.OAUTH["client_name"]).authorize_access_token(
+ request
+ )
deocded_token = decode_jwt(token["id_token"])
except OAuthError as e:
- logger.error(f'OAuth error: {e}')
+ logger.error(f"OAuth error: {e}")
if not settings.DEBUG:
capture_exception(e)
- return redirect(f'/{OAUTH_FAIL_REDIRECT}?state=someerror') # to be defined
+ return redirect(f"/{OAUTH_FAIL_REDIRECT}?state=someerror") # to be defined
user_data = _user_data_from_token_data(deocded_token)
- user, created = get_user_model().objects.create_or_update_by_email(user_data["email"], user_data["first_name"],
- user_data["last_name"], user_data["username"])
+ user, created = get_user_model().objects.create_or_update_by_email(
+ user_data["email"],
+ user_data["first_name"],
+ user_data["last_name"],
+ user_data["username"],
+ )
dj_login(request, user)
# todo: redirect to other page if new user
- return redirect(f'/{OAUTH_SUCCESS_REDIRECT}?state=success') # to be defined
+ return redirect(f"/{OAUTH_SUCCESS_REDIRECT}?state=success") # to be defined
def _user_data_from_token_data(token: dict) -> dict:
@@ -46,5 +52,5 @@ def _user_data_from_token_data(token: dict) -> dict:
"first_name": token.get("given_name", ""),
"last_name": token.get("family_name", ""),
"username": token.get("preferred_username", ""),
- "email": token.get("emails", [''])[0],
+ "email": token.get("emails", [""])[0],
}