Sith/com/calendar.py

95 lines
3.2 KiB
Python
Raw Normal View History

2025-01-04 18:57:31 +01:00
from datetime import datetime, timedelta
from pathlib import Path
from typing import final
import requests
2025-01-04 19:24:40 +01:00
from dateutil.relativedelta import relativedelta
2025-01-04 18:57:31 +01:00
from django.conf import settings
2025-02-20 17:47:51 +01:00
from django.db.models import QuerySet
2025-01-04 18:57:31 +01:00
from django.urls import reverse
from django.utils import timezone
2025-01-04 19:24:40 +01:00
from ical.calendar import Calendar
from ical.calendar_stream import IcsCalendarStream
from ical.event import Event
2025-01-04 18:57:31 +01:00
from com.models import NewsDate
2025-02-20 17:47:51 +01:00
from core.models import User
2025-01-04 18:57:31 +01:00
@final
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:
if (
cls._EXTERNAL_CALENDAR.exists()
and timezone.make_aware(
datetime.fromtimestamp(cls._EXTERNAL_CALENDAR.stat().st_mtime)
)
+ expiration
> timezone.now()
):
return cls._EXTERNAL_CALENDAR
return cls.make_external()
@classmethod
def make_external(cls) -> Path | None:
calendar = requests.get(
"https://calendar.google.com/calendar/ical/ae.utbm%40gmail.com/public/basic.ics"
2025-01-04 18:57:31 +01:00
)
if not calendar.ok:
2025-01-04 18:57:31 +01:00
return None
cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True)
with open(cls._EXTERNAL_CALENDAR, "wb") as f:
_ = f.write(calendar.content)
2025-01-04 18:57:31 +01:00
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
2025-02-20 17:47:51 +01:00
# 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(
cls.ics_from_queryset(
NewsDate.objects.filter(
news__is_moderated=True,
end_date__gte=timezone.now() - (relativedelta(months=6)),
)
)
)
return cls._INTERNAL_CALENDAR
@classmethod
def get_unmoderated(cls, user: User) -> bytes:
return cls.ics_from_queryset(
NewsDate.objects.viewable_by(user).filter(
news__is_moderated=False,
end_date__gte=timezone.now() - (relativedelta(months=6)),
),
)
@classmethod
def ics_from_queryset(cls, queryset: QuerySet) -> bytes:
2025-01-04 18:57:31 +01:00
calendar = Calendar()
2025-02-20 17:47:51 +01:00
for news_date in queryset.prefetch_related("news"):
2025-01-04 18:57:31 +01:00
event = Event(
2025-01-04 19:24:40 +01:00
summary=news_date.news.title,
start=news_date.start_date,
2025-01-04 18:57:31 +01:00
end=news_date.end_date,
url=reverse("com:news_detail", kwargs={"news_id": news_date.news.id}),
)
2025-01-04 19:24:40 +01:00
calendar.events.append(event)
2025-01-04 18:57:31 +01:00
2025-02-20 17:47:51 +01:00
return IcsCalendarStream.calendar_to_ics(calendar).encode("utf-8")