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