ovdashboard/api/ovdashboard_api/dav_calendar.py

136 lines
3.2 KiB
Python
Raw Normal View History

2022-09-04 18:48:46 +00:00
import logging
from dataclasses import dataclass
from datetime import datetime, timedelta
2022-09-04 20:59:45 +00:00
from functools import total_ordering
2022-09-04 18:48:46 +00:00
from typing import Iterator
from caldav import Calendar
2022-09-04 21:41:40 +00:00
from caldav.lib.error import ReportError
from pydantic import BaseModel, validator
2022-09-04 20:50:11 +00:00
from vobject.icalendar import VEvent
2022-09-04 18:48:46 +00:00
2022-09-04 22:30:40 +00:00
from .async_helpers import get_ttl_hash, run_in_executor, timed_alru_cache
from .dav_common import caldav_principal
2022-09-04 18:48:46 +00:00
_logger = logging.getLogger(__name__)
2022-09-04 21:41:40 +00:00
def _string_strip(in_str: str) -> str:
return in_str.strip()
2022-09-04 20:59:45 +00:00
@total_ordering
2022-09-04 18:48:46 +00:00
class CalEvent(BaseModel):
2022-09-04 21:41:40 +00:00
summary: str = ""
description: str = ""
dtstart: datetime = datetime.utcnow()
dtend: datetime = datetime.utcnow()
class Config:
frozen = True
2022-09-04 18:48:46 +00:00
2022-09-04 20:59:45 +00:00
def __lt__(self, other: "CalEvent") -> bool:
return self.dtstart < other.dtstart
def __eq__(self, other: "CalEvent") -> bool:
return self.dict() == other.dict()
2022-09-04 21:41:40 +00:00
_validate_summary = validator(
"summary",
allow_reuse=True,
)(_string_strip)
_validate_description = validator(
"description",
allow_reuse=True,
)(_string_strip)
@classmethod
def from_vevent(cls, event: VEvent) -> "CalEvent":
data = {}
for key in cls().dict().keys():
try:
data[key] = event.contents[key][0].value
except KeyError:
pass
return cls.parse_obj(data)
2022-09-04 18:48:46 +00:00
@timed_alru_cache(maxsize=20)
async def _get_calendar(
calendar_name: str,
) -> Calendar:
@run_in_executor
2022-09-04 20:50:24 +00:00
def _inner() -> Calendar:
2022-09-04 20:50:11 +00:00
return caldav_principal().calendar(calendar_name)
2022-09-04 20:50:24 +00:00
return await _inner()
2022-09-04 20:50:11 +00:00
@timed_alru_cache(maxsize=20)
async def _get_calendar_events(
calendar_name: str,
) -> list[CalEvent]:
@run_in_executor
def _inner() -> Iterator[VEvent]:
2022-09-04 23:25:40 +00:00
_logger.info(f"updating {calendar_name!r} ...")
2022-09-04 18:48:46 +00:00
2022-09-04 20:50:11 +00:00
calendar = caldav_principal().calendar(calendar_name)
2022-09-04 21:41:40 +00:00
date_start = datetime.utcnow().date()
time_min = datetime.min.time()
dt_start = datetime.combine(date_start, time_min)
dt_end = dt_start + timedelta(days=365)
try:
search_result = calendar.date_search(
start=dt_start,
end=dt_end,
2022-09-04 20:50:11 +00:00
expand=True,
2022-09-04 21:41:40 +00:00
verify_expand=True,
)
except ReportError:
_logger.warn("CalDAV server does not support expanded search")
search_result = calendar.date_search(
start=dt_start,
end=dt_end,
expand=False,
2022-09-04 20:50:11 +00:00
)
2022-09-04 21:41:40 +00:00
return (
vevent
for event in search_result
2022-09-04 20:50:11 +00:00
for vevent in event.vobject_instance.contents["vevent"]
)
2022-09-04 18:48:46 +00:00
2022-09-04 20:59:45 +00:00
return sorted([
2022-09-04 21:41:40 +00:00
CalEvent.from_vevent(vevent)
2022-09-04 20:50:11 +00:00
for vevent in await _inner()
2022-09-04 20:59:45 +00:00
])
2022-09-04 18:48:46 +00:00
@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
2022-09-04 20:50:11 +00:00
async def events(self) -> list[CalEvent]:
return await _get_calendar_events(
ttl_hash=get_ttl_hash(20),
calendar_name=self.calendar_name,
2022-09-04 18:48:46 +00:00
)