diff --git a/com/api.py b/com/api.py index 6aec227f..63a0e680 100644 --- a/com/api.py +++ b/com/api.py @@ -1,14 +1,10 @@ -from datetime import timedelta from pathlib import Path from django.conf import settings from django.http import Http404 -from django.urls import reverse -from django.utils import timezone -from ics import Calendar, Event from ninja_extra import ControllerBase, api_controller, route -from com.models import IcsCalendar, NewsDate +from com.models import IcsCalendar from core.views.files import send_raw_file @@ -33,23 +29,4 @@ class CalendarController(ControllerBase): @route.get("/internal.ics") def calendar_internal(self): - calendar = Calendar() - for news_date in NewsDate.objects.filter( - news__is_moderated=True, - end_date__gte=timezone.now() - - (timedelta(days=30) * 60), # Roughly get the last 6 months - ).prefetch_related("news"): - event = Event( - name=news_date.news.title, - begin=news_date.start_date, - end=news_date.end_date, - url=reverse("com:news_detail", kwargs={"news_id": news_date.news.id}), - ) - calendar.events.add(event) - - # Create a file so we can offload the download to the reverse proxy if available - file = self.CACHE_FOLDER / "internal.ics" - self.CACHE_FOLDER.mkdir(parents=True, exist_ok=True) - with open(file, "wb") as f: - _ = f.write(calendar.serialize().encode("utf-8")) - return send_raw_file(file) + return send_raw_file(IcsCalendar.get_internal()) diff --git a/com/apps.py b/com/apps.py new file mode 100644 index 00000000..0502c588 --- /dev/null +++ b/com/apps.py @@ -0,0 +1,9 @@ +from django.apps import AppConfig + + +class ComConfig(AppConfig): + name = "com" + verbose_name = "News and communication" + + def ready(self): + import com.signals # noqa F401 diff --git a/com/models.py b/com/models.py index 6ec3ce53..c7042a38 100644 --- a/com/models.py +++ b/com/models.py @@ -37,6 +37,7 @@ from django.templatetags.static import static from django.urls import reverse from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from ics import Calendar, Event from club.models import Club from core.models import Notification, Preferences, User @@ -46,6 +47,7 @@ from core.models import Notification, Preferences, User class IcsCalendar: _CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" _EXTERNAL_CALENDAR = _CACHE_FOLDER / "external.ics" + _INTERNAL_CALENDAR = _CACHE_FOLDER / "internal.ics" @classmethod def get_external(cls, expiration: timedelta = timedelta(hours=1)) -> Path | None: @@ -74,6 +76,35 @@ class IcsCalendar: _ = f.write(calendar.data) return cls._EXTERNAL_CALENDAR + @classmethod + def get_internal(cls) -> Path: + if not cls._INTERNAL_CALENDAR.exists(): + return cls.make_internal() + return cls._INTERNAL_CALENDAR + + @classmethod + def make_internal(cls) -> Path: + # Updated through a post_save signal on News in com.signals + calendar = Calendar() + for news_date in NewsDate.objects.filter( + news__is_moderated=True, + end_date__gte=timezone.now() + - (timedelta(days=30) * 60), # Roughly get the last 6 months + ).prefetch_related("news"): + event = Event( + name=news_date.news.title, + begin=news_date.start_date, + end=news_date.end_date, + url=reverse("com:news_detail", kwargs={"news_id": news_date.news.id}), + ) + calendar.events.add(event) + + # Create a file so we can offload the download to the reverse proxy if available + cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True) + with open(cls._INTERNAL_CALENDAR, "wb") as f: + _ = f.write(calendar.serialize().encode("utf-8")) + return cls._INTERNAL_CALENDAR + class Sith(models.Model): """A one instance class storing all the modifiable infos.""" diff --git a/com/signals.py b/com/signals.py new file mode 100644 index 00000000..b67a4131 --- /dev/null +++ b/com/signals.py @@ -0,0 +1,9 @@ +from django.db.models.base import post_save +from django.dispatch import receiver + +from com.models import IcsCalendar, News + + +@receiver(post_save, sender=News, dispatch_uid="update_internal_ics") +def update_internal_ics(*args, **kwargs): + _ = IcsCalendar.make_internal() diff --git a/core/management/commands/populate.py b/core/management/commands/populate.py index 222cc509..486f23cd 100644 --- a/core/management/commands/populate.py +++ b/core/management/commands/populate.py @@ -46,7 +46,7 @@ from accounting.models import ( SimplifiedAccountingType, ) from club.models import Club, Membership -from com.models import News, NewsDate, Sith, Weekmail +from com.models import IcsCalendar, News, NewsDate, Sith, Weekmail from core.models import Group, Page, PageRev, SithFile, User from core.utils import resize_image from counter.models import Counter, Product, ProductType, StudentCard @@ -764,6 +764,7 @@ Welcome to the wiki page! ] ) NewsDate.objects.bulk_create(news_dates) + IcsCalendar.make_internal() # Force refresh of the calendar after a bulk_create # Create some data for pedagogy