diff --git a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue index 1fbc0d46d7844cf143a92476de933c30f9ff3d7b..04b3d430ad15b635937530764d145a9a494c4f76 100644 --- a/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue +++ b/aleksis/apps/lesrooster/frontend/components/timetable_management/TimetableManagement.vue @@ -106,12 +106,12 @@ export default defineComponent({ }; }, skip() { - return this.internalTimeGrid === null; + return this.internalTimeGrid?.id == 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, ); } }, @@ -198,7 +198,17 @@ export default defineComponent({ skip() { return !this.readyForQueries; }, - result({ data: { lessonObjects } }) { + result( + { + data: { lessonObjects = [] } = { + lessonObjects: [], + }, + } = { + data: { + lessonObjects: [], + }, + }, + ) { this.lessonsUsed = {}; lessonObjects.forEach((lesson) => { let increment = @@ -345,10 +355,17 @@ 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 || + 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", - params: { id: this.selectedGroup.id }, + params: { + group: this.selectedGroup.id, + timeGrid: this.internalTimeGrid.id, + }, }); } this.$setToolBarTitle( @@ -359,6 +376,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 +851,18 @@ export default defineComponent({ this.draggedItem = null; } }, + setInitialTimeGrid(timeGrids) { + if (!this.internalTimeGrid?.id) { + this.internalTimeGrid = timeGrids.find( + 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> @@ -837,6 +879,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 @@ -847,17 +898,10 @@ 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" /> - - <time-grid-field - outlined - filled - label="Select Validity Range" - hide-details - v-model="internalTimeGrid" - /> </div> </v-col> diff --git a/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql b/aleksis/apps/lesrooster/frontend/components/validity_range/validityRange.graphql index 7124e1491f40ef9638f84e984f72f287fb04fcb1..dbdabf8395a23b206aeb65ef04a46c096842023d 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/frontend/index.js b/aleksis/apps/lesrooster/frontend/index.js index 5f072d5659068ce601a3a287c27a2d5b68ae7dbc..2d0f870a10d00d55981769ca021f4a22bfab80ec 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: ":group(\\d+)/:timeGrid(\\d+)?/", component: () => import("./components/timetable_management/TimetableManagement.vue"), name: "lesrooster.timetable_management", diff --git a/aleksis/apps/lesrooster/models.py b/aleksis/apps/lesrooster/models.py index a87c4de45a88fa564ca23f591132ea415bb84e48..56696a640117ac4cb01d091a335299e213197e43 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/__init__.py b/aleksis/apps/lesrooster/schema/__init__.py index d27e9288e94c596457496694b6955ba80cd74608..faa9187929c28b840ff1c10d7fbc4b0a85490442 100644 --- a/aleksis/apps/lesrooster/schema/__init__.py +++ b/aleksis/apps/lesrooster/schema/__init__.py @@ -259,8 +259,29 @@ 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") ) diff --git a/aleksis/apps/lesrooster/schema/validity_range.py b/aleksis/apps/lesrooster/schema/validity_range.py index a323858535e6fb3bc355bc3f4ca42b6f7a55aaed..0d9a8bd3a36069352af3daec60440ae162be8a12 100644 --- a/aleksis/apps/lesrooster/schema/validity_range.py +++ b/aleksis/apps/lesrooster/schema/validity_range.py @@ -18,6 +18,8 @@ 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")