From 1fc558bdb329194cd8372573ee3a560934373cb5 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Fri, 15 Mar 2024 17:10:30 +0100
Subject: [PATCH 1/8] Filter groups for TimetableManagement by group of
 selected timeGrid

---
 aleksis/apps/lesrooster/schema/__init__.py | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/aleksis/apps/lesrooster/schema/__init__.py b/aleksis/apps/lesrooster/schema/__init__.py
index d27e9288..81d89f23 100644
--- a/aleksis/apps/lesrooster/schema/__init__.py
+++ b/aleksis/apps/lesrooster/schema/__init__.py
@@ -259,8 +259,22 @@ class Query(graphene.ObjectType):
         if not info.context.user.has_perm("lesrooster.plan_timetables_rule"):
             return []
 
+        # This will fail if the ID is invalid, but won't, if it is empty
+        time_grid_obj: TimeGrid | None = TimeGrid.objects.get(pk=time_grid) if time_grid is not None else None
+
+        # If there is no time_grid, or it is a generic one, filter groups to have a fitting school_term
+        if time_grid_obj is None or time_grid_obj.group is None:
+            return (
+                Group.objects.filter(school_term__lr_validity_ranges__time_grids__id=time_grid)
+                .annotate(has_cg=Q(child_groups__isnull=False))
+                .order_by("-has_cg", "name")
+            )
+
+        group_id = time_grid_obj.group.pk
+
         return (
-            Group.objects.filter(school_term__lr_validity_ranges__time_grids__id=time_grid)
+            Group.objects.filter(Q(pk=group_id) | Q(parent_groups=group_id) | Q(parent_groups__parent_groups=group_id))
+            .distinct()
             .annotate(has_cg=Q(child_groups__isnull=False))
             .order_by("-has_cg", "name")
         )
-- 
GitLab


From 1a2eab940d741c0ade15500cff3d3a15b2115585 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Fri, 15 Mar 2024 17:28:48 +0100
Subject: [PATCH 2/8] Include timeGrid in TimetableManagement urls

---
 .../TimetableManagement.vue                   | 49 ++++++++++++++-----
 aleksis/apps/lesrooster/frontend/index.js     |  2 +-
 2 files changed, 38 insertions(+), 13 deletions(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index 1fbc0d46..a8fd2659 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -109,9 +109,9 @@ export default defineComponent({
         return this.internalTimeGrid === null;
       },
       result() {
-        if (!this.selectedGroup && this.$route.params.id && this.groups) {
+        if (this.$route.params.group && this.groups) {
           this.selectedGroup = this.groups.find(
-            (group) => group.id === this.$route.params.id,
+            (group) => group.id === this.$route.params.group,
           );
         }
       },
@@ -345,10 +345,14 @@ export default defineComponent({
   watch: {
     selectedGroup() {
       if (!this.selectedGroup) return;
-      if (this.selectedGroup.id != this.$route.params.id) {
+      if (this.selectedGroup.id != this.$route.params.group) {
+        // to be able to select a group, the timeGrid has to be set
         this.$router.push({
           name: "lesrooster.timetable_management",
-          params: { id: this.selectedGroup.id },
+          params: {
+            group: this.selectedGroup.id,
+            timeGrid: this.internalTimeGrid.id,
+          },
         });
       }
       this.$setToolBarTitle(
@@ -359,6 +363,19 @@ export default defineComponent({
       this.$apollo.queries.courses.refetch();
       this.$apollo.queries.lessonObjects.refetch();
     },
+    internalTimeGrid(newTimeGrid, oldTimeGrid) {
+      if (!oldTimeGrid) return;
+
+      if (this.selectedGroup?.id) {
+        this.$router.push({
+          name: "lesrooster.timetable_management",
+          params: {
+            group: this.selectedGroup.id,
+            timeGrid: this.internalTimeGrid.id,
+          },
+        });
+      }
+    },
   },
   methods: {
     itemMovedToLessons(eventData) {
@@ -821,6 +838,13 @@ export default defineComponent({
         this.draggedItem = null;
       }
     },
+    setInitialTimeGrid(timeGrids) {
+      if (!this.internalTimeGrid?.id && this.$route.params.timeGrid) {
+        this.internalTimeGrid = timeGrids.find(
+          (timeGrid) => timeGrid.id === this.$route.params.timeGrid,
+        );
+      }
+    }
   },
 });
 </script>
@@ -837,6 +861,15 @@ export default defineComponent({
 
           <v-spacer />
 
+          <time-grid-field
+            outlined
+            filled
+            label="Select Validity Range"
+            hide-details
+            v-model="internalTimeGrid"
+            @items="setInitialTimeGrid"
+          />
+
           <v-autocomplete
             outlined
             filled
@@ -850,14 +883,6 @@ export default defineComponent({
             :loading="$apollo.queries.gqlGroups"
             class="mr-4"
           />
-
-          <time-grid-field
-            outlined
-            filled
-            label="Select Validity Range"
-            hide-details
-            v-model="internalTimeGrid"
-          />
         </div>
       </v-col>
 
diff --git a/aleksis/apps/lesrooster/frontend/index.js b/aleksis/apps/lesrooster/frontend/index.js
index 5f072d56..659fd6ae 100644
--- a/aleksis/apps/lesrooster/frontend/index.js
+++ b/aleksis/apps/lesrooster/frontend/index.js
@@ -62,7 +62,7 @@ export default {
       },
       children: [
         {
-          path: ":id(\\d+)/",
+          path: ":timeGrid(\\d+)/:group(\\d+)/",
           component: () =>
             import("./components/timetable_management/TimetableManagement.vue"),
           name: "lesrooster.timetable_management",
-- 
GitLab


From 2736063a45d08d1866b7c1fe910f7b32c946ab44 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Fri, 15 Mar 2024 17:30:24 +0100
Subject: [PATCH 3/8] Fix: `TypeError: (destructured parameter).data is
 undefined`

---
 .../components/timetable_management/TimetableManagement.vue     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index a8fd2659..0ad5f50e 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -198,7 +198,7 @@ export default defineComponent({
       skip() {
         return !this.readyForQueries;
       },
-      result({ data: { lessonObjects } }) {
+      result({ data: { lessonObjects = [] } = { lessonObjects: [] } } = { data: { lessonObjects: [] } }) {
         this.lessonsUsed = {};
         lessonObjects.forEach((lesson) => {
           let increment =
-- 
GitLab


From e59bcad1fb496760d96e8f2062773499f25c3240 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Fri, 15 Mar 2024 17:30:48 +0100
Subject: [PATCH 4/8] Fix loading of group select

---
 .../components/timetable_management/TimetableManagement.vue    | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index 0ad5f50e..071712e9 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -880,7 +880,8 @@ export default defineComponent({
             item-value="id"
             return-object
             v-model="selectedGroup"
-            :loading="$apollo.queries.gqlGroups"
+            :loading="$apollo.queries.groups.loading"
+            :disabled="!internalTimeGrid?.id"
             class="mr-4"
           />
         </div>
-- 
GitLab


From 25bbd55b82d8829f90834c01b6da10a84f038a2a Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Thu, 21 Mar 2024 13:15:44 +0100
Subject: [PATCH 5/8] Add property `isCurrent` to validityranges

---
 .../frontend/components/validity_range/validityRange.graphql  | 1 +
 aleksis/apps/lesrooster/models.py                             | 4 ++++
 aleksis/apps/lesrooster/schema/validity_range.py              | 1 +
 3 files changed, 6 insertions(+)

diff --git a/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql b/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql
index 7124e149..dbdabf83 100644
--- a/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql
+++ b/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql
@@ -122,6 +122,7 @@ query timeGrids($orderBy: [String], $filters: JSONString) {
     validityRange {
       id
       name
+      isCurrent
       dateStart
       dateEnd
     }
diff --git a/aleksis/apps/lesrooster/models.py b/aleksis/apps/lesrooster/models.py
index a87c4de4..56696a64 100644
--- a/aleksis/apps/lesrooster/models.py
+++ b/aleksis/apps/lesrooster/models.py
@@ -72,6 +72,10 @@ class ValidityRange(ExtensibleModel):
         """Get the currently active validity range."""
         return cls.get_current()
 
+    @property
+    def is_current(self) -> bool:
+        return self.date_start <= (today := timezone.now().date()) and self.date_end >= today
+
     def clean(self):
         """Ensure that there is only one validity range at each point of time."""
         if self.date_end < self.date_start:
diff --git a/aleksis/apps/lesrooster/schema/validity_range.py b/aleksis/apps/lesrooster/schema/validity_range.py
index a3238585..0e126cd0 100644
--- a/aleksis/apps/lesrooster/schema/validity_range.py
+++ b/aleksis/apps/lesrooster/schema/validity_range.py
@@ -18,6 +18,7 @@ from ..models import ValidityRange
 
 
 class ValidityRangeType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
+    is_current = graphene.Boolean()
     class Meta:
         model = ValidityRange
         fields = ("id", "school_term", "name", "date_start", "date_end", "status", "time_grids")
-- 
GitLab


From 28f242aad0e82a8e5fad5a68b6b2e92ca948a981 Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Thu, 21 Mar 2024 13:16:06 +0100
Subject: [PATCH 6/8] Correctly skip groups query

---
 .../components/timetable_management/TimetableManagement.vue     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index 071712e9..245efc12 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -106,7 +106,7 @@ export default defineComponent({
         };
       },
       skip() {
-        return this.internalTimeGrid === null;
+        return this.internalTimeGrid?.id == null;
       },
       result() {
         if (this.$route.params.group && this.groups) {
-- 
GitLab


From 598156ba511883e045c2a581c80d4675e4e1996a Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Thu, 21 Mar 2024 13:21:45 +0100
Subject: [PATCH 7/8] Allow timeGrid in TimetableManagement url to be empty

---
 .../timetable_management/TimetableManagement.vue | 16 ++++++++++++----
 aleksis/apps/lesrooster/frontend/index.js        |  2 +-
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index 245efc12..9c71795f 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -345,7 +345,10 @@ export default defineComponent({
   watch: {
     selectedGroup() {
       if (!this.selectedGroup) return;
-      if (this.selectedGroup.id != this.$route.params.group) {
+      if (
+        this.selectedGroup.id != this.$route.params.group ||
+        this.internalTimeGrid.id != this.$route.params.timeGrid
+      ) {
         // to be able to select a group, the timeGrid has to be set
         this.$router.push({
           name: "lesrooster.timetable_management",
@@ -839,12 +842,17 @@ export default defineComponent({
       }
     },
     setInitialTimeGrid(timeGrids) {
-      if (!this.internalTimeGrid?.id && this.$route.params.timeGrid) {
+      if (!this.internalTimeGrid?.id) {
         this.internalTimeGrid = timeGrids.find(
-          (timeGrid) => timeGrid.id === this.$route.params.timeGrid,
+          this.$route.params.timeGrid
+            ? (timeGrid) => timeGrid.id === this.$route.params.timeGrid
+            : (timeGrid) =>
+                timeGrid.validityRange.isCurrent &&
+                (!timeGrid.group ||
+                  timeGrid.group?.id === this.$route.params.group),
         );
       }
-    }
+    },
   },
 });
 </script>
diff --git a/aleksis/apps/lesrooster/frontend/index.js b/aleksis/apps/lesrooster/frontend/index.js
index 659fd6ae..2d0f870a 100644
--- a/aleksis/apps/lesrooster/frontend/index.js
+++ b/aleksis/apps/lesrooster/frontend/index.js
@@ -62,7 +62,7 @@ export default {
       },
       children: [
         {
-          path: ":timeGrid(\\d+)/:group(\\d+)/",
+          path: ":group(\\d+)/:timeGrid(\\d+)?/",
           component: () =>
             import("./components/timetable_management/TimetableManagement.vue"),
           name: "lesrooster.timetable_management",
-- 
GitLab


From d8fbe90799f9849c1d867d54cd21b979c879e29d Mon Sep 17 00:00:00 2001
From: Julian Leucker <leuckerj@gmail.com>
Date: Thu, 21 Mar 2024 13:28:21 +0100
Subject: [PATCH 8/8] Reformat

---
 .../timetable_management/TimetableManagement.vue    | 12 +++++++++++-
 aleksis/apps/lesrooster/schema/__init__.py          | 13 ++++++++++---
 aleksis/apps/lesrooster/schema/validity_range.py    |  1 +
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
index 9c71795f..04b3d430 100644
--- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
+++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue
@@ -198,7 +198,17 @@ export default defineComponent({
       skip() {
         return !this.readyForQueries;
       },
-      result({ data: { lessonObjects = [] } = { lessonObjects: [] } } = { data: { lessonObjects: [] } }) {
+      result(
+        {
+          data: { lessonObjects = [] } = {
+            lessonObjects: [],
+          },
+        } = {
+          data: {
+            lessonObjects: [],
+          },
+        },
+      ) {
         this.lessonsUsed = {};
         lessonObjects.forEach((lesson) => {
           let increment =
diff --git a/aleksis/apps/lesrooster/schema/__init__.py b/aleksis/apps/lesrooster/schema/__init__.py
index 81d89f23..faa91879 100644
--- a/aleksis/apps/lesrooster/schema/__init__.py
+++ b/aleksis/apps/lesrooster/schema/__init__.py
@@ -260,9 +260,12 @@ class Query(graphene.ObjectType):
             return []
 
         # This will fail if the ID is invalid, but won't, if it is empty
-        time_grid_obj: TimeGrid | None = TimeGrid.objects.get(pk=time_grid) if time_grid is not None else None
+        time_grid_obj: TimeGrid | None = (
+            TimeGrid.objects.get(pk=time_grid) if time_grid is not None else None
+        )
 
-        # If there is no time_grid, or it is a generic one, filter groups to have a fitting school_term
+        # If there is no time_grid, or it is a generic one, filter groups
+        # to have a fitting school_term
         if time_grid_obj is None or time_grid_obj.group is None:
             return (
                 Group.objects.filter(school_term__lr_validity_ranges__time_grids__id=time_grid)
@@ -273,7 +276,11 @@ class Query(graphene.ObjectType):
         group_id = time_grid_obj.group.pk
 
         return (
-            Group.objects.filter(Q(pk=group_id) | Q(parent_groups=group_id) | Q(parent_groups__parent_groups=group_id))
+            Group.objects.filter(
+                Q(pk=group_id)
+                | Q(parent_groups=group_id)
+                | Q(parent_groups__parent_groups=group_id)
+            )
             .distinct()
             .annotate(has_cg=Q(child_groups__isnull=False))
             .order_by("-has_cg", "name")
diff --git a/aleksis/apps/lesrooster/schema/validity_range.py b/aleksis/apps/lesrooster/schema/validity_range.py
index 0e126cd0..0d9a8bd3 100644
--- a/aleksis/apps/lesrooster/schema/validity_range.py
+++ b/aleksis/apps/lesrooster/schema/validity_range.py
@@ -19,6 +19,7 @@ from ..models import ValidityRange
 
 class ValidityRangeType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
     is_current = graphene.Boolean()
+
     class Meta:
         model = ValidityRange
         fields = ("id", "school_term", "name", "date_start", "date_end", "status", "time_grids")
-- 
GitLab