From 7ef59d2545d14f7a8db14294cb99b51e00eee819 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Wed, 1 Apr 2020 12:37:42 +0200
Subject: [PATCH] Improve import of lessons

- Save references to term, lesson id and element number
- Update existing lessons
- Add logging
---
 aleksis/apps/untis/model_extensions.py        |   4 +
 .../untis/util/mysql/importers/lessons.py     | 134 ++++++++++++++----
 2 files changed, 110 insertions(+), 28 deletions(-)

diff --git a/aleksis/apps/untis/model_extensions.py b/aleksis/apps/untis/model_extensions.py
index b5b61bd..89ab7cb 100644
--- a/aleksis/apps/untis/model_extensions.py
+++ b/aleksis/apps/untis/model_extensions.py
@@ -11,3 +11,7 @@ core_models.Group.field(import_ref_untis=IntegerField())
 chronos_models.Subject.field(import_ref_untis=IntegerField())
 chronos_models.Room.field(import_ref_untis=IntegerField())
 chronos_models.SupervisionArea.field(import_ref_untis=IntegerField())
+chronos_models.Lesson.field(lesson_id_untis=IntegerField())
+chronos_models.Lesson.field(element_id_untis=IntegerField())
+chronos_models.Lesson.field(term_untis=IntegerField())
+chronos_models.LessonPeriod.field(element_id_untis=IntegerField())
diff --git a/aleksis/apps/untis/util/mysql/importers/lessons.py b/aleksis/apps/untis/util/mysql/importers/lessons.py
index bfe2b78..d754916 100644
--- a/aleksis/apps/untis/util/mysql/importers/lessons.py
+++ b/aleksis/apps/untis/util/mysql/importers/lessons.py
@@ -1,3 +1,5 @@
+import logging
+
 from django.utils.translation import gettext as _
 
 from aleksis.apps.chronos import models as chronos_models
@@ -7,6 +9,19 @@ from aleksis.core.util import messages
 from .... import models as mysql_models
 from ..util import run_default_filter, untis_split_third, untis_date_to_date, get_term
 
+logger = logging.getLogger(__name__)
+
+
+def sync_m2m(new_items, m2m_qs):
+    for item in new_items:
+        if item not in m2m_qs.all():
+            m2m_qs.add(item)
+            logger.info("  Many-to-many sync: item added")
+    for item in m2m_qs.all():
+        if item not in new_items:
+            m2m_qs.remove(item)
+            logger.info("  Many-to-many sync: item removed")
+
 
 def import_lessons(
     time_periods_ref, rooms_ref, subjects_ref, teachers_ref, classes_ref
@@ -25,10 +40,7 @@ def import_lessons(
 
         if not lesson.lesson_tt:
             messages.warning(
-                None,
-                message=_("Skip lesson {} because there are missing times.").format(
-                    lesson_id
-                ),
+                None, message=_("  Skip because missing times").format(lesson_id),
             )
             continue
 
@@ -67,6 +79,8 @@ def import_lessons(
 
         # All part lessons (courses)
         for i, el in enumerate(raw_lesson_data_2):
+            logger.info("  Lesson part {}".format(i))
+
             # Get plain ids
             teacher_id = int(el[0])
             subject_id = int(el[2])
@@ -83,15 +97,9 @@ def import_lessons(
                 subject = subjects_ref[subject_id]
             else:
                 messages.warning(
-                    None,
-                    message=_(
-                        "Skip lesson {}, element {} because there is missing a subject.".format(
-                            lesson_id, i
-                        )
-                    ),
+                    None, message=_("    Skip because missing subject".format(i)),
                 )
                 continue
-                # raise Exception("Subject needed.")
 
             # Get classes
             course_classes = []
@@ -100,43 +108,113 @@ def import_lessons(
                 course_classes.append(c)
 
             # Build names and refs for course groups
-            short_name = "{}-{}".format(
+            group_short_name = "{}-{}".format(
                 "".join([c.short_name for c in course_classes]), subject.abbrev
             )
-            name = "{}: {}".format(
+            group_name = "{}: {}".format(
                 ", ".join([c.short_name for c in course_classes]), subject.abbrev
             )
-            import_ref = "{}-{}".format(lesson_id, i)
+            group_import_ref = -int("{}{}".format(lesson_id, i))
 
             # Get or create course group
             course_group, created = core_models.Group.objects.get_or_create(
-                short_name=short_name, defaults={"name": name}
+                short_name=group_short_name, defaults={"name": group_name}
             )
-            course_group.import_ref = import_ref
-            course_group.name = name
-
+            course_group.import_ref_untis = group_import_ref
+            course_group.name = group_name
             course_group.save()
             course_group.parent_groups.set(course_classes)
 
+            if created:
+                logger.info("    Course group created")
+
             # Create new lesson
             date_start = untis_date_to_date(term.datefrom)
             date_end = untis_date_to_date(term.dateto)
-            lesson = chronos_models.Lesson.objects.create(
-                subject=subject, date_start=date_start, date_end=date_end
+
+            # Get old lesson
+            old_lesson_qs = chronos_models.Lesson.objects.filter(
+                lesson_id_untis=lesson_id, element_id_untis=i, term_untis=term.term_id
             )
 
-            # Set groups
-            lesson.groups.set([course_group])
+            if old_lesson_qs.exists():
+                # Update existing lesson
+                logger.info("    Existing lesson found")
+
+                old_lesson = old_lesson_qs[0]
+
+                if (
+                    old_lesson.subject != subject
+                    or old_lesson.date_start != date_start
+                    or old_lesson.date_end != date_end
+                ):
+                    old_lesson.subject = subject
+                    old_lesson.date_start = date_start
+                    old_lesson.date_end = date_end
+                    old_lesson.save()
+                    logger.info("    Subject, start date and end date updated")
+                lesson = old_lesson
+            else:
+                # Create new lesson
+
+                lesson = chronos_models.Lesson.objects.create(
+                    subject=subject,
+                    date_start=date_start,
+                    date_end=date_end,
+                    lesson_id_untis=lesson_id,
+                    element_id_untis=i,
+                    term_untis=term.term_id,
+                )
+                logger.info("    New lesson created")
+
+            # Sync groups
+            groups = [course_group]
+            sync_m2m(groups, lesson.groups)
 
-            # Set teacher
-            if teacher:
-                lesson.teachers.set([teacher])
+            # Sync teachers
+            teachers = [teacher] if teacher else []
+            sync_m2m(teachers, lesson.teachers)
 
             # All times for this course
+            old_lesson_periods_qs = chronos_models.LessonPeriod.objects.filter(
+                lesson=lesson
+            )
+
+            # If length has changed, delete all lesson periods
+            if old_lesson_periods_qs.count() != len(time_periods):
+                old_lesson_periods_qs.delete()
+                logger.info("    Lesson periods deleted")
+
+            # Sync time periods
             for j, time_period in enumerate(time_periods):
-                rooms = rooms_per_periods[j]
+                logger.info("    Import lesson period {}".format(time_period))
 
+                # Get room if provided
+                rooms = rooms_per_periods[j]
                 if i < len(rooms):
-                    lesson.periods.add(time_period, through_defaults={"room": rooms[i]})
+                    room = rooms[i]
                 else:
-                    lesson.periods.add(time_period)
+                    room = None
+
+                # Check if an old lesson period is provided
+                old_lesson_period_qs = old_lesson_periods_qs.filter(element_id_untis=j)
+                if old_lesson_period_qs.exists():
+                    # Update old lesson period
+
+                    old_lesson_period = old_lesson_period_qs[0]
+                    if (
+                        old_lesson_period.period != time_period
+                        or old_lesson_period.room != room
+                    ):
+                        old_lesson_period.period = time_period
+                        old_lesson_period.room = room
+                        old_lesson_period.save()
+                        logger.info("      Time period and room updated")
+                else:
+                    # Create new lesson period
+
+                    lesson.periods.add(
+                        time_period,
+                        through_defaults={"room": room, "element_id_untis": j},
+                    )
+                    logger.info("      New time period added")
-- 
GitLab