WIP: Add import test
This commit is contained in:
parent
c520b4f454
commit
977e0cb7c4
|
|
@ -0,0 +1,107 @@
|
|||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.core.create_default_users import create_default_users
|
||||
from vbv_lernwelt.course.creators.test_course import create_test_course
|
||||
from vbv_lernwelt.course.models import CourseSession
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse, CourseSessionAssignment
|
||||
from vbv_lernwelt.importer.services import create_or_update_course_session
|
||||
from vbv_lernwelt.learnpath.models import LearningContentAttendanceCourse, LearningContentAssignment
|
||||
|
||||
TEST_SESSION = {
|
||||
"ID": "AG 2023 A",
|
||||
"Generation": "2023",
|
||||
"Region": "Aargau",
|
||||
"Sprache": "de",
|
||||
"Klasse": "A",
|
||||
"Kickoff Start": "30.08.2023, 09:15",
|
||||
"Kickoff Ende": "30.08.2023, 17:00",
|
||||
"Kickoff Raum": "E34",
|
||||
"Kickoff Standort": "HKV Aarau",
|
||||
"Kickoff Adresse": "Bahnhofstrasse 46, 5000, Aarau",
|
||||
"Basis Start": "06.09.2023, 09:15",
|
||||
"Basis Ende": "06.09.2023, 17:00",
|
||||
"Basis Raum": "E34",
|
||||
"Basis Standort": "HKV Aarau",
|
||||
"Basis Adresse": "Bahnhofstrasse 46, 5000, Aarau",
|
||||
"Fahrzeug Start": "08.11.2023, 09:15",
|
||||
"Fahrzeug Ende": "08.11.2023, 17:00",
|
||||
"Fahrzeug Raum": "E34",
|
||||
"Fahrzeug Standort": "HKV Aarau",
|
||||
"Fahrzeug Adresse": "Bahnhofstrasse 46, 5000, Aarau",
|
||||
"Haushalt Teil 1 Start": "17.01.2024, 09:15",
|
||||
"Hauhalt Teil 1 Ende": "17.01.2024, 17:00",
|
||||
"Haushalt Teil 1 Raum": "E34",
|
||||
"Haushalt Teil 1 Standort": "HKV Aarau",
|
||||
"Haushalt Teil 1 Adresse": "Bahnhofstrasse 46, 5000, Aarau",
|
||||
"Haushalt Teil 2 Start": "20.03.2024, 09:15",
|
||||
"Haushalt Teil 2 Ende": "20.03.2024, 17:00",
|
||||
"Haushalt Teil 2 Raum": "E34",
|
||||
"Haushalt Teil 2 Standort": "HKV Aarau",
|
||||
"Haushalt Teil 2 Adresse": "Bahnhofstrasse 46, 5000, Aarau",
|
||||
}
|
||||
|
||||
TEST_CIRCLES = ["Fahrzeug"]
|
||||
|
||||
LP_DATA = {
|
||||
"Fahrzeug":
|
||||
{
|
||||
"de": {
|
||||
"title": "Fahrzeug",
|
||||
"slug": "fahrzeug",
|
||||
"presence_course": "fahrzeug-lc-präsenzkurs-fahrzeug",
|
||||
"assignments": [
|
||||
"fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice",
|
||||
"fahrzeug-lc-fahrzeug-mein-erstes-auto",
|
||||
]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class EdoniqSessionTrainerImportTestCase(TestCase):
|
||||
def setUp(self) -> None:
|
||||
create_default_users()
|
||||
self.course = create_test_course(with_sessions=False)
|
||||
|
||||
def test_session_import(self):
|
||||
create_or_update_course_session(self.course, TEST_SESSION, "de", circle_keys=TEST_CIRCLES)
|
||||
cs = CourseSession.objects.get(import_id="AG 2023 A")
|
||||
|
||||
self.assertEqual(cs.course, self.course)
|
||||
|
||||
for circle_name in TEST_CIRCLES:
|
||||
self._check_attendance(circle_name, cs)
|
||||
self._check_assignments(circle_name, cs)
|
||||
|
||||
def _check_attendance(self, circle_name: str, cs: CourseSession):
|
||||
attendance = LearningContentAttendanceCourse.objects.filter(
|
||||
slug=f"{self.course.slug}-lp-circle-{LP_DATA[circle_name]['de']['presence_course']}",
|
||||
)
|
||||
self.assertEqual(attendance.count(), 1) # only one attendance per circle
|
||||
|
||||
csac = CourseSessionAttendanceCourse.objects.get(
|
||||
course_session=cs, learning_content=attendance.first()
|
||||
)
|
||||
|
||||
self.assertEqual(
|
||||
f"{TEST_SESSION[f'{circle_name} Raum']}, {TEST_SESSION[f'{circle_name} Standort']}, {TEST_SESSION[f'{circle_name} Adresse']}",
|
||||
csac.location)
|
||||
self.assertEqual(f"{TEST_SESSION[f'{circle_name} Start']}",
|
||||
timezone.localtime(csac.due_date.start).strftime("%d.%m.%Y, %H:%M"))
|
||||
self.assertEqual(f"{TEST_SESSION[f'{circle_name} Ende']}",
|
||||
timezone.localtime(csac.due_date.end).strftime("%d.%m.%Y, %H:%M"))
|
||||
self.assertEqual("", csac.trainer)
|
||||
|
||||
def _check_assignments(self, circle_name: str, cs: CourseSession):
|
||||
for assignment_slug in LP_DATA[circle_name]["de"]["assignments"]:
|
||||
csa = CourseSessionAssignment.objects.get(
|
||||
course_session=cs,
|
||||
learning_content=LearningContentAssignment.objects.get(
|
||||
slug=f"{self.course.slug}-lp-circle-{assignment_slug}"
|
||||
),
|
||||
)
|
||||
if csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value:
|
||||
self.assertEqual(f"{TEST_SESSION[f'{circle_name} Start']}",
|
||||
timezone.localtime(csa.submission_deadline.end).strftime("%d.%m.%Y, %H:%M"))
|
||||
|
|
@ -2,27 +2,171 @@ from datetime import date, datetime, time
|
|||
from typing import Any, Dict, List
|
||||
|
||||
import structlog
|
||||
from django.utils import timezone
|
||||
from openpyxl.reader.excel import load_workbook
|
||||
|
||||
from vbv_lernwelt.assignment.models import AssignmentType
|
||||
from vbv_lernwelt.core.models import User
|
||||
from vbv_lernwelt.course.consts import COURSE_UK, COURSE_UK_FR, COURSE_UK_IT
|
||||
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse
|
||||
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse, CourseSessionAssignment
|
||||
from vbv_lernwelt.importer.utils import (
|
||||
calc_header_tuple_list_from_pyxl_sheet,
|
||||
parse_circle_group_string,
|
||||
try_parse_datetime,
|
||||
)
|
||||
from vbv_lernwelt.learnpath.models import Circle, LearningContentAttendanceCourse
|
||||
from vbv_lernwelt.learnpath.models import Circle, LearningContentAttendanceCourse, LearningContentAssignment
|
||||
|
||||
logger = structlog.get_logger(__name__)
|
||||
|
||||
CIRCLE_NAMES = {
|
||||
"Kickoff": {"de": "Kickoff", "fr": "Lancement", "it": "Introduzione"},
|
||||
"Basis": {"de": "Basis", "fr": "Base", "it": "Base"},
|
||||
"Fahrzeug": {"de": "Fahrzeug", "fr": "Véhicule", "it": "Veicolo"},
|
||||
"Haushalt Teil 1": {"de": "Haushalt Teil 1", "fr": "Habitat partie 1", "it": ""},
|
||||
"Haushalt Teil 2": {"de": "Haushalt Teil 2", "fr": "Haushalt Teil 2", "it": ""},
|
||||
# it's easier to define the data here, constructing slugs is error-prone and there are some exceptions
|
||||
LP_DATA = {
|
||||
"Kickoff":
|
||||
{
|
||||
"de": {
|
||||
"title": "Kickoff",
|
||||
"slug": "kickoff",
|
||||
"presence_course": "kickoff-lc-präsenzkurs-kickoff",
|
||||
"assignments": [
|
||||
"kickoff-lc-versicherungswirtschaft",
|
||||
"kickoff-lc-redlichkeitserklärung",
|
||||
"kickoff-lc-reflexion",
|
||||
]
|
||||
},
|
||||
"fr": {
|
||||
"title": "Lancement",
|
||||
"slug": "lancement",
|
||||
"presence_course": "lancement-lc-cours-de-présence-lancement",
|
||||
"assignments": [
|
||||
"lancement-lc-secteur-de-lassurance",
|
||||
"lancement-lc-redlichkeitserklärung",
|
||||
"lancement-lc-réflexion",
|
||||
]
|
||||
},
|
||||
"it": {
|
||||
"title": "Introduzione",
|
||||
"slug": "introduzione",
|
||||
"presence_course": "introduzione-lc-corso-di-presenza-introduzione",
|
||||
"assignments": [
|
||||
"introduzione-lc-settore-assicurativo",
|
||||
"introduzione-lc-redlichkeitserklärung",
|
||||
"introduzione-lc-riflessione",
|
||||
]
|
||||
},
|
||||
},
|
||||
"Basis":
|
||||
{
|
||||
"de": {
|
||||
"title": "Basis",
|
||||
"slug": "basis",
|
||||
"presence_course": "basis-lc-präsenzkurs-basis",
|
||||
"assignments": [
|
||||
"basis-lc-vorbereitungsauftrag-circle-basis",
|
||||
]
|
||||
},
|
||||
"fr": {
|
||||
"title": "Base",
|
||||
"slug": "base",
|
||||
"presence_course": "base-lc-cours-de-présence-base",
|
||||
"assignments": [
|
||||
"base-lc-mandats-préparatoires-circle-base",
|
||||
]
|
||||
},
|
||||
"it": {
|
||||
"title": "Base",
|
||||
"slug": "base",
|
||||
"presence_course": "base-lc-corso-di-presenza-base",
|
||||
"assignments": [
|
||||
"base-lc-vorbereitungsauftrag-circle-basis",
|
||||
]
|
||||
},
|
||||
},
|
||||
"Fahrzeug":
|
||||
{
|
||||
"de": {
|
||||
"title": "Fahrzeug",
|
||||
"slug": "fahrzeug",
|
||||
"presence_course": "fahrzeug-lc-präsenzkurs-fahrzeug",
|
||||
"assignments": [
|
||||
"fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice",
|
||||
"fahrzeug-lc-fahrzeug-mein-erstes-auto",
|
||||
]
|
||||
},
|
||||
"fr": {
|
||||
"title": "Véhicule",
|
||||
"slug": "véhicule",
|
||||
"presence_course": "véhicule-lc-cours-de-présence-véhicule-à-moteur",
|
||||
"assignments": [
|
||||
"véhicule-lc-vérification-dune-police-dassurance-de-véhicule-à-moteur",
|
||||
"véhicule-lc-véhicule-à-moteur-ma-première-voiture",
|
||||
]
|
||||
},
|
||||
"it": {
|
||||
"title": "Veicolo",
|
||||
"slug": "veicolo",
|
||||
"presence_course": "veicolo-lc-corso-di-presenza-veicolo",
|
||||
"assignments": [
|
||||
"veicolo-lc-verifica-di-una-polizza-di-assicurazione-veicoli-a-motore",
|
||||
"veicolo-lc-veicolo-la-mia-prima-auto",
|
||||
]
|
||||
},
|
||||
},
|
||||
"Haushalt Teil 1":
|
||||
{
|
||||
"de": {
|
||||
"title": "Haushalt Teil 1",
|
||||
"slug": "haushalt-teil-1",
|
||||
"presence_course": "",
|
||||
"assignments": []
|
||||
},
|
||||
"fr": {
|
||||
"title": "Habitat partie 1",
|
||||
"slug": "habitat-partie-1",
|
||||
"presence_course": "",
|
||||
"assignments": []
|
||||
},
|
||||
"it": {}
|
||||
},
|
||||
"Haushalt Teil 2": {
|
||||
"de": {
|
||||
"title": "Haushalt Teil 2",
|
||||
"slug": "haushalt-teil-2",
|
||||
"presence_course": "",
|
||||
"assignments": []
|
||||
},
|
||||
"fr": {
|
||||
"title": "Habitat partie 2",
|
||||
"slug": "habitat-partie-2",
|
||||
"presence_course": "",
|
||||
"assignments": []
|
||||
},
|
||||
"it": {}
|
||||
},
|
||||
}
|
||||
|
||||
# the request is always, so we cannot rely on the language in the request
|
||||
TRANSLATIONS = {
|
||||
"de": {
|
||||
"raum": "Raum",
|
||||
"standort": "Standort",
|
||||
"adresse": "Adresse",
|
||||
"start": "Start",
|
||||
"ende": "Ende",
|
||||
},
|
||||
"fr": {
|
||||
"raum": "Raum",
|
||||
"standort": "Standort",
|
||||
"adresse": "Adresse",
|
||||
"start": "Start",
|
||||
"ende": "Ende",
|
||||
},
|
||||
"it": {
|
||||
"raum": "Raum",
|
||||
"standort": "Standort",
|
||||
"adresse": "Adresse",
|
||||
"start": "Start",
|
||||
"ende": "Ende",
|
||||
}
|
||||
}
|
||||
|
||||
T2L_IGNORE_FIELDS = ["Vorname", "Name", "Email", "Sprache", "Durchführungen"]
|
||||
|
|
@ -85,6 +229,7 @@ def import_course_sessions_from_excel(
|
|||
):
|
||||
workbook = load_workbook(filename=filename)
|
||||
sheet = workbook["Schulungen Durchführung"]
|
||||
no_course = course is None
|
||||
|
||||
tuple_list = calc_header_tuple_list_from_pyxl_sheet(sheet)
|
||||
|
||||
|
|
@ -97,7 +242,7 @@ def import_course_sessions_from_excel(
|
|||
if restrict_language and language != restrict_language:
|
||||
continue
|
||||
|
||||
if not course:
|
||||
if no_course:
|
||||
course = get_uk_course(language)
|
||||
|
||||
create_or_update_course_session(
|
||||
|
|
@ -110,6 +255,7 @@ def create_or_update_course_session(
|
|||
data: Dict[str, Any],
|
||||
language: str,
|
||||
circle_keys=None,
|
||||
lp_data=LP_DATA,
|
||||
):
|
||||
"""
|
||||
:param data: the following keys are required to process the data: Generation, Region, Klasse
|
||||
|
|
@ -148,25 +294,51 @@ def create_or_update_course_session(
|
|||
|
||||
cs.save()
|
||||
for circle in circle_keys:
|
||||
circle_name = CIRCLE_NAMES[circle][language]
|
||||
circle_data = lp_data[circle][language]
|
||||
|
||||
attendance_course_lc = LearningContentAttendanceCourse.objects.filter(
|
||||
slug=f"{course.slug}-lp-circle-{circle_name.lower()}-lc-präsenzkurs-{circle_name.lower()}"
|
||||
slug=f"{course.slug}-lp-circle-{circle_data['presence_course']}"
|
||||
).first()
|
||||
|
||||
room = data[f"{circle} {TRANSLATIONS[language]['raum']}"]
|
||||
place = data[f"{circle} {TRANSLATIONS[language]['standort']}"]
|
||||
address = data[f"{circle} {TRANSLATIONS[language]['adresse']}"]
|
||||
location = f"{room}, {place}, {address}"
|
||||
start = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['start']}"])[1]
|
||||
end = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['ende']}"])[1]
|
||||
|
||||
if attendance_course_lc:
|
||||
# update existing data
|
||||
csa, _created = CourseSessionAttendanceCourse.objects.get_or_create(
|
||||
course_session=cs, learning_content=attendance_course_lc
|
||||
)
|
||||
|
||||
location = f"{data[f'{circle} Raum']}, {data[f'{circle} Standort']}, {data[f'{circle} Adresse']}"
|
||||
csa.location = location
|
||||
expert = CourseSessionUser.objects.filter(course_session_id=cs.id,
|
||||
role=CourseSessionUser.Role.EXPERT).first()
|
||||
|
||||
csa.trainer = ""
|
||||
csa.due_date.start = try_parse_datetime(data[f"{circle} Start"])[1]
|
||||
csa.due_date.end = try_parse_datetime(data[f"{circle} Ende"])[1]
|
||||
if expert:
|
||||
csa.trainer = f"{expert.user.first_name} {expert.user.last_name}"
|
||||
csa.due_date.start = timezone.make_aware(start)
|
||||
csa.due_date.end = timezone.make_aware(end)
|
||||
csa.due_date.save()
|
||||
csa.save()
|
||||
|
||||
for assignment_slug in circle_data["assignments"]:
|
||||
logger.debug(
|
||||
"import",
|
||||
slug=f"{course.slug}-lp-circle-{assignment_slug}"
|
||||
)
|
||||
|
||||
csa, _created = CourseSessionAssignment.objects.get_or_create(
|
||||
course_session=cs,
|
||||
learning_content=LearningContentAssignment.objects.get(
|
||||
slug=f"{course.slug}-lp-circle-{assignment_slug}"
|
||||
),
|
||||
)
|
||||
|
||||
if csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value:
|
||||
csa.submission_deadline.end = timezone.make_aware(start)
|
||||
csa.submission_deadline.save()
|
||||
|
||||
return cs
|
||||
|
||||
|
|
@ -264,7 +436,7 @@ def create_or_update_trainer(course: Course, data: Dict[str, Any], language="de"
|
|||
# circle expert handling
|
||||
circle_data = parse_circle_group_string(data["Circles"])
|
||||
for circle_key in circle_data:
|
||||
circle_name = CIRCLE_NAMES[circle_key][language]
|
||||
circle_name = CIRCLE_DATA[circle_key][language]["title"]
|
||||
|
||||
# print(circle_name, groups)
|
||||
import_id = f"{data['Generation'].strip()} {group}"
|
||||
|
|
|
|||
Loading…
Reference in New Issue