Add CourseSessionEdoniqTest to importer

Add tests for CourseSessionEdoniqTest
This commit is contained in:
Christian Cueni 2023-08-21 10:17:57 +02:00
parent c140f225ea
commit 9e68abd5f9
6 changed files with 150 additions and 59 deletions

View File

@ -76,43 +76,3 @@ async function startTest() {
</div> </div>
</LearningContentSimpleLayout> </LearningContentSimpleLayout>
</template> </template>
<!--<template>-->
<!-- <div class="container-medium">-->
<!-- <div class="lg:mt-8">-->
<!-- <h1>{{ content.title }}</h1>-->
<!-- <p class="text-large my-4 lg:my-8">{{ value.description }}</p>-->
<!-- <a :href="value.url" target="_blank" class="button btn-primary">Test starten</a>-->
<!-- </div>-->
<!-- </div>-->
<!--</template>-->
<!--<script setup lang="ts">-->
<!--import type { LearningContent } from "@/types";-->
<!--interface Value {-->
<!-- description: string;-->
<!-- url: string;-->
<!--}-->
<!--defineProps<{-->
<!-- value: Value;-->
<!-- content: LearningContent;-->
<!--}>();-->
<!--</script>-->

View File

@ -1,4 +1,4 @@
# Generated by Django 3.2.20 on 2023-08-17 13:14 # Generated by Django 3.2.20 on 2023-08-20 14:06
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@ -6,9 +6,9 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
("learnpath", "0003_auto_20230810_0817"),
("course", "0003_alter_coursecompletion_additional_json_data"), ("course", "0003_alter_coursecompletion_additional_json_data"),
("duedate", "0002_alter_duedate_start"), ("duedate", "0002_alter_duedate_start"),
("learnpath", "0003_auto_20230810_0817"),
("course_session", "0003_initial"), ("course_session", "0003_initial"),
] ]
@ -33,10 +33,11 @@ class Migration(migrations.Migration):
), ),
), ),
( (
"due_date", "deadline",
models.OneToOneField( models.OneToOneField(
null=True,
on_delete=django.db.models.deletion.CASCADE, on_delete=django.db.models.deletion.CASCADE,
related_name="edoniq_test_due_date", related_name="test_submission_deadline",
to="duedate.duedate", to="duedate.duedate",
), ),
), ),

View File

@ -163,11 +163,34 @@ class CourseSessionEdoniqTest(models.Model):
"learnpath.LearningContentEdoniqTest", "learnpath.LearningContentEdoniqTest",
on_delete=models.CASCADE, on_delete=models.CASCADE,
) )
due_date = models.OneToOneField( deadline = models.OneToOneField(
"duedate.DueDate", "duedate.DueDate",
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="edoniq_test_due_date", related_name="test_submission_deadline",
null=True,
) )
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):
if not self.pk:
title = ""
url = ""
page = None
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(
url=url,
title=f"{title}",
learning_content_description=edoniq_test_type_description,
description=f"{_('Abgabe Termin')}",
course_session=self.course_session,
page=page,
)
super().save(*args, **kwargs)

View File

@ -12,7 +12,14 @@ from vbv_lernwelt.learnpath.models import (
@admin.register(DueDate) @admin.register(DueDate)
class DueDateAdmin(admin.ModelAdmin): class DueDateAdmin(admin.ModelAdmin):
date_hierarchy = "end" date_hierarchy = "end"
list_display = ["title", "course_session", "start", "end", "is_undefined"] list_display = [
"title",
"course_session",
"learning_content_description",
"start",
"end",
"is_undefined",
]
list_filter = ["course_session"] list_filter = ["course_session"]
readonly_fields = ["course_session", "page"] readonly_fields = ["course_session", "page"]

View File

@ -12,6 +12,7 @@ from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import ( from vbv_lernwelt.course_session.models import (
CourseSessionAssignment, CourseSessionAssignment,
CourseSessionAttendanceCourse, CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
) )
from vbv_lernwelt.importer.utils import ( from vbv_lernwelt.importer.utils import (
calc_header_tuple_list_from_pyxl_sheet, calc_header_tuple_list_from_pyxl_sheet,
@ -22,6 +23,7 @@ from vbv_lernwelt.learnpath.models import (
Circle, Circle,
LearningContentAssignment, LearningContentAssignment,
LearningContentAttendanceCourse, LearningContentAttendanceCourse,
LearningContentEdoniqTest,
) )
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
@ -38,7 +40,7 @@ LP_DATA = {
"kickoff-lc-redlichkeitserklärung", "kickoff-lc-redlichkeitserklärung",
"kickoff-lc-reflexion", "kickoff-lc-reflexion",
], ],
"tests": ["kickoff-lc-wissens-und-verständnisfragen"], "edoniq_tests": ["kickoff-lc-wissens-und-verständnisfragen"],
}, },
"fr": { "fr": {
"title": "Lancement", "title": "Lancement",
@ -49,6 +51,7 @@ LP_DATA = {
"lancement-lc-redlichkeitserklärung", "lancement-lc-redlichkeitserklärung",
"lancement-lc-réflexion", "lancement-lc-réflexion",
], ],
"edoniq_tests": [],
}, },
"it": { "it": {
"title": "Introduzione", "title": "Introduzione",
@ -59,6 +62,7 @@ LP_DATA = {
"introduzione-lc-redlichkeitserklärung", "introduzione-lc-redlichkeitserklärung",
"introduzione-lc-riflessione", "introduzione-lc-riflessione",
], ],
"edoniq_tests": [],
}, },
}, },
"Basis": { "Basis": {
@ -69,6 +73,7 @@ LP_DATA = {
"assignments": [ "assignments": [
"basis-lc-vorbereitungsauftrag-circle-basis", "basis-lc-vorbereitungsauftrag-circle-basis",
], ],
"edoniq_tests": [],
}, },
"fr": { "fr": {
"title": "Base", "title": "Base",
@ -77,6 +82,7 @@ LP_DATA = {
"assignments": [ "assignments": [
"base-lc-mandats-préparatoires-circle-base", "base-lc-mandats-préparatoires-circle-base",
], ],
"edoniq_tests": [],
}, },
"it": { "it": {
"title": "Base", "title": "Base",
@ -85,6 +91,7 @@ LP_DATA = {
"assignments": [ "assignments": [
"base-lc-vorbereitungsauftrag-circle-basis", "base-lc-vorbereitungsauftrag-circle-basis",
], ],
"edoniq_tests": [],
}, },
}, },
"Fahrzeug": { "Fahrzeug": {
@ -96,6 +103,7 @@ LP_DATA = {
"fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice", "fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice",
"fahrzeug-lc-fahrzeug-mein-erstes-auto", "fahrzeug-lc-fahrzeug-mein-erstes-auto",
], ],
"edoniq_tests": [],
}, },
"fr": { "fr": {
"title": "Véhicule", "title": "Véhicule",
@ -105,6 +113,7 @@ LP_DATA = {
"véhicule-lc-vérification-dune-police-dassurance-de-véhicule-à-moteur", "véhicule-lc-vérification-dune-police-dassurance-de-véhicule-à-moteur",
"véhicule-lc-véhicule-à-moteur-ma-première-voiture", "véhicule-lc-véhicule-à-moteur-ma-première-voiture",
], ],
"edoniq_tests": [],
}, },
"it": { "it": {
"title": "Veicolo", "title": "Veicolo",
@ -114,6 +123,7 @@ LP_DATA = {
"veicolo-lc-verifica-di-una-polizza-di-assicurazione-veicoli-a-motore", "veicolo-lc-verifica-di-una-polizza-di-assicurazione-veicoli-a-motore",
"veicolo-lc-veicolo-la-mia-prima-auto", "veicolo-lc-veicolo-la-mia-prima-auto",
], ],
"edoniq_tests": [],
}, },
}, },
"Haushalt Teil 1": { "Haushalt Teil 1": {
@ -122,12 +132,14 @@ LP_DATA = {
"slug": "haushalt-teil-1", "slug": "haushalt-teil-1",
"presence_course": "", "presence_course": "",
"assignments": [], "assignments": [],
"edoniq_tests": [],
}, },
"fr": { "fr": {
"title": "Habitat partie 1", "title": "Habitat partie 1",
"slug": "habitat-partie-1", "slug": "habitat-partie-1",
"presence_course": "", "presence_course": "",
"assignments": [], "assignments": [],
"edoniq_tests": [],
}, },
"it": {}, "it": {},
}, },
@ -137,12 +149,14 @@ LP_DATA = {
"slug": "haushalt-teil-2", "slug": "haushalt-teil-2",
"presence_course": "", "presence_course": "",
"assignments": [], "assignments": [],
"edoniq_tests": [],
}, },
"fr": { "fr": {
"title": "Habitat partie 2", "title": "Habitat partie 2",
"slug": "habitat-partie-2", "slug": "habitat-partie-2",
"presence_course": "", "presence_course": "",
"assignments": [], "assignments": [],
"edoniq_tests": [],
}, },
"it": {}, "it": {},
}, },
@ -304,6 +318,7 @@ def create_or_update_course_session(
cs.save() cs.save()
for circle in circle_keys: for circle in circle_keys:
circle_data = lp_data[circle][language] circle_data = lp_data[circle][language]
logger.debug("import", data=circle_data)
attendance_course_lc = LearningContentAttendanceCourse.objects.filter( attendance_course_lc = LearningContentAttendanceCourse.objects.filter(
slug=f"{course.slug}-lp-circle-{circle_data['presence_course']}" slug=f"{course.slug}-lp-circle-{circle_data['presence_course']}"
@ -313,10 +328,12 @@ def create_or_update_course_session(
place = data[f"{circle} {TRANSLATIONS[language]['standort']}"] place = data[f"{circle} {TRANSLATIONS[language]['standort']}"]
address = data[f"{circle} {TRANSLATIONS[language]['adresse']}"] address = data[f"{circle} {TRANSLATIONS[language]['adresse']}"]
location = f"{room}, {place}, {address}" location = f"{room}, {place}, {address}"
start = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['start']}"])[ presence_day_start = try_parse_datetime(
1 data[f"{circle} {TRANSLATIONS[language]['start']}"]
] )[1]
end = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['ende']}"])[1] presence_day_end = try_parse_datetime(
data[f"{circle} {TRANSLATIONS[language]['ende']}"]
)[1]
if attendance_course_lc: if attendance_course_lc:
create_or_update_course_session_attendance( create_or_update_course_session_attendance(
@ -325,13 +342,18 @@ def create_or_update_course_session(
course.slug, course.slug,
circle_data["slug"], circle_data["slug"],
location, location,
start, presence_day_start,
end, presence_day_end,
) )
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, start, end cs, course.slug, assignment_slug, presence_day_start, presence_day_end
)
for test_slug in circle_data["edoniq_tests"]:
create_or_update_course_session_edoniq_test(
cs, course.slug, test_slug, presence_day_start
) )
return cs return cs
@ -343,8 +365,8 @@ def create_or_update_course_session_attendance(
course_slug: str, course_slug: str,
circle_slug: str, circle_slug: str,
location: str, location: str,
start: date, start: datetime,
end: date, end: datetime,
): ):
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
@ -369,7 +391,11 @@ def create_or_update_course_session_attendance(
def create_or_update_course_session_assignment( def create_or_update_course_session_assignment(
cs: CourseSession, course_slug: str, assignment_slug: str, start: date, end: date cs: CourseSession,
course_slug: str,
assignment_slug: str,
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}")
@ -402,6 +428,22 @@ def create_or_update_course_session_assignment(
csa.submission_deadline.save() csa.submission_deadline.save()
def create_or_update_course_session_edoniq_test(
cs: CourseSession, course_slug: str, test_slug: str, start: datetime
):
learning_content = LearningContentEdoniqTest.objects.filter(
slug=f"{course_slug}-lp-circle-{test_slug}"
).first()
if learning_content:
cset, _created = CourseSessionEdoniqTest.objects.get_or_create(
course_session=cs, learning_content=learning_content
)
cset.deadline.end = timezone.make_aware(start) + timezone.timedelta(days=10)
cset.deadline.save()
def validate_row_data(data: Dict[str, any], required_headers: List[str]): def validate_row_data(data: Dict[str, any], required_headers: List[str]):
logger.debug( logger.debug(
"validate_row_data_missing_header", "validate_row_data_missing_header",

View File

@ -1,6 +1,7 @@
import os import os
from django.test import TestCase from django.test import TestCase
from django.utils.timezone import make_naive
from openpyxl.reader.excel import load_workbook from openpyxl.reader.excel import load_workbook
from vbv_lernwelt.assignment.models import AssignmentType from vbv_lernwelt.assignment.models import AssignmentType
@ -11,14 +12,19 @@ from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import ( from vbv_lernwelt.course_session.models import (
CourseSessionAssignment, CourseSessionAssignment,
CourseSessionAttendanceCourse, CourseSessionAttendanceCourse,
CourseSessionEdoniqTest,
) )
from vbv_lernwelt.duedate.models import DueDate from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.importer.services import ( from vbv_lernwelt.importer.services import (
create_or_update_course_session, create_or_update_course_session,
create_or_update_course_session_edoniq_test,
DataImportError, DataImportError,
validate_row_data, validate_row_data,
) )
from vbv_lernwelt.importer.utils import calc_header_tuple_list_from_pyxl_sheet from vbv_lernwelt.importer.utils import (
calc_header_tuple_list_from_pyxl_sheet,
try_parse_datetime,
)
from vbv_lernwelt.learnpath.models import Circle from vbv_lernwelt.learnpath.models import Circle
test_dir = os.path.dirname(os.path.abspath(__file__)) test_dir = os.path.dirname(os.path.abspath(__file__))
@ -261,3 +267,55 @@ class CreateOrUpdateCourseSessionTestCase(TestCase):
"Generation", "Generation",
], ],
) )
class CreateOrUpdateEdoniqTestCase(TestCase):
def setUp(self):
create_default_users()
self.course = create_test_course(include_vv=False)
row = [
("ID", "AG 2023 A"),
("Generation", 2023),
("Region", "Aargau"),
("Sprache", "de"),
("Klasse", "A"),
("Kickoff Start", "06.06.2023, 13:30"),
("Kickoff Ende", "06.06.2023, 15:00"),
(
"Kickoff Raum",
"E64",
),
("Kickoff Standort", "HKV Aarau"),
("Kickoff Adresse", "Bahnhofstrasse 460, 5001, Aarau"),
]
data = dict(row)
self.cs = create_or_update_course_session(
self.course, data, language="de", circle_keys=["Kickoff"]
)
def _create_or_update_edonqi_test(self, date: str):
start_date = try_parse_datetime(date)[1]
create_or_update_course_session_edoniq_test(
self.cs,
"test-lehrgang",
"fahrzeug-lc-wissens-und-verständnisfragen",
make_naive(start_date),
)
def test_create_course_session(self):
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
test = CourseSessionEdoniqTest.objects.first()
self.assertEqual(test.deadline.end.isoformat(), "2023-06-16T11:30:00+00:00")
def test_update_course_session(self):
self._create_or_update_edonqi_test("2023-06-06T11:30:00+00:00")
duedate_count = DueDate.objects.count()
self._create_or_update_edonqi_test("2023-06-07T11:30:00+00:00")
self.assertEqual(duedate_count, DueDate.objects.count())
test = CourseSessionEdoniqTest.objects.first()
self.assertEqual(test.deadline.end.isoformat(), "2023-07-16T11:30:00+00:00")