From 815d81a47116b35ce4a908c36dff49e9e76db6c0 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Tue, 18 Jul 2023 13:58:45 +0200 Subject: [PATCH 1/3] Simplify media library models --- .../components/mediaLibrary/OverviewCard.vue | 27 +- .../mediaLibrary/MLCategoryDetailPage.vue | 182 --------- .../mediaLibrary/MLCategoryIndexPage.vue | 52 --- client/src/pages/mediaLibrary/MLIndexPage.vue | 57 --- .../pages/mediaLibrary/MLMediaListPage.vue | 88 ----- .../mediaLibrary/MediaLibraryCategoryPage.vue | 40 ++ .../mediaLibrary/MediaLibraryContentPage.vue | 79 ++++ .../mediaLibrary/MediaLibraryIndexPage.vue | 33 ++ ...entPage.vue => MediaLibraryParentPage.vue} | 23 +- client/src/router/index.ts | 16 +- client/src/stores/mediaLibrary.ts | 7 - client/src/types.ts | 40 +- client/tailwind.config.js | 6 - .../create_vv_competence_profile.py | 3 +- server/vbv_lernwelt/course/consts.py | 1 - .../course/creators/test_course.py | 137 +++---- .../create_default_media_library.py | 345 ++++-------------- .../media_library/migrations/0001_initial.py | 319 ++++------------ server/vbv_lernwelt/media_library/models.py | 183 +++++++--- .../tests/media_library_factories.py | 122 ++++--- 20 files changed, 642 insertions(+), 1118 deletions(-) delete mode 100644 client/src/pages/mediaLibrary/MLCategoryDetailPage.vue delete mode 100644 client/src/pages/mediaLibrary/MLCategoryIndexPage.vue delete mode 100644 client/src/pages/mediaLibrary/MLIndexPage.vue delete mode 100644 client/src/pages/mediaLibrary/MLMediaListPage.vue create mode 100644 client/src/pages/mediaLibrary/MediaLibraryCategoryPage.vue create mode 100644 client/src/pages/mediaLibrary/MediaLibraryContentPage.vue create mode 100644 client/src/pages/mediaLibrary/MediaLibraryIndexPage.vue rename client/src/pages/mediaLibrary/{MLParentPage.vue => MediaLibraryParentPage.vue} (66%) diff --git a/client/src/components/mediaLibrary/OverviewCard.vue b/client/src/components/mediaLibrary/OverviewCard.vue index cd498527..fe592f57 100644 --- a/client/src/components/mediaLibrary/OverviewCard.vue +++ b/client/src/components/mediaLibrary/OverviewCard.vue @@ -4,11 +4,13 @@ export interface Props { description: string; call2Action: string; link: string; - icon?: string; + iconUrl?: string; + externalLink?: boolean; } withDefaults(defineProps(), { - icon: "", + iconUrl: "", + externalLink: false, }); @@ -16,8 +18,19 @@ withDefaults(defineProps(), {

{{ title }}

-

{{ description }}

+ +

+ + {{ call2Action }} + + (), {
-
+
+ icon +
diff --git a/client/src/pages/mediaLibrary/MLCategoryDetailPage.vue b/client/src/pages/mediaLibrary/MLCategoryDetailPage.vue deleted file mode 100644 index d8fcc49c..00000000 --- a/client/src/pages/mediaLibrary/MLCategoryDetailPage.vue +++ /dev/null @@ -1,182 +0,0 @@ - - - - - diff --git a/client/src/pages/mediaLibrary/MLCategoryIndexPage.vue b/client/src/pages/mediaLibrary/MLCategoryIndexPage.vue deleted file mode 100644 index 225c1d7e..00000000 --- a/client/src/pages/mediaLibrary/MLCategoryIndexPage.vue +++ /dev/null @@ -1,52 +0,0 @@ - - - - - diff --git a/client/src/pages/mediaLibrary/MLIndexPage.vue b/client/src/pages/mediaLibrary/MLIndexPage.vue deleted file mode 100644 index 046b81cb..00000000 --- a/client/src/pages/mediaLibrary/MLIndexPage.vue +++ /dev/null @@ -1,57 +0,0 @@ - - - - - diff --git a/client/src/pages/mediaLibrary/MLMediaListPage.vue b/client/src/pages/mediaLibrary/MLMediaListPage.vue deleted file mode 100644 index 645ad84a..00000000 --- a/client/src/pages/mediaLibrary/MLMediaListPage.vue +++ /dev/null @@ -1,88 +0,0 @@ - - - - - diff --git a/client/src/pages/mediaLibrary/MediaLibraryCategoryPage.vue b/client/src/pages/mediaLibrary/MediaLibraryCategoryPage.vue new file mode 100644 index 00000000..303587da --- /dev/null +++ b/client/src/pages/mediaLibrary/MediaLibraryCategoryPage.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/client/src/pages/mediaLibrary/MediaLibraryContentPage.vue b/client/src/pages/mediaLibrary/MediaLibraryContentPage.vue new file mode 100644 index 00000000..a7569e74 --- /dev/null +++ b/client/src/pages/mediaLibrary/MediaLibraryContentPage.vue @@ -0,0 +1,79 @@ + + + + + diff --git a/client/src/pages/mediaLibrary/MediaLibraryIndexPage.vue b/client/src/pages/mediaLibrary/MediaLibraryIndexPage.vue new file mode 100644 index 00000000..1e7beacb --- /dev/null +++ b/client/src/pages/mediaLibrary/MediaLibraryIndexPage.vue @@ -0,0 +1,33 @@ + + + + + diff --git a/client/src/pages/mediaLibrary/MLParentPage.vue b/client/src/pages/mediaLibrary/MediaLibraryParentPage.vue similarity index 66% rename from client/src/pages/mediaLibrary/MLParentPage.vue rename to client/src/pages/mediaLibrary/MediaLibraryParentPage.vue index a3cb202d..ab407d65 100644 --- a/client/src/pages/mediaLibrary/MLParentPage.vue +++ b/client/src/pages/mediaLibrary/MediaLibraryParentPage.vue @@ -12,7 +12,7 @@ const props = defineProps<{ const mediaLibraryStore = useMediaLibraryStore(); onMounted(async () => { - log.debug("MediaLibraryView mounted", props.courseSlug); + log.debug("MediaLibraryParentPage mounted", props.courseSlug); try { await mediaLibraryStore.loadMediaLibraryPage(`${props.courseSlug}-media`); @@ -34,18 +34,23 @@ onMounted(async () => { Übersicht -
  • +
  • - Handlungsfelder + {{ category.title }} + + + {{ category.title }} +
  • -
  • Allgemeines zu Versicherungen
  • -
  • Lernmedien
  • -
  • - Lexikon -
  • +
  • diff --git a/client/src/router/index.ts b/client/src/router/index.ts index d1d6daf4..d744a32b 100644 --- a/client/src/router/index.ts +++ b/client/src/router/index.ts @@ -43,25 +43,21 @@ const router = createRouter({ { path: "/course/:courseSlug/media", props: true, - component: () => import("@/pages/mediaLibrary/MLParentPage.vue"), + component: () => import("@/pages/mediaLibrary/MediaLibraryParentPage.vue"), children: [ { path: "", - component: () => import("@/pages/mediaLibrary/MLIndexPage.vue"), + component: () => import("@/pages/mediaLibrary/MediaLibraryIndexPage.vue"), }, { - path: "category/:mediaCategorySlug/media", + path: ":categorySlug", props: true, - component: () => import("@/pages/mediaLibrary/MLMediaListPage.vue"), + component: () => import("@/pages/mediaLibrary/MediaLibraryCategoryPage.vue"), }, { - path: "category/:mediaCategorySlug", + path: ":categorySlug/:contentSlug", props: true, - component: () => import("@/pages/mediaLibrary/MLCategoryDetailPage.vue"), - }, - { - path: "category", - component: () => import("@/pages/mediaLibrary/MLCategoryIndexPage.vue"), + component: () => import("@/pages/mediaLibrary/MediaLibraryContentPage.vue"), }, ], }, diff --git a/client/src/stores/mediaLibrary.ts b/client/src/stores/mediaLibrary.ts index 681fc6a2..11e5fb6d 100644 --- a/client/src/stores/mediaLibrary.ts +++ b/client/src/stores/mediaLibrary.ts @@ -5,8 +5,6 @@ import { defineStore } from "pinia"; export type MediaLibraryStoreState = { mediaLibraryPage: MediaLibraryPage | undefined; - selectedLearningPath: { id: number; name: string }; - availableLearningPaths: { id: number; name: string }[]; }; export const useMediaLibraryStore = defineStore({ @@ -14,11 +12,6 @@ export const useMediaLibraryStore = defineStore({ state: () => { return { mediaLibraryPage: undefined, - selectedLearningPath: { id: 1, name: "Alle Lehrgänge" }, - availableLearningPaths: [ - { id: 1, name: "Alle Lehrgänge" }, - { id: 2, name: "Versicherungsvermittler/-in" }, - ], } as MediaLibraryStoreState; }, getters: {}, diff --git a/client/src/types.ts b/client/src/types.ts index 1982b820..e3e68284 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -244,26 +244,36 @@ export interface MediaContentCollection { }; } -export interface MediaCategoryPage extends BaseCourseWagtailPage { - content_type: "media_library.MediaCategoryPage"; - overview_icon: string; - detail_image: string; - introduction_text: string; - description_title: string; - description_text: string; - items: { - type: "item"; - value: string; - id: string; - }[]; - course_category: CourseCategory; - body: MediaContentCollection[]; +export interface MediaLibraryContentPage extends BaseCourseWagtailPage { + readonly content_type: "media_library.MediaLibraryContentPage"; + readonly icon_overview_url: string; + readonly icon_detail_url: string; + readonly description: string; + readonly body: string; } +export interface MediaLibraryCategoryPage extends BaseCourseWagtailPage { + readonly content_type: "media_library.MediaLibraryCategoryPage"; + readonly icon_url: string; + readonly description: string; + readonly children: MediaLibraryContentPage[]; +} + +export interface MediaLibraryUrlPage extends BaseCourseWagtailPage { + readonly content_type: "media_library.MediaLibraryUrlPage"; + readonly icon_url: string; + readonly content_url: string; + readonly url_open_blank: boolean; + readonly description: string; +} + +export type MediaLibraryPageChild = MediaLibraryCategoryPage | MediaLibraryUrlPage; + export interface MediaLibraryPage extends BaseCourseWagtailPage { readonly content_type: "media_library.MediaLibraryPage"; readonly course: Course; - readonly children: MediaCategoryPage[]; + readonly description: string; + readonly children: MediaLibraryPageChild[]; } export interface AssignmentPerformanceObjective { diff --git a/client/tailwind.config.js b/client/tailwind.config.js index cee8a0c6..edfeca98 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -28,12 +28,6 @@ module.exports = { "8xl": "88rem", "9xl": "96rem", }, - backgroundImage: { - "handlungsfelder-overview": - "url('/static/icons/icon-handlungsfelder-overview.svg')", - "lernmedien-overview": "url('/static/icons/icon-lernmedien-overview.svg')", - message: "url('/static/icons/icon-message.svg')", - }, borderColor: (theme) => ({ DEFAULT: theme("colors.gray.500"), }), diff --git a/server/vbv_lernwelt/competence/create_vv_competence_profile.py b/server/vbv_lernwelt/competence/create_vv_competence_profile.py index fba2a9be..eb279405 100644 --- a/server/vbv_lernwelt/competence/create_vv_competence_profile.py +++ b/server/vbv_lernwelt/competence/create_vv_competence_profile.py @@ -4,12 +4,11 @@ from vbv_lernwelt.competence.factories import ( PerformanceCriteriaFactory, ) from vbv_lernwelt.competence.models import CompetencePage -from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID from vbv_lernwelt.course.models import CoursePage from vbv_lernwelt.learnpath.models import LearningPath, LearningUnit -def create_vv_competence_profile(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID): +def create_vv_competence_profile(course_id): course_page = CoursePage.objects.get(course_id=course_id) slug_prefix = course_page.get_children().exact_type(LearningPath).first().slug diff --git a/server/vbv_lernwelt/course/consts.py b/server/vbv_lernwelt/course/consts.py index 30fe9e57..bfd0a3bc 100644 --- a/server/vbv_lernwelt/course/consts.py +++ b/server/vbv_lernwelt/course/consts.py @@ -1,5 +1,4 @@ COURSE_TEST_ID = -1 -COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID = -2 COURSE_UK = -3 COURSE_VERSICHERUNGSVERMITTLERIN_ID = -4 COURSE_UK_FR = -5 diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index 44a39e8a..bb70aa38 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -1,4 +1,3 @@ -import json from datetime import datetime import wagtail_factories @@ -63,13 +62,10 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import ( TopicFactory, ) from vbv_lernwelt.media_library.tests.media_library_factories import ( - create_external_link_block, - create_learn_media_block, - create_media_collection, - ExternalLinkBlockFactory, - LearnMediaBlockFactory, - MediaCategoryPageFactory, + MediaLibraryCategoryPageFactory, + MediaLibraryContentPageFactory, MediaLibraryPageFactory, + MediaLibraryUrlPageFactory, ) @@ -306,7 +302,7 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst. f"

    In der Mediathek unter dem Handlungsfeld «{title}» findest du alle relevanten Ressourcen für deine Fachkompetenzen.

    " f"

    Wir empfehlen dir vor der Absolvierung der weiteren Lerneinheiten dich in die Thematik einzulesen.

    " ), - content_url=f"/course/{lp.get_course().slug}/media/category/{slugify(title)}", + content_url=f"/course/{lp.get_course().slug}/media/handlungsfelder/{slugify(title)}", ) LearningContentAssignmentFactory( title="Fahrzeug - Mein erstes Auto", @@ -384,7 +380,7 @@ def create_test_circle_reisen(lp): LearningContentMediaLibraryFactory( title=f"Mediathek Reisen", parent=circle, - content_url=f"/media/test-lehrgang-media/category/reisen", + content_url=f"/course/test-lehrgang/media/handlungsfelder/reisen", ) LearningSequenceFactory(title="Analyse", parent=circle) @@ -493,65 +489,70 @@ def create_test_media_library(): parent=course_page, ) - icons = [ - "icon-hf-fahrzeug", - "icon-hf-reisen", - "icon-hf-einkommenssicherung", + media_lib_handlungsfelder = MediaLibraryCategoryPageFactory( + title="Handlungsfelder", + parent=media_lib_page, + ) + + media_lib_allgemeines = MediaLibraryCategoryPageFactory( + title="Allgemeines", + parent=media_lib_page, + ) + + MediaLibraryUrlPageFactory( + title="Lexikon", + parent=media_lib_page, + content_url="https://www.vbv.ch/de/der-vbv/lernen-lehren/lexikon", + url_open_blank=True, + ) + + handlungsfelder = [ + "Fahrzeug", + "Reisen", + "Einkommenssicherung", + "Haushalt", + "Wohneigentum", + "Pensionierung", + "Rechtsstreitigkeiten", + "KMU", + "Gesundheit", ] - for idx, cat in enumerate(course.coursecategory_set.all()): - overview_icon = icons[(idx + 2) % len(icons)] - 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 = """ -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() - items = [ - ("item", "Motorfahrzeughaftpflichtversicherung"), - ("item", "Motorfahrzeugkaskoversicherung"), - ("item", "Insassenunfallversicherung"), - ] - body_data = json.dumps( - [ - create_media_collection( - title="Lernmedien", - contents=[ - create_learn_media_block(LearnMediaBlockFactory()), - create_learn_media_block(LearnMediaBlockFactory()), - create_learn_media_block(LearnMediaBlockFactory()), - create_learn_media_block(LearnMediaBlockFactory()), - ], - ), - create_media_collection( - title="Links", - contents=[ - create_external_link_block( - ExternalLinkBlockFactory( - title="Nationales Versicherungsbüro", - url="https://www.vbv.ch/", - ) - ), - create_external_link_block( - ExternalLinkBlockFactory( - title="Adressen der Strassenverkehrsämter", - url="https://www.vbv.ch/", - ) - ), - ], - ), - ] + + for cat in handlungsfelder: + MediaLibraryContentPageFactory( + title=cat, + parent=media_lib_handlungsfelder, + icon_detail_url=f"/static/icons/handlungsfelder/icon-hf-{cat.lower()}-detail.svg", + icon_overview_url=f"/static/icons/handlungsfelder/icon-hf-{cat.lower()}.svg", + description=""" + 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(), + body=RichText( + f"

    Lernmedien

    " + f"

    Allgemeines

    " + f"
    • Mit Risiken im Strassenverkehr umgehen
    • Versicherungsschutz
    • Vertragsarten
    • Zusammenfassung
    " + ), ) - media_category = MediaCategoryPageFactory( - overview_icon=overview_icon, - title=cat.title, - course_category=cat, - parent=media_lib_page, - introduction_text=introduction_text, - description_title=description_title, - description_text=description_text, - items=items, - body=body_data, + + allgemeines = [ + "Versicherungswirtschaft", + "Steuern", + "Verkauf", + "Recht", + "Lern- und Arbeitstechnik", + "Sozialversicherungen", + "Hilfsmittel", + ] + + for cat in allgemeines: + MediaLibraryContentPageFactory( + title=cat, + parent=media_lib_allgemeines, + body=RichText( + f"

    Lernmedien

    " + f"

    Allgemeines

    " + f"
    • Mit Risiken im Strassenverkehr umgehen
    • Versicherungsschutz
    • Vertragsarten
    • Zusammenfassung
    " + ), ) 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 cb21944b..9b0f8d7c 100644 --- a/server/vbv_lernwelt/media_library/create_default_media_library.py +++ b/server/vbv_lernwelt/media_library/create_default_media_library.py @@ -1,19 +1,15 @@ -import json +from wagtail.rich_text import RichText -from vbv_lernwelt.course.consts import COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID from vbv_lernwelt.course.models import Course, CoursePage from vbv_lernwelt.media_library.tests.media_library_factories import ( - create_external_link_block, - create_learn_media_block, - create_media_collection, - ExternalLinkBlockFactory, - LearnMediaBlockFactory, - MediaCategoryPageFactory, + MediaLibraryCategoryPageFactory, + MediaLibraryContentPageFactory, MediaLibraryPageFactory, + MediaLibraryUrlPageFactory, ) -def create_default_media_library(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID): +def create_default_media_library(course_id): course = Course.objects.get(id=course_id) course_page = CoursePage.objects.get(course_id=course_id) @@ -22,273 +18,70 @@ def create_default_media_library(course_id=COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ parent=course_page, ) - icons = [ - "icon-hf-fahrzeug", - "icon-hf-reisen", - "icon-hf-einkommenssicherung", - "icon-hf-gesundheit", - "icon-hf-haushalt", - "icon-hf-sparen", - "icon-hf-pensionierung", - "icon-hf-kmu", - "icon-hf-wohneigentum", - "icon-hf-rechtsstreitigkeiten", - "icon-hf-vererben", - "icon-hf-selbstandigkeit", + media_lib_handlungsfelder = MediaLibraryCategoryPageFactory( + title="Handlungsfelder", + parent=media_lib_page, + ) + + media_lib_allgemeines = MediaLibraryCategoryPageFactory( + title="Allgemeines", + parent=media_lib_page, + ) + + MediaLibraryUrlPageFactory( + title="Lexikon", + parent=media_lib_page, + content_url="https://www.vbv.ch/de/der-vbv/lernen-lehren/lexikon", + url_open_blank=True, + ) + + handlungsfelder = [ + "Fahrzeug", + "Reisen", + "Einkommenssicherung", + "Haushalt", + "Wohneigentum", + "Pensionierung", + "Rechtsstreitigkeiten", + "KMU", + "Gesundheit", ] - for idx, cat in enumerate(course.coursecategory_set.all()): - overview_icon = icons[(idx + len(icons) - 1) % len(icons)] - detail_image = f"{overview_icon}-detail" - if cat.title == "Fahrzeug": - media_category = MediaCategoryPageFactory( - overview_icon=overview_icon, - detail_image=detail_image, - title=cat.title, - course_category=cat, - parent=media_lib_page, - 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(), - items=[ - ("item", text) - for text in [ - "Motorfahrzeughaftpflichtversicherung", - "Motorfahrzeugkaskoversicherung", - "Insassenunfallversicherung", - "(Verkehrsrechtsschutzversicherung)", - "(Fahrzeugassistance)", - "Überblick und gesetzliche Grundlagen", - "Versicherungsschutz: versicherte Personen und Sachen, örtlicher Geltungsbereich, versicherte Gefahren, versicherte Schäden, wichtigste Ausschlüsse, Garantie- und Versicherungssumme", - "Versicherungsleistung", - "Bonus-/Malus-System", - "Mögliche Zusatzversicherungen wie Bonusschutz, Grobfahrlässigkeit, persönliche Effekten, Parkschaden, Scheinwerfer, Ersatzfahrzeug", - "Abgrenzungen zur Hausratversicherung (u.a. einfacher Diebstahl auswärts)", - "Abgrenzung zu KVG und UVG", - "Nutzen Insassenunfall", - ] - ], - body=json.dumps( - [ - create_media_collection( - title="Lernmedien", - contents=[ - create_learn_media_block( - LearnMediaBlockFactory( - title="VBV 303/7 Motorfahrzeugkasko", - description="PDF", - url="/static/media/demo_oktober/07_Motorfahrzeugkaskoversicherung.pdf", - ) - ), - create_learn_media_block( - LearnMediaBlockFactory( - title="VBV 303/16 Motorfahrzeughaftpflicht", - description="PDF", - url="/static/media/demo_oktober/16_Motorfahrzeughaftpflichtversicherung.pdf", - ) - ), - ], - ), - create_media_collection( - title="Links", - contents=[ - create_external_link_block( - ExternalLinkBlockFactory( - title="Nationales Versicherungsbüro", - url="https://www.nbi-ngf.ch/de/ngf", - ) - ), - create_external_link_block( - ExternalLinkBlockFactory( - title="Adressen der Strassenverkehrsämter", - url="https://asa.ch/strassenverkehrsaemter/adressen/", - ) - ), - create_external_link_block( - ExternalLinkBlockFactory( - title="Bundesamt für Statistik – Strassenverkehrsunfälle", - url="https://www.bfs.admin.ch/bfs/de/home/statistiken/mobilitaet-verkehr/unfaelle-umweltauswirkungen/verkehrsunfaelle/strassenverkehr.html", - ) - ), - create_external_link_block( - ExternalLinkBlockFactory( - title="Beratungsstelle für Unfallverhütung – Unfallursachen", - url="https://www.bfu.ch/de/dossiers/risiken-im-strassenverkehr", - ) - ), - ], - ), - # create_media_collection( - # title="Verankerung im Lernpfad", - # description="Anhand der Story von Rafael Fasel und seinem Ford Mustang lernst du in diesem berufstypischem Handlungsfeld 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.", - # contents=[ - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Einstieg – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/einstieg#lu-fahrzeug", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Analyse – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/analyse#lu-fahrzeug", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Lösung – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/lösung#lu-fahrzeug", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Abschluss – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/abschluss#lu-fahrzeug", - # ) - # ), - # ], - # ), - # create_media_collection( - # title="Querverweise", - # contents=[ - # create_relative_link_block( - # RelativeLinkBlockFactory( - # title="Rechtsstreitigkeiten", - # description="VBV 303/12.3 Verkehrsrechtsschutz", - # url="/media/versicherungsvermittler-in-media/category/rechtsstreitigkeiten", - # ) - # ), - # create_relative_link_block( - # RelativeLinkBlockFactory( - # title="Reisen", - # description="VBV 303/13 Reiseversicherung", - # url="/media/versicherungsvermittler-in-media/category/reisen", - # ) - # ), - # ], - # ), - ] - ), - ) - elif cat.title == "Reisen": - media_category = MediaCategoryPageFactory( - overview_icon=overview_icon, - detail_image=detail_image, - title=cat.title, - course_category=cat, - parent=media_lib_page, - introduction_text=""" -Auf keine Zeit im Jahr freuen wir uns mehr als auf unsere Ferien. -Neue Orte, neue Bekanntschaften, neue Erfahrungen oder einfach mal abschalten – es gibt viele Gründe, sich fürs Reisen zu begeistern. + for cat in handlungsfelder: + MediaLibraryContentPageFactory( + title=cat, + parent=media_lib_handlungsfelder, + icon_detail_url=f"/static/icons/handlungsfelder/icon-hf-{cat.lower()}-detail.svg", + icon_overview_url=f"/static/icons/handlungsfelder/icon-hf-{cat.lower()}.svg", + description=""" + 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(), + body=RichText( + f"

    Lernmedien

    " + f"

    Allgemeines

    " + f"
    • Mit Risiken im Strassenverkehr umgehen
    • Versicherungsschutz
    • Vertragsarten
    • Zusammenfassung
    " + ), + ) -Bereits während der Vorbereitung und Planung, aber auch während der Reise selbst, -gehen wir bewusst und unbewusst verschiedene Risiken ein. -Diese können negative Folgen verschiedener Art nach sich ziehen, darunter rechtliche, finanzielle oder gesundheitliche Folgen. - """.strip(), - items=[ - ("item", text) - for text in [ - "Annullierungskosten", - "Personenassistance", - "Fahrzeugassistance", - "Evtl. Reisegepäck", - "(Hausratversicherung)", - "(Krankenversicherung)", - "(Einzelunfallversicherung)", - "(Privat- und Verkehrsrechtsschutz)", - "Überblick und gesetzliche Grundlagen", - "Versicherungsschutz: versicherte Personen, örtlicher Geltungsbereich, versicherte Ereignisse, Ausschlüsse, Garantiesumme, Dauer", - "Versicherungsleistung", - ] - ], - body=json.dumps( - [ - create_media_collection( - title="Lernmedien", - contents=[ - create_learn_media_block( - LearnMediaBlockFactory( - title="VBV 303/13 Reiseversicherung", - description="PDF", - url="/static/media/demo_oktober/13_Reiseversicherung.pdf", - ) - ), - create_learn_media_block( - LearnMediaBlockFactory( - title="Fach-Check «Reisen»", - description="Applikation", - link_display_text="Zum Fach-Check", - url="/static/media/demo_oktober/fach_check_reisen/index.html", - ) - ), - ], - ), - # create_media_collection( - # title="Verankerung im Lernpfad", - # description="Begleite Emma Durand und Ayla Yilmaz bei den Vorbereitungen auf ihre grosse Reise durch Amerika und lerne dabei, welche Risiken durch welche Versicherungen abgedeckt werden können.", - # contents=[ - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Einstieg – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/einstieg#lu-reisen", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Analyse – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/analyse#lu-reisen", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Lösung – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/lösung#lu-reisen", - # ) - # ), - # create_internal_link_block( - # InternalLinkBlockFactory( - # title="Circle: Abschluss – Lernsequenz: Anwenden", - # url="/learn/versicherungsvermittler-in-lp/abschluss#lu-reisen", - # ) - # ), - # ], - # ), - # create_media_collection( - # title="Querverweise", - # contents=[ - # create_relative_link_block( - # RelativeLinkBlockFactory( - # title="Haushalt", - # description="VBV 303/03 Hausratversicherung", - # url="/media/versicherungsvermittler-in-media/category/haushalt", - # ) - # ), - # create_relative_link_block( - # RelativeLinkBlockFactory( - # title="Rechtsstreitigkeiten", - # desciption="VBV 303/12 Rechtschutzversicherung", - # url="/media/versicherungsvermittler-in-media/category/rechtsstreitigkeiten", - # ) - # ), - # create_relative_link_block( - # RelativeLinkBlockFactory( - # title="Gesundheit", - # description="VBV 304/Teil E Obligatorische Krankenversicherung", - # url="/media/versicherungsvermittler-in-media/category/gesundheit", - # ) - # ), - # ], - # ), - ] - ), - ) - else: - media_category = MediaCategoryPageFactory( - overview_icon=overview_icon, - title=cat.title, - course_category=cat, - parent=media_lib_page, - detail_image=detail_image, - ) + allgemeines = [ + "Versicherungswirtschaft", + "Steuern", + "Verkauf", + "Recht", + "Lern- und Arbeitstechnik", + "Sozialversicherungen", + "Hilfsmittel", + ] + + for cat in allgemeines: + MediaLibraryContentPageFactory( + title=cat, + parent=media_lib_allgemeines, + body=RichText( + f"

    Lernmedien

    " + f"

    Allgemeines

    " + f"
    • Mit Risiken im Strassenverkehr umgehen
    • Versicherungsschutz
    • Vertragsarten
    • Zusammenfassung
    " + ), + ) diff --git a/server/vbv_lernwelt/media_library/migrations/0001_initial.py b/server/vbv_lernwelt/media_library/migrations/0001_initial.py index 9b84fef1..004d5f13 100644 --- a/server/vbv_lernwelt/media_library/migrations/0001_initial.py +++ b/server/vbv_lernwelt/media_library/migrations/0001_initial.py @@ -1,8 +1,7 @@ -# Generated by Django 3.2.13 on 2023-07-14 12:28 +# Generated by Django 3.2.13 on 2023-07-18 13:48 import django.db.models.deletion import taggit.managers -import wagtail.blocks import wagtail.fields import wagtail.models.collections import wagtail.search.index @@ -15,13 +14,76 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ("course", "0002_initial"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"), ("wagtailcore", "0083_workflowcontenttype"), + ("taggit", "0004_alter_taggeditem_content_type_alter_taggeditem_tag"), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ + migrations.CreateModel( + name="MediaLibraryCategoryPage", + 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", wagtail.fields.RichTextField(blank=True)), + ( + "icon_url", + models.CharField( + default="/static/icons/icon-handlungsfelder-overview.svg", + max_length=255, + ), + ), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page",), + ), + migrations.CreateModel( + name="MediaLibraryContentPage", + 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", wagtail.fields.RichTextField(blank=True)), + ( + "icon_overview_url", + models.CharField( + default="/static/icons/handlungsfelder/icon-hf-fahrzeug.svg", + max_length=255, + ), + ), + ( + "icon_detail_url", + models.CharField( + default="/static/icons/handlungsfelder/icon-hf-fahrzeug-detail.svg", + max_length=255, + ), + ), + ("body", wagtail.fields.RichTextField(blank=True)), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page",), + ), migrations.CreateModel( name="MediaLibraryPage", fields=[ @@ -36,6 +98,7 @@ class Migration(migrations.Migration): to="wagtailcore.page", ), ), + ("description", wagtail.fields.RichTextField(blank=True)), ], options={ "abstract": False, @@ -43,7 +106,7 @@ class Migration(migrations.Migration): bases=("wagtailcore.page",), ), migrations.CreateModel( - name="MediaCategoryPage", + name="MediaLibraryUrlPage", fields=[ ( "page_ptr", @@ -56,245 +119,21 @@ class Migration(migrations.Migration): to="wagtailcore.page", ), ), - ("introduction_text", models.TextField(default="")), + ("description", wagtail.fields.RichTextField(blank=True)), ( - "description_title", - models.TextField( - default="Das erwartet dich in diesem Handlungsfeld" + "icon_url", + models.CharField( + default="/static/icons/icon-handlungsfelder-overview.svg", + max_length=255, ), ), - ("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), - ), - ( - "detail_image", - models.CharField(default="image-hf-fahrzeug", max_length=255), - ), + ("content_url", models.URLField(blank=True)), + ("url_open_blank", models.BooleanField(default=False)), ( "body", - wagtail.fields.StreamField( - [ - ( - "content_collection", - wagtail.blocks.StructBlock( - [ - ("title", wagtail.blocks.TextBlock()), - ( - "description", - wagtail.blocks.TextBlock( - default="", required=False - ), - ), - ( - "contents", - wagtail.blocks.StreamBlock( - [ - ( - "learn_media", - wagtail.blocks.StructBlock( - [ - ( - "title", - wagtail.blocks.TextBlock(), - ), - ( - "description", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "icon_url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "link_display_text", - wagtail.blocks.CharBlock( - default="Link öffnen", - max_length=255, - ), - ), - ( - "url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "open_window", - wagtail.blocks.BooleanBlock( - default=False - ), - ), - ] - ), - ), - ( - "external_link", - wagtail.blocks.StructBlock( - [ - ( - "title", - wagtail.blocks.TextBlock(), - ), - ( - "description", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "icon_url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "link_display_text", - wagtail.blocks.CharBlock( - default="Link öffnen", - max_length=255, - ), - ), - ( - "url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "open_window", - wagtail.blocks.BooleanBlock( - default=False - ), - ), - ] - ), - ), - ( - "internal_link", - wagtail.blocks.StructBlock( - [ - ( - "title", - wagtail.blocks.TextBlock(), - ), - ( - "description", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "icon_url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "link_display_text", - wagtail.blocks.CharBlock( - default="Link öffnen", - max_length=255, - ), - ), - ( - "url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "open_window", - wagtail.blocks.BooleanBlock( - default=False - ), - ), - ] - ), - ), - ( - "relative_link", - wagtail.blocks.StructBlock( - [ - ( - "title", - wagtail.blocks.TextBlock(), - ), - ( - "description", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "icon_url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "link_display_text", - wagtail.blocks.CharBlock( - default="Link öffnen", - max_length=255, - ), - ), - ( - "url", - wagtail.blocks.TextBlock( - default="", - required=False, - ), - ), - ( - "open_window", - wagtail.blocks.BooleanBlock( - default=False - ), - ), - ] - ), - ), - ] - ), - ), - ] - ), - ) - ], - null=True, - use_json_field=True, - ), - ), - ( - "course_category", - models.ForeignKey( + wagtail.fields.RichTextField( blank=True, - null=True, - on_delete=django.db.models.deletion.SET_NULL, - to="course.coursecategory", + help_text="Wird nur angezeigt, wenn kein Link angegeben ist.", ), ), ], @@ -329,7 +168,7 @@ class Migration(migrations.Migration): ("display_text", models.CharField(default="", max_length=1024)), ("description", models.TextField(default="")), ("link_display_text", models.CharField(default="", max_length=1024)), - ("thumbnail", models.URLField()), + ("thumbnail", models.CharField(default="", max_length=1024)), ( "collection", models.ForeignKey( diff --git a/server/vbv_lernwelt/media_library/models.py b/server/vbv_lernwelt/media_library/models.py index 72923c43..79dc057e 100644 --- a/server/vbv_lernwelt/media_library/models.py +++ b/server/vbv_lernwelt/media_library/models.py @@ -2,25 +2,33 @@ import re from django.db import models from django.utils.text import slugify -from wagtail import blocks, fields -from wagtail.admin.panels import FieldPanel, StreamFieldPanel +from wagtail.admin.panels import FieldPanel from wagtail.documents.models import AbstractDocument, Document -from wagtail.fields import StreamField +from wagtail.fields import RichTextField from wagtail.models import Page +from vbv_lernwelt.core.constants import ( + DEFAULT_RICH_TEXT_FEATURES, + DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER, +) from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.course.models import CourseBasePage -from vbv_lernwelt.media_library.content_blocks import MediaContentCollection class MediaLibraryPage(CourseBasePage): - serialize_field_names = ["course", "children"] - parent_page_types = ["course.CoursePage"] - subpage_types = ["media_library.MediaCategoryPage"] + subpage_types = [ + "media_library.MediaLibraryCategoryPage", + "media_library.MediaLibraryUrlPage", + ] + + serialize_field_names = ["course", "description", "children"] + + description = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES) content_panels = [ FieldPanel("title", classname="full title"), + FieldPanel("description", classname="full title"), ] def save(self, clean=True, user=None, log_action=False, **kwargs): @@ -34,55 +42,25 @@ class MediaLibraryPage(CourseBasePage): return f"/course/{self.slug.replace('-media', '')}/media" -class MediaCategoryPage(CourseBasePage): +class MediaLibraryCategoryPage(CourseBasePage): """ - Handlungsfeld. zB. Fahrzeug + Kategorie unter Mediathek: "Allgemeines" oder "Handlungsfelder" """ - serialize_field_names = [ - "course_category", - "introduction_text", - "overview_icon", - "detail_image", - "description_title", - "description_text", - "items", - "body", - ] - - 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, - ) + subpage_types = ["media_library.MediaLibraryContentPage"] - overview_icon = models.CharField(max_length=255, default="icon-hf-fahrzeug") - detail_image = models.CharField(max_length=255, default="image-hf-fahrzeug") + serialize_field_names = ["children", "icon_url", "description"] - body = fields.StreamField( - [("content_collection", MediaContentCollection())], - use_json_field=True, - null=True, + description = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES) + icon_url = models.CharField( + max_length=255, default="/static/icons/icon-handlungsfelder-overview.svg" ) content_panels = [ FieldPanel("title"), - FieldPanel("course_category"), - FieldPanel("introduction_text"), - FieldPanel("description_title"), - FieldPanel("description_text"), - FieldPanel("items"), - StreamFieldPanel("body"), + FieldPanel("icon_url"), + FieldPanel("description"), ] def save(self, clean=True, user=None, log_action=False, **kwargs): @@ -90,22 +68,125 @@ class MediaCategoryPage(CourseBasePage): slugify(f"{self.get_parent().slug}-cat-{self.title}", allow_unicode=True), ignore_page_id=self.id, ) - super(MediaCategoryPage, self).save(clean, user, log_action, **kwargs) + super(MediaLibraryCategoryPage, self).save(clean, user, log_action, **kwargs) def get_frontend_url(self): r = re.compile(r"^(?P.+?)-media-cat-(?P.+)$") m = r.match(self.slug) - return f"/course/{m.group('coursePart')}/media/category/{m.group('catPart')}" + return f"/course/{m.group('coursePart')}/media/{m.group('catPart')}" + + +class MediaLibraryContentPage(CourseBasePage): + """ + Inhalt einer Seite in der Mediathek, entweder Handlungsfeld wie "Fahrzeug" oder + Unterseite unter "Allgemein" wie "Versicherungswirtschaft" + """ + + parent_page_types = ["media_library.MediaLibraryCategoryPage"] + + serialize_field_names = [ + "icon_overview_url", + "icon_detail_url", + "description", + "body", + ] + + description = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES) + icon_overview_url = models.CharField( + max_length=255, default="/static/icons/handlungsfelder/icon-hf-fahrzeug.svg" + ) + icon_detail_url = models.CharField( + max_length=255, + default="/static/icons/handlungsfelder/icon-hf-fahrzeug-detail.svg", + ) + + body = RichTextField( + blank=True, features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER + ["h2"] + ) + + content_panels = [ + FieldPanel("title"), + FieldPanel("icon_overview_url"), + FieldPanel("icon_detail_url"), + FieldPanel("description"), + FieldPanel("body"), + ] + + def save(self, clean=True, user=None, log_action=False, **kwargs): + self.slug = find_available_slug( + slugify( + f"{self.get_parent().slug}-content-{self.title}", allow_unicode=True + ), + ignore_page_id=self.id, + ) + super(MediaLibraryContentPage, self).save(clean, user, log_action, **kwargs) + + def get_frontend_url(self): + r = re.compile( + r"^(?P.+?)-media-cat-(?P.+?)-content-(?P.+)$" + ) + m = r.match(self.slug) + return f"/course/{m.group('coursePart')}/media/{m.group('catPart')}/{m.group('contentPart')}" + + +class MediaLibraryUrlPage(CourseBasePage): + """ + Link in der Mediathek auf externe Seite, wie Lernmedien und/oder Lexikon + """ + + parent_page_types = ["media_library.MediaLibraryPage"] + + serialize_field_names = [ + "children", + "icon_url", + "content_url", + "url_open_blank", + "description", + "body", + ] + + description = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES) + icon_url = models.CharField( + max_length=255, default="/static/icons/icon-handlungsfelder-overview.svg" + ) + content_url = models.URLField(blank=True) + url_open_blank = models.BooleanField(default=False) + body = RichTextField( + blank=True, + features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER + ["h2"], + help_text="Wird nur angezeigt, wenn kein Link angegeben ist.", + ) + + content_panels = [ + FieldPanel("title"), + FieldPanel("icon_url"), + FieldPanel("content_url"), + FieldPanel("url_open_blank"), + FieldPanel("description"), + FieldPanel("body"), + ] + + def save(self, clean=True, user=None, log_action=False, **kwargs): + self.slug = find_available_slug( + slugify(f"{self.get_parent().slug}-cat-{self.title}", allow_unicode=True), + ignore_page_id=self.id, + ) + super(MediaLibraryUrlPage, self).save(clean, user, log_action, **kwargs) + + def get_frontend_url(self): + r = re.compile(r"^(?P.+?)-media-cat-(?P.+)$") + m = r.match(self.slug) + return f"/course/{m.group('coursePart')}/media/{m.group('catPart')}" class LibraryDocument(AbstractDocument): - # Todo: check https://filepreviews.io/ + """Unused for now""" - # Custom field example: + # Todo: check https://filepreviews.io/ display_text = models.CharField(max_length=1024, default="") description = models.TextField(default="") link_display_text = models.CharField(max_length=1024, default="") - thumbnail = models.URLField() + thumbnail = models.CharField(default="", max_length=1024) admin_form_fields = Document.admin_form_fields + ( "display_text", 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 d37b702d..dbccc970 100644 --- a/server/vbv_lernwelt/media_library/tests/media_library_factories.py +++ b/server/vbv_lernwelt/media_library/tests/media_library_factories.py @@ -1,7 +1,7 @@ -import json import uuid import wagtail_factories +from wagtail.rich_text import RichText from vbv_lernwelt.media_library.content_blocks import ( ExternalLinkBlock, @@ -11,8 +11,10 @@ from vbv_lernwelt.media_library.content_blocks import ( ) from vbv_lernwelt.media_library.models import ( LibraryDocument, - MediaCategoryPage, + MediaLibraryCategoryPage, + MediaLibraryContentPage, MediaLibraryPage, + MediaLibraryUrlPage, ) @@ -31,6 +33,34 @@ class MediaLibraryPageFactory(wagtail_factories.PageFactory): model = MediaLibraryPage +class MediaLibraryCategoryPageFactory(wagtail_factories.PageFactory): + title = "Handlungsfelder" + description = RichText( + "Finde alle Ressourcen der Handlungsfelder wie Lernmedien, Links und andere nützliche Informationen." + ) + + class Meta: + model = MediaLibraryCategoryPage + + +class MediaLibraryUrlPageFactory(wagtail_factories.PageFactory): + title = "Lexikon" + description = RichText("Hier könnte etwas stehen zum Lexikon") + icon_url = "/static/icons/icon-lernmedien-overview.svg" + content_url = "https://www.vbv.ch/de/der-vbv/lernen-lehren/lexikon" + url_open_blank = True + + class Meta: + model = MediaLibraryUrlPage + + +class MediaLibraryContentPageFactory(wagtail_factories.PageFactory): + title = "Fahrzeug" + + class Meta: + model = MediaLibraryContentPage + + class LearnMediaBlockFactory(wagtail_factories.StructBlockFactory): class Meta: model = LearnMediaBlock @@ -153,47 +183,47 @@ def create_document_collection(document_ids=None): } -class MediaCategoryPageFactory(wagtail_factories.PageFactory): - title = "Fahrzeug (Platzhalter)" - introduction_text = """ -Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. -Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. -Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. -Nulla consequat massa quis enim. Donec. -""".strip() - description_title = "Das erwartet dich in diesem Handlungsfeld" - description_text = """ -Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. -Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. -Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. -Nulla consequat massa quis enim. Donec. -""".strip() - overview_icon = "icon-hf-fahrzeug" - body = json.dumps( - [ - create_media_collection( - title="Lernmedien", - contents=[create_learn_media_block() for _ in range(8)], - ), - create_media_collection( - title="Links", - contents=[create_external_link_block() for _ in range(4)], - ), - create_media_collection( - title="Verankerung im Lernpfad", - contents=[create_internal_link_block() for _ in range(3)], - ), - create_media_collection( - title="Querverweise", - contents=[create_relative_link_block() for _ in range(2)], - ), - ] - ) - items = [ - ("item", "Versicherung 1"), - ("item", "Versicherung 2"), - ("item", "Versicherung 3"), - ] - - class Meta: - model = MediaCategoryPage +# class MediaCategoryPageFactory(wagtail_factories.PageFactory): +# title = "Fahrzeug (Platzhalter)" +# introduction_text = """ +# Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. +# Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. +# Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. +# Nulla consequat massa quis enim. Donec. +# """.strip() +# description_title = "Das erwartet dich in diesem Handlungsfeld" +# description_text = """ +# Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. +# Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. +# Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. +# Nulla consequat massa quis enim. Donec. +# """.strip() +# overview_icon = "icon-hf-fahrzeug" +# body = json.dumps( +# [ +# create_media_collection( +# title="Lernmedien", +# contents=[create_learn_media_block() for _ in range(8)], +# ), +# create_media_collection( +# title="Links", +# contents=[create_external_link_block() for _ in range(4)], +# ), +# create_media_collection( +# title="Verankerung im Lernpfad", +# contents=[create_internal_link_block() for _ in range(3)], +# ), +# create_media_collection( +# title="Querverweise", +# contents=[create_relative_link_block() for _ in range(2)], +# ), +# ] +# ) +# items = [ +# ("item", "Versicherung 1"), +# ("item", "Versicherung 2"), +# ("item", "Versicherung 3"), +# ] +# +# class Meta: +# model = MediaCategoryPage From f357e34536ce5dd84aa4b4d437310a55452a91b0 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 21 Jul 2023 11:38:44 +0200 Subject: [PATCH 2/3] Fix cypress tests --- cypress/e2e/mediaLibrary.cy.js | 4 ++-- server/vbv_lernwelt/media_library/tests/test_api.py | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/cypress/e2e/mediaLibrary.cy.js b/cypress/e2e/mediaLibrary.cy.js index a1fd68d1..041cdc86 100644 --- a/cypress/e2e/mediaLibrary.cy.js +++ b/cypress/e2e/mediaLibrary.cy.js @@ -23,7 +23,7 @@ describe("mediaLibrary.cy.js", () => { }); it("handlungsfeld should be accessible via direct url", () => { - cy.visit("/course/test-lehrgang/media/category/fahrzeug"); + cy.visit("/course/test-lehrgang/media/handlungsfelder/fahrzeug"); cy.get('[data-cy="hf-title"]').should("contain", "Fahrzeug"); }); @@ -31,6 +31,6 @@ describe("mediaLibrary.cy.js", () => { cy.visit("/course/test-lehrgang/learn/fahrzeug/handlungsfeld-fahrzeug"); cy.get('[data-cy="media-library-link"]') .invoke("attr", "href") - .should("contain", "/media/category/fahrzeug"); + .should("contain", "/media/handlungsfelder/fahrzeug"); }); }); diff --git a/server/vbv_lernwelt/media_library/tests/test_api.py b/server/vbv_lernwelt/media_library/tests/test_api.py index 9a8ce011..56a87ec1 100644 --- a/server/vbv_lernwelt/media_library/tests/test_api.py +++ b/server/vbv_lernwelt/media_library/tests/test_api.py @@ -22,4 +22,6 @@ class MediaLibraryAPITestCase(APITestCase): data = response.json() self.assertEqual(media_library.title, data["title"]) - self.assertEqual("Fahrzeug", data["children"][1]["title"]) + self.assertEqual("Handlungsfelder", data["children"][0]["title"]) + self.assertEqual("Allgemeines", data["children"][1]["title"]) + self.assertEqual("Fahrzeug", data["children"][0]["children"][0]["title"]) From d90030b6142d0b56c329d27f65c3c38ca09bbcb7 Mon Sep 17 00:00:00 2001 From: Daniel Egger Date: Fri, 21 Jul 2023 13:55:48 +0200 Subject: [PATCH 3/3] Update urls in course creation scripts --- .../vbv_lernwelt/course/creators/uk_course.py | 16 ++++++----- .../learnpath/create_vv_new_learning_path.py | 28 +++++++++---------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/server/vbv_lernwelt/course/creators/uk_course.py b/server/vbv_lernwelt/course/creators/uk_course.py index 7204b8e4..4e0442c0 100644 --- a/server/vbv_lernwelt/course/creators/uk_course.py +++ b/server/vbv_lernwelt/course/creators/uk_course.py @@ -218,12 +218,14 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst. first_title = title.split()[0] if first_title in [ - "Haushalt", + "Fahrzeug", "Reisen", - "Wohneigentum", - "KMU", "Einkommenssicherung", + "Haushalt", + "Wohneigentum", "Pensionierung", + "Rechtsstreitigkeiten", + "KMU", "Gesundheit", ]: LearningContentMediaLibraryFactory( @@ -233,7 +235,7 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst. f"

    In der Mediathek unter dem Handlungsfeld «{title}» findest du alle relevanten Ressourcen für deine Fachkompetenzen.

    " f"

    Wir empfehlen dir vor der Absolvierung der weiteren Lerneinheiten dich in die Thematik einzulesen.

    " ), - content_url=f"/course/überbetriebliche-kurse/media/category/{slugify(first_title)}", + content_url=f"/course/überbetriebliche-kurse/media/handlungsfelder/{slugify(first_title)}", ) else: LearningContentMediaLibraryFactory( @@ -874,7 +876,7 @@ def create_uk_circle_fahrzeug(lp, title="Fahrzeug"): f"

    In der Mediathek unter dem Handlungsfeld «{title}» findest du alle relevanten Ressourcen für deine Fachkompetenzen.

    " f"

    Wir empfehlen dir vor der Absolvierung der weiteren Lerneinheiten dich in die Thematik einzulesen.

    " ), - content_url=f"/course/{course_slug}/media/category/{slugify(title)}", + content_url=f"/course/überbetriebliche-kurse/media/handlungsfelder/fahrzeug", ) LearningContentAssignmentFactory( title="Fahrzeug - Mein erstes Auto", @@ -1008,7 +1010,7 @@ def create_uk_fr_circle_fahrzeug(lp, title="Véhicule"): f"

    Trouve toutes les ressources des champs d’action, comme les outils didactiques, les liens et autres informations utiles.

    " f"

    Nous te recommandons de te familiariser avec le sujet avant de suivre les autres unités de cours.

    " ), - content_url=f"/course/{course_slug}/media/category/{slugify(title)}", + content_url=f"/course/{course_slug}/media", ) LearningContentAssignmentFactory( title="Véhicule à moteur – Ma première voiture", @@ -1146,7 +1148,7 @@ def create_uk_it_circle_fahrzeug(lp, title="Veicolo"): f"

    Nella mediateca, sotto il campo d'azione «Veicolo», troverai tutte le risorse rilevanti per le tue competenze professionali.

    " f"

    Si consiglia di leggere l'argomento prima di completare le altre unità di apprendimento.

    " ), - content_url=f"/course/{course_slug}/media/category/{slugify(title)}", + content_url=f"/course/{course_slug}/media", ) LearningContentAssignmentFactory( title="Veicolo, la mia prima auto", diff --git a/server/vbv_lernwelt/learnpath/create_vv_new_learning_path.py b/server/vbv_lernwelt/learnpath/create_vv_new_learning_path.py index 3d668c21..472ecc54 100644 --- a/server/vbv_lernwelt/learnpath/create_vv_new_learning_path.py +++ b/server/vbv_lernwelt/learnpath/create_vv_new_learning_path.py @@ -311,7 +311,7 @@ def create_circle_fahrzeug(lp, title="Fahrzeug"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/fahrzeug", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -398,7 +398,7 @@ def create_circle_haushalt(lp, title="Haushalt"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -460,7 +460,7 @@ def create_circle_rechtsstreitigkeiten(lp, title="Rechtsstreitigkeiten"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Rechtsstreitigkeiten", parent=circle) @@ -493,7 +493,7 @@ def create_circle_reisen(lp, title="Reisen"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -576,7 +576,7 @@ def create_circle_einkommenssicherung(lp, title="Einkommenssicherung"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -664,7 +664,7 @@ def create_circle_wohneigentum(lp, title="Wohneigentum"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Wohneigentum", parent=circle) @@ -717,7 +717,7 @@ def create_circle_pensionierung(lp, title="Pensionierung"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -795,7 +795,7 @@ def create_circle_erben(lp, title="Erben/Vererben"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Erben/Vererben", parent=circle) @@ -854,7 +854,7 @@ def create_circle_gesundheit(lp, title="Gesundheit"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -938,7 +938,7 @@ def create_circle_sparen(lp, title="Sparen"): LearningContentMediaLibraryFactory( title="Mediathek", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Sparen", parent=circle) @@ -973,7 +973,7 @@ def create_circle_selbstaendigkeit(lp, title="Selbstständigkeit"): LearningContentMediaLibraryFactory( title="Mediathek", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Selbstständigkeit", parent=circle) @@ -1006,7 +1006,7 @@ def create_circle_kmu(lp, title="KMU"): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle) @@ -1080,7 +1080,7 @@ def create_circle_standard_small( LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title, allow_unicode=True)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title, allow_unicode=True)}", ) if lu_title is None: @@ -1110,7 +1110,7 @@ def create_circle_standard(lp, title, lc_title, goals=None, description=None): LearningContentMediaLibraryFactory( title=f"Mediathek {title}", parent=circle, - content_url=f"/course/versicherungsvermittler-in/media/category/{slugify(title)}", + content_url=f"/course/versicherungsvermittler-in/media/handlungsfelder/{slugify(title)}", ) LearningSequenceFactory(title="Einstieg", parent=circle)