90 lines
2.5 KiB
Python
90 lines
2.5 KiB
Python
import logging
|
|
from datetime import UTC, datetime, timedelta
|
|
from typing import cast
|
|
|
|
from asyncify import asyncify
|
|
from cachetools import cachedmethod
|
|
from caldav import Calendar, DAVClient, Event, Principal
|
|
from vobject.base import Component, toVName
|
|
|
|
from ..calevent import CalEvent
|
|
from ..config import Config
|
|
from ..settings import SETTINGS
|
|
from .helpers import REDIS, RedisCache, davkey
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class CalDAV:
|
|
_caldav_client = DAVClient(
|
|
url=SETTINGS.caldav.url,
|
|
username=SETTINGS.caldav.username,
|
|
password=SETTINGS.caldav.password,
|
|
)
|
|
|
|
_cache = RedisCache(
|
|
cache=REDIS,
|
|
ttl=SETTINGS.caldav.cache_ttl,
|
|
)
|
|
|
|
@classmethod
|
|
@property
|
|
def principal(cls) -> Principal:
|
|
"""
|
|
Gets the `Principal` object of the main CalDAV client.
|
|
"""
|
|
|
|
return cls._caldav_client.principal()
|
|
|
|
@classmethod
|
|
@property
|
|
@asyncify
|
|
@cachedmethod(cache=lambda cls: cls._cache, key=davkey("calendars"))
|
|
def calendars(cls) -> list[str]:
|
|
"""
|
|
Asynchroneously lists all calendars using the main WebDAV client.
|
|
"""
|
|
|
|
_logger.debug("calendars")
|
|
return [str(cal.name) for cal in cls.principal.calendars()]
|
|
|
|
@classmethod
|
|
@asyncify
|
|
@cachedmethod(cache=lambda cls: cls._cache, key=davkey("get_calendar"))
|
|
def get_calendar(cls, calendar_name: str) -> Calendar:
|
|
"""
|
|
Get a calendar by name using the CalDAV principal object.
|
|
"""
|
|
|
|
return cls.principal.calendar(calendar_name)
|
|
|
|
@classmethod
|
|
@asyncify
|
|
@cachedmethod(cache=lambda cls: cls._cache, key=davkey("get_events", slice(1, 2)))
|
|
def get_events(cls, calendar_name: str, cfg: Config) -> list[CalEvent]:
|
|
"""
|
|
Get a sorted list of events by CalDAV calendar name.
|
|
"""
|
|
|
|
_logger.info(f"downloading {calendar_name!r} ...")
|
|
|
|
dt_start = datetime.combine(
|
|
datetime.now(UTC).date(),
|
|
datetime.min.time(),
|
|
)
|
|
dt_end = dt_start + timedelta(days=cfg.calendar.future_days)
|
|
|
|
search_result = cls.principal.calendar(calendar_name).search(
|
|
start=dt_start,
|
|
end=dt_end,
|
|
expand=True,
|
|
comp_class=Event,
|
|
split_expanded=False,
|
|
)
|
|
|
|
vevents = []
|
|
for event in search_result:
|
|
vobject = cast(Component, event.vobject_instance)
|
|
vevents.extend(vobject.contents[toVName("vevent")])
|
|
|
|
return sorted(CalEvent.from_vevent(vevent) for vevent in vevents)
|