Merged in feature/VBV-289-kn-auftrag-datenmodell-definieren-rebase1 (pull request #53)
Feature/VBV-289 kn auftrag datenmodell definieren rebase1 Approved-by: Elia Bieri
This commit is contained in:
commit
87a3676ca2
|
|
@ -6,6 +6,7 @@ import log from "loglevel";
|
|||
import type { Component } from "vue";
|
||||
import { computed } from "vue";
|
||||
|
||||
import AssignmentBlock from "@/pages/learningPath/learningContentPage/blocks/AssignmentBlock.vue";
|
||||
import AttendanceDayBlock from "@/pages/learningPath/learningContentPage/blocks/AttendanceDayBlock.vue";
|
||||
import DescriptionBlock from "./blocks/DescriptionBlock.vue";
|
||||
import DescriptionTextBlock from "./blocks/DescriptionTextBlock.vue";
|
||||
|
|
@ -35,7 +36,7 @@ const block = computed(() => {
|
|||
const COMPONENTS: Record<LearningContentType, Component> = {
|
||||
placeholder: PlaceholderBlock,
|
||||
video: VideoBlock,
|
||||
assignment: DescriptionTextBlock,
|
||||
assignment: AssignmentBlock,
|
||||
resource: DescriptionTextBlock,
|
||||
exercise: IframeBlock,
|
||||
test: IframeBlock,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
<script setup lang="ts">
|
||||
import { useAssignmentStore } from "@/stores/assignmentStore";
|
||||
import { useCourseSessionsStore } from "@/stores/courseSessions";
|
||||
import type {
|
||||
Assignment,
|
||||
CourseSessionAssignmentDetails,
|
||||
LearningContent,
|
||||
} from "@/types";
|
||||
import * as log from "loglevel";
|
||||
import { onMounted, reactive } from "vue";
|
||||
|
||||
const assignmentStore = useAssignmentStore();
|
||||
|
||||
interface State {
|
||||
assignment: Assignment | undefined;
|
||||
courseSessionAssignmentDetails: CourseSessionAssignmentDetails | undefined;
|
||||
}
|
||||
|
||||
const state: State = reactive({
|
||||
assignment: undefined,
|
||||
courseSessionAssignmentDetails: undefined,
|
||||
});
|
||||
|
||||
const props = defineProps<{
|
||||
assignmentId: number;
|
||||
learningContent: LearningContent;
|
||||
}>();
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("AssignmentView mounted", props.assignmentId, props.learningContent);
|
||||
|
||||
const courseSessionsStore = useCourseSessionsStore();
|
||||
|
||||
try {
|
||||
state.assignment = await assignmentStore.loadAssignment(props.assignmentId);
|
||||
state.courseSessionAssignmentDetails = courseSessionsStore.findAssignmentDetails(
|
||||
props.learningContent.id
|
||||
);
|
||||
} catch (error) {
|
||||
log.error(error);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container-medium">
|
||||
<div v-if="state.assignment" class="lg:mt-12">
|
||||
<h2>Einleitung</h2>
|
||||
|
||||
<h3 class="mt-8">Ausgangslage</h3>
|
||||
<p>{{ state.assignment.starting_position }}</p>
|
||||
|
||||
<h3 class="mt-8">Abgabetermin</h3>
|
||||
<p v-if="state.courseSessionAssignmentDetails">
|
||||
{{ state.courseSessionAssignmentDetails }}
|
||||
</p>
|
||||
<p v-else>Keine Abgabedaten erfasst für diese Durchführung</p>
|
||||
|
||||
<pre class="mt-16">
|
||||
{{ state.assignment }}
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<template>
|
||||
<AssignmentView
|
||||
:assignment-id="props.value.assignment"
|
||||
:learning-content="props.content"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import AssignmentView from "@/pages/learningPath/learningContentPage/assignment/AssignmentView.vue";
|
||||
import type { LearningContent } from "@/types";
|
||||
|
||||
interface Value {
|
||||
description: string;
|
||||
assignment: number;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
value: Value;
|
||||
content: LearningContent;
|
||||
}>();
|
||||
</script>
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
import { itGet } from "@/fetchHelpers";
|
||||
import type { Assignment } from "@/types";
|
||||
import log from "loglevel";
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
export type AssignmentStoreState = {
|
||||
assignment: Assignment | undefined;
|
||||
};
|
||||
|
||||
export const useAssignmentStore = defineStore({
|
||||
id: "assignmentStore",
|
||||
state: () => {
|
||||
return {
|
||||
assignment: undefined,
|
||||
} as AssignmentStoreState;
|
||||
},
|
||||
getters: {},
|
||||
actions: {
|
||||
async loadAssignment(assignmentId: number) {
|
||||
log.debug("load assignment", assignmentId);
|
||||
const assignmentData = await itGet(`/api/course/page/${assignmentId}/`);
|
||||
|
||||
if (!assignmentData) {
|
||||
throw `No assignment found with: ${assignmentId}`;
|
||||
}
|
||||
|
||||
this.assignment = assignmentData;
|
||||
return this.assignment;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
@ -3,6 +3,7 @@ import { deleteCircleDocument } from "@/services/files";
|
|||
import type {
|
||||
CircleDocument,
|
||||
CourseSession,
|
||||
CourseSessionAssignmentDetails,
|
||||
CourseSessionAttendanceDay,
|
||||
CourseSessionUser,
|
||||
ExpertSessionUser,
|
||||
|
|
@ -224,6 +225,16 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
}
|
||||
}
|
||||
|
||||
function findAssignmentDetails(
|
||||
contentId: number
|
||||
): CourseSessionAssignmentDetails | undefined {
|
||||
if (currentCourseSession.value) {
|
||||
return currentCourseSession.value.assignment_details_list.find(
|
||||
(assignmentDetails) => assignmentDetails.learningContentId === contentId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
uniqueCourseSessionsByCourse,
|
||||
currentCourseSession,
|
||||
|
|
@ -238,6 +249,7 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
|||
startUpload,
|
||||
removeDocument,
|
||||
findAttendanceDay,
|
||||
findAssignmentDetails,
|
||||
|
||||
// TODO: only used to be changed by router.afterEach
|
||||
currentCourseSlug,
|
||||
|
|
|
|||
|
|
@ -285,6 +285,63 @@ export interface MediaLibraryPage extends BaseCourseWagtailPage {
|
|||
readonly children: MediaCategoryPage[];
|
||||
}
|
||||
|
||||
export interface AssignmentPerformanceObjective {
|
||||
readonly type: "performance_objective";
|
||||
readonly id: string;
|
||||
readonly value: {
|
||||
text: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssignmentTaskBlockExplanation {
|
||||
readonly type: "explanation";
|
||||
readonly id: string;
|
||||
readonly value: {
|
||||
readonly text: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssignmentTaskBlockUserConfirmation {
|
||||
readonly type: "user_confirmation";
|
||||
readonly id: string;
|
||||
readonly value: {
|
||||
readonly text: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface AssignmentTaskBlockUserTextInput {
|
||||
readonly type: "user_text_input";
|
||||
readonly id: string;
|
||||
readonly value: {
|
||||
readonly text?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type AssignmentTaskBlock =
|
||||
| AssignmentTaskBlockExplanation
|
||||
| AssignmentTaskBlockUserConfirmation
|
||||
| AssignmentTaskBlockUserTextInput;
|
||||
|
||||
export interface AssignmentTask {
|
||||
readonly type: "task";
|
||||
readonly id: string;
|
||||
readonly value: {
|
||||
title: string;
|
||||
file_submission_required: boolean;
|
||||
content: AssignmentTaskBlock[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Assignment extends BaseCourseWagtailPage {
|
||||
readonly type: "assignment.Assignment";
|
||||
readonly starting_position: string;
|
||||
readonly effort_required: string;
|
||||
readonly performance_objectives: AssignmentPerformanceObjective[];
|
||||
readonly assessment_description: string;
|
||||
readonly assessment_document_url: string;
|
||||
readonly tasks: AssignmentTask[];
|
||||
}
|
||||
|
||||
export interface PerformanceCriteria extends BaseCourseWagtailPage {
|
||||
readonly type: "competence.PerformanceCriteria";
|
||||
readonly competence_id: string;
|
||||
|
|
@ -348,6 +405,11 @@ export interface CourseSessionAttendanceDay {
|
|||
trainer: string;
|
||||
}
|
||||
|
||||
export interface CourseSessionAssignmentDetails {
|
||||
learningContentId: number;
|
||||
deadlineDateTimeUtc: string;
|
||||
}
|
||||
|
||||
export interface CourseSession {
|
||||
id: number;
|
||||
created_at: string;
|
||||
|
|
@ -361,6 +423,7 @@ export interface CourseSession {
|
|||
course_url: string;
|
||||
media_library_url: string;
|
||||
attendance_days: CourseSessionAttendanceDay[];
|
||||
assignment_details_list: CourseSessionAssignmentDetails[];
|
||||
documents: CircleDocument[];
|
||||
users: CourseSessionUser[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -113,6 +113,7 @@ LOCAL_APPS = [
|
|||
"vbv_lernwelt.feedback",
|
||||
"vbv_lernwelt.files",
|
||||
"vbv_lernwelt.notify",
|
||||
"vbv_lernwelt.assignment",
|
||||
]
|
||||
# https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps
|
||||
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ urlpatterns = [
|
|||
path(r"api/course/sessions/", get_course_sessions, name="get_course_sessions"),
|
||||
path(r"api/course/sessions/<course_slug>/users/", get_course_session_users,
|
||||
name="get_course_session_users"),
|
||||
path(r"api/course/page/<slug>/", course_page_api_view,
|
||||
path(r"api/course/page/<slug_or_id>/", course_page_api_view,
|
||||
name="course_page_api_view"),
|
||||
path(r"api/course/completion/mark/", mark_course_completion_view,
|
||||
name="mark_course_completion"),
|
||||
|
|
|
|||
|
|
@ -540,7 +540,7 @@ wagtail==3.0.1
|
|||
# wagtail-grapple
|
||||
# wagtail-headless-preview
|
||||
# wagtail-localize
|
||||
wagtail-factories==2.0.1
|
||||
wagtail-factories==4.0.0
|
||||
# via -r requirements.in
|
||||
wagtail-grapple==0.18.0
|
||||
# via -r requirements.in
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ python-json-logger
|
|||
concurrent-log-handler
|
||||
|
||||
wagtail>=3,<4
|
||||
wagtail-factories
|
||||
wagtail-factories>=4
|
||||
wagtail-localize
|
||||
wagtail_grapple
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ wagtail==3.0.1
|
|||
# wagtail-grapple
|
||||
# wagtail-headless-preview
|
||||
# wagtail-localize
|
||||
wagtail-factories==2.0.1
|
||||
wagtail-factories==4.0.0
|
||||
# via -r requirements.in
|
||||
wagtail-grapple==0.18.0
|
||||
# via -r requirements.in
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AssignmentConfig(AppConfig):
|
||||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "vbv_lernwelt.assignment"
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
from vbv_lernwelt.assignment.models import TaskContentStreamBlock
|
||||
from vbv_lernwelt.assignment.tests.assignment_factories import (
|
||||
AssignmentFactory,
|
||||
AssignmentListPageFactory,
|
||||
ExplanationBlockFactory,
|
||||
PerformanceObjectiveBlockFactory,
|
||||
TaskBlockFactory,
|
||||
UserTextInputBlockFactory,
|
||||
)
|
||||
from vbv_lernwelt.core.utils import replace_whitespace
|
||||
from vbv_lernwelt.course.consts import COURSE_UK
|
||||
from vbv_lernwelt.course.models import CoursePage
|
||||
from wagtail.blocks import StreamValue
|
||||
|
||||
|
||||
def create_uk_assignments(course_id=COURSE_UK):
|
||||
course_page = CoursePage.objects.get(course_id=course_id)
|
||||
assignment_page = AssignmentListPageFactory(
|
||||
parent=course_page,
|
||||
)
|
||||
|
||||
assignment = AssignmentFactory(
|
||||
parent=assignment_page,
|
||||
title="Überprüfen einer Motorfahrzeugs-Versicherungspolice",
|
||||
effort_required="ca. 5 Stunden",
|
||||
starting_position=replace_whitespace(
|
||||
"""
|
||||
Jemand aus deiner Familie oder aus deinem Freundeskreis möchte sein
|
||||
Versicherungspolice überprüfen lassen. Diese Person kommt nun mit ihrer Police auf dich zu
|
||||
und bittet dich als Versicherungsprofi, diese kritisch zu überprüfen und ihr ggf. Anpassungsvorschläge
|
||||
zu unterbreiten. In diesem Kompetenznachweis kannst du nun dein Wissen und Können im Bereich
|
||||
der Motorfahrzeugversicherung unter Beweis stellen.
|
||||
"""
|
||||
),
|
||||
performance_objectives=[
|
||||
(
|
||||
"performance_objective",
|
||||
PerformanceObjectiveBlockFactory(
|
||||
text="Sie erläutern die Leistungen und Produkte im Versicherungsbereich."
|
||||
),
|
||||
),
|
||||
(
|
||||
"performance_objective",
|
||||
PerformanceObjectiveBlockFactory(
|
||||
text="Sie beurteilen gängige Versicherungslösungen fachkundig."
|
||||
),
|
||||
),
|
||||
],
|
||||
assessment_document_url="https://www.vbv.ch",
|
||||
assessment_description="Diese geleitete Fallarbeit wird auf Grund des folgenden Beurteilungsintrument bewertet.",
|
||||
)
|
||||
|
||||
assignment.tasks = []
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 1: Beispiel einer Versicherungspolice finden",
|
||||
# it is hard to create a StreamValue programmatically, we have to
|
||||
# create a `StreamValue` manually. Ask the Daniel and/or Ramon
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(text="Dies ist ein Beispieltext."),
|
||||
),
|
||||
(
|
||||
"user_confirmation",
|
||||
ExplanationBlockFactory(
|
||||
text="Ja, ich habe Motorfahrzeugversicherungspolice von jemandem aus meiner Familie oder meinem Freundeskreis erhalten."
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 2: Kundensituation und Ausgangslage",
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Erläutere die Kundensituation und die Ausgangslage.
|
||||
* Hast du alle Informationen, die du für den Policen-Check benötigst?
|
||||
* Halte die wichtigsten Eckwerte des aktuellen Versicherungsverhältnisse in deiner Dokumentation fest (z.B wie lang wo versichert, Alter des Fahrzeugs, Kundenprofil, etc.)
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
("user_text_input", UserTextInputBlockFactory()),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 3: Aktuelle Versicherung",
|
||||
# TODO: add document upload
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Zeige nun detailliert auf, wie dein Kundenbeispiel momentan versichert ist.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
("user_text_input", UserTextInputBlockFactory()),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 4: Deine Empfehlungen",
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Erarbeite nun basierend auf deinen Erkenntnissen eine Empfehlung für die Person.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Gibt es zusätzliche Deckungen, die du der Person empfehlen würdest? Begründe deine Empfehlung
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Gibt es Deckungen, die du streichen würdest? Begründe deine Empfehlung.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Wenn die Person gemäss deiner Einschätzung genau richtig versichert ist, argumentiere, warum dies der Fall ist.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 5: Reflexion",
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Reflektiere dein Handeln und halte deine Erkenntnisse fest. Frage dich dabei:
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
War die Bearbeitung dieser geleiteten Fallarbeit erfolgreich? Begründe deine Einschätzung.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Was ist dir bei der Bearbeitung des Auftrags gut gelungen?
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Was ist dir bei der Bearbeitung des Auftrags weniger gut gelungen?
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.tasks.append(
|
||||
(
|
||||
"task",
|
||||
TaskBlockFactory(
|
||||
title="Teilaufgabe 6: Learnings",
|
||||
content=StreamValue(
|
||||
TaskContentStreamBlock(),
|
||||
stream_data=[
|
||||
(
|
||||
"explanation",
|
||||
ExplanationBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Leite aus der Teilaufgabe 5 deine persönlichen Learnings ab.
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Was würdest du beim nächsten Mal anders machen?
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
UserTextInputBlockFactory(
|
||||
text=replace_whitespace(
|
||||
"""
|
||||
Was hast du beim Bearbeiten des Auftrags Neues gelernt?
|
||||
"""
|
||||
)
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
assignment.save()
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
# Generated by Django 3.2.13 on 2023-04-11 09:30
|
||||
|
||||
import django.db.models.deletion
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
import vbv_lernwelt.assignment.models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("wagtailcore", "0069_log_entry_jsonfield"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Assignment",
|
||||
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",
|
||||
),
|
||||
),
|
||||
(
|
||||
"starting_position",
|
||||
models.TextField(help_text="Erläuterung der Ausgangslage"),
|
||||
),
|
||||
(
|
||||
"effort_required",
|
||||
models.CharField(
|
||||
blank=True, help_text="Zeitaufwand als Text", max_length=100
|
||||
),
|
||||
),
|
||||
(
|
||||
"performance_objectives",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"performance_objective",
|
||||
wagtail.blocks.StructBlock(
|
||||
[("text", wagtail.blocks.TextBlock())]
|
||||
),
|
||||
)
|
||||
],
|
||||
blank=True,
|
||||
help_text="Leistungsziele des Auftrags",
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
(
|
||||
"assessment_description",
|
||||
models.TextField(
|
||||
blank=True, help_text="Beschreibung der Bewertung"
|
||||
),
|
||||
),
|
||||
(
|
||||
"assessment_document_url",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="URL zum Beurteilungsinstrument",
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
(
|
||||
"tasks",
|
||||
wagtail.fields.StreamField(
|
||||
[
|
||||
(
|
||||
"task",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("title", wagtail.blocks.TextBlock()),
|
||||
(
|
||||
"file_submission_required",
|
||||
wagtail.blocks.BooleanBlock(required=False),
|
||||
),
|
||||
(
|
||||
"content",
|
||||
wagtail.blocks.StreamBlock(
|
||||
[
|
||||
(
|
||||
"explanation",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"text",
|
||||
wagtail.blocks.TextBlock(),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
(
|
||||
"user_text_input",
|
||||
vbv_lernwelt.assignment.models.UserTextInputBlock(),
|
||||
),
|
||||
(
|
||||
"user_confirmation",
|
||||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
(
|
||||
"text",
|
||||
wagtail.blocks.TextBlock(),
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
],
|
||||
blank=True,
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
)
|
||||
],
|
||||
blank=True,
|
||||
help_text="Teilaufgaben",
|
||||
use_json_field=True,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Auftrag",
|
||||
},
|
||||
bases=("wagtailcore.page",),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="AssignmentListPage",
|
||||
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",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
bases=("wagtailcore.page",),
|
||||
),
|
||||
]
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
from django.db import models
|
||||
from slugify import slugify
|
||||
from wagtail import blocks
|
||||
from wagtail.admin.panels import FieldPanel
|
||||
from wagtail.fields import StreamField
|
||||
from wagtail.models import Page
|
||||
|
||||
from vbv_lernwelt.core.model_utils import find_available_slug
|
||||
from vbv_lernwelt.course.models import CourseBasePage
|
||||
|
||||
|
||||
class AssignmentListPage(CourseBasePage):
|
||||
subpage_types = ["assignment.Assignment"]
|
||||
parent_page_types = ["course.CoursePage"]
|
||||
|
||||
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
||||
self.slug = find_available_slug(
|
||||
slugify(f"{self.get_parent().slug}-assignment", allow_unicode=True)
|
||||
)
|
||||
super(AssignmentListPage, self).save(clean, user, log_action, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title}"
|
||||
|
||||
|
||||
# class AssignmentSubmission(modModel):
|
||||
# created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
class ExplanationBlock(blocks.StructBlock):
|
||||
text = blocks.TextBlock()
|
||||
|
||||
class Meta:
|
||||
icon = "comment"
|
||||
|
||||
|
||||
class PerformanceObjectiveBlock(blocks.StructBlock):
|
||||
text = blocks.TextBlock()
|
||||
|
||||
class Meta:
|
||||
icon = "tick"
|
||||
|
||||
|
||||
class UserTextInputBlock(blocks.StaticBlock):
|
||||
text = blocks.TextBlock(blank=True)
|
||||
|
||||
class Meta:
|
||||
icon = "edit"
|
||||
|
||||
|
||||
class UserConfirmationBlock(blocks.StructBlock):
|
||||
text = blocks.TextBlock()
|
||||
|
||||
class Meta:
|
||||
icon = "tick-inverse"
|
||||
|
||||
|
||||
class TaskContentStreamBlock(blocks.StreamBlock):
|
||||
explanation = ExplanationBlock()
|
||||
user_text_input = UserTextInputBlock()
|
||||
user_confirmation = UserConfirmationBlock()
|
||||
|
||||
|
||||
class TaskBlock(blocks.StructBlock):
|
||||
title = blocks.TextBlock()
|
||||
file_submission_required = blocks.BooleanBlock(required=False)
|
||||
content = TaskContentStreamBlock(
|
||||
blank=True,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
icon = "tasks"
|
||||
label = "Teilauftrag"
|
||||
|
||||
|
||||
class Assignment(CourseBasePage):
|
||||
serialize_field_names = [
|
||||
"starting_position",
|
||||
"effort_required",
|
||||
"performance_objectives",
|
||||
"assessment_description",
|
||||
"assessment_document_url",
|
||||
"tasks",
|
||||
]
|
||||
|
||||
starting_position = models.TextField(help_text="Erläuterung der Ausgangslage")
|
||||
effort_required = models.CharField(
|
||||
max_length=100, help_text="Zeitaufwand als Text", blank=True
|
||||
)
|
||||
|
||||
performance_objectives = StreamField(
|
||||
[
|
||||
("performance_objective", PerformanceObjectiveBlock()),
|
||||
],
|
||||
use_json_field=True,
|
||||
blank=True,
|
||||
help_text="Leistungsziele des Auftrags",
|
||||
)
|
||||
assessment_description = models.TextField(
|
||||
blank=True, help_text="Beschreibung der Bewertung"
|
||||
)
|
||||
assessment_document_url = models.CharField(
|
||||
max_length=255,
|
||||
blank=True,
|
||||
help_text="URL zum Beurteilungsinstrument",
|
||||
)
|
||||
|
||||
tasks = StreamField(
|
||||
[
|
||||
("task", TaskBlock()),
|
||||
],
|
||||
use_json_field=True,
|
||||
blank=True,
|
||||
help_text="Teilaufgaben",
|
||||
)
|
||||
|
||||
content_panels = Page.content_panels + [
|
||||
FieldPanel("starting_position"),
|
||||
FieldPanel("effort_required"),
|
||||
FieldPanel("performance_objectives"),
|
||||
FieldPanel("assessment_description"),
|
||||
FieldPanel("assessment_document_url"),
|
||||
FieldPanel("tasks"),
|
||||
]
|
||||
|
||||
subpage_types = []
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Auftrag"
|
||||
|
||||
def save(self, clean=True, user=None, log_action=False, **kwargs):
|
||||
self.slug = find_available_slug(
|
||||
slugify(f"{self.get_parent().slug}-{self.title}", allow_unicode=True)
|
||||
)
|
||||
super(Assignment, self).save(clean, user, log_action, **kwargs)
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import wagtail_factories
|
||||
from factory import SubFactory
|
||||
|
||||
from vbv_lernwelt.assignment.models import (
|
||||
Assignment,
|
||||
AssignmentListPage,
|
||||
ExplanationBlock,
|
||||
PerformanceObjectiveBlock,
|
||||
TaskBlock,
|
||||
TaskContentStreamBlock,
|
||||
UserConfirmationBlock,
|
||||
UserTextInputBlock,
|
||||
)
|
||||
from vbv_lernwelt.core.utils import replace_whitespace
|
||||
|
||||
|
||||
class ExplanationBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
text = "Dies ist ein Beispieltext."
|
||||
|
||||
class Meta:
|
||||
model = ExplanationBlock
|
||||
|
||||
|
||||
class UserConfirmationBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
text = "Ja, ich habe Motorfahrzeugversicherungspolice von jemandem aus meiner Familie oder meinem Freundeskreis erhalten."
|
||||
|
||||
class Meta:
|
||||
model = UserConfirmationBlock
|
||||
|
||||
|
||||
class TaskContentStreamBlockFactory(wagtail_factories.StreamBlockFactory):
|
||||
explanation = SubFactory(ExplanationBlockFactory)
|
||||
user_confirmation = SubFactory(UserConfirmationBlockFactory)
|
||||
|
||||
class Meta:
|
||||
model = TaskContentStreamBlock
|
||||
|
||||
|
||||
class UserTextInputBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
class Meta:
|
||||
model = UserTextInputBlock
|
||||
|
||||
|
||||
class TaskBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
title = "Teilauftrag"
|
||||
file_submission_required = False
|
||||
content = TaskContentStreamBlockFactory()
|
||||
|
||||
class Meta:
|
||||
model = TaskBlock
|
||||
|
||||
|
||||
class PerformanceObjectiveBlockFactory(wagtail_factories.StructBlockFactory):
|
||||
text = "Die Teilnehmer können die wichtigsten Eckwerte eines Versicherungsverhältnisses erfassen."
|
||||
|
||||
class Meta:
|
||||
model = PerformanceObjectiveBlock
|
||||
|
||||
|
||||
class AssignmentFactory(wagtail_factories.PageFactory):
|
||||
title = "Auftrag"
|
||||
starting_position = replace_whitespace(
|
||||
"""
|
||||
Jemand aus deiner Familie oder aus deinem Freundeskreis möchte sein
|
||||
Versicherungspolice überprüfen lassen. Diese Person kommt nun mit ihrer Police auf dich zu
|
||||
und bittet dich als Versicherungsprofi, diese kritisch zu überprüfen und ihr gg. Anpassungsvorschläge
|
||||
zu unterbreiten. In diesem Kompetenznachweis kannst du nun dein Wissen und Können im Bereich
|
||||
der Motorfahrzeugversicherung unter Beweis stellen.
|
||||
"""
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Assignment
|
||||
|
||||
|
||||
class AssignmentListPageFactory(wagtail_factories.PageFactory):
|
||||
title = "Aufträge"
|
||||
|
||||
class Meta:
|
||||
model = AssignmentListPage
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
import logging
|
||||
import re
|
||||
|
||||
import structlog
|
||||
from django.conf import settings
|
||||
|
|
@ -50,3 +51,7 @@ def first_true(iterable, default=False, pred=None):
|
|||
# first_true([a,b,c], x) --> a or b or c or x
|
||||
# first_true([a,b], x, f) --> a if f(a) else b if f(b) else x
|
||||
return next(filter(pred, iterable), default)
|
||||
|
||||
|
||||
def replace_whitespace(text, replacement=" "):
|
||||
return re.sub(r"\s+", replacement, text).strip()
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import djclick as click
|
||||
from wagtail.models import Page
|
||||
|
||||
from vbv_lernwelt.assignment.creators.create_assignments import create_uk_assignments
|
||||
from vbv_lernwelt.competence.create_uk_competence_profile import (
|
||||
create_uk_competence_profile,
|
||||
create_uk_fr_competence_profile,
|
||||
|
|
@ -137,6 +138,7 @@ def create_course_uk_de():
|
|||
create_versicherungsvermittlerin_with_categories(
|
||||
course_id=COURSE_UK, title="Überbetriebliche Kurse"
|
||||
)
|
||||
create_uk_assignments(course_id=COURSE_UK)
|
||||
create_uk_learning_path(course_id=COURSE_UK)
|
||||
create_uk_competence_profile(course_id=COURSE_UK)
|
||||
create_default_media_library(course_id=COURSE_UK)
|
||||
|
|
@ -156,6 +158,14 @@ def create_course_uk_de():
|
|||
"trainer": "Roland Grossenbacher, roland.grossenbacher@helvetia.ch",
|
||||
}
|
||||
],
|
||||
assignment_details_list=[
|
||||
{
|
||||
"learningContentId": LearningContent.objects.get(
|
||||
slug="überbetriebliche-kurse-lp-circle-fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice"
|
||||
).id,
|
||||
"deadlineDateTimeUtc": "2023-05-30T19:00:00Z",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
# figma demo users and data
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ from slugify import slugify
|
|||
from wagtail.models import Locale, Page, Site
|
||||
from wagtail_localize.models import LocaleSynchronization
|
||||
|
||||
from vbv_lernwelt.assignment.models import Assignment
|
||||
from vbv_lernwelt.core.admin import User
|
||||
from vbv_lernwelt.course.consts import COURSE_UK, COURSE_UK_FR
|
||||
from vbv_lernwelt.course.models import CoursePage
|
||||
from vbv_lernwelt.learnpath.tests.learning_path_factories import (
|
||||
AssignmentBlockFactory,
|
||||
AttendanceDayBlockFactory,
|
||||
CircleFactory,
|
||||
FeedbackBlockFactory,
|
||||
|
|
@ -282,6 +284,20 @@ damit du erfolgreich mit deinem Lernpfad (durch-)starten kannst.
|
|||
title="Reflexion",
|
||||
parent=circle,
|
||||
)
|
||||
LearningContentFactory(
|
||||
title="Überprüfen einer Motorfahrzeug-Versicherungspolice",
|
||||
parent=circle,
|
||||
contents=[
|
||||
(
|
||||
"assignment",
|
||||
AssignmentBlockFactory(
|
||||
assignment=Assignment.objects.get(
|
||||
slug__startswith="überbetriebliche-kurse-assignment-überprüfen-einer-motorfahrzeugs"
|
||||
)
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
LearningContentFactory(
|
||||
title="Feedback",
|
||||
parent=circle,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.2.13 on 2023-04-06 09:19
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("course", "0003_auto_20230404_0837"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="coursesession",
|
||||
name="assignment_details_list",
|
||||
field=models.JSONField(blank=True, default=list),
|
||||
),
|
||||
]
|
||||
|
|
@ -121,6 +121,7 @@ class CoursePage(CourseBasePage):
|
|||
"learnpath.LearningPath",
|
||||
"competence.CompetenceProfilePage",
|
||||
"media_library.MediaLibraryPage",
|
||||
"assignment.AssignmentListPage",
|
||||
]
|
||||
course = models.OneToOneField("course.Course", on_delete=models.PROTECT)
|
||||
|
||||
|
|
@ -185,6 +186,7 @@ class CourseSession(models.Model):
|
|||
end_date = models.DateField(null=True, blank=True)
|
||||
|
||||
attendance_days = models.JSONField(default=list, blank=True)
|
||||
assignment_details_list = models.JSONField(default=list, blank=True)
|
||||
|
||||
additional_json_data = models.JSONField(default=dict, blank=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ class CourseSessionSerializer(serializers.ModelSerializer):
|
|||
"end_date",
|
||||
"additional_json_data",
|
||||
"attendance_days",
|
||||
"assignment_details_list",
|
||||
"learning_path_url",
|
||||
"competence_url",
|
||||
"media_library_url",
|
||||
|
|
|
|||
|
|
@ -33,9 +33,12 @@ logger = structlog.get_logger(__name__)
|
|||
|
||||
|
||||
@api_view(["GET"])
|
||||
def course_page_api_view(request, slug):
|
||||
def course_page_api_view(request, slug_or_id):
|
||||
try:
|
||||
page = Page.objects.get(slug=slug, locale__language_code="de-CH")
|
||||
if slug_or_id.isdigit():
|
||||
page = Page.objects.get(id=slug_or_id)
|
||||
else:
|
||||
page = Page.objects.get(slug=slug_or_id, locale__language_code="de-CH")
|
||||
if not has_course_access_by_page_request(request, page):
|
||||
raise PermissionDenied()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.2.13 on 2023-04-03 16:05
|
||||
# Generated by Django 3.2.13 on 2023-04-04 08:28
|
||||
|
||||
import wagtail.blocks
|
||||
import wagtail.fields
|
||||
|
|
@ -104,8 +104,13 @@ class Migration(migrations.Migration):
|
|||
wagtail.blocks.StructBlock(
|
||||
[
|
||||
("description", wagtail.blocks.TextBlock()),
|
||||
("url", wagtail.blocks.TextBlock()),
|
||||
("text", wagtail.blocks.RichTextBlock(required=False)),
|
||||
(
|
||||
"assignment",
|
||||
wagtail.blocks.PageChooserBlock(
|
||||
help_text="Choose the corresponding assignment.",
|
||||
required=True,
|
||||
),
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
from wagtail import blocks
|
||||
from wagtail.blocks import PageChooserBlock
|
||||
|
||||
|
||||
class AssignmentBlock(blocks.StructBlock):
|
||||
description = blocks.TextBlock()
|
||||
url = blocks.TextBlock()
|
||||
text = blocks.RichTextBlock(required=False)
|
||||
assignment = PageChooserBlock(
|
||||
required=True, help_text="Choose the corresponding assignment."
|
||||
)
|
||||
|
||||
class Meta:
|
||||
icon = "media"
|
||||
|
|
|
|||
Loading…
Reference in New Issue