diff --git a/client/src/pages/learningPath/learningContentPage/LearningContentParent.vue b/client/src/pages/learningPath/learningContentPage/LearningContentParent.vue index e34bae26..554d165d 100644 --- a/client/src/pages/learningPath/learningContentPage/LearningContentParent.vue +++ b/client/src/pages/learningPath/learningContentPage/LearningContentParent.vue @@ -1,5 +1,7 @@ + + diff --git a/client/src/pages/learningPath/learningContentPage/blocks/TestBlock.vue b/client/src/pages/learningPath/learningContentPage/blocks/TestBlock.vue new file mode 100644 index 00000000..f413dc37 --- /dev/null +++ b/client/src/pages/learningPath/learningContentPage/blocks/TestBlock.vue @@ -0,0 +1,59 @@ + + + diff --git a/client/src/services/circle.ts b/client/src/services/circle.ts index 7cacaff2..1455c12e 100644 --- a/client/src/services/circle.ts +++ b/client/src/services/circle.ts @@ -14,11 +14,13 @@ import type { import groupBy from "lodash/groupBy"; import partition from "lodash/partition"; import values from "lodash/values"; +import log from "loglevel"; function isLearningContentType(object: any): object is LearningContent { return ( object?.content_type === "learnpath.LearningContentAssignment" || object?.content_type === "learnpath.LearningContentAttendanceCourse" || + object?.content_type === "learnpath.LearningContentDocumentList" || object?.content_type === "learnpath.LearningContentFeedback" || object?.content_type === "learnpath.LearningContentLearningModule" || object?.content_type === "learnpath.LearningContentMediaLibrary" || @@ -83,6 +85,7 @@ export function parseLearningSequences( learningUnit.learningContents.push(child); } else { + log.error("Unknown CircleChild found...", child); throw new Error("Unknown CircleChild found..."); } }); diff --git a/client/src/types.ts b/client/src/types.ts index 1a090d74..9e25f7d1 100644 --- a/client/src/types.ts +++ b/client/src/types.ts @@ -23,6 +23,7 @@ export interface CircleLight { export type LearningContent = | LearningContentAssignment | LearningContentAttendanceCourse + | LearningContentDocumentList | LearningContentFeedback | LearningContentLearningModule | LearningContentMediaLibrary @@ -55,6 +56,17 @@ export interface LearningContentAttendanceCourse extends LearningContentInterfac readonly content_type: "learnpath.LearningContentAttendanceCourse"; } +export interface LearningContentDocument { + readonly type: "document"; + readonly id: string; + readonly value: MediaLibraryContentBlockValue; +} + +export interface LearningContentDocumentList extends LearningContentInterface { + readonly content_type: "learnpath.LearningContentDocumentList"; + readonly documents: LearningContentDocument[]; +} + export interface LearningContentFeedback extends LearningContentInterface { readonly content_type: "learnpath.LearningContentFeedback"; } @@ -73,11 +85,12 @@ export interface LearningContentPlaceholder extends LearningContentInterface { export interface LearningContentRichText extends LearningContentInterface { readonly content_type: "learnpath.LearningContentRichText"; - text: string; + readonly text: string; } export interface LearningContentTest extends LearningContentInterface { readonly content_type: "learnpath.LearningContentTest"; + readonly checkbox_text: string; } export interface LearningContentVideo extends LearningContentInterface { diff --git a/client/src/utils/typeMaps.ts b/client/src/utils/typeMaps.ts index fa443537..dbe01f63 100644 --- a/client/src/utils/typeMaps.ts +++ b/client/src/utils/typeMaps.ts @@ -26,6 +26,8 @@ export function learningContentTypeData( } case "learnpath.LearningContentAttendanceCourse": return { title: "Präsenzkurs", icon: "it-icon-lc-training" }; + case "learnpath.LearningContentDocumentList": + return { title: "Dokumente", icon: "it-icon-lc-document" }; case "learnpath.LearningContentLearningModule": return { title: "Lernmodul", icon: "it-icon-lc-learning-module" }; case "learnpath.LearningContentMediaLibrary": @@ -42,5 +44,7 @@ export function learningContentTypeData( return { title: "In Umsetzung", icon: "it-icon-lc-document" }; } - return assertUnreachable(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + return assertUnreachable(`did not handle ${lc.content_type}`); } diff --git a/client/src/utils/utils.ts b/client/src/utils/utils.ts index c6a55326..52bceeee 100644 --- a/client/src/utils/utils.ts +++ b/client/src/utils/utils.ts @@ -1,3 +1,3 @@ -export function assertUnreachable(): never { - throw new Error("Didn't expect to get here"); +export function assertUnreachable(msg: string): never { + throw new Error("Didn't expect to get here, " + msg); } diff --git a/server/vbv_lernwelt/assignment/creators/create_assignments.py b/server/vbv_lernwelt/assignment/creators/create_assignments.py index 2668002a..2036fd65 100644 --- a/server/vbv_lernwelt/assignment/creators/create_assignments.py +++ b/server/vbv_lernwelt/assignment/creators/create_assignments.py @@ -21,7 +21,7 @@ from wagtail.blocks.list_block import ListBlock, ListValue from wagtail.rich_text import RichText -def create_uk_casework(course_id=COURSE_UK): +def create_uk_fahrzeug_casework(course_id=COURSE_UK): assignment_list_page = ( CoursePage.objects.get(course_id=course_id) .get_children() @@ -465,7 +465,7 @@ def create_uk_casework(course_id=COURSE_UK): return assignment -def create_uk_prep_assignment(course_id=COURSE_UK): +def create_uk_fahrzeug_prep_assignment(course_id=COURSE_UK): assignment_list_page = ( CoursePage.objects.get(course_id=course_id) .get_children() @@ -489,7 +489,7 @@ def create_uk_prep_assignment(course_id=COURSE_UK):
  • d2pv.ük4: Sie erläutern die Prozesse und Abläufe im privaten Versicherungsbereich. (K2)
  • - +

    Handlungskompetenz e4: Betriebsbezogene Inhalte multimedial aufbereiten
    Arbeitssituation 1: Charakteristiken der Branche und Stärken des Betriebs einbringen
    @@ -499,10 +499,10 @@ def create_uk_prep_assignment(course_id=COURSE_UK):

  • e4.pv.ük4: Sie erläutern die Grundlagen der Produkte von Privatversicherungen. (K2)
  • - +

    Ausgangslage

    - Stell dir vor, du hast die Autoprüfung abgeschlossen und nun kann es endlich losgehen mit deiner Mobilität. + Stell dir vor, du hast die Autoprüfung abgeschlossen und nun kann es endlich losgehen mit deiner Mobilität. Welches wird dein erstes eigenes Auto sein? Dieses Auto möchtest du natürlich auch schützen und richtig versichern.

    @@ -780,6 +780,477 @@ def create_uk_prep_assignment(course_id=COURSE_UK): return assignment +def create_uk_kickoff_prep_assignment(course_id=COURSE_UK): + assignment_list_page = ( + CoursePage.objects.get(course_id=course_id) + .get_children() + .exact_type(AssignmentListPage) + .first() + ) + + assignment = AssignmentFactory( + parent=assignment_list_page, + assignment_type=AssignmentType.PREP_ASSIGNMENT.name, + title="Kickoff - Versicherungswirtschaft", + effort_required="ca. 2 Stunden", + intro_text=replace_whitespace( + """ +

    Handlungskompetenzen, Arbeitssituationen & Leistungsziele

    +

    + Handlungskompetenz d2: Informations- und Beratungsgespräch mit Kunden oder Lieferanten führen
    + Arbeitssituation 4: Kunden beraten und dazugehörige Prozesse abwickeln
    +

    +

    + +

    + Handlungskompetenz c3: Betriebliche Prozesse dokumentieren, koordinieren und umsetzen
    + Arbeitssituation 5: Anträge verarbeiten, Verträge gestalten, Produkte entwickeln und pflegen
    +

    +

    + +

    Ausgangslage

    +

    + Das Leben hat nicht nur Sonnenseiten, es birgt auch Risiken in sich. So können wir schon morgen einen + Unfall haben oder schwer erkranken. Oder ein Haus wird durch ein Hagelzug ziemlich beschädigt. Vor + solchen Risiken kann sich niemand vollständig schützen. Jedoch können wir uns gegen den finanziellen + Schaden absichern. Diese Art von Schutz bieten Versicherungen.
    + In dieser Sequenz beschäftigen wir uns damit, das eigentlich eine Versicherung ausmacht. +

    + """ + ), + performance_objectives=[], + ) + + assignment.tasks = [] + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 1: Verschaffe dir einen ersten Überblick zum Thema.", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + 'Schaue dazu das folgende Video: Wie funktioniert eine Versicherung? – Einfach erklärt ' + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText( + "Ja, ich habe das Video angeschaut und verstanden." + ) + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 2: «Idee Versicherung» lesen", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + "Lese und bearbeite im Buch «Idee Versicherung» die Seiten 12 bis 30." + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText( + "Ja, ich habe die Seiten gelesen und verstanden." + ) + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 3: Skizze erstellen", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    + Erstelle aufgrund des Gelesenen eine Skizze, welche für dich das Grundprinzip einer Versicherung darstellt. +

    +

    + Nimm diese Skizze mit in den Unterricht. +

    + """ + ) + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText("Ja, ich habe die Skizze erstellt.") + ), + ), + ], + ), + ), + ) + ) + + assignment.save() + + return assignment + + +def create_uk_basis_prep_assignment(course_id=COURSE_UK): + assignment_list_page = ( + CoursePage.objects.get(course_id=course_id) + .get_children() + .exact_type(AssignmentListPage) + .first() + ) + + assignment = AssignmentFactory( + parent=assignment_list_page, + assignment_type=AssignmentType.PREP_ASSIGNMENT.name, + title="Circle Basis", + effort_required="ca. 1 Tag", + intro_text=replace_whitespace( + """ +

    Handlungskompetenzen, Arbeitssituationen & Leistungsziele

    +

    + Handlungskompetenz d2: Informations- und Beratungsgespräch mit Kunden oder Lieferanten führen
    + Arbeitssituation 4: Kunden beraten und dazugehörige Prozesse abwickeln
    +

    +

    + +

    + Handlungskompetenz c3: Betriebliche Prozesse dokumentieren, koordinieren und umsetzen
    + Arbeitssituation 5: Anträge verarbeiten, Verträge gestalten, Produkte entwickeln und pflegen
    +

    +

    + +

    Ausgangslage

    +

    + Du bist in einer spannenden Branche gelandet. In der Welt der Versicherungen. Für das Funktionieren + einer modernen Volkswirtschaft sind Versicherungen von grosser Bedeutung. Deshalb ist es zwingend + notwendig, dass du dich mit den Grundlagen der Versicherungen auseinandersetzt. +

    + """ + ), + performance_objectives=[], + ) + + assignment.tasks = [] + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 1: Verschaffe dir einen ersten Überblick zum Thema.", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + """ + Verschaffe dir einen ersten Überblick zu den unterschiedlichen Prozessen + eines Versicherungsunternehmens. Lese dazu im Lernmedium «Kunde und + Versicherung» den Abschnitt «Die Wertschöpfungsprozesse eines + Versicherers» (Seiten 14 bis 17) + """ + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText("Ja, ich habe den Abschnitt gelesen.") + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 2: Interview", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + """ +

    Interviewe in deinem Lehrbetrieb je eine Person aus zwei von dir gewählten + Geschäftsprozess-Bereichen (z. B. Underwriting und Schaden) und frage sie + folgendes:

    + + """ + ) + ), + ), + ( + "user_text_input", + UserTextInputBlockFactory( + text=RichText( + "Notiere deine Ergebnisse elektronisch oder physisch und bring alles ins Training mit." + ) + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 3: Überblick Kennzahlen", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    + Verschaffe dir einen ersten Überblick zu den unterschiedlichen Kennzahlen. + Lese dazu im Lernmedium «Kunde und Versicherung» den Abschnitt «Das + Unternehmen Versicherung in Zahlen» (Seiten 26 bis 32) +

    + """ + ) + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText("Ja, ich habe den Abschnitt gelesen.") + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 4: Kennzahlen im Detail", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    + Bring aus deinem Lehrbetrieb 3 Kennzahlen aus dem letzten Geschäftsjahr in Erfahrung: +

    + +

    Wenn du nicht weiterkommst, frag deine Ansprechperson im Lehrbetrieb.

    + """ + ) + ) + ), + ), + ( + "user_text_input", + UserTextInputBlockFactory( + text=RichText( + "Notiere deine Ergebnisse elektronisch oder physisch und bring alles ins Training mit." + ) + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 5: Rechte und Pflichten", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    Lese im Lehrmittel «Kunde und Versicherung» den Abschnitt «Die Rechteund Pflichten der Parteien» (Seiten 130 bis 141)

    + """ + ) + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText("Ja, ich habe den Abschnitt gelesen.") + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 6: Versicherungsvertraggesetz (VVG)", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    Besorge dir ein VVG (Versicherungsvertragsgesetz) auf www.fedlex.admin.ch

    + """ + ) + ) + ), + ), + ( + "user_confirmation", + ExplanationBlockFactory( + text=RichText("Ja, ich habe das VVG besorgt.") + ), + ), + ], + ), + ), + ) + ) + + assignment.tasks.append( + ( + "task", + TaskBlockFactory( + title="Teilaufgabe 7: Rechte und Pflichten im Lehrbetrieb", + # it is hard to create a StreamValue programmatically, we have to + # create a `StreamValue` manually. Ask Daniel and/or Ramon + content=StreamValue( + TaskContentStreamBlock(), + stream_data=[ + ( + "explanation", + ExplanationBlockFactory( + text=RichText( + replace_whitespace( + """ +

    + Kläre im Lehrbetrieb zwei Fragen: +

    + + """ + ) + ) + ), + ), + ( + "user_text_input", + UserTextInputBlockFactory( + text=RichText( + "Notiere deine Ergebnisse elektronisch oder physisch und bring alles ins Training mit." + ) + ), + ), + ], + ), + ), + ) + ) + + assignment.save() + + return assignment + + def create_uk_reflection(course_id=COURSE_UK, circle_title="Fahrzeug"): assignment_list_page = ( CoursePage.objects.get(course_id=course_id) @@ -791,15 +1262,15 @@ def create_uk_reflection(course_id=COURSE_UK, circle_title="Fahrzeug"): assignment = AssignmentFactory( parent=assignment_list_page, assignment_type=AssignmentType.REFLECTION.name, - title=f"{circle_title} - Reflexionsfragen", + title=f"Reflexion", effort_required="ca. 1 Stunde", intro_text=replace_whitespace( """

    - Du hast in diesem Circle viele neue Inhalte und Inputs erhalten. - Nun ist es Zeit, nochmals auf dein Kompetenzprofil zu schauen. - Das Beantworten von Reflexionsfragen hilft dir den eigenen Lern- und Denkprozess sichtbar und begreifbar zu machen. - Es deckt deine persönlichen Stärken und Schwächen während der Erarbeitung auf und hilft dir, dich laufend zu verbessern. + Du hast in diesem Circle viele neue Inhalte und Inputs erhalten. + Nun ist es Zeit, nochmals auf dein Kompetenzprofil zu schauen. + Das Beantworten von Reflexionsfragen hilft dir den eigenen Lern- und Denkprozess sichtbar und begreifbar zu machen. + Es deckt deine persönlichen Stärken und Schwächen während der Erarbeitung auf und hilft dir, dich laufend zu verbessern.

    """ ), diff --git a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py b/server/vbv_lernwelt/assignment/tests/test_assignment_api.py index 383ac419..86fe4edf 100644 --- a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py +++ b/server/vbv_lernwelt/assignment/tests/test_assignment_api.py @@ -3,7 +3,9 @@ import json from django.utils import timezone from rest_framework.test import APITestCase -from vbv_lernwelt.assignment.creators.create_assignments import create_uk_casework +from vbv_lernwelt.assignment.creators.create_assignments import ( + create_uk_fahrzeug_casework, +) from vbv_lernwelt.assignment.models import ( AssignmentCompletion, AssignmentCompletionAuditLog, @@ -20,7 +22,7 @@ class AssignmentApiTestCase(APITestCase): def setUp(self) -> None: create_default_users() create_test_course(include_vv=False) - self.assignment = create_uk_casework(course_id=COURSE_TEST_ID) + self.assignment = create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID) self.assignment_subtasks = self.assignment.filter_user_subtasks() self.cs = CourseSession.objects.create( diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index a78f63c7..709c18c2 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -7,8 +7,8 @@ from wagtail.models import Site from wagtail.rich_text import RichText from vbv_lernwelt.assignment.creators.create_assignments import ( - create_uk_casework, - create_uk_prep_assignment, + create_uk_fahrzeug_casework, + create_uk_fahrzeug_prep_assignment, ) from vbv_lernwelt.assignment.models import Assignment from vbv_lernwelt.assignment.services import update_assignment_completion @@ -72,8 +72,8 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False): _assignment_list_page = AssignmentListPageFactory( parent=course_page, ) - create_uk_casework(course_id=COURSE_TEST_ID) - create_uk_prep_assignment(course_id=COURSE_TEST_ID) + create_uk_fahrzeug_casework(course_id=COURSE_TEST_ID) + create_uk_fahrzeug_prep_assignment(course_id=COURSE_TEST_ID) create_test_learning_path(include_uk=include_uk, include_vv=include_vv) create_test_media_library() diff --git a/server/vbv_lernwelt/course/creators/uk_course.py b/server/vbv_lernwelt/course/creators/uk_course.py index 84f28b82..8301a362 100644 --- a/server/vbv_lernwelt/course/creators/uk_course.py +++ b/server/vbv_lernwelt/course/creators/uk_course.py @@ -14,14 +14,19 @@ from vbv_lernwelt.learnpath.tests.learning_path_factories import ( CircleFactory, LearningContentAssignmentFactory, LearningContentAttendanceCourseFactory, + LearningContentDocumentListFactory, LearningContentFeedbackFactory, LearningContentMediaLibraryFactory, LearningContentPlaceholderFactory, + LearningContentTestFactory, LearningPathFactory, LearningSequenceFactory, LearningUnitFactory, TopicFactory, ) +from vbv_lernwelt.media_library.tests.media_library_factories import ( + LearnMediaBlockFactory, +) def create_uk_learning_path(course_id=COURSE_UK, user=None, skip_locales=True): @@ -225,23 +230,59 @@ In diesem Circle erfährst du wie die überbetrieblichen Kurse aufgebaut sind. Z ) LearningUnitFactory(title="Vorbereitung", parent=circle) LearningContentMediaLibraryFactory( - title=f"Handlungsfeld «{title}»", + title=f"Allgemeines zu Versicherungen", parent=circle, description=RichText( - f"

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

    " + f"

    In der Mediathek unter «Allgemeines zu Versicherungen» 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(title)}", + content_url=f"/course/überbetriebliche-kurse/media", ) - LearningContentPlaceholderFactory( - title="Vorbereitungsauftrag", + LearningContentAssignmentFactory( + title="Versicherungswirtschaft", + assignment_type="PREP_ASSIGNMENT", parent=circle, - ) + content_assignment=Assignment.objects.get( + slug__startswith=f"überbetriebliche-kurse-assignment-kickoff-versicherungswirtschaft" + ), + ), LearningSequenceFactory(title="Training", parent=circle) LearningUnitFactory(title="Unterlagen", parent=circle) - LearningContentPlaceholderFactory( + LearningContentDocumentListFactory( title="Unterlagen für den Unterricht", parent=circle, + documents=[ + ( + "document", + LearnMediaBlockFactory( + title="Präsentation 1a", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Präsentation 1b", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Arbeitsblatt Einteilung der Versicherungen", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf", + ), + ), + ], ) LearningUnitFactory(title="Präsenzkurs", parent=circle) LearningContentAttendanceCourseFactory( @@ -252,11 +293,15 @@ In diesem Circle erfährst du wie die überbetrieblichen Kurse aufgebaut sind. Z parent=circle, ) LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end") - LearningUnitFactory(title="Transfer", parent=circle) - LearningContentPlaceholderFactory( + LearningUnitFactory(title="Reflexion", parent=circle) + LearningContentAssignmentFactory( title="Reflexion", + assignment_type="REFLECTION", parent=circle, - ) + content_assignment=Assignment.objects.get( + slug__startswith=f"überbetriebliche-kurse-assignment-reflexion" + ), + ), def create_uk_circle_basis(lp, title="Basis"): @@ -273,23 +318,69 @@ In diesem Circle lernst du die wichtigsten Grundlagen bezüglich Versicherungswi ) LearningUnitFactory(title="Vorbereitung", parent=circle) LearningContentMediaLibraryFactory( - title=f"Handlungsfeld «{title}»", + title=f"Allgemeines zu Versicherungen", parent=circle, description=RichText( - f"

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

    " + f"

    In der Mediathek unter «Allgemeines zu Versicherungen» 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(title)}", + content_url=f"/course/überbetriebliche-kurse/media", ) - LearningContentPlaceholderFactory( - title="Vorbereitungsauftrag", + LearningContentAssignmentFactory( + title="Vorbereitungsauftrag Circle Basis", + assignment_type="PREP_ASSIGNMENT", parent=circle, - ) + content_assignment=Assignment.objects.get( + slug__startswith=f"überbetriebliche-kurse-assignment-circle-basis" + ), + ), LearningSequenceFactory(title="Training", parent=circle) LearningUnitFactory(title="Unterlagen", parent=circle) - LearningContentPlaceholderFactory( + LearningContentDocumentListFactory( title="Unterlagen für den Unterricht", parent=circle, + documents=[ + ( + "document", + LearnMediaBlockFactory( + title="Präsentation", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_BA_04_A_Risikomanagement.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Risikomanagement", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_BA_04_A_Risikomanagement.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Quizzes", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_BA_05_QR_Quizzes.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Versicherungsrecht", + description="Antragsannahme, Bindefrist und Widerrufsrecht – Kleine Fallstudie", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_BA_06_A_Versicherungsrecht.pdf", + ), + ), + ], ) LearningUnitFactory(title="Präsenzkurs", parent=circle) LearningContentAttendanceCourseFactory( @@ -297,19 +388,28 @@ In diesem Circle lernst du die wichtigsten Grundlagen bezüglich Versicherungswi parent=circle, ) LearningUnitFactory(title="Kompetenznachweis", parent=circle) - LearningContentPlaceholderFactory( + LearningContentTestFactory( title="Wissens- und Verständnisfragen", parent=circle, + description=RichText( + "

    Folgender Test mit Wissens- und Verständnisfragen ist Teil des Kompetenznachweises. Der Test kann nur einmal durchgeführt werden und ist notenrelevant.

    " + ), + checkbox_text="Hiermit bestätige ich, dass ich die Anweisungen verstanden habe und den Test durchführen möchte.", + content_url="http://example.com", ) LearningContentFeedbackFactory( parent=circle, ) LearningSequenceFactory(title="Transfer", parent=circle, icon="it-icon-ls-end") - LearningUnitFactory(title="Transfer", parent=circle) - LearningContentPlaceholderFactory( + LearningUnitFactory(title="Reflexion", parent=circle) + LearningContentAssignmentFactory( title="Reflexion", + assignment_type="REFLECTION", parent=circle, - ) + content_assignment=Assignment.objects.get( + slug__startswith=f"überbetriebliche-kurse-assignment-reflexion" + ), + ), def create_uk_circle_fahrzeug(lp, title="Fahrzeug"): @@ -343,9 +443,81 @@ def create_uk_circle_fahrzeug(lp, title="Fahrzeug"): ), LearningSequenceFactory(title="Training", parent=circle) LearningUnitFactory(title="Unterlagen", parent=circle) - LearningContentPlaceholderFactory( + LearningContentDocumentListFactory( title="Unterlagen für den Unterricht", parent=circle, + documents=[ + ( + "document", + LearnMediaBlockFactory( + title="Präsentation", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_00_Präsentation.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Begriffe", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_02_Begriffe.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Checkliste", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_04_Checkliste.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Arbeitsblatt Schadenfälle", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_05_A_Schaden.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Überblick Kasko", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_07_Kasko.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Offerte und anschliessendes Verkaufsgespräch", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_08_A_Offerte und Verkaufsgespräch.pdf", + ), + ), + ( + "document", + LearnMediaBlockFactory( + title="Arbeitsblatt Schadenfälle Reserve", + description="", + icon_url="/static/icons/demo/icon-hf-book.png", + link_display_text="Dokument anzeigen", + url="/static/media/uk/de_üK1_FZ_A_Reserve_Schaden.pdf", + ), + ), + ], ) LearningUnitFactory(title="Präsenzkurs", parent=circle) LearningContentAttendanceCourseFactory( @@ -365,11 +537,11 @@ def create_uk_circle_fahrzeug(lp, title="Fahrzeug"): ), ), LearningContentAssignmentFactory( - title="Reflexionsfragen Fahrzeug", + title="Reflexion", assignment_type="REFLECTION", parent=circle, content_assignment=Assignment.objects.get( - slug__startswith=f"{course_slug}-assignment-fahrzeug-reflexionsfragen" + slug__startswith=f"{course_slug}-assignment-reflexion" ), ), diff --git a/server/vbv_lernwelt/course/management/commands/create_default_courses.py b/server/vbv_lernwelt/course/management/commands/create_default_courses.py index ef19099e..f681bc5b 100644 --- a/server/vbv_lernwelt/course/management/commands/create_default_courses.py +++ b/server/vbv_lernwelt/course/management/commands/create_default_courses.py @@ -3,8 +3,10 @@ import random import djclick as click from vbv_lernwelt.assignment.creators.create_assignments import ( - create_uk_casework, - create_uk_prep_assignment, + create_uk_basis_prep_assignment, + create_uk_fahrzeug_casework, + create_uk_fahrzeug_prep_assignment, + create_uk_kickoff_prep_assignment, create_uk_reflection, ) from vbv_lernwelt.assignment.models import Assignment @@ -169,8 +171,10 @@ def create_course_uk_de(): _assignment_list_page = AssignmentListPageFactory( parent=course.coursepage, ) - create_uk_casework(course_id=COURSE_UK) - create_uk_prep_assignment(course_id=COURSE_UK) + create_uk_kickoff_prep_assignment(course_id=COURSE_UK) + create_uk_basis_prep_assignment(course_id=COURSE_UK) + create_uk_fahrzeug_casework(course_id=COURSE_UK) + create_uk_fahrzeug_prep_assignment(course_id=COURSE_UK) create_uk_reflection(course_id=COURSE_UK) # learning path @@ -427,8 +431,8 @@ def create_course_training_de(): _assignment_list_page = AssignmentListPageFactory( parent=course.coursepage, ) - create_uk_casework(course_id=COURSE_UK_TRAINING) - create_uk_prep_assignment(course_id=COURSE_UK_TRAINING) + create_uk_fahrzeug_casework(course_id=COURSE_UK_TRAINING) + create_uk_fahrzeug_prep_assignment(course_id=COURSE_UK_TRAINING) create_uk_reflection(course_id=COURSE_UK_TRAINING) create_uk_training_learning_path(course_id=COURSE_UK_TRAINING) diff --git a/server/vbv_lernwelt/learnpath/migrations/0005_learningcontentdocumentlist.py b/server/vbv_lernwelt/learnpath/migrations/0005_learningcontentdocumentlist.py new file mode 100644 index 00000000..406cfc8c --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0005_learningcontentdocumentlist.py @@ -0,0 +1,85 @@ +# Generated by Django 3.2.13 on 2023-05-26 10:34 + +import django.db.models.deletion +import wagtail.blocks +import wagtail.fields +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("wagtailcore", "0083_workflowcontenttype"), + ("learnpath", "0004_learningcontentassignment_assignment_type"), + ] + + operations = [ + migrations.CreateModel( + name="LearningContentDocumentList", + fields=[ + ( + "page_ptr", + models.OneToOneField( + auto_created=True, + on_delete=django.db.models.deletion.CASCADE, + parent_link=True, + primary_key=True, + serialize=False, + to="wagtailcore.page", + ), + ), + ("minutes", models.PositiveIntegerField(default=15)), + ("description", wagtail.fields.RichTextField(blank=True)), + ("content_url", models.TextField(blank=True)), + ( + "documents", + wagtail.fields.StreamField( + [ + ( + "document", + 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), + ), + ] + ), + ) + ], + blank=True, + use_json_field=True, + ), + ), + ], + options={ + "abstract": False, + }, + bases=("wagtailcore.page",), + ), + ] diff --git a/server/vbv_lernwelt/learnpath/migrations/0006_learningcontenttest_checkbox_text.py b/server/vbv_lernwelt/learnpath/migrations/0006_learningcontenttest_checkbox_text.py new file mode 100644 index 00000000..545416ae --- /dev/null +++ b/server/vbv_lernwelt/learnpath/migrations/0006_learningcontenttest_checkbox_text.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.13 on 2023-05-26 14:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("learnpath", "0005_learningcontentdocumentlist"), + ] + + operations = [ + migrations.AddField( + model_name="learningcontenttest", + name="checkbox_text", + field=models.TextField(blank=True), + ), + ] diff --git a/server/vbv_lernwelt/learnpath/models.py b/server/vbv_lernwelt/learnpath/models.py index 61c8de62..24d6e6cb 100644 --- a/server/vbv_lernwelt/learnpath/models.py +++ b/server/vbv_lernwelt/learnpath/models.py @@ -3,13 +3,14 @@ import re from django.db import models from django.utils.text import slugify from wagtail.admin.panels import FieldPanel, PageChooserPanel -from wagtail.fields import RichTextField +from wagtail.fields import RichTextField, StreamField from wagtail.models import Page from vbv_lernwelt.assignment.models import AssignmentType from vbv_lernwelt.core.constants import DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER from vbv_lernwelt.core.model_utils import find_available_slug from vbv_lernwelt.course.models import CourseBasePage, CoursePage +from vbv_lernwelt.media_library.content_blocks import LearnMediaBlock class LearningPath(CourseBasePage): @@ -306,9 +307,18 @@ class LearningContentMediaLibrary(LearningContent): class LearningContentTest(LearningContent): + serialize_field_names = LearningContent.serialize_field_names + [ + "checkbox_text", + ] parent_page_types = ["learnpath.Circle"] subpage_types = [] + checkbox_text = models.TextField(blank=True) + + content_panels = LearningContent.content_panels + [ + FieldPanel("checkbox_text", classname="Text"), + ] + class LearningContentRichText(LearningContent): text = RichTextField(blank=True, features=DEFAULT_RICH_TEXT_FEATURES_WITH_HEADER) @@ -352,6 +362,29 @@ class LearningContentAssignment(LearningContent): ] +class LearningContentDocumentList(LearningContent): + serialize_field_names = LearningContent.serialize_field_names + [ + "documents", + ] + parent_page_types = ["learnpath.Circle"] + subpage_types = [] + + documents = StreamField( + [ + ("document", LearnMediaBlock()), + ], + use_json_field=True, + blank=True, + ) + + content_panels = [ + FieldPanel("title", classname="full title"), + FieldPanel("minutes"), + FieldPanel("description"), + FieldPanel("documents"), + ] + + def find_slug_with_parent_prefix(page, type_prefix, slug_postfix=None): parent_slug = page.get_ancestors().exact_type(LearningPath, Circle).last().slug if parent_slug: diff --git a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py index 77dc136e..c1d5be7c 100644 --- a/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py +++ b/server/vbv_lernwelt/learnpath/tests/learning_path_factories.py @@ -5,6 +5,7 @@ from vbv_lernwelt.learnpath.models import ( Circle, LearningContentAssignment, LearningContentAttendanceCourse, + LearningContentDocumentList, LearningContentFeedback, LearningContentLearningModule, LearningContentMediaLibrary, @@ -17,6 +18,9 @@ from vbv_lernwelt.learnpath.models import ( LearningUnit, Topic, ) +from vbv_lernwelt.media_library.tests.media_library_factories import ( + LearnMediaBlockFactory, +) class LearningPathFactory(wagtail_factories.PageFactory): @@ -172,3 +176,17 @@ class LearningContentAssignmentFactory(wagtail_factories.PageFactory): class Meta: model = LearningContentAssignment + + +class LearningContentDocumentListFactory(wagtail_factories.PageFactory): + title = "Dokumente" + minutes = 0 + content_url = "" + description = RichText("") + documents = [ + ("document", LearnMediaBlockFactory()), + ("document", LearnMediaBlockFactory()), + ] + + class Meta: + model = LearningContentDocumentList diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_BA_04_A_Risikomanagement.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_04_A_Risikomanagement.pdf new file mode 100644 index 00000000..c93d0d43 Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_04_A_Risikomanagement.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_BA_05_QR_Quizzes.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_05_QR_Quizzes.pdf new file mode 100644 index 00000000..0c709a4c Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_05_QR_Quizzes.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_BA_06_A_Versicherungsrecht.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_06_A_Versicherungsrecht.pdf new file mode 100644 index 00000000..e5babfee Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_BA_06_A_Versicherungsrecht.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_00_Präsentation.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_00_Präsentation.pdf new file mode 100644 index 00000000..edb8086a Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_00_Präsentation.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_02_Begriffe.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_02_Begriffe.pdf new file mode 100644 index 00000000..3dac0068 Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_02_Begriffe.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_04_Checkliste.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_04_Checkliste.pdf new file mode 100644 index 00000000..023c0f50 Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_04_Checkliste.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_05_A_Schaden.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_05_A_Schaden.pdf new file mode 100644 index 00000000..b6a9f3fa Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_05_A_Schaden.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_07_Kasko.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_07_Kasko.pdf new file mode 100644 index 00000000..882af130 Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_07_Kasko.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_08_A_Offerte und Verkaufsgespräch.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_08_A_Offerte und Verkaufsgespräch.pdf new file mode 100644 index 00000000..a2b3a38f Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_08_A_Offerte und Verkaufsgespräch.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_A_Reserve_Schaden.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_A_Reserve_Schaden.pdf new file mode 100644 index 00000000..6b2d892a Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_FZ_A_Reserve_Schaden.pdf differ diff --git a/server/vbv_lernwelt/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf b/server/vbv_lernwelt/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf new file mode 100644 index 00000000..989ce098 Binary files /dev/null and b/server/vbv_lernwelt/static/media/uk/de_üK1_KO_03b_A_Einteilungen.pdf differ diff --git a/trufflehog-exclude-patterns.txt b/trufflehog-exclude-patterns.txt index ae2b4ade..452dbd7b 100644 --- a/trufflehog-exclude-patterns.txt +++ b/trufflehog-exclude-patterns.txt @@ -2,6 +2,7 @@ server/requirements/ env_secrets/ env/bitbucket/Dockerfile env/docker_local.env +server/vbv_lernwelt/assignment/creators/create_assignments.py server/vbv_lernwelt/static/ server/vbv_lernwelt/media/ supabase.md @@ -9,4 +10,4 @@ scripts/supabase/init.sql ramon.wenger@iterativ.ch.gpg .envs/ client/package-lock.json -package-lock.json \ No newline at end of file +package-lock.json