2023-10-26 20:42:26 +00:00
|
|
|
import functools
|
2023-10-18 16:55:54 +00:00
|
|
|
import logging
|
|
|
|
from datetime import datetime, timedelta
|
2023-10-26 17:04:26 +00:00
|
|
|
from typing import cast
|
2023-10-18 16:55:54 +00:00
|
|
|
|
|
|
|
from asyncify import asyncify
|
2023-10-26 20:42:26 +00:00
|
|
|
from cachetools import TTLCache, cachedmethod
|
2023-10-26 16:50:04 +00:00
|
|
|
from caldav import Calendar, DAVClient, Event, Principal
|
2023-10-26 17:04:26 +00:00
|
|
|
from vobject.base import Component, toVName
|
2023-10-18 16:55:54 +00:00
|
|
|
|
2023-11-09 11:13:48 +00:00
|
|
|
from ..calevent import CalEvent
|
|
|
|
from ..config import Config
|
|
|
|
from ..settings import SETTINGS
|
2023-10-26 20:42:26 +00:00
|
|
|
from .webdav import davkey
|
2023-10-18 16:55:54 +00:00
|
|
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class CalDAV:
|
|
|
|
_caldav_client = DAVClient(
|
|
|
|
url=SETTINGS.caldav.url,
|
|
|
|
username=SETTINGS.caldav.username,
|
|
|
|
password=SETTINGS.caldav.password,
|
|
|
|
)
|
|
|
|
|
2023-10-26 20:42:26 +00:00
|
|
|
_cache = TTLCache(
|
|
|
|
ttl=SETTINGS.caldav.cache_ttl,
|
|
|
|
maxsize=SETTINGS.caldav.cache_size,
|
|
|
|
)
|
|
|
|
|
2023-10-18 16:55:54 +00:00
|
|
|
@classmethod
|
2023-10-26 15:46:12 +00:00
|
|
|
@property
|
2023-10-18 16:55:54 +00:00
|
|
|
def principal(cls) -> Principal:
|
|
|
|
"""
|
|
|
|
Gets the `Principal` object of the main CalDAV client.
|
|
|
|
"""
|
|
|
|
|
|
|
|
return cls._caldav_client.principal()
|
|
|
|
|
2023-10-26 15:46:12 +00:00
|
|
|
@classmethod
|
2023-10-18 16:55:54 +00:00
|
|
|
@property
|
|
|
|
@asyncify
|
2023-10-26 20:42:26 +00:00
|
|
|
@cachedmethod(
|
2023-10-26 21:12:51 +00:00
|
|
|
cache=lambda cls: cls._cache,
|
2023-10-26 20:42:26 +00:00
|
|
|
key=functools.partial(davkey, "calendars"),
|
|
|
|
)
|
2023-10-18 16:55:54 +00:00
|
|
|
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()]
|
|
|
|
|
2023-10-26 16:04:03 +00:00
|
|
|
@classmethod
|
2023-10-18 16:55:54 +00:00
|
|
|
@asyncify
|
2023-10-26 20:42:26 +00:00
|
|
|
@cachedmethod(
|
2023-10-26 21:12:51 +00:00
|
|
|
cache=lambda cls: cls._cache,
|
2023-10-26 20:42:26 +00:00
|
|
|
key=functools.partial(davkey, "get_calendar"),
|
|
|
|
)
|
2023-10-18 16:55:54 +00:00
|
|
|
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
|
2023-10-26 16:04:03 +00:00
|
|
|
@asyncify
|
2023-10-26 20:42:26 +00:00
|
|
|
@cachedmethod(
|
2023-10-26 21:12:51 +00:00
|
|
|
cache=lambda cls: cls._cache,
|
2023-10-26 20:42:26 +00:00
|
|
|
key=functools.partial(davkey, "get_events"),
|
|
|
|
)
|
2023-10-18 16:55:54 +00:00
|
|
|
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} ...")
|
|
|
|
|
2023-10-26 17:04:26 +00:00
|
|
|
dt_start = datetime.combine(
|
2023-10-26 22:22:01 +00:00
|
|
|
datetime.now().date(),
|
2023-10-26 17:04:26 +00:00
|
|
|
datetime.min.time(),
|
|
|
|
)
|
|
|
|
dt_end = dt_start + timedelta(days=cfg.calendar.future_days)
|
2023-10-18 16:55:54 +00:00
|
|
|
|
2023-10-26 17:04:26 +00:00
|
|
|
search_result = cls.principal.calendar(calendar_name).search(
|
2023-10-26 16:50:04 +00:00
|
|
|
start=dt_start,
|
|
|
|
end=dt_end,
|
|
|
|
expand=True,
|
|
|
|
comp_class=Event,
|
|
|
|
split_expanded=False,
|
|
|
|
)
|
2023-10-18 16:55:54 +00:00
|
|
|
|
|
|
|
vevents = []
|
|
|
|
for event in search_result:
|
2023-10-26 17:04:26 +00:00
|
|
|
vobject = cast(Component, event.vobject_instance)
|
|
|
|
vevents.extend(vobject.contents[toVName("vevent")])
|
2023-10-18 16:55:54 +00:00
|
|
|
|
|
|
|
return sorted(CalEvent.from_vevent(vevent) for vevent in vevents)
|