Use signals to update internal ics

This commit is contained in:
Antoine Bartuccio 2025-01-03 13:56:40 +01:00
parent a60e1f1fdc
commit 65df55a635
5 changed files with 53 additions and 26 deletions

View File

@ -1,14 +1,10 @@
from datetime import timedelta
from pathlib import Path from pathlib import Path
from django.conf import settings from django.conf import settings
from django.http import Http404 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 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 from core.views.files import send_raw_file
@ -33,23 +29,4 @@ class CalendarController(ControllerBase):
@route.get("/internal.ics") @route.get("/internal.ics")
def calendar_internal(self): def calendar_internal(self):
calendar = Calendar() return send_raw_file(IcsCalendar.get_internal())
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)

9
com/apps.py Normal file
View File

@ -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

View File

@ -37,6 +37,7 @@ from django.templatetags.static import static
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from ics import Calendar, Event
from club.models import Club from club.models import Club
from core.models import Notification, Preferences, User from core.models import Notification, Preferences, User
@ -46,6 +47,7 @@ from core.models import Notification, Preferences, User
class IcsCalendar: class IcsCalendar:
_CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" _CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars"
_EXTERNAL_CALENDAR = _CACHE_FOLDER / "external.ics" _EXTERNAL_CALENDAR = _CACHE_FOLDER / "external.ics"
_INTERNAL_CALENDAR = _CACHE_FOLDER / "internal.ics"
@classmethod @classmethod
def get_external(cls, expiration: timedelta = timedelta(hours=1)) -> Path | None: def get_external(cls, expiration: timedelta = timedelta(hours=1)) -> Path | None:
@ -74,6 +76,35 @@ class IcsCalendar:
_ = f.write(calendar.data) _ = f.write(calendar.data)
return cls._EXTERNAL_CALENDAR 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): class Sith(models.Model):
"""A one instance class storing all the modifiable infos.""" """A one instance class storing all the modifiable infos."""

9
com/signals.py Normal file
View File

@ -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()

View File

@ -46,7 +46,7 @@ from accounting.models import (
SimplifiedAccountingType, SimplifiedAccountingType,
) )
from club.models import Club, Membership 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.models import Group, Page, PageRev, SithFile, User
from core.utils import resize_image from core.utils import resize_image
from counter.models import Counter, Product, ProductType, StudentCard from counter.models import Counter, Product, ProductType, StudentCard
@ -764,6 +764,7 @@ Welcome to the wiki page!
] ]
) )
NewsDate.objects.bulk_create(news_dates) NewsDate.objects.bulk_create(news_dates)
IcsCalendar.make_internal() # Force refresh of the calendar after a bulk_create
# Create some data for pedagogy # Create some data for pedagogy