Simplify media library models

This commit is contained in:
Daniel Egger 2023-07-18 13:58:45 +02:00
parent fb7b5a7753
commit 815d81a471
20 changed files with 642 additions and 1118 deletions

View File

@ -4,11 +4,13 @@ export interface Props {
description: string;
call2Action: string;
link: string;
icon?: string;
iconUrl?: string;
externalLink?: boolean;
}
withDefaults(defineProps<Props>(), {
icon: "",
iconUrl: "",
externalLink: false,
});
</script>
@ -16,8 +18,19 @@ withDefaults(defineProps<Props>(), {
<div class="flex flex-col justify-between bg-white p-8 pb-4 lg:flex-row lg:pb-8">
<div class="mb-4 lg:mb-0">
<h3 class="mb-4">{{ title }}</h3>
<p class="mb-4">{{ description }}</p>
<!-- eslint-disable vue/no-v-html -->
<p class="default-wagtail-rich-text mb-4" v-html="description"></p>
<a
v-if="externalLink"
:href="link"
target="_blank"
class="btn-text inline-flex items-center py-2 pl-0 pr-3"
>
<span class="inline">{{ call2Action }}</span>
<it-icon-arrow-right class="ml-1 h-5 w-5"></it-icon-arrow-right>
</a>
<router-link
v-else
:to="link"
class="btn-text inline-flex items-center py-2 pl-0 pr-3"
:data-cy="`${title}-link`"
@ -26,11 +39,9 @@ withDefaults(defineProps<Props>(), {
<it-icon-arrow-right class="ml-1 h-5 w-5"></it-icon-arrow-right>
</router-link>
</div>
<div
v-if="icon"
:class="[`bg-${icon}`]"
class="-ml-8 h-32 bg-contain bg-left bg-no-repeat lg:-mr-8 lg:ml-0 lg:block lg:w-2/6 lg:bg-right"
></div>
<div v-if="iconUrl" class="flex justify-end lg:w-2/6">
<img :src="iconUrl" class="w-full lg:w-48" alt="icon" />
</div>
</div>
</template>

View File

@ -1,182 +0,0 @@
<script setup lang="ts">
import LinkCard from "@/components/mediaLibrary/LinkCard.vue";
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import type { MediaBlockType } from "@/types";
import * as log from "loglevel";
import { computed } from "vue";
import { useRoute } from "vue-router";
const props = defineProps<{
mediaCategorySlug: string;
}>();
const route = useRoute();
log.debug("MediaCategoryDetailView created", props.mediaCategorySlug, route);
const mediaStore = useMediaLibraryStore();
const mediaCategory = computed(() => {
return mediaStore.mediaLibraryPage?.children.find((category) =>
category.slug.endsWith(props.mediaCategorySlug)
);
});
const backLink = computed(() => {
if (route.query.back) {
return route.query.back;
} else {
return `${mediaStore.mediaLibraryPage?.frontend_url}/category`;
}
});
const maxCardItems = 4;
const maxListItems = 6;
const displayAsCard = (itemType: MediaBlockType): boolean => {
return itemType === "learn_media" || itemType === "relative_link";
};
function hasMoreItems<T>(items: T[], maxItems: number): boolean {
return items.length > maxItems;
}
function getMaxDisplayItems<T>(items: T[], maxItems: number) {
return items.slice(0, maxItems);
}
function getMaxDisplayItemsForType<T>(itemType: MediaBlockType, items: T[]) {
return displayAsCard(itemType)
? getMaxDisplayItems(items, maxCardItems)
: getMaxDisplayItems(items, maxListItems);
}
function hasMoreItemsForType<T>(itemType: MediaBlockType, items: T[]) {
const maxItems = displayAsCard(itemType) ? maxCardItems : maxListItems;
return hasMoreItems(items, maxItems);
}
</script>
<template>
<div
v-if="mediaCategory"
class="fixed top-0 h-full w-full overflow-y-scroll bg-white"
>
<div class="bg-gray-200 pb-4 lg:pb-12">
<div class="container-large">
<nav class="py-4 lg:pb-8">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="(backLink as string)"
>
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<div class="flex justify-between md:flex-col lg:flex-row">
<div class="lg:w-6/12">
<h3 class="text-large mb-3 font-normal text-gray-900">
{{ $t("mediaLibrary.handlungsfelder.title_one") }}
</h3>
<h1 class="mb-4 lg:mb-8" data-cy="hf-title">{{ mediaCategory.title }}</h1>
<p class="text-large">{{ mediaCategory.introduction_text }}</p>
</div>
<div>
<img
class="float-left hidden md:mt-8 md:block lg:float-right lg:mt-0 lg:h-full lg:max-w-[505px]"
:src="`/static/icons/handlungsfelder/${mediaCategory.detail_image}.svg`"
alt=""
/>
</div>
</div>
</div>
</div>
<div class="container-large">
<section class="mb-20 mt-8 lg:w-2/3">
<h2 class="mb-4">{{ mediaCategory.description_title }}</h2>
<!-- as long as there is no real content Chrigi says p class="mb-4">{{ mediaCategory.description_text }}</p-->
<ul>
<li
v-for="item in mediaCategory.items"
:key="item.id"
class="mb-2 flex items-center"
>
<it-icon-check class="mr-4 h-8 w-8 flex-none text-sky-500"></it-icon-check>
{{ item.value }}
</li>
</ul>
</section>
<template
v-for="content_collection in mediaCategory.body"
:key="content_collection.value.title"
>
<section v-if="content_collection.value?.contents?.length" class="mb-20">
<h2 class="mb-4">{{ content_collection.value.title }}</h2>
<p class="mb-4 lg:w-2/3">
{{ content_collection.value.description }}
</p>
<ul
:class="{
'grid grid-cols-1 gap-4 lg:grid-cols-2': displayAsCard(
content_collection.value.contents[0].type
),
'border-t': !displayAsCard(content_collection.value.contents[0].type),
'mb-6': hasMoreItemsForType(
content_collection.value.contents[0].type,
content_collection.value.contents
),
}"
>
<li
v-for="mediaItem in getMaxDisplayItemsForType(
content_collection.value.contents[0].type,
content_collection.value.contents
)"
:key="mediaItem.id"
>
<LinkCard
v-if="displayAsCard(mediaItem.type)"
:title="mediaItem.value.title"
:icon="mediaItem.value.icon_url"
:description="mediaItem.value.description"
:url="mediaItem.value.url"
:link-text="mediaItem.value.link_display_text"
:open-window="mediaItem.value.open_window"
/>
<div
v-else
class="flex flex-col justify-between border-b py-4 lg:flex-row lg:items-center"
>
<h4 class="text-bold">{{ mediaItem.value.title }}</h4>
<MediaLink
:blank="mediaItem.value.open_window"
:to="mediaItem.value.url"
class="link"
>
{{ mediaItem.value.link_display_text }}
</MediaLink>
</div>
</li>
</ul>
<router-link
v-if="
hasMoreItemsForType(
content_collection.value.contents[0].type,
content_collection.value.contents
)
"
:to="`${mediaCategory.frontend_url}/media`"
class="btn-text inline-flex items-center py-2 pl-0"
>
<span>Alle anschauen</span>
<it-icon-arrow-right></it-icon-arrow-right>
</router-link>
</section>
</template>
</div>
</div>
</template>
<style scoped></style>

View File

@ -1,52 +0,0 @@
<script setup lang="ts">
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import { computed, ref, watch } from "vue";
log.debug("HandlungsfelderOverview created");
const mediaStore = useMediaLibraryStore();
const dropdownSelected = ref(mediaStore.selectedLearningPath);
const categories = computed(() => {
if (mediaStore.mediaLibraryPage) {
return mediaStore.mediaLibraryPage.children.filter(
(cat) => cat.course_category.general === false
);
}
return [];
});
watch(dropdownSelected, (newValue) =>
mediaStore.$patch({
selectedLearningPath: newValue,
})
);
</script>
<template>
<div class="container-large">
<div class="mb-10 mt-6 flex flex-col items-center justify-between lg:flex-row">
<h1>
{{ $t("mediaLibrary.handlungsfelder.title", { count: categories.length }) }}
</h1>
<!-- <ItDropdownSelect v-model="dropdownSelected" :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
</div>
<div v-if="mediaStore.mediaLibraryPage">
<ul class="grid grid-cols-1 gap-5 md:grid-cols-4">
<li v-for="cat in categories" :key="cat.id" class="bg-white p-4">
<router-link :to="cat.frontend_url" :data-cy="`${cat.title}-link`">
<img
class="m-auto"
:src="`/static/icons/handlungsfelder/${cat.overview_icon}.svg`"
alt=""
/>
<h3 class="text-center text-base">{{ cat.title }}</h3>
</router-link>
</li>
</ul>
</div>
</div>
</template>
<style scoped></style>

View File

@ -1,57 +0,0 @@
<script setup lang="ts">
import OverviewCard from "@/components/mediaLibrary/OverviewCard.vue";
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import { computed, ref, watch } from "vue";
log.debug("MediaMainView created");
const mediaStore = useMediaLibraryStore();
const dropdownSelected = ref(mediaStore.selectedLearningPath);
const generalCategory = computed(() => {
if (mediaStore.mediaLibraryPage) {
return mediaStore.mediaLibraryPage.children.find(
(cat) => cat.course_category.general
);
}
return undefined;
});
watch(dropdownSelected, (newValue) =>
mediaStore.$patch({
selectedLearningPath: newValue,
})
);
</script>
<template>
<div class="container-large">
<div class="mb-12 mt-6 flex flex-col justify-between lg:flex-row">
<h1>{{ $t("mediaLibrary.title") }}</h1>
<!-- <ItDropdownSelect-->
<!-- v-model="dropdownSelected"-->
<!-- :items="mediaStore.availableLearningPaths"></ItDropdownSelect>-->
</div>
<OverviewCard
v-if="mediaStore.mediaLibraryPage"
:title="$t('mediaLibrary.handlungsfelder.title_other')"
:call2-action="$t('general.show')"
:link="`${mediaStore.mediaLibraryPage.frontend_url}/category`"
:description="$t('mediaLibrary.handlungsfelder.description')"
icon="handlungsfelder-overview"
class="mb-6"
></OverviewCard>
<OverviewCard
v-if="mediaStore.mediaLibraryPage && generalCategory"
:title="$t('mediaLibrary.learningMedia.titel')"
:call2-action="$t('general.show')"
:link="`${generalCategory.frontend_url}/media`"
:description="$t('mediaLibrary.learningMedia.description')"
icon="lernmedien-overview"
class="mb-6"
></OverviewCard>
</div>
</template>
<style scoped></style>

View File

@ -1,88 +0,0 @@
<script setup lang="ts">
import MediaLink from "@/components/mediaLibrary/MediaLink.vue";
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import { computed } from "vue";
const props = defineProps<{
courseSlug: string;
mediaCategorySlug: string;
}>();
log.debug("MLMediaListPage created", props.courseSlug);
const mediaStore = useMediaLibraryStore();
const mediaCategory = computed(() => {
return mediaStore.mediaLibraryPage?.children.find((category) =>
category.slug.endsWith(props.mediaCategorySlug)
);
});
const mediaList = computed(() => {
if (mediaCategory.value) {
const learnMediaCollection = mediaCategory.value.body.find((contentCollection) => {
if (contentCollection.value.contents.length) {
return contentCollection.value.contents[0].type === "learn_media";
}
});
return learnMediaCollection?.value;
}
return undefined;
});
</script>
<template>
<div
v-if="mediaCategory && mediaStore.mediaLibraryPage && mediaList"
class="fixed top-0 h-full w-full overflow-y-scroll bg-white"
>
<div class="bg-gray-200">
<div class="container-large">
<nav class="py-4 lg:pb-8">
<a class="btn-text inline-flex items-center pl-0" @click="$router.back()">
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</a>
</nav>
<h1 class="mb-4">
{{ `${mediaCategory.title}: ${mediaList.title}` }}
</h1>
</div>
</div>
<div class="container-large">
<section class="mb-20">
<ul class="border-t">
<li
v-for="item in mediaList.contents"
:key="item.id"
class="flex items-center justify-between border-b py-4"
>
<div class="flex items-center justify-between">
<div v-if="item.value.icon_url">
<img class="mr-6 max-h-[70px]" :src="item.value.icon_url" />
</div>
<div>
<h4 class="text-bold">{{ item.value.title }}</h4>
<p v-if="item.value.description" class="mb-2">
{{ item.value.description }}
</p>
</div>
</div>
<div class="">
<media-link
:to="item.value.url"
:blank="item.value.open_window"
class="link"
>
{{ item.value.link_display_text }}
</media-link>
</div>
</li>
</ul>
</section>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,40 @@
<script setup lang="ts">
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import { computed } from "vue";
import type { MediaLibraryCategoryPage } from "@/types";
const props = defineProps<{
categorySlug: string;
}>();
log.debug("MediaLibraryCategoryPage created", props);
const mediaStore = useMediaLibraryStore();
const category = computed(() => {
return mediaStore.mediaLibraryPage?.children.find((c) =>
c.frontend_url.endsWith(props.categorySlug)
) as MediaLibraryCategoryPage | undefined;
});
</script>
<template>
<div v-if="mediaStore.mediaLibraryPage && category" class="container-large">
<div class="mb-10 mt-6 flex flex-col items-center justify-between lg:flex-row">
<h1>{{ category.title }}</h1>
</div>
<div>
<ul class="grid grid-cols-1 gap-5 md:grid-cols-4">
<li v-for="cat in category.children" :key="cat.id" class="bg-white p-4">
<router-link :to="cat.frontend_url" :data-cy="`${cat.title}-link`">
<img class="m-auto" :src="cat.icon_overview_url" alt="" />
<h3 class="text-center text-base">{{ cat.title }}</h3>
</router-link>
</li>
</ul>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,79 @@
<script setup lang="ts">
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import { computed } from "vue";
import type { MediaLibraryCategoryPage, MediaLibraryContentPage } from "@/types";
const props = defineProps<{
categorySlug: string;
contentSlug: string;
}>();
log.debug("MediaLibraryContentPage created", props);
const mediaStore = useMediaLibraryStore();
const parentCategory = computed(() => {
return mediaStore.mediaLibraryPage?.children.find((c) =>
c.frontend_url.endsWith(props.categorySlug)
) as MediaLibraryCategoryPage | undefined;
});
const mediaCategory = computed(() => {
return parentCategory.value?.children.find((c) =>
c.frontend_url.endsWith(props.contentSlug)
) as MediaLibraryContentPage | undefined;
});
</script>
<template>
<div
v-if="mediaStore.mediaLibraryPage && mediaCategory"
class="fixed top-0 h-full w-full overflow-y-scroll bg-white"
>
<div class="bg-gray-200 pb-4 lg:pb-12">
<div class="container-large">
<nav class="py-4 lg:pb-8">
<router-link
class="btn-text inline-flex items-center pl-0"
:to="
parentCategory?.frontend_url || mediaStore.mediaLibraryPage.frontend_url
"
>
<it-icon-arrow-left />
<span>{{ $t("general.back") }}</span>
</router-link>
</nav>
<div class="flex justify-between md:flex-col lg:flex-row">
<div class="lg:w-6/12">
<h3 class="text-large mb-3 font-normal text-gray-900">
{{ $t("mediaLibrary.handlungsfelder.title_one") }}
</h3>
<h1 class="mb-4 lg:mb-8" data-cy="hf-title">{{ mediaCategory.title }}</h1>
<!-- eslint-disable vue/no-v-html -->
<p
class="default-wagtail-rich-text text-large"
v-html="mediaCategory.description"
></p>
</div>
<div>
<img
class="float-left hidden md:mt-8 md:block lg:float-right lg:mt-0 lg:h-full lg:w-[505px]"
:src="mediaCategory.icon_detail_url"
alt=""
/>
</div>
</div>
</div>
</div>
<div class="container-large">
<!-- eslint-disable vue/no-v-html -->
<section
class="default-wagtail-rich-text mb-20 mt-8 lg:w-2/3"
v-html="mediaCategory.body"
></section>
</div>
</div>
</template>
<style scoped></style>

View File

@ -0,0 +1,33 @@
<script setup lang="ts">
import { useMediaLibraryStore } from "@/stores/mediaLibrary";
import * as log from "loglevel";
import OverviewCard from "@/components/mediaLibrary/OverviewCard.vue";
log.debug("MediaLibraryIndexPage created");
const mediaStore = useMediaLibraryStore();
</script>
<template>
<div v-if="mediaStore.mediaLibraryPage" class="container-large">
<div class="mb-12 mt-6 flex flex-col justify-between lg:flex-row">
<h1>{{ mediaStore.mediaLibraryPage.title }}</h1>
</div>
<OverviewCard
v-for="category in mediaStore.mediaLibraryPage.children"
:key="category.id"
:title="category.title"
:call2-action="$t('general.show')"
:link="
category.content_type === 'media_library.MediaLibraryUrlPage'
? category.content_url
: category.frontend_url
"
:description="category.description"
:icon-url="category.icon_url"
:external-link="category.content_type === 'media_library.MediaLibraryUrlPage'"
class="mb-6"
></OverviewCard>
</div>
</template>
<style scoped></style>

View File

@ -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
</router-link>
</li>
<li class="ml-6 inline-block lg:ml-12">
<li
v-for="category in mediaLibraryStore.mediaLibraryPage.children"
:key="category.id"
class="ml-6 inline-block lg:ml-12"
>
<router-link
:to="`${mediaLibraryStore.mediaLibraryPage.frontend_url}/category`"
v-if="category.content_type === 'media_library.MediaLibraryCategoryPage'"
:to="category.frontend_url"
>
Handlungsfelder
{{ category.title }}
</router-link>
<a v-else :href="category.content_url" target="_blank">
{{ category.title }}
</a>
</li>
<li class="ml-6 inline-block lg:ml-12">Allgemeines zu Versicherungen</li>
<li class="ml-6 inline-block lg:ml-12">Lernmedien</li>
<li class="ml-6 inline-block lg:ml-12">
<a href="https://www.vbv.ch/de/der-vbv/lernen-lehren/lexikon">Lexikon</a>
</li>
<li class="ml-6 inline-block lg:ml-12"></li>
</ul>
</nav>
<main>

View File

@ -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"),
},
],
},

View File

@ -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: {},

View File

@ -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 {

View File

@ -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"),
}),

View File

@ -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

View File

@ -1,5 +1,4 @@
COURSE_TEST_ID = -1
COURSE_VERSICHERUNGSVERMITTLERIN_OLD_ID = -2
COURSE_UK = -3
COURSE_VERSICHERUNGSVERMITTLERIN_ID = -4
COURSE_UK_FR = -5

View File

@ -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"<p>In der Mediathek unter dem Handlungsfeld «{title}» findest du alle relevanten Ressourcen für deine Fachkompetenzen.</p>"
f"<p>Wir empfehlen dir vor der Absolvierung der weiteren Lerneinheiten dich in die Thematik einzulesen.</p>"
),
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"<h2>Lernmedien</h2>"
f"<h3>Allgemeines</h3>"
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
),
)
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"<h2>Lernmedien</h2>"
f"<h3>Allgemeines</h3>"
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
),
)

View File

@ -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"<h2>Lernmedien</h2>"
f"<h3>Allgemeines</h3>"
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
),
)
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"<h2>Lernmedien</h2>"
f"<h3>Allgemeines</h3>"
f"<ul><li>Mit Risiken im Strassenverkehr umgehen</li><li>Versicherungsschutz</li><li>Vertragsarten</li><li>Zusammenfassung</li></ul>"
),
)

View File

@ -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(

View File

@ -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<coursePart>.+?)-media-cat-(?P<catPart>.+)$")
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<coursePart>.+?)-media-cat-(?P<catPart>.+?)-content-(?P<contentPart>.+)$"
)
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<coursePart>.+?)-media-cat-(?P<catPart>.+)$")
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",

View File

@ -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