From 77537a84c25111c90195d516232e91dabecc25c2 Mon Sep 17 00:00:00 2001 From: Thomas Girod Date: Sun, 6 Apr 2025 17:12:00 +0200 Subject: [PATCH] remove external calendar --- com/api.py | 21 +---- com/ics_calendar.py | 29 ------- .../com/components/ics-calendar-index.ts | 9 --- com/tests/test_api.py | 77 +------------------ 4 files changed, 2 insertions(+), 134 deletions(-) diff --git a/com/api.py b/com/api.py index 6de78a3c..9dd70606 100644 --- a/com/api.py +++ b/com/api.py @@ -1,8 +1,6 @@ -from pathlib import Path from typing import Literal -from django.conf import settings -from django.http import Http404, HttpResponse +from django.http import HttpResponse from ninja import Query from ninja_extra import ControllerBase, api_controller, paginate, route from ninja_extra.pagination import PageNumberPaginationExtra @@ -18,23 +16,6 @@ from core.views.files import send_raw_file @api_controller("/calendar") class CalendarController(ControllerBase): - CACHE_FOLDER: Path = settings.MEDIA_ROOT / "com" / "calendars" - - @route.get("/external.ics", url_name="calendar_external") - def calendar_external(self): - """Return the ICS file of the AE Google Calendar - - Because of Google's cors rules, we can't just do a request to google ics - from the frontend. Google is blocking CORS request in its responses headers. - The only way to do it from the frontend is to use Google Calendar API with an API key - This is not especially desirable as your API key is going to be provided to the frontend. - - This is why we have this backend based solution. - """ - if (calendar := IcsCalendar.get_external()) is not None: - return send_raw_file(calendar) - raise Http404 - @route.get("/internal.ics", url_name="calendar_internal") def calendar_internal(self): return send_raw_file(IcsCalendar.get_internal()) diff --git a/com/ics_calendar.py b/com/ics_calendar.py index 1c95a2b3..e5324c8a 100644 --- a/com/ics_calendar.py +++ b/com/ics_calendar.py @@ -1,8 +1,6 @@ -from datetime import datetime, timedelta from pathlib import Path from typing import final -import requests from dateutil.relativedelta import relativedelta from django.conf import settings from django.db.models import F, QuerySet @@ -19,35 +17,8 @@ from core.models import User @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" - ) - if not calendar.ok: - return None - - cls._CACHE_FOLDER.mkdir(parents=True, exist_ok=True) - with open(cls._EXTERNAL_CALENDAR, "wb") as f: - _ = f.write(calendar.content) - return cls._EXTERNAL_CALENDAR - @classmethod def get_internal(cls) -> Path: if not cls._INTERNAL_CALENDAR.exists(): diff --git a/com/static/bundled/com/components/ics-calendar-index.ts b/com/static/bundled/com/components/ics-calendar-index.ts index 0b4976b0..d8fc79d7 100644 --- a/com/static/bundled/com/components/ics-calendar-index.ts +++ b/com/static/bundled/com/components/ics-calendar-index.ts @@ -8,7 +8,6 @@ import dayGridPlugin from "@fullcalendar/daygrid"; import iCalendarPlugin from "@fullcalendar/icalendar"; import listPlugin from "@fullcalendar/list"; import { - calendarCalendarExternal, calendarCalendarInternal, calendarCalendarUnpublished, newsDeleteNews, @@ -151,11 +150,6 @@ export class IcsCalendar extends inheritHtmlElement("div") { format: "ics", className: "internal", }, - { - url: `${await makeUrl(calendarCalendarExternal)}${cacheInvalidate}`, - format: "ics", - className: "external", - }, { url: `${await makeUrl(calendarCalendarUnpublished)}${cacheInvalidate}`, format: "ics", @@ -224,9 +218,6 @@ export class IcsCalendar extends inheritHtmlElement("div") { }; const makePopupTools = (event: EventImpl) => { - if (event.source.internalEventSource.ui.classNames.includes("external")) { - return null; - } if (!(this.canDelete || this.canModerate)) { return null; } diff --git a/com/tests/test_api.py b/com/tests/test_api.py index 7c3bcb7b..bfb7bb94 100644 --- a/com/tests/test_api.py +++ b/com/tests/test_api.py @@ -1,8 +1,6 @@ from dataclasses import dataclass -from datetime import datetime, timedelta +from datetime import timedelta from pathlib import Path -from typing import Callable -from unittest.mock import MagicMock, patch from urllib.parse import quote import pytest @@ -11,7 +9,6 @@ from django.contrib.auth.models import Permission from django.http import HttpResponse from django.test import Client, TestCase from django.urls import reverse -from django.utils import timezone from django.utils.timezone import now from model_bakery import baker, seq from pytest_django.asserts import assertNumQueries @@ -41,78 +38,6 @@ def accel_redirect_to_file(response: HttpResponse) -> Path | None: ) -@pytest.mark.django_db -class TestExternalCalendar: - @pytest.fixture - def mock_request(self): - mock = MagicMock() - with patch("requests.get", mock): - yield mock - - @pytest.fixture - def mock_current_time(self): - mock = MagicMock() - original = timezone.now - with patch("django.utils.timezone.now", mock): - yield mock, original - - @pytest.fixture(autouse=True) - def clear_cache(self): - IcsCalendar._EXTERNAL_CALENDAR.unlink(missing_ok=True) - - def test_fetch_error(self, client: Client, mock_request: MagicMock): - mock_request.return_value = MockResponse(ok=False, value="not allowed") - assert client.get(reverse("api:calendar_external")).status_code == 404 - - def test_fetch_success(self, client: Client, mock_request: MagicMock): - external_response = MockResponse(ok=True, value="Definitely an ICS") - mock_request.return_value = external_response - response = client.get(reverse("api:calendar_external")) - assert response.status_code == 200 - out_file = accel_redirect_to_file(response) - assert out_file is not None - assert out_file.exists() - with open(out_file, "r") as f: - assert f.read() == external_response.value - - def test_fetch_caching( - self, - client: Client, - mock_request: MagicMock, - mock_current_time: tuple[MagicMock, Callable[[], datetime]], - ): - fake_current_time, original_timezone = mock_current_time - start_time = original_timezone() - - fake_current_time.return_value = start_time - external_response = MockResponse(200, "Definitely an ICS") - mock_request.return_value = external_response - - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - mock_request.return_value = MockResponse(200, "This should be ignored") - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - mock_request.assert_called_once() - - fake_current_time.return_value = start_time + timedelta(hours=1, seconds=1) - external_response = MockResponse(200, "This won't be ignored") - mock_request.return_value = external_response - - with open( - accel_redirect_to_file(client.get(reverse("api:calendar_external"))), "r" - ) as f: - assert f.read() == external_response.value - - assert mock_request.call_count == 2 - - @pytest.mark.django_db class TestInternalCalendar: @pytest.fixture(autouse=True)