diff --git a/api/ovdashboard_api/dav_calendar.py b/api/ovdashboard_api/dav_calendar.py new file mode 100644 index 0000000..9376955 --- /dev/null +++ b/api/ovdashboard_api/dav_calendar.py @@ -0,0 +1,72 @@ +import logging +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import Iterator + +from caldav import Calendar +from pydantic import BaseModel +from vobject.base import VBase + +from . import caldav_principal, get_ttl_hash, run_in_executor, timed_alru_cache + +_logger = logging.getLogger(__name__) + + +class CalEvent(BaseModel): + summary: str + description: str + dtstart: datetime + dtend: datetime + + +@timed_alru_cache(maxsize=20) +async def _get_calendar( + calendar_name: str, +) -> Calendar: + + @run_in_executor + def _get_calendar_inner() -> Calendar: + _logger.info(f"updating {calendar_name}") + print(f"updating {calendar_name}") + + return caldav_principal().calendar(calendar_name) + + return await _get_calendar_inner() + + +@dataclass(frozen=True) +class DavCalendar: + calendar_name: str + + @property + async def calendar(self) -> Calendar: + return await _get_calendar( + ttl_hash=get_ttl_hash(20), + calendar_name=self.calendar_name, + ) + + @property + async def events(self) -> Iterator[CalEvent]: + + async def _find_vevents() -> Iterator[VBase]: + calendar = await self.calendar + + return ( + vevent + for event in calendar.date_search( + start=datetime.now(), + end=datetime.now() + timedelta(days=365), + expand=True, + ) + for vevent in event.vobject_instance.contents["vevent"] + ) + + return ( + CalEvent( + summary=vevent.summary.value, + description=vevent.description.value, + dtstart=vevent.dtstart.value, + dtend=vevent.dtend.value, + ) + for vevent in await _find_vevents() + ) diff --git a/api/ovdashboard_api/routers/calendar.py b/api/ovdashboard_api/routers/calendar.py index c63e7ec..d8edd3d 100644 --- a/api/ovdashboard_api/routers/calendar.py +++ b/api/ovdashboard_api/routers/calendar.py @@ -1,10 +1,8 @@ -from datetime import datetime, timedelta from typing import Iterator from fastapi import APIRouter, Depends -from pydantic import BaseModel -from .. import caldav_principal +from ..dav_calendar import CalEvent, DavCalendar from ._common import CalendarNameLister, PrefixFinder, PrefixUnique router = APIRouter(prefix="/calendar", tags=["calendar"]) @@ -28,30 +26,8 @@ async def find_calendars( return list(names) -class CalEvent(BaseModel): - summary: str - description: str - dtstart: datetime - dtend: datetime - - @router.get("/get/{prefix}", response_model=list[CalEvent]) async def get_calendar( name: Iterator[str] = Depends(_unique), ) -> list[CalEvent]: - calendar = caldav_principal().calendar(name=name) - - return ( - CalEvent( - summary=vevent.summary.value, - description=vevent.description.value, - dtstart=vevent.dtstart.value, - dtend=vevent.dtend.value, - ) - for event in calendar.date_search( - start=datetime.now(), - end=datetime.now() + timedelta(days=365), - expand=True, - ) - for vevent in event.vobject_instance.contents["vevent"] - ) + return list(await DavCalendar(name).events)