WIP: Add update test, add trainer test

This commit is contained in:
Christian Cueni 2023-08-08 16:00:24 +02:00
parent 977e0cb7c4
commit 502aa9c543
2 changed files with 301 additions and 148 deletions

View File

@ -3,11 +3,23 @@ from django.utils import timezone
from vbv_lernwelt.assignment.models import AssignmentType from vbv_lernwelt.assignment.models import AssignmentType
from vbv_lernwelt.core.create_default_users import create_default_users from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.core.models import User
from vbv_lernwelt.course.creators.test_course import create_test_course from vbv_lernwelt.course.creators.test_course import create_test_course
from vbv_lernwelt.course.models import CourseSession from vbv_lernwelt.course.models import CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse, CourseSessionAssignment from vbv_lernwelt.course_session.models import (
from vbv_lernwelt.importer.services import create_or_update_course_session CourseSessionAssignment,
from vbv_lernwelt.learnpath.models import LearningContentAttendanceCourse, LearningContentAssignment CourseSessionAttendanceCourse,
)
from vbv_lernwelt.duedate.models import DueDate
from vbv_lernwelt.importer.services import (
create_or_update_course_session,
create_or_update_trainer,
)
from vbv_lernwelt.learnpath.models import (
Circle,
LearningContentAssignment,
LearningContentAttendanceCourse,
)
TEST_SESSION = { TEST_SESSION = {
"ID": "AG 2023 A", "ID": "AG 2023 A",
@ -45,37 +57,104 @@ TEST_SESSION = {
TEST_CIRCLES = ["Fahrzeug"] TEST_CIRCLES = ["Fahrzeug"]
LP_DATA = { LP_DATA = {
"Fahrzeug": "Fahrzeug": {
{ "de": {
"de": { "title": "Fahrzeug",
"title": "Fahrzeug", "slug": "fahrzeug",
"slug": "fahrzeug", "presence_course": "fahrzeug-lc-präsenzkurs-fahrzeug",
"presence_course": "fahrzeug-lc-präsenzkurs-fahrzeug", "assignments": [
"assignments": [ "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", ],
]
},
}, },
},
}
TEST_TRAINER = {
"Name": "Muster",
"Vorname": "Hans",
"Email": "Hans.muster@eiger-versicherung.ch",
"Sprache": "de",
"Generation": "AG 2023",
"Klasse": "A",
"Circles": "Fahrzeug",
"Anrede": "Herr",
"Tel. Geschäft": "031 321 32 32",
"Tel. Mobil": "079 321 32 32",
"Strasse": "Thunstrasse 1",
"PLZ": "3014",
"Wohnort": "Bern",
} }
class EdoniqSessionTrainerImportTestCase(TestCase): class EdoniqSessionImportTestCase(TestCase):
def setUp(self) -> None: def setUp(self) -> None:
create_default_users() create_default_users()
self.course = create_test_course(with_sessions=False) self.course = create_test_course(with_sessions=False)
def test_session_import(self): def test_session_import(self):
create_or_update_course_session(self.course, TEST_SESSION, "de", circle_keys=TEST_CIRCLES) create_or_update_course_session(
self.course, TEST_SESSION, "de", circle_keys=TEST_CIRCLES
)
cs = CourseSession.objects.get(import_id="AG 2023 A") cs = CourseSession.objects.get(import_id="AG 2023 A")
self.assertEqual(cs.course, self.course) self.assertEqual(cs.course, self.course)
for circle_name in TEST_CIRCLES: for circle_name in TEST_CIRCLES:
self._check_attendance(circle_name, cs) self._check_attendance(circle_name, cs, TEST_SESSION)
self._check_assignments(circle_name, cs) self._check_assignments(circle_name, cs, TEST_SESSION)
def _check_attendance(self, circle_name: str, cs: CourseSession): self.assertEqual(4, DueDate.objects.count())
def test_update_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")
trainer1 = User.objects.get(email="test-trainer1@example.com")
csu = CourseSessionUser.objects.create(
course_session=cs,
user=trainer1,
role=CourseSessionUser.Role.EXPERT,
)
csu.expert.add(Circle.objects.get(slug="test-lehrgang-lp-circle-fahrzeug"))
UPDATE_SESSION = {
"ID": "AG 2023 A",
"Generation": "2023",
"Region": "Aargau",
"Sprache": "de",
"Klasse": "A",
"Fahrzeug Start": "09.11.2023, 10:15",
"Fahrzeug Ende": "09.11.2023, 16:00",
"Fahrzeug Raum": "E35",
"Fahrzeug Standort": "HKV Aarau2",
"Fahrzeug Adresse": "Bahnhofstrasse 460, 5001, Aarau2",
}
create_or_update_course_session(
self.course, UPDATE_SESSION, "de", circle_keys=TEST_CIRCLES
)
for circle_name in TEST_CIRCLES:
self._check_attendance(
circle_name,
cs,
UPDATE_SESSION,
trainer_name=f"{trainer1.first_name} {trainer1.last_name}",
)
self._check_assignments(circle_name, cs, UPDATE_SESSION)
self.assertEqual(4, DueDate.objects.count())
def _check_attendance(
self,
circle_name: str,
cs: CourseSession,
session_data: dict,
trainer_name: str = "",
):
attendance = LearningContentAttendanceCourse.objects.filter( attendance = LearningContentAttendanceCourse.objects.filter(
slug=f"{self.course.slug}-lp-circle-{LP_DATA[circle_name]['de']['presence_course']}", slug=f"{self.course.slug}-lp-circle-{LP_DATA[circle_name]['de']['presence_course']}",
) )
@ -86,15 +165,22 @@ class EdoniqSessionTrainerImportTestCase(TestCase):
) )
self.assertEqual( self.assertEqual(
f"{TEST_SESSION[f'{circle_name} Raum']}, {TEST_SESSION[f'{circle_name} Standort']}, {TEST_SESSION[f'{circle_name} Adresse']}", f"{session_data[f'{circle_name} Raum']}, {session_data[f'{circle_name} Standort']}, {session_data[f'{circle_name} Adresse']}",
csac.location) 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(
self.assertEqual(f"{TEST_SESSION[f'{circle_name} Ende']}", f"{session_data[f'{circle_name} Start']}",
timezone.localtime(csac.due_date.end).strftime("%d.%m.%Y, %H:%M")) timezone.localtime(csac.due_date.start).strftime("%d.%m.%Y, %H:%M"),
self.assertEqual("", csac.trainer) )
self.assertEqual(
f"{session_data[f'{circle_name} Ende']}",
timezone.localtime(csac.due_date.end).strftime("%d.%m.%Y, %H:%M"),
)
self.assertEqual(trainer_name, csac.trainer)
def _check_assignments(self, circle_name: str, cs: CourseSession): def _check_assignments(
self, circle_name: str, cs: CourseSession, session_data: dict
):
for assignment_slug in LP_DATA[circle_name]["de"]["assignments"]: for assignment_slug in LP_DATA[circle_name]["de"]["assignments"]:
csa = CourseSessionAssignment.objects.get( csa = CourseSessionAssignment.objects.get(
course_session=cs, course_session=cs,
@ -102,6 +188,65 @@ class EdoniqSessionTrainerImportTestCase(TestCase):
slug=f"{self.course.slug}-lp-circle-{assignment_slug}" slug=f"{self.course.slug}-lp-circle-{assignment_slug}"
), ),
) )
if csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value: if (
self.assertEqual(f"{TEST_SESSION[f'{circle_name} Start']}", csa.learning_content.assignment_type
timezone.localtime(csa.submission_deadline.end).strftime("%d.%m.%Y, %H:%M")) == AssignmentType.PREP_ASSIGNMENT.value
):
self.assertEqual(
f"{session_data[f'{circle_name} Start']}",
timezone.localtime(csa.submission_deadline.end).strftime(
"%d.%m.%Y, %H:%M"
),
)
class EdoniqTrainerImportTestCase(TestCase):
def setUp(self) -> None:
create_default_users()
self.course = create_test_course(with_sessions=False)
def test_trainer_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")
create_or_update_trainer(self.course, TEST_TRAINER)
csu = CourseSessionUser.objects.filter(
course_session=cs,
role=CourseSessionUser.Role.EXPERT,
).first()
self.assertEqual(csu.user.first_name, TEST_TRAINER["Vorname"])
self.assertEqual(csu.expert.first().slug, "test-lehrgang-lp-circle-fahrzeug")
def test_update_trainer_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")
create_or_update_trainer(self.course, TEST_TRAINER)
UPDATE_TRAINER = {
"Name": "Hansmeier",
"Vorname": "Hans",
"Email": "Hans.muster@eiger-versicherung.ch",
"Sprache": "de",
"Generation": "AG 2023",
"Klasse": "A",
"Circles": "Fahrzeug",
"Anrede": "Herr",
"Tel. Geschäft": "031 321 32 32",
"Tel. Mobil": "079 321 32 32",
"Strasse": "Thunstrasse 1",
"PLZ": "3014",
"Wohnort": "Bern",
}
csu = CourseSessionUser.objects.filter(
course_session=cs,
role=CourseSessionUser.Role.EXPERT,
).first()
create_or_update_trainer(self.course, UPDATE_TRAINER)
self.assertEqual(csu.user.first_name, UPDATE_TRAINER["Vorname"])
self.assertEqual(csu.expert.first().slug, "test-lehrgang-lp-circle-fahrzeug")

View File

@ -9,138 +9,141 @@ from vbv_lernwelt.assignment.models import AssignmentType
from vbv_lernwelt.core.models import User 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.consts import COURSE_UK, COURSE_UK_FR, COURSE_UK_IT
from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser from vbv_lernwelt.course.models import Course, CourseSession, CourseSessionUser
from vbv_lernwelt.course_session.models import CourseSessionAttendanceCourse, CourseSessionAssignment from vbv_lernwelt.course_session.models import (
CourseSessionAssignment,
CourseSessionAttendanceCourse,
)
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,
parse_circle_group_string, parse_circle_group_string,
try_parse_datetime, try_parse_datetime,
) )
from vbv_lernwelt.learnpath.models import Circle, LearningContentAttendanceCourse, LearningContentAssignment from vbv_lernwelt.learnpath.models import (
Circle,
LearningContentAssignment,
LearningContentAttendanceCourse,
)
logger = structlog.get_logger(__name__) logger = structlog.get_logger(__name__)
# it's easier to define the data here, constructing slugs is error-prone and there are some exceptions # it's easier to define the data here, constructing slugs is error-prone and there are some exceptions
LP_DATA = { LP_DATA = {
"Kickoff": "Kickoff": {
{ "de": {
"de": { "title": "Kickoff",
"title": "Kickoff", "slug": "kickoff",
"slug": "kickoff", "presence_course": "kickoff-lc-präsenzkurs-kickoff",
"presence_course": "kickoff-lc-präsenzkurs-kickoff", "assignments": [
"assignments": [ "kickoff-lc-versicherungswirtschaft",
"kickoff-lc-versicherungswirtschaft", "kickoff-lc-redlichkeitserklärung",
"kickoff-lc-redlichkeitserklärung", "kickoff-lc-reflexion",
"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": "fr": {
{ "title": "Lancement",
"de": { "slug": "lancement",
"title": "Basis", "presence_course": "lancement-lc-cours-de-présence-lancement",
"slug": "basis", "assignments": [
"presence_course": "basis-lc-präsenzkurs-basis", "lancement-lc-secteur-de-lassurance",
"assignments": [ "lancement-lc-redlichkeitserklärung",
"basis-lc-vorbereitungsauftrag-circle-basis", "lancement-lc-réflexion",
] ],
},
"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": "it": {
{ "title": "Introduzione",
"de": { "slug": "introduzione",
"title": "Fahrzeug", "presence_course": "introduzione-lc-corso-di-presenza-introduzione",
"slug": "fahrzeug", "assignments": [
"presence_course": "fahrzeug-lc-präsenzkurs-fahrzeug", "introduzione-lc-settore-assicurativo",
"assignments": [ "introduzione-lc-redlichkeitserklärung",
"fahrzeug-lc-überprüfen-einer-motorfahrzeug-versicherungspolice", "introduzione-lc-riflessione",
"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": },
{ "Basis": {
"de": { "de": {
"title": "Haushalt Teil 1", "title": "Basis",
"slug": "haushalt-teil-1", "slug": "basis",
"presence_course": "", "presence_course": "basis-lc-präsenzkurs-basis",
"assignments": [] "assignments": [
}, "basis-lc-vorbereitungsauftrag-circle-basis",
"fr": { ],
"title": "Habitat partie 1",
"slug": "habitat-partie-1",
"presence_course": "",
"assignments": []
},
"it": {}
}, },
"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": { "Haushalt Teil 2": {
"de": { "de": {
"title": "Haushalt Teil 2", "title": "Haushalt Teil 2",
"slug": "haushalt-teil-2", "slug": "haushalt-teil-2",
"presence_course": "", "presence_course": "",
"assignments": [] "assignments": [],
}, },
"fr": { "fr": {
"title": "Habitat partie 2", "title": "Habitat partie 2",
"slug": "habitat-partie-2", "slug": "habitat-partie-2",
"presence_course": "", "presence_course": "",
"assignments": [] "assignments": [],
}, },
"it": {} "it": {},
}, },
} }
@ -166,7 +169,7 @@ TRANSLATIONS = {
"adresse": "Adresse", "adresse": "Adresse",
"start": "Start", "start": "Start",
"ende": "Ende", "ende": "Ende",
} },
} }
T2L_IGNORE_FIELDS = ["Vorname", "Name", "Email", "Sprache", "Durchführungen"] T2L_IGNORE_FIELDS = ["Vorname", "Name", "Email", "Sprache", "Durchführungen"]
@ -304,7 +307,9 @@ 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']}"])[1] start = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['start']}"])[
1
]
end = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['ende']}"])[1] end = try_parse_datetime(data[f"{circle} {TRANSLATIONS[language]['ende']}"])[1]
if attendance_course_lc: if attendance_course_lc:
@ -313,8 +318,11 @@ def create_or_update_course_session(
) )
csa.location = location csa.location = location
expert = CourseSessionUser.objects.filter(course_session_id=cs.id, expert = CourseSessionUser.objects.filter(
role=CourseSessionUser.Role.EXPERT).first() course_session_id=cs.id,
expert__slug=f"{course.slug}-lp-circle-{circle_data['slug']}",
role=CourseSessionUser.Role.EXPERT,
).first()
if expert: if expert:
csa.trainer = f"{expert.user.first_name} {expert.user.last_name}" csa.trainer = f"{expert.user.first_name} {expert.user.last_name}"
@ -324,10 +332,7 @@ def create_or_update_course_session(
csa.save() csa.save()
for assignment_slug in circle_data["assignments"]: for assignment_slug in circle_data["assignments"]:
logger.debug( logger.debug("import", slug=f"{course.slug}-lp-circle-{assignment_slug}")
"import",
slug=f"{course.slug}-lp-circle-{assignment_slug}"
)
csa, _created = CourseSessionAssignment.objects.get_or_create( csa, _created = CourseSessionAssignment.objects.get_or_create(
course_session=cs, course_session=cs,
@ -336,7 +341,10 @@ def create_or_update_course_session(
), ),
) )
if csa.learning_content.assignment_type == AssignmentType.PREP_ASSIGNMENT.value: if (
csa.learning_content.assignment_type
== AssignmentType.PREP_ASSIGNMENT.value
):
csa.submission_deadline.end = timezone.make_aware(start) csa.submission_deadline.end = timezone.make_aware(start)
csa.submission_deadline.save() csa.submission_deadline.save()
@ -436,7 +444,7 @@ def create_or_update_trainer(course: Course, data: Dict[str, Any], language="de"
# circle expert handling # circle expert handling
circle_data = parse_circle_group_string(data["Circles"]) circle_data = parse_circle_group_string(data["Circles"])
for circle_key in circle_data: for circle_key in circle_data:
circle_name = CIRCLE_DATA[circle_key][language]["title"] circle_name = LP_DATA[circle_key][language]["title"]
# print(circle_name, groups) # print(circle_name, groups)
import_id = f"{data['Generation'].strip()} {group}" import_id = f"{data['Generation'].strip()} {group}"