diff --git a/.gitignore b/.gitignore index a511dff6..0daeaac9 100644 --- a/.gitignore +++ b/.gitignore @@ -61,8 +61,6 @@ target/ # pyenv .python-version - - # Environments .venv venv/ @@ -76,7 +74,6 @@ venv/ # mypy .mypy_cache/ - ### Node template # Logs logs @@ -159,10 +156,6 @@ typings/ # Local History for Visual Studio Code .history/ - - - - ### Windows template # Windows thumbnail cache files Thumbs.db @@ -272,6 +265,7 @@ tags ### Project template +.~lock.* .pytest_cache/ .ipython/ vendors.js diff --git a/server/requirements/requirements-dev.txt b/server/requirements/requirements-dev.txt index 62594c31..0753ad9e 100644 --- a/server/requirements/requirements-dev.txt +++ b/server/requirements/requirements-dev.txt @@ -319,7 +319,9 @@ mypy-extensions==0.4.3 nodeenv==1.6.0 # via pre-commit openpyxl==3.1.2 - # via wagtail + # via + # -r requirements.in + # wagtail packaging==21.3 # via # build diff --git a/server/requirements/requirements.in b/server/requirements/requirements.in index 7910d91e..dfaf5b87 100644 --- a/server/requirements/requirements.in +++ b/server/requirements/requirements.in @@ -47,3 +47,4 @@ azure-storage-blob azure-identity boto3 +openpyxl diff --git a/server/requirements/requirements.txt b/server/requirements/requirements.txt index b3015ddd..7c36d7b5 100644 --- a/server/requirements/requirements.txt +++ b/server/requirements/requirements.txt @@ -190,7 +190,9 @@ msal==1.22.0 msal-extensions==1.0.0 # via azure-identity openpyxl==3.1.2 - # via wagtail + # via + # -r requirements.in + # wagtail packaging==21.3 # via # marshmallow diff --git a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py b/server/vbv_lernwelt/assignment/tests/test_assignment_api.py index 86fe4edf..704705b6 100644 --- a/server/vbv_lernwelt/assignment/tests/test_assignment_api.py +++ b/server/vbv_lernwelt/assignment/tests/test_assignment_api.py @@ -28,6 +28,7 @@ class AssignmentApiTestCase(APITestCase): self.cs = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) self.student = User.objects.get(username="student") self.student_csu = CourseSessionUser.objects.create( diff --git a/server/vbv_lernwelt/assignment/tests/test_services.py b/server/vbv_lernwelt/assignment/tests/test_services.py index 64e24d77..56bf9558 100644 --- a/server/vbv_lernwelt/assignment/tests/test_services.py +++ b/server/vbv_lernwelt/assignment/tests/test_services.py @@ -33,6 +33,7 @@ class UpdateAssignmentCompletionTestCase(TestCase): self.course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Bern 2022 a", + import_id="Bern 2022 a", ) self.user = User.objects.get(username="student") self.trainer = User.objects.get(username="admin") diff --git a/server/vbv_lernwelt/core/migrations/0002_alter_user_managers.py b/server/vbv_lernwelt/core/migrations/0002_alter_user_managers.py new file mode 100644 index 00000000..f795d66e --- /dev/null +++ b/server/vbv_lernwelt/core/migrations/0002_alter_user_managers.py @@ -0,0 +1,20 @@ +# Generated by Django 3.2.13 on 2023-05-31 14:34 + +import django.contrib.auth.models +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + ] + + operations = [ + migrations.AlterModelManagers( + name='user', + managers=[ + ('objects', django.contrib.auth.models.UserManager()), + ], + ), + ] diff --git a/server/vbv_lernwelt/core/models.py b/server/vbv_lernwelt/core/models.py index c6727611..bd687d08 100644 --- a/server/vbv_lernwelt/core/models.py +++ b/server/vbv_lernwelt/core/models.py @@ -2,8 +2,6 @@ from django.contrib.auth.models import AbstractUser from django.db import models from django.db.models import JSONField -from vbv_lernwelt.core.managers import UserManager - class User(AbstractUser): """ diff --git a/server/vbv_lernwelt/course/creators/test_course.py b/server/vbv_lernwelt/course/creators/test_course.py index e6681709..66b46e69 100644 --- a/server/vbv_lernwelt/course/creators/test_course.py +++ b/server/vbv_lernwelt/course/creators/test_course.py @@ -83,11 +83,13 @@ def create_test_course(include_uk=True, include_vv=True, with_sessions=False): cs_bern = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Bern 2022 a", + import_id="Test Bern 2022 a", id=TEST_COURSE_SESSION_BERN_ID, ) cs_zurich = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Zürich 2022 a", + import_id="Test Zürich 2022 a", id=TEST_COURSE_SESSION_ZURICH_ID, ) diff --git a/server/vbv_lernwelt/course/management/commands/create_default_courses.py b/server/vbv_lernwelt/course/management/commands/create_default_courses.py index f681bc5b..6eba6df4 100644 --- a/server/vbv_lernwelt/course/management/commands/create_default_courses.py +++ b/server/vbv_lernwelt/course/management/commands/create_default_courses.py @@ -117,6 +117,7 @@ def create_versicherungsvermittlerin_course(): cs = CourseSession.objects.create( course_id=COURSE_VERSICHERUNGSVERMITTLERIN_ID, title="Versicherungsvermittler/-in", + import_id="Versicherungsvermittler/-in", ) for user_data in default_users: CourseSessionUser.objects.create( @@ -185,6 +186,7 @@ def create_course_uk_de(): cs = CourseSession.objects.create( course_id=COURSE_UK, title="Bern 2023 a", + import_id="Bern 2023 a", attendance_courses=[ { "learningContentId": LearningContentAttendanceCourse.objects.get( @@ -280,6 +282,7 @@ def create_course_uk_de(): cs = CourseSession.objects.create( course_id=COURSE_UK, title="Zürich 2023 a", + import_id="Zürich 2023 a", ) # for user_data in default_users: # CourseSessionUser.objects.create( @@ -322,6 +325,7 @@ def create_course_uk_fr(): cs = CourseSession.objects.create( course_id=COURSE_UK_FR, title="Cours hors établissement année 1 - Région Fribourg", + import_id="Cours hors établissement année 1 - Région Fribourg", ) csu = CourseSessionUser.objects.create( @@ -441,7 +445,8 @@ def create_course_training_de(): cs = CourseSession.objects.create( course_id=COURSE_UK_TRAINING, - title="Demo-Tag", + title="myVBV Training", + import_id="myVBV Training", attendance_courses=[ { "learningContentId": LearningContentAttendanceCourse.objects.get( diff --git a/server/vbv_lernwelt/course/migrations/0004_import_fields.py b/server/vbv_lernwelt/course/migrations/0004_import_fields.py new file mode 100644 index 00000000..a9d05b81 --- /dev/null +++ b/server/vbv_lernwelt/course/migrations/0004_import_fields.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.13 on 2023-05-31 14:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('course', '0003_rename_attendance_days_coursesession_attendance_courses'), + ] + + operations = [ + migrations.AddField( + model_name='coursesession', + name='generation', + field=models.TextField(blank=True, default=''), + ), + migrations.AddField( + model_name='coursesession', + name='group', + field=models.TextField(blank=True, default=''), + ), + migrations.AddField( + model_name='coursesession', + name='import_id', + field=models.TextField(default='', unique=True), + preserve_default=False, + ), + migrations.AddField( + model_name='coursesession', + name='region', + field=models.TextField(blank=True, default=''), + ), + migrations.AlterField( + model_name='coursesession', + name='title', + field=models.TextField(unique=True), + ), + ] diff --git a/server/vbv_lernwelt/course/models.py b/server/vbv_lernwelt/course/models.py index f0bd86b2..4942e936 100644 --- a/server/vbv_lernwelt/course/models.py +++ b/server/vbv_lernwelt/course/models.py @@ -215,7 +215,12 @@ class CourseSession(models.Model): updated_at = models.DateTimeField(auto_now=True) course = models.ForeignKey("course.Course", on_delete=models.CASCADE) - title = models.TextField() + title = models.TextField(unique=True) + import_id = models.TextField(unique=True) + + generation = models.TextField(blank=True, default="") + region = models.TextField(blank=True, default="") + group = models.TextField(blank=True, default="") start_date = models.DateField(null=True, blank=True) end_date = models.DateField(null=True, blank=True) diff --git a/server/vbv_lernwelt/course/tests/test_completion_api.py b/server/vbv_lernwelt/course/tests/test_completion_api.py index 4c5db0af..1860e29a 100644 --- a/server/vbv_lernwelt/course/tests/test_completion_api.py +++ b/server/vbv_lernwelt/course/tests/test_completion_api.py @@ -22,6 +22,7 @@ class CourseCompletionApiTestCase(APITestCase): self.cs = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) csu = CourseSessionUser.objects.create( course_session=self.cs, diff --git a/server/vbv_lernwelt/course/tests/test_course_session_api.py b/server/vbv_lernwelt/course/tests/test_course_session_api.py index 17d3616c..423b8493 100644 --- a/server/vbv_lernwelt/course/tests/test_course_session_api.py +++ b/server/vbv_lernwelt/course/tests/test_course_session_api.py @@ -19,6 +19,7 @@ class CourseCompletionApiTestCase(APITestCase): self.course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) self.client.login(username="student", password="test") diff --git a/server/vbv_lernwelt/course/tests/test_document_uploads.py b/server/vbv_lernwelt/course/tests/test_document_uploads.py index 4d08b6cf..2d69adb3 100644 --- a/server/vbv_lernwelt/course/tests/test_document_uploads.py +++ b/server/vbv_lernwelt/course/tests/test_document_uploads.py @@ -23,6 +23,7 @@ class DocumentUploadApiTestCase(APITestCase): self.course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) csu = CourseSessionUser.objects.create( diff --git a/server/vbv_lernwelt/feedback/tests/test_feedback_api.py b/server/vbv_lernwelt/feedback/tests/test_feedback_api.py index 6ad2136c..22efad7a 100644 --- a/server/vbv_lernwelt/feedback/tests/test_feedback_api.py +++ b/server/vbv_lernwelt/feedback/tests/test_feedback_api.py @@ -23,6 +23,7 @@ class FeedbackApiBaseTestCase(APITestCase): self.course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) csu = CourseSessionUser.objects.create( diff --git a/server/vbv_lernwelt/importer/services.py b/server/vbv_lernwelt/importer/services.py index 1b17bf6c..04c836ef 100644 --- a/server/vbv_lernwelt/importer/services.py +++ b/server/vbv_lernwelt/importer/services.py @@ -1,4 +1,7 @@ +from typing import Dict, Any + from vbv_lernwelt.core.models import User +from vbv_lernwelt.course.models import Course, CourseSession def create_or_update_user( @@ -28,3 +31,32 @@ def create_or_update_user( user.save() return user + + +def create_or_update_course_session(course: Course, data: Dict[str, Any]): + """ + :param data: the following keys are required to process the data: Generation, Region, Klasse + :return: + """ + + # TODO: validation + generation = str(data["Generation"]).strip() + region = data["Region"].strip() + group = data["Klasse"].strip() + import_id = data["ID"].strip() + + title = f"{region} {generation} {group}" + + cs, _created = CourseSession.objects.get_or_create( + import_id=import_id, course=course + ) + + cs.generation = generation + cs.region = region + cs.group = group + cs.import_id = import_id + + cs.additional_json_data["import_data"] = data + cs.save() + + return cs diff --git a/server/vbv_lernwelt/importer/tests/Schulungen_Durchfuehrung_Trainer.xlsx b/server/vbv_lernwelt/importer/tests/Schulungen_Durchfuehrung_Trainer.xlsx new file mode 100644 index 00000000..9df30a5f Binary files /dev/null and b/server/vbv_lernwelt/importer/tests/Schulungen_Durchfuehrung_Trainer.xlsx differ diff --git a/server/vbv_lernwelt/importer/tests/Schulungen_Teilnehmende.xlsx b/server/vbv_lernwelt/importer/tests/Schulungen_Teilnehmende.xlsx new file mode 100644 index 00000000..9efe3135 Binary files /dev/null and b/server/vbv_lernwelt/importer/tests/Schulungen_Teilnehmende.xlsx differ diff --git a/server/vbv_lernwelt/importer/tests/test_import_course_sessions.py b/server/vbv_lernwelt/importer/tests/test_import_course_sessions.py new file mode 100644 index 00000000..470928b1 --- /dev/null +++ b/server/vbv_lernwelt/importer/tests/test_import_course_sessions.py @@ -0,0 +1,74 @@ +import os + +from django.test import TestCase +from openpyxl.reader.excel import load_workbook + +from vbv_lernwelt.course.factories import CourseFactory +from vbv_lernwelt.importer.services import create_or_update_course_session + +test_dir = os.path.dirname(os.path.abspath(__file__)) + + +class ImportCourseSessionTestCase(TestCase): + def test_open_excel_file(self): + workbook = load_workbook( + filename=f"{test_dir}/Schulungen_Durchfuehrung_Trainer.xlsx" + ) + sheet = workbook["Schulungen Durchführung"] + + # Read the header row separately + header = [cell.value for cell in sheet[1]] + + # Loop through the remaining rows in the sheet + for row in sheet.iter_rows(min_row=2, values_only=True): + row_with_header = list(zip(header, row)) + print(row_with_header) + + def test_create_or_update_course_session(self): + row = [ + ("ID", "DE 2023 A"), + ("Generation", 2023), + ("Region", "Deutschschweiz"), + ("Klasse", "A"), + ("Fahrzeug Start", "06.06.2023, 13:30"), + ("Fahrzeug Ende", "06.06.2023, 15:00"), + ( + "Fahrzeug Raum", + "https://teams.microsoft.com/l/meetup-join/19%3ameeting_N2I5YzViZTQtYTM2Ny00OTYwLTgzNzAtYWI4OTQzODcxNTlj%40thread.v2/0?context=%7b%22Tid%22%3a%22fedd03c8-a756-4803-8f27-0db8f7c488f2%22%2c%22Oid%22%3a%22f92e6382-3884-4e71-a2fd-b305a75d9812%22%7d", + ), + ("Fahrzeug Standort", None), + ("Fahrzeug Adresse", None), + ] + + print(dict(row)) + + +class CreateOrUpdateCourseSessionTestCase(TestCase): + def setUp(self): + self.course = CourseFactory(title="myVBV Training") + + def test_create(self): + row = [ + ("ID", "DE 2023"), + ("Generation", 2023), + ("Region", "Deutschschweiz"), + ("Klasse", "A"), + ("Fahrzeug Start", "06.06.2023, 13:30"), + ("Fahrzeug Ende", "06.06.2023, 15:00"), + ( + "Fahrzeug Raum", + "https://teams.microsoft.com/l/meetup-join/19%3ameeting_N2I5YzViZTQtYTM2Ny00OTYwLTgzNzAtYWI4OTQzODcxNTlj%40thread.v2/0?context=%7b%22Tid%22%3a%22fedd03c8-a756-4803-8f27-0db8f7c488f2%22%2c%22Oid%22%3a%22f92e6382-3884-4e71-a2fd-b305a75d9812%22%7d", + ), + ("Fahrzeug Standort", None), + ("Fahrzeug Adresse", None), + ] + + data = dict(row) + + cs = create_or_update_course_session(self.course, data) + + self.assertEqual(cs.import_id, "DE 2023") + self.assertEqual(cs.title, "Deutschschweiz 2023 A") + self.assertEqual(cs.generation, "2023") + self.assertEqual(cs.region, "Deutschschweiz") + self.assertEqual(cs.group, "A") diff --git a/server/vbv_lernwelt/learnpath/tests/test_api.py b/server/vbv_lernwelt/learnpath/tests/test_api.py index da6c8cc3..46c45490 100644 --- a/server/vbv_lernwelt/learnpath/tests/test_api.py +++ b/server/vbv_lernwelt/learnpath/tests/test_api.py @@ -45,6 +45,7 @@ class TestRetrieveLearingPathContents(APITestCase): course_session = CourseSession.objects.create( course_id=COURSE_TEST_ID, title="Test Lehrgang Session", + import_id="Test Lehrgang Session", ) CourseSessionUser.objects.create( course_session=course_session,