From 2cf8863bf0b00a9472070c994fd49a32a957629b Mon Sep 17 00:00:00 2001 From: magicfelix <felix@felix-zauberer.de> Date: Fri, 4 Apr 2025 13:23:47 +0200 Subject: [PATCH] Implement calendar-based AnnouncementManager --- aleksis/core/managers.py | 81 ++++++++++++++++++++++++++++++++++++++-- aleksis/core/models.py | 59 ++--------------------------- 2 files changed, 81 insertions(+), 59 deletions(-) diff --git a/aleksis/core/managers.py b/aleksis/core/managers.py index 7fc89a5a7..5ae52e070 100644 --- a/aleksis/core/managers.py +++ b/aleksis/core/managers.py @@ -1,9 +1,11 @@ -from datetime import date -from typing import TYPE_CHECKING, Union +from datetime import date, datetime +from typing import TYPE_CHECKING, Optional, Union from django.apps import apps -from django.db.models import BooleanField, Case, QuerySet, Value, When +from django.contrib.contenttypes.models import ContentType +from django.db.models import BooleanField, Case, Model, QuerySet, Value, When from django.db.models.manager import Manager +from django.utils import timezone from calendarweek import CalendarWeek from django_cte.cte import CTEQuerySet @@ -225,3 +227,76 @@ class HolidayQuerySet(DateRangeQuerySetMixin, CalendarEventQuerySet): class HolidayManager(CalendarEventManager): queryset_class = HolidayQuerySet + + +class AnnouncementQuerySet(CalendarEventQuerySet): + """Queryset for announcements providing time-based utility functions.""" + + def relevant_for(self, obj: Union[Model, QuerySet]) -> QuerySet: + """Get all relevant announcements. + + Get a QuerySet with all announcements relevant for a certain Model (e.g. a Group) + or a set of models in a QuerySet. + """ + if isinstance(obj, QuerySet): + ct = ContentType.objects.get_for_model(obj.model) + pks = list(obj.values_list("pk", flat=True)) + else: + ct = ContentType.objects.get_for_model(obj) + pks = [obj.pk] + + return self.filter(recipients__content_type=ct, recipients__recipient_id__in=pks) + + def at_time(self, when: Optional[datetime] = None) -> QuerySet: + """Get all announcements at a certain time.""" + when = when or timezone.now() + + # Get announcements by time + announcements = self.filter(datetime_start__lte=when, datetime_end__gte=when) + + return announcements + + def on_date(self, when: Optional[date] = None) -> QuerySet: + """Get all announcements at a certain date.""" + when = when or timezone.now().date() + + # Get announcements by time + announcements = self.filter(datetime_start__date__lte=when, datetime_end__date__gte=when) + + return announcements + + def within_days(self, start: date, stop: date) -> QuerySet: + """Get all announcements valid for a set of days.""" + # Get announcements + announcements = self.filter(datetime_start__date__lte=stop, datetime_end__date__gte=start) + + return announcements + + def for_person(self, person: "Person") -> list: # noqa: F821 + """Get all announcements for one person.""" + # Filter by person + announcements_for_person = [] + for announcement in self: + if person in announcement.recipient_persons: + announcements_for_person.append(announcement) + + return announcements_for_person + + +class AnnouncementManager(CalendarEventManager): + queryset_class = AnnouncementQuerySet + + def relevant_for(self, obj: Union[Model, QuerySet]) -> QuerySet: + return self.get_queryset().relevant_for(obj) + + def at_time(self, when: Optional[datetime] = None) -> QuerySet: + return self.get_queryset().at_time(when) + + def on_date(self, when: Optional[date] = None) -> QuerySet: + return self.get_queryset().on_date(when) + + def within_days(self, start: date, stop: date) -> QuerySet: + return self.get_queryset().within_days(start, stop) + + def for_person(self, person: "Person") -> list: # noqa: F821 + return self.get_queryset().for_person(person) diff --git a/aleksis/core/models.py b/aleksis/core/models.py index f32545c04..c50d5aa6e 100644 --- a/aleksis/core/models.py +++ b/aleksis/core/models.py @@ -65,6 +65,7 @@ from aleksis.core.data_checks import ( from .managers import ( AlekSISBaseManagerWithoutMigrations, + AnnouncementManager, CalendarEventManager, CalendarEventMixinManager, GroupManager, @@ -87,7 +88,7 @@ from .mixins import ( SchoolTermRelatedExtensibleModel, ) from .tasks import send_notification -from .util.core_helpers import generate_random_code, get_site_preferences, now_tomorrow +from .util.core_helpers import generate_random_code, get_site_preferences from .util.email import send_email from .util.model_helpers import ICONS @@ -960,60 +961,6 @@ class Notification(ExtensibleModel, TimeStampedModel): ] -class AnnouncementQuerySet(models.QuerySet): - """Queryset for announcements providing time-based utility functions.""" - - def relevant_for(self, obj: Union[models.Model, models.QuerySet]) -> models.QuerySet: - """Get all relevant announcements. - - Get a QuerySet with all announcements relevant for a certain Model (e.g. a Group) - or a set of models in a QuerySet. - """ - if isinstance(obj, models.QuerySet): - ct = ContentType.objects.get_for_model(obj.model) - pks = list(obj.values_list("pk", flat=True)) - else: - ct = ContentType.objects.get_for_model(obj) - pks = [obj.pk] - - return self.filter(recipients__content_type=ct, recipients__recipient_id__in=pks) - - def at_time(self, when: Optional[datetime] = None) -> models.QuerySet: - """Get all announcements at a certain time.""" - when = when or timezone.now() - - # Get announcements by time - announcements = self.filter(valid_from__lte=when, valid_until__gte=when) - - return announcements - - def on_date(self, when: Optional[date] = None) -> models.QuerySet: - """Get all announcements at a certain date.""" - when = when or timezone.now().date() - - # Get announcements by time - announcements = self.filter(valid_from__date__lte=when, valid_until__date__gte=when) - - return announcements - - def within_days(self, start: date, stop: date) -> models.QuerySet: - """Get all announcements valid for a set of days.""" - # Get announcements - announcements = self.filter(valid_from__date__lte=stop, valid_until__date__gte=start) - - return announcements - - def for_person(self, person: Person) -> list: - """Get all announcements for one person.""" - # Filter by person - announcements_for_person = [] - for announcement in self: - if person in announcement.recipient_persons: - announcements_for_person.append(announcement) - - return announcements_for_person - - class DashboardWidget(RegistryObject, PolymorphicModel, PureDjangoModel): """Base class for dashboard widgets on the index page.""" @@ -1907,7 +1854,7 @@ class Announcement(CalendarEvent): _class_name = "announcement" dav_verbose_name = "Announcements" - objects = models.Manager.from_queryset(AnnouncementQuerySet)() + objects = AnnouncementManager() title = models.CharField(max_length=150, verbose_name=_("Title")) description = models.TextField(max_length=500, verbose_name=_("Description"), blank=True) -- GitLab