From c1a605911caf3a4dd9c7d007db6a5f4ca3e6d49d Mon Sep 17 00:00:00 2001
From: Dominik George <dominik.george@teckids.org>
Date: Sat, 29 Jan 2022 12:40:42 +0100
Subject: [PATCH] Honour plan versions

---
 CHANGELOG.rst                                 | 10 ++++++++
 aleksis/apps/untis/commands.py                |  6 ++---
 .../management/commands/untis_import_mysql.py |  7 +++++-
 aleksis/apps/untis/tasks.py                   |  4 ++--
 .../apps/untis/util/mysql/importers/terms.py  | 23 +++++++++++++++++--
 aleksis/apps/untis/util/mysql/main.py         |  4 ++--
 6 files changed, 44 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1997d1d..b1fa8cc 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -9,6 +9,16 @@ and this project adheres to `Semantic Versioning`_.
 Unreleased
 ----------
 
+Added
+~~~~~
+
+* Plan version can be explicitly selected (defaulting to newest)
+
+Fixed
+~~~~~
+
+* Import now only imports one plan version
+
 `2.1`_ - 2022-01-13
 -------------------
 
diff --git a/aleksis/apps/untis/commands.py b/aleksis/apps/untis/commands.py
index ce6b17a..ae85233 100644
--- a/aleksis/apps/untis/commands.py
+++ b/aleksis/apps/untis/commands.py
@@ -27,15 +27,15 @@ class ImportCommand:
         return None
 
     @classmethod
-    def run(cls, background: bool = False):
+    def run(cls, background: bool = False, version: Optional[int] = None):
         """Run the import command (foreground/background)."""
         if background:
             from .tasks import TASKS
 
             task = TASKS[cls.task_name]
-            task.delay()
+            task.delay(version=version)
         else:
-            _untis_import_mysql(cls.get_terms())
+            _untis_import_mysql(cls.get_terms(), version=version)
 
 
 class CurrentImportCommand(ImportCommand):
diff --git a/aleksis/apps/untis/management/commands/untis_import_mysql.py b/aleksis/apps/untis/management/commands/untis_import_mysql.py
index a53a9e6..1aa6bdd 100644
--- a/aleksis/apps/untis/management/commands/untis_import_mysql.py
+++ b/aleksis/apps/untis/management/commands/untis_import_mysql.py
@@ -13,8 +13,13 @@ class Command(BaseCommand):
             action="store_true",
             help="Run import job in background using Celery",
         )
+        parser.add_argument(
+            "--plan-version",
+            help="Select explicit Untis plan version",
+        )
 
     def handle(self, *args, **options):
         command = COMMANDS_BY_NAME[options["command"]]
         background = options["background"]
-        command.run(background=background)
+        version = options.get("plan_version", None)
+        command.run(background=background, version=version)
diff --git a/aleksis/apps/untis/tasks.py b/aleksis/apps/untis/tasks.py
index 6da6405..3e45b18 100644
--- a/aleksis/apps/untis/tasks.py
+++ b/aleksis/apps/untis/tasks.py
@@ -6,8 +6,8 @@ TASKS = {}
 for import_command in ImportCommand.__subclasses__():
 
     @app.task(name=import_command.task_name, bind=True)
-    def _task(self):
+    def _task(self, *args, **kwargs):
         import_command = COMMANDS_BY_TASK_NAME[self.name]
-        import_command.run()
+        import_command.run(*args, **kwargs)
 
     TASKS[import_command.task_name] = _task
diff --git a/aleksis/apps/untis/util/mysql/importers/terms.py b/aleksis/apps/untis/util/mysql/importers/terms.py
index e8b6620..092a9f9 100644
--- a/aleksis/apps/untis/util/mysql/importers/terms.py
+++ b/aleksis/apps/untis/util/mysql/importers/terms.py
@@ -2,7 +2,7 @@ import logging
 from datetime import date
 from typing import Dict, Optional
 
-from django.db.models import QuerySet
+from django.db.models import Max, OuterRef, QuerySet, Subquery
 from django.utils import timezone
 
 from tqdm import tqdm
@@ -49,12 +49,31 @@ logger = logging.getLogger(__name__)
 
 def import_terms(
     qs: Optional[QuerySet] = None,
+    version: Optional[int] = None,
 ) -> Dict[int, chronos_models.ValidityRange]:
     """Import terms and school years as validity ranges and school terms."""
     ranges_ref = {}
 
     if not isinstance(qs, QuerySet):
-        qs = run_using(mysql_models.Terms.objects).all()
+        qs = run_using(mysql_models.Terms.objects)
+
+    if version is None:
+        # Select newest version per term / validity range
+        sub_qs = (
+            run_using(mysql_models.Terms.objects)
+            .filter(
+                school_id=OuterRef("school_id"),
+                schoolyear_id=OuterRef("schoolyear_id"),
+                term_id=OuterRef("term_id"),
+            )
+            .values("school_id", "schoolyear_id", "term_id")
+            .annotate(max_version=Max("version_id"))
+            .values("max_version")
+        )
+        qs = qs.filter(version_id=Subquery(sub_qs))
+    else:
+        # Select passed version
+        qs = qs.filter(version_id=version)
 
     school_terms = {}
     for term in tqdm(qs, desc="Import terms (as validity ranges)", **TQDM_DEFAULTS):
diff --git a/aleksis/apps/untis/util/mysql/main.py b/aleksis/apps/untis/util/mysql/main.py
index c372de0..b3e6ca5 100644
--- a/aleksis/apps/untis/util/mysql/main.py
+++ b/aleksis/apps/untis/util/mysql/main.py
@@ -25,9 +25,9 @@ from .importers.lessons import import_lessons
 from .importers.substitutions import import_substitutions
 
 
-def untis_import_mysql(terms: Optional[QuerySet] = None):
+def untis_import_mysql(terms: Optional[QuerySet] = None, version: Optional[int] = None):
     # School terms and validity ranges
-    validity_ref = import_terms(terms)
+    validity_ref = import_terms(terms, version=version)
 
     for validity_range in tqdm(
         validity_ref.values(), desc="Import data for terms", **TQDM_DEFAULTS
-- 
GitLab