From a5df2c078f616cbcd93d030ade0213773278c8f1 Mon Sep 17 00:00:00 2001
From: Jonathan Weth <git@jonathanweth.de>
Date: Sun, 11 Aug 2024 17:20:37 +0200
Subject: [PATCH] Update AutomaticPlan to work with new models

---
 aleksis/apps/chronos/models.py              |  13 +-
 aleksis/apps/chronos/util/change_tracker.py | 126 +-------------------
 2 files changed, 11 insertions(+), 128 deletions(-)

diff --git a/aleksis/apps/chronos/models.py b/aleksis/apps/chronos/models.py
index 35f4e011..a5457a88 100644
--- a/aleksis/apps/chronos/models.py
+++ b/aleksis/apps/chronos/models.py
@@ -1209,7 +1209,7 @@ class AutomaticPlan(LiveDocument):
     @property
     def current_start_day(self) -> date:
         """Get first day which should be shown in the PDF."""
-        return TimePeriod.get_next_relevant_day(timezone.now().date(), datetime.now().time())
+        return timezone.now().date()
 
     @property
     def current_end_day(self) -> date:
@@ -1248,11 +1248,16 @@ class AutomaticPlan(LiveDocument):
                     continue
 
                 # Check if the changed object is relevant for the time period of the PDF file
-                if isinstance(version.object, Event):
+                if not version.object.amends:
+                    return
+
+                if version.object.datetime_start:
+                    date_start = version.object.datetime_start.date()
+                    date_end = version.object.datetime_end.date()
+                else:
                     date_start = version.object.date_start
                     date_end = version.object.date_end
-                else:
-                    date_start = date_end = version.object.date
+
                 if date_start <= self.current_end_day and date_end >= self.current_start_day:
                     update = True
                     break
diff --git a/aleksis/apps/chronos/util/change_tracker.py b/aleksis/apps/chronos/util/change_tracker.py
index c9b8afb1..669f5c4e 100644
--- a/aleksis/apps/chronos/util/change_tracker.py
+++ b/aleksis/apps/chronos/util/change_tracker.py
@@ -1,9 +1,4 @@
-from typing import Any, Optional
-
 from django.contrib.contenttypes.models import ContentType
-from django.db import transaction
-from django.db.models import Model
-from django.db.models.signals import m2m_changed, post_save, pre_delete
 from django.dispatch import Signal, receiver
 
 from celery import shared_task
@@ -11,126 +6,9 @@ from reversion.models import Revision
 
 
 def _get_substitution_models():
-    from aleksis.apps.chronos.models import (
-        Event,
-        ExtraLesson,
-        LessonSubstitution,
-        SupervisionSubstitution,
-    )
-
-    return [LessonSubstitution, Event, ExtraLesson, SupervisionSubstitution]
-
-
-class TimetableChange:
-    """A change to timetable models."""
-
-    def __init__(
-        self,
-        instance: Model,
-        changed_fields: Optional[dict[str, Any]] = None,
-        created: bool = False,
-        deleted: bool = False,
-    ):
-        self.instance = instance
-        self.changed_fields = changed_fields or {}
-        self.created = created
-        self.deleted = deleted
-
-
-class TimetableDataChangeTracker:
-    """Helper class for tracking changes in timetable models by using signals."""
-
-    @classmethod
-    def get_models(cls) -> list[type[Model]]:
-        """Return all models that should be tracked."""
-        from aleksis.apps.chronos.models import (
-            Event,
-            ExtraLesson,
-            LessonSubstitution,
-            SupervisionSubstitution,
-        )
-
-        return [LessonSubstitution, Event, ExtraLesson, SupervisionSubstitution]
-
-    def __init__(self):
-        self.changes = {}
-        self.m2m_fields = {}
-
-        if transaction.get_autocommit():
-            raise RuntimeError("Cannot use change tracker outside of transaction")
-
-        for model in self.get_models():
-            post_save.connect(self._handle_save, sender=model, weak=False)
-            pre_delete.connect(self._handle_delete, sender=model, weak=False)
-
-            # Register signals for all relevant m2m fields
-            m2m_fields = {
-                getattr(model, f.name).through: f
-                for f in model._meta.get_fields()
-                if f.many_to_many
-            }
-            self.m2m_fields.update(m2m_fields)
-            for through_model, _field in m2m_fields.items():
-                m2m_changed.connect(self._handle_m2m_changed, sender=through_model, weak=False)
-
-        transaction.on_commit(self.close)
-
-    def get_instance_key(self, instance: Model) -> str:
-        """Get unique string key for an instance."""
-        return f"{instance._meta.model_name}_{instance.id}"
-
-    def _add_change(self, change: TimetableChange):
-        """Add a change to the list of changes and update, if necessary."""
-        key = self.get_instance_key(change.instance)
-        if key not in self.changes or change.deleted or change.created:
-            self.changes[key] = change
-        else:
-            self.changes[key].changed_fields.update(change.changed_fields)
-
-    def _handle_save(self, sender: type[Model], instance: Model, created: bool, **kwargs):
-        """Handle the save signal."""
-        change = TimetableChange(instance, created=created)
-        if not created:
-            change.changed_fields = instance.tracker.changed()
-        self._add_change(change)
-
-    def _handle_delete(self, sender: type[Model], instance: Model, **kwargs):
-        """Handle the delete signal."""
-        change = TimetableChange(instance, deleted=True)
-        self._add_change(change)
-
-    def _handle_m2m_changed(
-        self,
-        sender: type[Model],
-        instance: Model,
-        action: str,
-        model: type[Model],
-        pk_set: set,
-        **kwargs,
-    ):
-        """Handle the m2m_changed signal."""
-        if action in ["pre_add", "pre_remove", "pre_clear"]:
-            field_name = self.m2m_fields[sender].name
-            current_value = list(getattr(instance, field_name).all())
-            if self.get_instance_key(instance) in self.changes:
-                change = self.changes[self.get_instance_key(instance)]
-                if field_name in change.changed_fields:
-                    current_value = None
-
-            if current_value is not None:
-                change = TimetableChange(instance, changed_fields={field_name: current_value})
-                self._add_change(change)
-
-    def close(self):
-        """Disconnect signals and send change signal."""
-        for model in self.get_models():
-            post_save.disconnect(self._handle_save, sender=model)
-            pre_delete.disconnect(self._handle_delete, sender=model)
-
-        for through_model, _field in self.m2m_fields.items():
-            m2m_changed.disconnect(self._handle_m2m_changed, sender=through_model)
+    from aleksis.apps.chronos.models import LessonEvent, SupervisionEvent
 
-        timetable_data_changed.send(sender=self, changes=self.changes)
+    return [LessonEvent, SupervisionEvent]
 
 
 chronos_revision_created = Signal()
-- 
GitLab