Merged in feature/refactor-duedates (pull request #196)
Feature/refactor duedates Approved-by: Christian Cueni
This commit is contained in:
commit
f4d22416e9
|
|
@ -4,23 +4,33 @@ import type { DueDate } from "@/types";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
dueDate: DueDate;
|
dueDate: DueDate;
|
||||||
|
singleLine?: boolean;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex items-center justify-between py-4">
|
<div
|
||||||
|
class="flex justify-between py-4"
|
||||||
|
:class="{ 'flex-col': props.singleLine, 'items-center': !props.singleLine }"
|
||||||
|
>
|
||||||
<div class="space-y-1">
|
<div class="space-y-1">
|
||||||
<a class="text-bold underline" :href="props.dueDate.url">
|
<div>
|
||||||
{{ props.dueDate.title }}
|
<a class="text-bold underline" :href="props.dueDate.url">
|
||||||
</a>
|
{{ props.dueDate.title }}
|
||||||
<p class="text-small text-gray-900">
|
</a>
|
||||||
{{ props.dueDate.learning_content_description }}
|
</div>
|
||||||
<span v-if="props.dueDate.description !== ''">:</span>
|
<div class="text-small text-gray-900">
|
||||||
{{ props.dueDate.description }}
|
<div v-if="props.dueDate.date_type_translation_key">
|
||||||
</p>
|
{{ $t(props.dueDate.assignment_type_translation_key) }}:
|
||||||
|
{{ $t(props.dueDate.date_type_translation_key) }}
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
{{ $t(props.dueDate.assignment_type_translation_key) }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<div>
|
||||||
{{ formatDate(props.dueDate.start, props.dueDate.end) }}
|
{{ formatDate(props.dueDate.start, props.dueDate.end) }}
|
||||||
</p>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,3 @@
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<ul>
|
|
||||||
<li
|
|
||||||
v-for="dueDate in dueDatesDisplayed"
|
|
||||||
:key="dueDate.id"
|
|
||||||
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
|
||||||
>
|
|
||||||
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div v-if="allDueDates.length > props.maxCount" class="flex items-center pt-6">
|
|
||||||
<!--a href="">{{ $t("dueDates.showAllDueDates") }}</a-->
|
|
||||||
<it-icon-arrow-right />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||||
import type { DueDate } from "@/types";
|
import type { DueDate } from "@/types";
|
||||||
|
|
@ -37,3 +17,23 @@ const dueDatesDisplayed = computed(() => {
|
||||||
return props.dueDates.slice(0, props.maxCount);
|
return props.dueDates.slice(0, props.maxCount);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<ul>
|
||||||
|
<li
|
||||||
|
v-for="dueDate in dueDatesDisplayed"
|
||||||
|
:key="dueDate.id"
|
||||||
|
:class="{ 'first:border-t': props.showTopBorder, 'border-b': true }"
|
||||||
|
>
|
||||||
|
<DueDateSingle :due-date="dueDate"></DueDateSingle>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div v-if="allDueDates.length > props.maxCount" class="flex items-center pt-6">
|
||||||
|
<!--a href="">{{ $t("dueDates.showAllDueDates") }}</a-->
|
||||||
|
<it-icon-arrow-right />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="allDueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { Dayjs } from "dayjs";
|
import type { Dayjs } from "dayjs";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
export const formatDate = (start: Dayjs, end: Dayjs) => {
|
export const formatDate = (start: Dayjs, end: Dayjs) => {
|
||||||
const startDateString = getDateString(start);
|
const startDateString = getDateString(start);
|
||||||
|
|
@ -7,7 +8,7 @@ export const formatDate = (start: Dayjs, end: Dayjs) => {
|
||||||
// if start isundefined, dont show the day twice
|
// if start isundefined, dont show the day twice
|
||||||
|
|
||||||
if (!start.isValid() && !end.isValid()) {
|
if (!start.isValid() && !end.isValid()) {
|
||||||
return "Termin nicht festgelegt";
|
return i18next.t("Termin nicht festgelegt");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!start || (!start.isValid() && end.isValid())) {
|
if (!start || (!start.isValid() && end.isValid())) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
|
"Alle": "Alle",
|
||||||
"Anwesenheit Präsenzkurse": "Anwesenheit Präsenzkurse",
|
"Anwesenheit Präsenzkurse": "Anwesenheit Präsenzkurse",
|
||||||
"Anwesenheit bestätigen": "Anwesenheit bestätigen",
|
"Anwesenheit bestätigen": "Anwesenheit bestätigen",
|
||||||
"Anwesenheit prüfen": "Anwesenheit prüfen",
|
"Anwesenheit prüfen": "Anwesenheit prüfen",
|
||||||
|
|
@ -7,6 +8,7 @@
|
||||||
"Ergebnisse anschauen": "Ergebnisse anschauen",
|
"Ergebnisse anschauen": "Ergebnisse anschauen",
|
||||||
"Feedback": "Feedback",
|
"Feedback": "Feedback",
|
||||||
"Feedback anschauen": "Feedback anschauen",
|
"Feedback anschauen": "Feedback anschauen",
|
||||||
|
"Feedback: Feedback zum Lehrgang": "Feedback: Feedback zum Lehrgang",
|
||||||
"MS Teams öffnen": "MS Teams öffnen",
|
"MS Teams öffnen": "MS Teams öffnen",
|
||||||
"Nächste Termine": "Nächste Termine",
|
"Nächste Termine": "Nächste Termine",
|
||||||
"Passwort": "Passwort",
|
"Passwort": "Passwort",
|
||||||
|
|
@ -22,10 +24,10 @@
|
||||||
"assignmentSubmitted": "Du hast deine Ergebnisse erfolgreich abgegeben.",
|
"assignmentSubmitted": "Du hast deine Ergebnisse erfolgreich abgegeben.",
|
||||||
"confirmSubmitPerson": "Hiermit bestätige ich, dass die folgende Person meine Ergebnisse bewerten soll.",
|
"confirmSubmitPerson": "Hiermit bestätige ich, dass die folgende Person meine Ergebnisse bewerten soll.",
|
||||||
"confirmSubmitResults": "Hiermit bestätige ich, dass ich die Zusammenfassung meiner Ergebnisse überprüft habe und so abgeben will.",
|
"confirmSubmitResults": "Hiermit bestätige ich, dass ich die Zusammenfassung meiner Ergebnisse überprüft habe und so abgeben will.",
|
||||||
|
"dueDateEvaluation": "Freigabetermin Bewertung",
|
||||||
"dueDateIntroduction": "Reiche deine Ergebnisse pünktlich ein bis am: ",
|
"dueDateIntroduction": "Reiche deine Ergebnisse pünktlich ein bis am: ",
|
||||||
"dueDateNotSet": "Keine Abgabedaten wurden erfasst für diese Durchführung",
|
"dueDateNotSet": "Keine Abgabedaten wurden erfasst für diese Durchführung",
|
||||||
"dueDateSubmission": "Abgabetermin",
|
"dueDateSubmission": "Abgabetermin",
|
||||||
"dueDateTitle": "Abgabetermin",
|
|
||||||
"edit": "Bearbeiten",
|
"edit": "Bearbeiten",
|
||||||
"effortTitle": "Zeitaufwand",
|
"effortTitle": "Zeitaufwand",
|
||||||
"initialSituationTitle": "Ausgangslage",
|
"initialSituationTitle": "Ausgangslage",
|
||||||
|
|
@ -107,7 +109,7 @@
|
||||||
"showAllDueDates": "Alle Termine anzeigen"
|
"showAllDueDates": "Alle Termine anzeigen"
|
||||||
},
|
},
|
||||||
"edoniqTest": {
|
"edoniqTest": {
|
||||||
"qualifiesForExtendedTime": "edoniqTest.qualifiesForExtendedTime"
|
"qualifiesForExtendedTime": "Ich habe Anrecht auf einen Nachteilsausgleich"
|
||||||
},
|
},
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"answers": "Antworten",
|
"answers": "Antworten",
|
||||||
|
|
@ -204,6 +206,7 @@
|
||||||
"attendanceCourse": "Präsenzkurs",
|
"attendanceCourse": "Präsenzkurs",
|
||||||
"casework": "Geleitete Fallarbeit",
|
"casework": "Geleitete Fallarbeit",
|
||||||
"documents": "Dokumente",
|
"documents": "Dokumente",
|
||||||
|
"edoniqTest": "Wissens- und Verständnisfragen",
|
||||||
"feedback": "Feedback",
|
"feedback": "Feedback",
|
||||||
"learningModule": "Lernmodul",
|
"learningModule": "Lernmodul",
|
||||||
"placeholder": "In Umsetzung",
|
"placeholder": "In Umsetzung",
|
||||||
|
|
@ -250,7 +253,6 @@
|
||||||
"titel": "Lernmedien"
|
"titel": "Lernmedien"
|
||||||
},
|
},
|
||||||
"overview": "Übersicht",
|
"overview": "Übersicht",
|
||||||
"show": "Mediathek anzeigen",
|
|
||||||
"title": "Mediathek"
|
"title": "Mediathek"
|
||||||
},
|
},
|
||||||
"messages": {
|
"messages": {
|
||||||
|
|
@ -273,7 +275,7 @@
|
||||||
"selfEvaluationNo": "@:selfEvaluation: Muss ich nochmals anschauen.",
|
"selfEvaluationNo": "@:selfEvaluation: Muss ich nochmals anschauen.",
|
||||||
"selfEvaluationYes": "@:selfEvaluation: Ich kann das.",
|
"selfEvaluationYes": "@:selfEvaluation: Ich kann das.",
|
||||||
"steps": "Schritt {{current}} von {{max}}",
|
"steps": "Schritt {{current}} von {{max}}",
|
||||||
"title": "@:selfEvaluation.selfEvaluation {{title}}",
|
"title": "Selbsteinschätzung {{title}}",
|
||||||
"yes": "Ja, ich kann das"
|
"yes": "Ja, ich kann das"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@
|
||||||
"selfEvaluationNo": "@:selfEvaluation: Il faut que je regarde cela encore une fois de plus près.",
|
"selfEvaluationNo": "@:selfEvaluation: Il faut que je regarde cela encore une fois de plus près.",
|
||||||
"selfEvaluationYes": "@:selfEvaluation: Je maîtrise cette question.",
|
"selfEvaluationYes": "@:selfEvaluation: Je maîtrise cette question.",
|
||||||
"steps": "Étape {{current}} sur {{max}}",
|
"steps": "Étape {{current}} sur {{max}}",
|
||||||
"title": "@:selfEvaluation.selfEvaluation {{title}}",
|
"title": "Selbsteinschätzung {{title}}",
|
||||||
"yes": "Oui, je maîtrise cette question"
|
"yes": "Oui, je maîtrise cette question"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
||||||
|
|
@ -275,7 +275,7 @@
|
||||||
"selfEvaluationNo": "@:selfEvaluation: Devo riguardarlo ancora una volta.",
|
"selfEvaluationNo": "@:selfEvaluation: Devo riguardarlo ancora una volta.",
|
||||||
"selfEvaluationYes": "@:selfEvaluation: Ho compreso tutto.",
|
"selfEvaluationYes": "@:selfEvaluation: Ho compreso tutto.",
|
||||||
"steps": "Passo {{current}} di {{max}}",
|
"steps": "Passo {{current}} di {{max}}",
|
||||||
"title": "@:selfEvaluation.selfEvaluation {{title}}",
|
"title": "Selbsteinschätzung {{title}}",
|
||||||
"yes": "Sì, ho compreso tutto"
|
"yes": "Sì, ho compreso tutto"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ const getNextStepLink = (courseSession: CourseSession) => {
|
||||||
<DueDatesList
|
<DueDatesList
|
||||||
class="bg-white p-6"
|
class="bg-white p-6"
|
||||||
:due-dates="allDueDates"
|
:due-dates="allDueDates"
|
||||||
:max-count="10"
|
:max-count="13"
|
||||||
:show-top-border="false"
|
:show-top-border="false"
|
||||||
></DueDatesList>
|
></DueDatesList>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,9 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useCurrentCourseSession } from "@/composables";
|
import { useCurrentCourseSession } from "@/composables";
|
||||||
import type { Dayjs } from "dayjs";
|
import DueDateSingle from "@/components/dueDates/DueDateSingle.vue";
|
||||||
import type { DueDate } from "@/types";
|
|
||||||
|
|
||||||
const courseSession = useCurrentCourseSession();
|
const courseSession = useCurrentCourseSession();
|
||||||
const dueDates = courseSession.value.due_dates.slice(0, 2);
|
const dueDates = courseSession.value.due_dates.slice(0, 2);
|
||||||
|
|
||||||
const formatDate = (date: Dayjs) => {
|
|
||||||
return date.format("DD.MM.YYYY");
|
|
||||||
};
|
|
||||||
|
|
||||||
const formatDateLine = (dueDate: DueDate) => {
|
|
||||||
let line = `${formatDate(dueDate.start)} - ${dueDate.learning_content_description}`;
|
|
||||||
if (dueDate.description.length !== 0) {
|
|
||||||
line += `: ${dueDate.description}`;
|
|
||||||
}
|
|
||||||
return line;
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
@ -27,7 +14,7 @@ const formatDateLine = (dueDate: DueDate) => {
|
||||||
:key="dueDate.id"
|
:key="dueDate.id"
|
||||||
class="border-t border-gray-500 pt-2"
|
class="border-t border-gray-500 pt-2"
|
||||||
>
|
>
|
||||||
{{ formatDateLine(dueDate) }}
|
<DueDateSingle :due-date="dueDate" :single-line="true"></DueDateSingle>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="dueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
<div v-if="dueDates.length === 0">{{ $t("dueDates.noDueDatesAvailable") }}</div>
|
||||||
<a class="border-t border-gray-500 pt-8 underline" href="">
|
<a class="border-t border-gray-500 pt-8 underline" href="">
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ const step = useRouteQuery("step");
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 class="mb-4 mt-8">{{ $t("assignment.dueDateTitle") }}asdf</h3>
|
<h3 class="mb-4 mt-8">{{ $t("assignment.dueDateSubmission") }}</h3>
|
||||||
<p v-if="props.dueDate?.toString() === 'Invalid Date'" class="text-large">
|
<p v-if="props.dueDate?.toString() === 'Invalid Date'" class="text-large">
|
||||||
{{ $t("assignment.dueDateIntroduction") }}
|
{{ $t("assignment.dueDateIntroduction") }}
|
||||||
<DateEmbedding :single-date="dueDate"></DateEmbedding>
|
<DateEmbedding :single-date="dueDate"></DateEmbedding>
|
||||||
|
|
|
||||||
|
|
@ -209,14 +209,14 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => {
|
||||||
|
|
||||||
function sortDueDates(dueDates: DueDate[]) {
|
function sortDueDates(dueDates: DueDate[]) {
|
||||||
dueDates.sort((a, b) => {
|
dueDates.sort((a, b) => {
|
||||||
const dateA = dayjs(a.end);
|
const dateA = dayjs(a.start);
|
||||||
const dateB = dayjs(b.end);
|
const dateB = dayjs(b.start);
|
||||||
|
|
||||||
if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
|
if (!dateA.isValid() && !dateB.isValid()) return 0; // If both are invalid, they are equal
|
||||||
if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
|
if (!dateA.isValid()) return 1; // If dateA is invalid, it goes after dateB
|
||||||
if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA
|
if (!dateB.isValid()) return -1; // If dateB is invalid, it goes after dateA
|
||||||
|
|
||||||
return dateA.diff(dateB); // Otherwise, sort by the end date
|
return dateA.diff(dateB); // sort by `start`
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -575,8 +575,9 @@ export type DueDate = {
|
||||||
start: Dayjs;
|
start: Dayjs;
|
||||||
end: Dayjs;
|
end: Dayjs;
|
||||||
title: string;
|
title: string;
|
||||||
learning_content_description: string;
|
assignment_type_translation_key: string;
|
||||||
description: string;
|
date_type_translation_key: string;
|
||||||
|
subtitle: string;
|
||||||
url: string;
|
url: string;
|
||||||
course_session: number | null;
|
course_session: number | null;
|
||||||
page: number | null;
|
page: number | null;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-23 09:27
|
||||||
|
|
||||||
|
import wagtail.blocks
|
||||||
|
import wagtail.fields
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("assignment", "0005_alter_assignment_assignment_type"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignment",
|
||||||
|
name="evaluation_tasks",
|
||||||
|
field=wagtail.fields.StreamField(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"task",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("title", wagtail.blocks.TextBlock()),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
wagtail.blocks.RichTextBlock(
|
||||||
|
blank=True,
|
||||||
|
features=["ul", "bold", "italic", "link"],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("max_points", wagtail.blocks.IntegerBlock()),
|
||||||
|
(
|
||||||
|
"sub_tasks",
|
||||||
|
wagtail.blocks.ListBlock(
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
("title", wagtail.blocks.TextBlock()),
|
||||||
|
(
|
||||||
|
"description",
|
||||||
|
wagtail.blocks.RichTextBlock(
|
||||||
|
blank=True,
|
||||||
|
features=[
|
||||||
|
"ul",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"link",
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"points",
|
||||||
|
wagtail.blocks.IntegerBlock(),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
blank=True,
|
||||||
|
use_json_field=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
blank=True,
|
||||||
|
help_text="Beurteilungsschritte",
|
||||||
|
use_json_field=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="assignment",
|
||||||
|
name="tasks",
|
||||||
|
field=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.RichTextBlock(
|
||||||
|
features=[
|
||||||
|
"ul",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"link",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user_text_input",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"text",
|
||||||
|
wagtail.blocks.RichTextBlock(
|
||||||
|
blank=True,
|
||||||
|
features=[
|
||||||
|
"ul",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"link",
|
||||||
|
],
|
||||||
|
required=False,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user_confirmation",
|
||||||
|
wagtail.blocks.StructBlock(
|
||||||
|
[
|
||||||
|
(
|
||||||
|
"text",
|
||||||
|
wagtail.blocks.RichTextBlock(
|
||||||
|
features=[
|
||||||
|
"ul",
|
||||||
|
"bold",
|
||||||
|
"italic",
|
||||||
|
"link",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
blank=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
blank=True,
|
||||||
|
help_text="Teilaufgaben",
|
||||||
|
use_json_field=True,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -33,6 +33,8 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "created_at"
|
date_hierarchy = "created_at"
|
||||||
list_display = [
|
list_display = [
|
||||||
"user",
|
"user",
|
||||||
|
"user_last_name",
|
||||||
|
"user_first_name",
|
||||||
"course_session",
|
"course_session",
|
||||||
"role",
|
"role",
|
||||||
"created_at",
|
"created_at",
|
||||||
|
|
@ -45,13 +47,25 @@ class CourseSessionUserAdmin(admin.ModelAdmin):
|
||||||
"course_session__title",
|
"course_session__title",
|
||||||
]
|
]
|
||||||
list_filter = [
|
list_filter = [
|
||||||
"course_session",
|
|
||||||
"role",
|
"role",
|
||||||
|
"course_session",
|
||||||
]
|
]
|
||||||
raw_id_fields = [
|
raw_id_fields = [
|
||||||
"user",
|
"user",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def user_first_name(self, obj):
|
||||||
|
return obj.user.first_name
|
||||||
|
|
||||||
|
user_first_name.short_description = "First Name"
|
||||||
|
user_first_name.admin_order_field = "user__first_name"
|
||||||
|
|
||||||
|
def user_last_name(self, obj):
|
||||||
|
return obj.user.last_name
|
||||||
|
|
||||||
|
user_last_name.short_description = "Last Name"
|
||||||
|
user_last_name.admin_order_field = "user__last_name"
|
||||||
|
|
||||||
fieldsets = [
|
fieldsets = [
|
||||||
(None, {"fields": ("user", "course_session", "role")}),
|
(None, {"fields": ("user", "course_session", "role")}),
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-23 15:44
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("course", "0003_alter_coursecompletion_additional_json_data"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="course",
|
||||||
|
options={},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="coursesession",
|
||||||
|
options={"ordering": ["title"]},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="coursesessionuser",
|
||||||
|
options={
|
||||||
|
"ordering": ["user__last_name", "user__first_name", "user__email"]
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -24,9 +24,6 @@ class Course(models.Model):
|
||||||
_("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True
|
_("Slug"), max_length=255, unique=True, blank=True, allow_unicode=True
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = _("Lehrgang")
|
|
||||||
|
|
||||||
def get_course_url(self):
|
def get_course_url(self):
|
||||||
return f"/course/{self.slug}"
|
return f"/course/{self.slug}"
|
||||||
|
|
||||||
|
|
@ -245,6 +242,9 @@ class CourseSession(models.Model):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.title}"
|
return f"{self.title}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["title"]
|
||||||
|
|
||||||
|
|
||||||
class CourseSessionUser(models.Model):
|
class CourseSessionUser(models.Model):
|
||||||
"""
|
"""
|
||||||
|
|
@ -280,6 +280,7 @@ class CourseSessionUser(models.Model):
|
||||||
name="course_session_user_unique_course_session_user",
|
name="course_session_user_unique_course_session_user",
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
ordering = ["user__last_name", "user__first_name", "user__email"]
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
|
from django import forms
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from vbv_lernwelt.course_session.models import (
|
from vbv_lernwelt.course_session.models import (
|
||||||
CourseSessionAssignment,
|
CourseSessionAssignment,
|
||||||
CourseSessionAttendanceCourse,
|
CourseSessionAttendanceCourse,
|
||||||
|
CourseSessionEdoniqTest,
|
||||||
)
|
)
|
||||||
|
from vbv_lernwelt.learnpath.models import Circle
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CourseSessionAttendanceCourse)
|
@admin.register(CourseSessionAttendanceCourse)
|
||||||
class CourseSessionAttendanceCourseAdmin(admin.ModelAdmin):
|
class CourseSessionAttendanceCourseAdmin(admin.ModelAdmin):
|
||||||
# Inline fields are not possible for the DueDate model, because it is not a ForeignKey relatoion.
|
|
||||||
readonly_fields = [
|
readonly_fields = [
|
||||||
"course_session",
|
"course_session",
|
||||||
"learning_content",
|
"learning_content",
|
||||||
|
|
@ -17,18 +19,180 @@ class CourseSessionAttendanceCourseAdmin(admin.ModelAdmin):
|
||||||
]
|
]
|
||||||
list_display = [
|
list_display = [
|
||||||
"course_session",
|
"course_session",
|
||||||
|
"circle",
|
||||||
"learning_content",
|
"learning_content",
|
||||||
|
"start_date",
|
||||||
|
"end_date",
|
||||||
"trainer",
|
"trainer",
|
||||||
]
|
]
|
||||||
list_filter = ["course_session__course", "course_session"]
|
list_filter = ["course_session__course", "course_session"]
|
||||||
|
|
||||||
|
def start_date(self, obj):
|
||||||
|
return obj.due_date.start
|
||||||
|
|
||||||
|
start_date.admin_order_field = "due_date__start"
|
||||||
|
|
||||||
|
def end_date(self, obj):
|
||||||
|
return obj.due_date.end
|
||||||
|
|
||||||
|
end_date.admin_order_field = "due_date__end"
|
||||||
|
|
||||||
|
def circle(self, obj):
|
||||||
|
try:
|
||||||
|
return obj.learning_content.get_ancestors().exact_type(Circle).first().title
|
||||||
|
except Exception:
|
||||||
|
# noop
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Create a method that serves as a form field
|
||||||
|
def circle_display(self, obj=None):
|
||||||
|
if obj:
|
||||||
|
return self.circle(obj)
|
||||||
|
return None
|
||||||
|
|
||||||
|
circle_display.short_description = "Circle"
|
||||||
|
|
||||||
|
# Make circle display-only in the form
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
readonly_fields = super(
|
||||||
|
CourseSessionAttendanceCourseAdmin, self
|
||||||
|
).get_readonly_fields(request, obj)
|
||||||
|
return readonly_fields + ["circle_display"]
|
||||||
|
|
||||||
|
# Override get_form to include circle_display
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
form = super(CourseSessionAttendanceCourseAdmin, self).get_form(
|
||||||
|
request, obj, **kwargs
|
||||||
|
)
|
||||||
|
form.base_fields["circle_display"] = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label="Circle",
|
||||||
|
widget=forms.TextInput(attrs={"readonly": "readonly"}),
|
||||||
|
)
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
@admin.register(CourseSessionAssignment)
|
@admin.register(CourseSessionAssignment)
|
||||||
class CourseSessionAssignmentAdmin(admin.ModelAdmin):
|
class CourseSessionAssignmentAdmin(admin.ModelAdmin):
|
||||||
# Inline fields are not possible for the DueDate model, because it is not a ForeignKey relatoion.
|
readonly_fields = [
|
||||||
readonly_fields = ["course_session", "learning_content"]
|
|
||||||
list_display = [
|
|
||||||
"course_session",
|
"course_session",
|
||||||
"learning_content",
|
"learning_content",
|
||||||
|
"submission_deadline",
|
||||||
|
"evaluation_deadline",
|
||||||
]
|
]
|
||||||
list_filter = ["course_session__course"]
|
list_display = [
|
||||||
|
"course_session",
|
||||||
|
"circle",
|
||||||
|
"learning_content",
|
||||||
|
"submission_date",
|
||||||
|
"evaluation_date",
|
||||||
|
]
|
||||||
|
list_filter = ["course_session__course", "course_session"]
|
||||||
|
|
||||||
|
def submission_date(self, obj):
|
||||||
|
if obj.submission_deadline:
|
||||||
|
return obj.submission_deadline.start
|
||||||
|
return None
|
||||||
|
|
||||||
|
submission_date.admin_order_field = "submission_deadline__start"
|
||||||
|
|
||||||
|
def evaluation_date(self, obj):
|
||||||
|
if obj.evaluation_deadline:
|
||||||
|
return obj.evaluation_deadline.start
|
||||||
|
return None
|
||||||
|
|
||||||
|
evaluation_date.admin_order_field = "evaluation_deadline__start"
|
||||||
|
|
||||||
|
def circle(self, obj):
|
||||||
|
try:
|
||||||
|
return obj.learning_content.get_ancestors().exact_type(Circle).first().title
|
||||||
|
except Exception:
|
||||||
|
# noop
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Create a method that serves as a form field
|
||||||
|
def circle_display(self, obj=None):
|
||||||
|
if obj:
|
||||||
|
return self.circle(obj)
|
||||||
|
return None
|
||||||
|
|
||||||
|
circle_display.short_description = "Circle"
|
||||||
|
|
||||||
|
# Make circle display-only in the form
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
readonly_fields = super(CourseSessionAssignmentAdmin, self).get_readonly_fields(
|
||||||
|
request, obj
|
||||||
|
)
|
||||||
|
return readonly_fields + ["circle_display"]
|
||||||
|
|
||||||
|
# Override get_form to include circle_display
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
form = super(CourseSessionAssignmentAdmin, self).get_form(
|
||||||
|
request, obj, **kwargs
|
||||||
|
)
|
||||||
|
form.base_fields["circle_display"] = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label="Circle",
|
||||||
|
widget=forms.TextInput(attrs={"readonly": "readonly"}),
|
||||||
|
)
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(CourseSessionEdoniqTest)
|
||||||
|
class CourseSessionEdoniqTestAdmin(admin.ModelAdmin):
|
||||||
|
readonly_fields = [
|
||||||
|
"course_session",
|
||||||
|
"learning_content",
|
||||||
|
"deadline",
|
||||||
|
]
|
||||||
|
list_display = [
|
||||||
|
"course_session",
|
||||||
|
"circle",
|
||||||
|
"learning_content",
|
||||||
|
"deadline_date",
|
||||||
|
]
|
||||||
|
list_filter = ["course_session__course", "course_session"]
|
||||||
|
|
||||||
|
def deadline_date(self, obj):
|
||||||
|
if obj.deadline:
|
||||||
|
return obj.deadline.start
|
||||||
|
return None
|
||||||
|
|
||||||
|
deadline_date.admin_order_field = "deadline__start"
|
||||||
|
|
||||||
|
def circle(self, obj):
|
||||||
|
try:
|
||||||
|
return obj.learning_content.get_ancestors().exact_type(Circle).first().title
|
||||||
|
except Exception:
|
||||||
|
# noop
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Create a method that serves as a form field
|
||||||
|
def circle_display(self, obj=None):
|
||||||
|
if obj:
|
||||||
|
return self.circle(obj)
|
||||||
|
return None
|
||||||
|
|
||||||
|
circle_display.short_description = "Circle"
|
||||||
|
|
||||||
|
# Make circle display-only in the form
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
readonly_fields = super(CourseSessionEdoniqTestAdmin, self).get_readonly_fields(
|
||||||
|
request, obj
|
||||||
|
)
|
||||||
|
return readonly_fields + ["circle_display"]
|
||||||
|
|
||||||
|
# Override get_form to include circle_display
|
||||||
|
def get_form(self, request, obj=None, **kwargs):
|
||||||
|
form = super(CourseSessionEdoniqTestAdmin, self).get_form(
|
||||||
|
request, obj, **kwargs
|
||||||
|
)
|
||||||
|
form.base_fields["circle_display"] = forms.CharField(
|
||||||
|
required=False,
|
||||||
|
label="Circle",
|
||||||
|
widget=forms.TextInput(attrs={"readonly": "readonly"}),
|
||||||
|
)
|
||||||
|
return form
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django_jsonform.models.fields import JSONField as JSONSchemaField
|
from django_jsonform.models.fields import JSONField as JSONSchemaField
|
||||||
|
|
||||||
from vbv_lernwelt.assignment.models import AssignmentType
|
from vbv_lernwelt.assignment.models import AssignmentType
|
||||||
|
|
@ -30,6 +29,9 @@ class CourseSessionAttendanceCourse(models.Model):
|
||||||
location = models.CharField(max_length=255, blank=True, default="")
|
location = models.CharField(max_length=255, blank=True, default="")
|
||||||
trainer = models.CharField(max_length=255, blank=True, default="")
|
trainer = models.CharField(max_length=255, blank=True, default="")
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["course_session", "due_date__start"]
|
||||||
|
|
||||||
# because the attendance list is more of a snapshot of the current state
|
# because the attendance list is more of a snapshot of the current state
|
||||||
# we will store the attendance list as a JSONField
|
# we will store the attendance list as a JSONField
|
||||||
# the important field of the list type is "user_id"
|
# the important field of the list type is "user_id"
|
||||||
|
|
@ -53,23 +55,22 @@ class CourseSessionAttendanceCourse(models.Model):
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.pk:
|
if self.learning_content_id:
|
||||||
title = ""
|
if not self.due_date_id:
|
||||||
url = ""
|
self.due_date = DueDate.objects.create(
|
||||||
page = None
|
course_session=self.course_session,
|
||||||
if self.learning_content_id:
|
)
|
||||||
title = self.learning_content.title
|
|
||||||
page = self.learning_content.page_ptr
|
if not self.due_date.manual_override_fields:
|
||||||
url = self.learning_content.get_frontend_url()
|
self.due_date.url = self.learning_content.get_frontend_url()
|
||||||
|
self.due_date.title = self.learning_content.title
|
||||||
|
self.due_date.page = self.learning_content.page_ptr
|
||||||
|
self.due_date.assignment_type_translation_key = (
|
||||||
|
"learningContentTypes.attendanceCourse"
|
||||||
|
)
|
||||||
|
|
||||||
|
self.due_date.save()
|
||||||
|
|
||||||
self.due_date = DueDate.objects.create(
|
|
||||||
url=url,
|
|
||||||
title=f"{title}",
|
|
||||||
learning_content_description=f"{_('Präsenzkurs')}",
|
|
||||||
description="",
|
|
||||||
course_session=self.course_session,
|
|
||||||
page=page,
|
|
||||||
)
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|
@ -105,48 +106,61 @@ class CourseSessionAssignment(models.Model):
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
class Meta:
|
||||||
if not self.pk:
|
ordering = ["course_session", "submission_deadline__start"]
|
||||||
title = ""
|
|
||||||
url = ""
|
|
||||||
page = None
|
|
||||||
assignment_type_description = ""
|
|
||||||
if self.learning_content_id:
|
|
||||||
title = self.learning_content.title
|
|
||||||
page = self.learning_content.page_ptr
|
|
||||||
url = self.learning_content.get_frontend_url()
|
|
||||||
assignment_type = self.learning_content.assignment_type
|
|
||||||
assignment_type_descriptions = {
|
|
||||||
AssignmentType.CASEWORK.name: _("Geleitete Fallarbeit"),
|
|
||||||
AssignmentType.PREP_ASSIGNMENT.name: _("Vorbereitungsauftrag"),
|
|
||||||
AssignmentType.REFLECTION.name: _("Reflexion"),
|
|
||||||
}
|
|
||||||
assignment_type_description = assignment_type_descriptions.get(
|
|
||||||
assignment_type, ""
|
|
||||||
)
|
|
||||||
|
|
||||||
if assignment_type in {
|
def save(self, *args, **kwargs):
|
||||||
|
if self.learning_content_id:
|
||||||
|
title = self.learning_content.title
|
||||||
|
page = self.learning_content.page_ptr
|
||||||
|
url = self.learning_content.get_frontend_url()
|
||||||
|
assignment_type = self.learning_content.assignment_type
|
||||||
|
assignment_type_translation_keys = {
|
||||||
|
AssignmentType.CASEWORK.value: "learningContentTypes.casework",
|
||||||
|
AssignmentType.PREP_ASSIGNMENT.value: "learningContentTypes.prepAssignment",
|
||||||
|
AssignmentType.REFLECTION.value: "learningContentTypes.reflection",
|
||||||
|
}
|
||||||
|
|
||||||
|
if assignment_type in (
|
||||||
AssignmentType.CASEWORK.value,
|
AssignmentType.CASEWORK.value,
|
||||||
AssignmentType.PREP_ASSIGNMENT.value,
|
AssignmentType.PREP_ASSIGNMENT.value,
|
||||||
}: # Reflexion
|
):
|
||||||
self.submission_deadline = DueDate.objects.create(
|
if not self.submission_deadline_id:
|
||||||
url=url,
|
self.submission_deadline = DueDate.objects.create(
|
||||||
title=f"{title}",
|
course_session=self.course_session,
|
||||||
learning_content_description=assignment_type_description,
|
)
|
||||||
description=f"{_('Abgabe Termin')}",
|
|
||||||
course_session=self.course_session,
|
if not self.submission_deadline.manual_override_fields:
|
||||||
page=page,
|
self.submission_deadline.url = url
|
||||||
)
|
self.submission_deadline.title = title
|
||||||
|
self.submission_deadline.assignment_type_translation_key = (
|
||||||
|
assignment_type_translation_keys[assignment_type]
|
||||||
|
)
|
||||||
|
self.submission_deadline.date_type_translation_key = (
|
||||||
|
"assignment.dueDateSubmission"
|
||||||
|
)
|
||||||
|
self.submission_deadline.page = page
|
||||||
|
|
||||||
|
self.submission_deadline.save()
|
||||||
|
|
||||||
if assignment_type == AssignmentType.CASEWORK.value:
|
if assignment_type == AssignmentType.CASEWORK.value:
|
||||||
self.evaluation_deadline = DueDate.objects.create(
|
if not self.evaluation_deadline_id:
|
||||||
url=url,
|
self.evaluation_deadline = DueDate.objects.create(
|
||||||
title=f"{title}",
|
course_session=self.course_session,
|
||||||
learning_content_description=assignment_type_description,
|
)
|
||||||
description=f"{_('Freigabe Termin Bewertungen')}",
|
|
||||||
course_session=self.course_session,
|
if not self.evaluation_deadline.manual_override_fields:
|
||||||
page=page,
|
self.evaluation_deadline.url = url
|
||||||
)
|
self.evaluation_deadline.title = title
|
||||||
|
self.evaluation_deadline.assignment_type_translation_key = (
|
||||||
|
assignment_type_translation_keys[assignment_type]
|
||||||
|
)
|
||||||
|
self.evaluation_deadline.date_type_translation_key = (
|
||||||
|
"assignment.dueDateEvaluation"
|
||||||
|
)
|
||||||
|
self.evaluation_deadline.page = page
|
||||||
|
|
||||||
|
self.evaluation_deadline.save()
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
|
@ -170,27 +184,28 @@ class CourseSessionEdoniqTest(models.Model):
|
||||||
null=True,
|
null=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
ordering = ["course_session", "deadline__start"]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.course_session} - {self.learning_content}"
|
return f"{self.course_session} - {self.learning_content}"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if not self.pk:
|
if self.learning_content_id:
|
||||||
title = ""
|
if not self.deadline_id:
|
||||||
url = ""
|
self.deadline = DueDate.objects.create(
|
||||||
page = None
|
course_session=self.course_session,
|
||||||
edoniq_test_type_description = _("Wissens und Verständnisfragen")
|
)
|
||||||
if self.learning_content_id:
|
|
||||||
title = self.learning_content.title
|
|
||||||
page = self.learning_content.page_ptr
|
|
||||||
url = self.learning_content.get_frontend_url()
|
|
||||||
|
|
||||||
self.deadline = DueDate.objects.create(
|
if not self.deadline.manual_override_fields:
|
||||||
url=url,
|
self.deadline.url = self.learning_content.get_frontend_url()
|
||||||
title=f"{title}",
|
self.deadline.title = self.learning_content.title
|
||||||
learning_content_description=edoniq_test_type_description,
|
self.deadline.page = self.learning_content.page_ptr
|
||||||
description=f"{_('Abgabe Termin')}",
|
self.deadline.assignment_type_translation_key = (
|
||||||
course_session=self.course_session,
|
"learningContentTypes.edoniqTest"
|
||||||
page=page,
|
)
|
||||||
)
|
self.deadline.date_type_translation_key = "assignment.dueDateSubmission"
|
||||||
|
|
||||||
|
self.deadline.save()
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.duedate.models import DueDate
|
from vbv_lernwelt.duedate.models import DueDate
|
||||||
from vbv_lernwelt.learnpath.models import (
|
from vbv_lernwelt.learnpath.models import (
|
||||||
|
Circle,
|
||||||
LearningContentAttendanceCourse,
|
LearningContentAttendanceCourse,
|
||||||
LearningContentEdoniqTest,
|
LearningContentEdoniqTest,
|
||||||
)
|
)
|
||||||
|
|
@ -11,18 +12,52 @@ from vbv_lernwelt.learnpath.models import (
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
@admin.register(DueDate)
|
@admin.register(DueDate)
|
||||||
class DueDateAdmin(admin.ModelAdmin):
|
class DueDateAdmin(admin.ModelAdmin):
|
||||||
date_hierarchy = "end"
|
date_hierarchy = "start"
|
||||||
list_display = [
|
list_display = [
|
||||||
"title",
|
|
||||||
"course_session",
|
"course_session",
|
||||||
"learning_content_description",
|
"circle",
|
||||||
|
"title",
|
||||||
|
"display_subtitle",
|
||||||
"start",
|
"start",
|
||||||
"end",
|
"end",
|
||||||
"is_undefined",
|
|
||||||
]
|
]
|
||||||
list_filter = ["course_session"]
|
list_filter = ["course_session__course", "course_session"]
|
||||||
readonly_fields = ["course_session", "page"]
|
readonly_fields = ["course_session", "page"]
|
||||||
|
|
||||||
|
def get_readonly_fields(self, request, obj=None):
|
||||||
|
default_readonly = super(DueDateAdmin, self).get_readonly_fields(request, obj)
|
||||||
|
|
||||||
|
if obj is None:
|
||||||
|
return default_readonly
|
||||||
|
|
||||||
|
if not obj.manual_override_fields:
|
||||||
|
return default_readonly + [
|
||||||
|
"circle",
|
||||||
|
"title",
|
||||||
|
"subtitle",
|
||||||
|
"assignment_type_translation_key",
|
||||||
|
"date_type_translation_key",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
|
return default_readonly
|
||||||
|
|
||||||
|
def circle(self, obj):
|
||||||
|
try:
|
||||||
|
return obj.page.get_ancestors().exact_type(Circle).first().title
|
||||||
|
except Exception:
|
||||||
|
# noop
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Create a method that serves as a form field
|
||||||
|
def circle_display(self, obj=None):
|
||||||
|
if obj:
|
||||||
|
return self.circle(obj)
|
||||||
|
return None
|
||||||
|
|
||||||
|
circle_display.short_description = "Circle"
|
||||||
|
|
||||||
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
def formfield_for_foreignkey(self, db_field, request, **kwargs):
|
||||||
if db_field.name == "page":
|
if db_field.name == "page":
|
||||||
if request.resolver_match.kwargs.get("object_id"):
|
if request.resolver_match.kwargs.get("object_id"):
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-23 09:26
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("duedate", "0002_alter_duedate_start"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name="duedate",
|
||||||
|
options={"ordering": ["start", "end"]},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="description",
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="learning_content_description",
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="assignment_type_translation_key",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Translation Key aus dem Frontend",
|
||||||
|
max_length=1024,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="date_type_translation_key",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Translation Key aus dem Frontend",
|
||||||
|
max_length=1024,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="manual_override_fields",
|
||||||
|
field=models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Nur aktivieren, wenn man die Felder manuell überschreiben will",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="subtitle",
|
||||||
|
field=models.TextField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="Überschreibt den Untertitel bei `assignment_type_translation_key` und `date_type_translation_key`",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="end",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
blank=True, db_index=True, help_text="Enddatum ist optional", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="start",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
blank=True, db_index=True, help_text="Startdatum ist Pflicht", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="title",
|
||||||
|
field=models.CharField(
|
||||||
|
default="",
|
||||||
|
help_text="Title wird standarmässig vom LearningContent übernommen",
|
||||||
|
max_length=1024,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="url",
|
||||||
|
field=models.CharField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
help_text="URL wird vom LearningContent übernommen",
|
||||||
|
max_length=1024,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-23 13:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("duedate", "0003_auto_20230823_1126"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="duedate",
|
||||||
|
name="start",
|
||||||
|
field=models.DateTimeField(
|
||||||
|
db_index=True, help_text="Startdatum ist Pflicht", null=True
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -2,7 +2,6 @@ import datetime
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from wagtail.models import Page
|
from wagtail.models import Page
|
||||||
|
|
||||||
from vbv_lernwelt.core.models import User
|
from vbv_lernwelt.core.models import User
|
||||||
|
|
@ -10,14 +9,44 @@ from vbv_lernwelt.course.models import CourseSession
|
||||||
|
|
||||||
|
|
||||||
class DueDate(models.Model):
|
class DueDate(models.Model):
|
||||||
start = models.DateTimeField(null=True, db_index=True, blank=True)
|
start = models.DateTimeField(
|
||||||
end = models.DateTimeField(null=True, blank=True, db_index=True)
|
null=True, db_index=True, help_text="Startdatum ist Pflicht"
|
||||||
title = models.CharField(default="", max_length=1024)
|
)
|
||||||
learning_content_description = models.CharField(
|
end = models.DateTimeField(
|
||||||
default="", blank=True, max_length=1024
|
null=True, blank=True, db_index=True, help_text="Enddatum ist optional"
|
||||||
|
)
|
||||||
|
manual_override_fields = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
help_text="Nur aktivieren, wenn man die Felder manuell überschreiben will",
|
||||||
|
)
|
||||||
|
title = models.CharField(
|
||||||
|
default="",
|
||||||
|
max_length=1024,
|
||||||
|
help_text="Title wird standarmässig vom LearningContent übernommen",
|
||||||
|
)
|
||||||
|
assignment_type_translation_key = models.CharField(
|
||||||
|
default="",
|
||||||
|
blank=True,
|
||||||
|
max_length=1024,
|
||||||
|
help_text="Translation Key aus dem Frontend",
|
||||||
|
)
|
||||||
|
date_type_translation_key = models.CharField(
|
||||||
|
default="",
|
||||||
|
blank=True,
|
||||||
|
max_length=1024,
|
||||||
|
help_text="Translation Key aus dem Frontend",
|
||||||
|
)
|
||||||
|
subtitle = models.TextField(
|
||||||
|
default="",
|
||||||
|
blank=True,
|
||||||
|
help_text="Überschreibt den Untertitel bei `assignment_type_translation_key` und `date_type_translation_key`",
|
||||||
|
)
|
||||||
|
url = models.CharField(
|
||||||
|
default="",
|
||||||
|
blank=True,
|
||||||
|
max_length=1024,
|
||||||
|
help_text="URL wird vom LearningContent übernommen",
|
||||||
)
|
)
|
||||||
description = models.CharField(default="", blank=True, max_length=1024)
|
|
||||||
url = models.CharField(default="", blank=True, max_length=1024)
|
|
||||||
course_session = models.ForeignKey(
|
course_session = models.ForeignKey(
|
||||||
"course.CourseSession",
|
"course.CourseSession",
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
|
@ -26,23 +55,38 @@ class DueDate(models.Model):
|
||||||
|
|
||||||
page = models.ForeignKey(Page, on_delete=models.SET_NULL, null=True, blank=True)
|
page = models.ForeignKey(Page, on_delete=models.SET_NULL, null=True, blank=True)
|
||||||
|
|
||||||
def Meta(self):
|
class Meta:
|
||||||
ordering = ["start", "end"]
|
ordering = ["start", "end"]
|
||||||
verbose_name = _("Termin")
|
|
||||||
help = "The start date is mandatory. You can set the end date if you want to have a deadline with a duration."
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.is_undefined:
|
if self.is_undefined:
|
||||||
return f"DueDate: {self.title} undefined"
|
return f"{self.title} undefined"
|
||||||
start_str = self.start.strftime("%Y-%m-%d %H:%M") if self.start else "-"
|
|
||||||
result = f"DueDate: {self.title} {start_str}"
|
# Convert the UTC datetime to local time
|
||||||
|
local_start = timezone.localtime(self.start) if self.start else None
|
||||||
|
start_str = local_start.strftime("%Y-%m-%d %H:%M") if local_start else "-"
|
||||||
|
result = f"{self.title} {start_str}"
|
||||||
|
|
||||||
if self.end:
|
if self.end:
|
||||||
end_str = self.end.strftime("%Y-%m-%d %H:%M") if self.end else "-"
|
local_end = timezone.localtime(self.end) if self.end else None
|
||||||
|
end_str = local_end.strftime("%Y-%m-%d %H:%M") if local_end else "-"
|
||||||
result += f" - {end_str}"
|
result += f" - {end_str}"
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@property
|
||||||
|
def display_subtitle(self):
|
||||||
|
result = ""
|
||||||
|
if self.subtitle and self.manual_override_fields:
|
||||||
|
result = self.subtitle
|
||||||
|
|
||||||
|
result = self.assignment_type_translation_key
|
||||||
|
|
||||||
|
if self.date_type_translation_key:
|
||||||
|
result += ": " + self.date_type_translation_key
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_undefined(self):
|
def is_undefined(self):
|
||||||
return self.start is None
|
return self.start is None
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Generated by Django 3.2.20 on 2023-08-23 15:44
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
("course", "0004_auto_20230823_1744"),
|
||||||
|
("feedback", "0002_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="feedbackresponse",
|
||||||
|
name="course_session",
|
||||||
|
field=models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE, to="course.coursesession"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -62,4 +62,4 @@ class FeedbackResponse(models.Model):
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
circle = models.ForeignKey("learnpath.Circle", models.PROTECT)
|
circle = models.ForeignKey("learnpath.Circle", models.PROTECT)
|
||||||
course_session = models.ForeignKey("course.CourseSession", models.PROTECT)
|
course_session = models.ForeignKey("course.CourseSession", models.CASCADE)
|
||||||
|
|
|
||||||
|
|
@ -141,6 +141,7 @@ LP_DATA = {
|
||||||
"slug": "ménage-partie-1",
|
"slug": "ménage-partie-1",
|
||||||
"presence_course": "ménage-partie-1-lc-cours-de-présence-ménage-partie-1",
|
"presence_course": "ménage-partie-1-lc-cours-de-présence-ménage-partie-1",
|
||||||
"assignments": [],
|
"assignments": [],
|
||||||
|
"edoniq_tests": [],
|
||||||
},
|
},
|
||||||
"it": {
|
"it": {
|
||||||
"title": "Economica domestica parte 1",
|
"title": "Economica domestica parte 1",
|
||||||
|
|
@ -163,6 +164,7 @@ LP_DATA = {
|
||||||
"slug": "ménage-partie-2",
|
"slug": "ménage-partie-2",
|
||||||
"presence_course": "ménage-partie-2-lc-cours-de-présence-ménage-partie-2",
|
"presence_course": "ménage-partie-2-lc-cours-de-présence-ménage-partie-2",
|
||||||
"assignments": [],
|
"assignments": [],
|
||||||
|
"edoniq_tests": [],
|
||||||
},
|
},
|
||||||
"it": {
|
"it": {
|
||||||
"title": "Economica domestica parte 2",
|
"title": "Economica domestica parte 2",
|
||||||
|
|
@ -260,8 +262,14 @@ def create_or_update_user(
|
||||||
|
|
||||||
|
|
||||||
def import_course_sessions_from_excel(
|
def import_course_sessions_from_excel(
|
||||||
filename: str, course: Course = None, restrict_language=None
|
filename: str, course: Course = None, restrict_language=None, circle_keys=None
|
||||||
):
|
):
|
||||||
|
if circle_keys is None:
|
||||||
|
circle_keys = [
|
||||||
|
"Kickoff",
|
||||||
|
"Basis",
|
||||||
|
"Fahrzeug",
|
||||||
|
]
|
||||||
workbook = load_workbook(filename=filename)
|
workbook = load_workbook(filename=filename)
|
||||||
sheet = workbook["Schulungen Durchführung"]
|
sheet = workbook["Schulungen Durchführung"]
|
||||||
no_course = course is None
|
no_course = course is None
|
||||||
|
|
@ -281,7 +289,10 @@ def import_course_sessions_from_excel(
|
||||||
course = get_uk_course(language)
|
course = get_uk_course(language)
|
||||||
|
|
||||||
create_or_update_course_session(
|
create_or_update_course_session(
|
||||||
course, data, language, circle_keys=["Kickoff", "Basis", "Fahrzeug"]
|
course,
|
||||||
|
data,
|
||||||
|
language,
|
||||||
|
circle_keys=circle_keys,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -315,7 +326,7 @@ def create_or_update_course_session(
|
||||||
title = f"{region} {generation} {group}"
|
title = f"{region} {generation} {group}"
|
||||||
|
|
||||||
cs, _created = CourseSession.objects.get_or_create(
|
cs, _created = CourseSession.objects.get_or_create(
|
||||||
title=title, course=course, import_id=import_id
|
course=course, import_id=import_id
|
||||||
)
|
)
|
||||||
|
|
||||||
cs.additional_json_data["import_data"] = data
|
cs.additional_json_data["import_data"] = data
|
||||||
|
|
@ -360,7 +371,7 @@ def create_or_update_course_session(
|
||||||
|
|
||||||
for assignment_slug in circle_data["assignments"]:
|
for assignment_slug in circle_data["assignments"]:
|
||||||
create_or_update_course_session_assignment(
|
create_or_update_course_session_assignment(
|
||||||
cs, course.slug, assignment_slug, presence_day_start, presence_day_end
|
cs, course.slug, assignment_slug, presence_day_start
|
||||||
)
|
)
|
||||||
|
|
||||||
for test_slug in circle_data["edoniq_tests"]:
|
for test_slug in circle_data["edoniq_tests"]:
|
||||||
|
|
@ -389,6 +400,8 @@ def create_or_update_course_session_attendance(
|
||||||
csa, _created = CourseSessionAttendanceCourse.objects.get_or_create(
|
csa, _created = CourseSessionAttendanceCourse.objects.get_or_create(
|
||||||
course_session=cs, learning_content=attendance_course_lc
|
course_session=cs, learning_content=attendance_course_lc
|
||||||
)
|
)
|
||||||
|
# trigger save to update due date
|
||||||
|
csa.save()
|
||||||
|
|
||||||
csa.location = location
|
csa.location = location
|
||||||
expert = CourseSessionUser.objects.filter(
|
expert = CourseSessionUser.objects.filter(
|
||||||
|
|
@ -413,7 +426,6 @@ def create_or_update_course_session_assignment(
|
||||||
course_slug: str,
|
course_slug: str,
|
||||||
assignment_slug: str,
|
assignment_slug: str,
|
||||||
start: datetime,
|
start: datetime,
|
||||||
end: datetime,
|
|
||||||
):
|
):
|
||||||
logger.debug("import", slug=f"{course_slug}-lp-circle-{assignment_slug}")
|
logger.debug("import", slug=f"{course_slug}-lp-circle-{assignment_slug}")
|
||||||
|
|
||||||
|
|
@ -428,22 +440,31 @@ def create_or_update_course_session_assignment(
|
||||||
slug=f"{course_slug}-lp-circle-{assignment_slug}"
|
slug=f"{course_slug}-lp-circle-{assignment_slug}"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
# trigger save to update due date
|
||||||
|
csa.save()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value
|
csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value
|
||||||
and start
|
and start
|
||||||
):
|
):
|
||||||
csa.submission_deadline.end = timezone.make_aware(start)
|
csa.submission_deadline.start = timezone.make_aware(start)
|
||||||
|
csa.submission_deadline.end = None
|
||||||
csa.submission_deadline.save()
|
csa.submission_deadline.save()
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
csa.learning_content.assignment_type == AssignmentType.CASEWORK.value
|
csa.learning_content.assignment_type == AssignmentType.CASEWORK.value
|
||||||
and end
|
and start
|
||||||
):
|
):
|
||||||
csa.submission_deadline.end = timezone.make_aware(
|
csa.submission_deadline.start = timezone.make_aware(
|
||||||
start
|
start
|
||||||
) + timezone.timedelta(days=30)
|
) + timezone.timedelta(days=30)
|
||||||
|
csa.submission_deadline.end = None
|
||||||
csa.submission_deadline.save()
|
csa.submission_deadline.save()
|
||||||
|
csa.evaluation_deadline.start = timezone.make_aware(
|
||||||
|
start
|
||||||
|
) + timezone.timedelta(days=45)
|
||||||
|
csa.evaluation_deadline.end = None
|
||||||
|
csa.evaluation_deadline.save()
|
||||||
|
|
||||||
|
|
||||||
def create_or_update_course_session_edoniq_test(
|
def create_or_update_course_session_edoniq_test(
|
||||||
|
|
@ -457,8 +478,11 @@ def create_or_update_course_session_edoniq_test(
|
||||||
cset, _created = CourseSessionEdoniqTest.objects.get_or_create(
|
cset, _created = CourseSessionEdoniqTest.objects.get_or_create(
|
||||||
course_session=cs, learning_content=learning_content
|
course_session=cs, learning_content=learning_content
|
||||||
)
|
)
|
||||||
|
# trigger save to update due date
|
||||||
|
cset.save()
|
||||||
|
|
||||||
cset.deadline.end = timezone.make_aware(start) + timezone.timedelta(days=10)
|
cset.deadline.start = timezone.make_aware(start) + timezone.timedelta(days=10)
|
||||||
|
cset.deadline.end = None
|
||||||
cset.deadline.save()
|
cset.deadline.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
|
||||||
learning_content__assignment_type=AssignmentType.CASEWORK.value
|
learning_content__assignment_type=AssignmentType.CASEWORK.value
|
||||||
).first()
|
).first()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
casework.submission_deadline.end.isoformat(), "2023-07-06T11:30:00+00:00"
|
casework.submission_deadline.start.isoformat(), "2023-07-06T11:30:00+00:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_update_course_session(self):
|
def test_update_course_session(self):
|
||||||
|
|
@ -185,7 +185,7 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
|
||||||
learning_content__assignment_type=AssignmentType.CASEWORK.value
|
learning_content__assignment_type=AssignmentType.CASEWORK.value
|
||||||
).first()
|
).first()
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
casework.submission_deadline.end.isoformat(), "2023-07-06T12:30:00+00:00"
|
casework.submission_deadline.start.isoformat(), "2023-07-06T12:30:00+00:00"
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_import_course_session_twice(self):
|
def test_import_course_session_twice(self):
|
||||||
|
|
@ -309,7 +309,7 @@ class CreateOrUpdateEdoniqTestCase(TestCase):
|
||||||
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
|
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
|
||||||
|
|
||||||
test = CourseSessionEdoniqTest.objects.first()
|
test = CourseSessionEdoniqTest.objects.first()
|
||||||
self.assertEqual(test.deadline.end.isoformat(), "2023-06-16T11:30:00+00:00")
|
self.assertEqual(test.deadline.start.isoformat(), "2023-06-16T11:30:00+00:00")
|
||||||
|
|
||||||
def test_update_course_session(self):
|
def test_update_course_session(self):
|
||||||
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
|
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
|
||||||
|
|
@ -318,4 +318,4 @@ class CreateOrUpdateEdoniqTestCase(TestCase):
|
||||||
self.assertEqual(duedate_count, DueDate.objects.count())
|
self.assertEqual(duedate_count, DueDate.objects.count())
|
||||||
|
|
||||||
test = CourseSessionEdoniqTest.objects.first()
|
test = CourseSessionEdoniqTest.objects.first()
|
||||||
self.assertEqual(test.deadline.end.isoformat(), "2023-07-16T11:30:00+00:00")
|
self.assertEqual(test.deadline.start.isoformat(), "2023-07-16T11:30:00+00:00")
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,13 @@ def coursesessions_trainers_import(request):
|
||||||
def import_training_and_trainer(excel_file: str):
|
def import_training_and_trainer(excel_file: str):
|
||||||
import_course_sessions_from_excel(
|
import_course_sessions_from_excel(
|
||||||
excel_file,
|
excel_file,
|
||||||
|
circle_keys=[
|
||||||
|
"Kickoff",
|
||||||
|
"Basis",
|
||||||
|
"Fahrzeug",
|
||||||
|
"Haushalt Teil 1",
|
||||||
|
"Haushalt Teil 2",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
import_trainers_from_excel_for_training(excel_file)
|
import_trainers_from_excel_for_training(excel_file)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue