diff --git a/aleksis/apps/alsijil/managers.py b/aleksis/apps/alsijil/managers.py index 7350cf375a12205b7ebe56b9d5fd57bd045a2477..862e33a6d6a1cf0c8893359a65cb9a8ea6440da5 100644 --- a/aleksis/apps/alsijil/managers.py +++ b/aleksis/apps/alsijil/managers.py @@ -1,6 +1,13 @@ +from datetime import date, datetime +from typing import Optional, Sequence, Union + from django.db.models import QuerySet +from django.db.models.query import Prefetch from django.db.models.query_utils import Q +from calendarweek import CalendarWeek + +from aleksis.apps.chronos.managers import DateRangeQuerySetMixin from aleksis.core.managers import CurrentSiteManagerWithoutMigrations @@ -42,3 +49,51 @@ class LessonDocumentationQuerySet(QuerySet): def not_empty(self): """Get all not empty lesson documentations.""" return self.filter(~Q(topic="") | ~Q(group_note="") | ~Q(homework="")) + + +class GroupRoleManager(CurrentSiteManagerWithoutMigrations): + pass + + +class GroupRoleQuerySet(QuerySet): + def with_assignments( + self, time_ref: Union[date, CalendarWeek], groups: Sequence["Group"] + ) -> QuerySet: + from aleksis.apps.alsijil.models import GroupRoleAssignment + + if isinstance(time_ref, CalendarWeek): + qs = GroupRoleAssignment.objects.in_week(time_ref) + else: + qs = GroupRoleAssignment.objects.on_day(time_ref) + + qs = qs.for_groups(groups).distinct() + return self.prefetch_related(Prefetch("assignments", queryset=qs,)) + + +class GroupRoleAssignmentManager(CurrentSiteManagerWithoutMigrations): + pass + + +class GroupRoleAssignmentQuerySet(DateRangeQuerySetMixin, QuerySet): + def within_dates(self, start: date, end: date): + """Filter for all role assignments within a date range.""" + return self.filter( + Q(date_start__lte=end) & (Q(date_end__gte=start) | Q(date_end__isnull=True)) + ) + + def at_time(self, when: Optional[datetime] = None): + """Filter for role assignments assigned at a certain point in time.""" + now = when or datetime.now() + + return self.on_day(now.date()) + + def for_groups(self, groups: Sequence["Group"]): + """Filter all role assignments for a sequence of groups.""" + qs = self + for group in groups: + qs = qs.for_group(group) + return qs + + def for_group(self, group: "Group"): + """Filter all role assignments for a group.""" + return self.filter(Q(groups=group) | Q(groups__child_groups=group)) diff --git a/aleksis/apps/alsijil/models.py b/aleksis/apps/alsijil/models.py index 2335efe5f7e8b56a020198554dca9beaf0edaac5..e47e92a65846706ec0eb8e4d8d94689a941bb742 100644 --- a/aleksis/apps/alsijil/models.py +++ b/aleksis/apps/alsijil/models.py @@ -13,6 +13,10 @@ from aleksis.apps.alsijil.data_checks import ( PersonalNoteOnHolidaysDataCheck, ) from aleksis.apps.alsijil.managers import ( + GroupRoleAssignmentManager, + GroupRoleAssignmentQuerySet, + GroupRoleManager, + GroupRoleQuerySet, LessonDocumentationManager, LessonDocumentationQuerySet, PersonalNoteManager, @@ -242,6 +246,8 @@ class ExtraMark(ExtensibleModel): class GroupRole(ExtensibleModel): + objects = GroupRoleManager.from_queryset(GroupRoleQuerySet)() + name = models.CharField(max_length=255, verbose_name=_("Name")) icon = models.CharField(max_length=50, blank=True, choices=ICONS, verbose_name=_("Icon")) colour = models.CharField(max_length=50, blank=True, choices=COLOURS, verbose_name=_("Colour")) @@ -255,6 +261,8 @@ class GroupRole(ExtensibleModel): class GroupRoleAssignment(GroupPropertiesMixin, ExtensibleModel): + objects = GroupRoleAssignmentManager.from_queryset(GroupRoleAssignmentQuerySet)() + role = models.ForeignKey( GroupRole, on_delete=models.CASCADE, @@ -282,6 +290,13 @@ class GroupRoleAssignment(GroupPropertiesMixin, ExtensibleModel): date_end = date_format(self.date_end) if self.date_end else "?" return f"{self.role}: {self.person}, {date_format(self.date_start)}–{date_end}" + @property + def date_range(self) -> str: + if not self.date_end: + return f"{date_format(self.date_start)}–?" + else: + return f"{date_format(self.date_start)}–{date_format(self.date_end)}" + class Meta: verbose_name = _("Group role assignment") verbose_name_plural = _("Group role assignments") diff --git a/aleksis/apps/alsijil/views.py b/aleksis/apps/alsijil/views.py index 6519310b0657924e08aa94ab971acc7f5373d91a..f83f2cfac818b733c3724c2d358f3b20c26782f9 100644 --- a/aleksis/apps/alsijil/views.py +++ b/aleksis/apps/alsijil/views.py @@ -934,16 +934,7 @@ class AssignedGroupRolesView(PermissionRequiredMixin, DetailView): today = timezone.now().date() context["today"] = today - self.roles = GroupRole.objects.prefetch_related( - Prefetch( - "assignments", - queryset=GroupRoleAssignment.objects.filter( - Q(date_start__lte=today) & (Q(date_end__gte=today) | Q(date_end__isnull=True)) - ) - .filter(Q(groups=self.object) | Q(groups__child_groups=self.object)) - .distinct(), - ) - ) + self.roles = GroupRole.objects.with_assignments(today, [self.object]) context["roles"] = self.roles assignments = ( GroupRoleAssignment.objects.filter(