ovdashboard/api/ovdashboard_api/core/calevent.py

94 lines
2.3 KiB
Python
Raw Permalink Normal View History

"""
Definition of an asyncio compatible CalDAV calendar.
Caches events using `timed_alru_cache`.
"""
2023-10-26 16:21:07 +00:00
import functools
import logging
from datetime import UTC, datetime
from typing import Annotated, Self
from pydantic import AfterValidator, BaseModel, ConfigDict, StringConstraints
from vobject.base import Component
2023-10-26 16:21:07 +00:00
_logger = logging.getLogger(__name__)
type StrippedStr = Annotated[str, StringConstraints(strip_whitespace=True)]
def make_utc(v: datetime) -> datetime:
if v.tzinfo is None:
return v.replace(tzinfo=UTC)
return v
type UTCDateTime = Annotated[datetime, AfterValidator(make_utc)]
2023-10-26 16:21:07 +00:00
@functools.total_ordering
class CalEvent(BaseModel):
"""
A CalDAV calendar event.
Properties are to be named as in the EVENT component of
RFC5545 (iCalendar).
https://icalendar.org/iCalendar-RFC-5545/3-6-1-event-component.html
"""
model_config = ConfigDict(frozen=True)
summary: StrippedStr = ""
description: StrippedStr = ""
dtstart: UTCDateTime = datetime.now(UTC)
dtend: UTCDateTime = datetime.now(UTC)
def __lt__(self, other: Self) -> bool:
"""
Order Events by start time.
"""
return self.dtstart < other.dtstart
def __eq__(self, other: Self) -> bool:
"""
Compare all properties.
"""
return self.model_dump() == other.model_dump()
@classmethod
def from_vevent(cls, event: Component) -> Self:
"""
Create a CalEvent instance from a `VObject.VEvent` object.
"""
data = {}
keys = ("summary", "description", "dtstart", "dtend", "duration")
for key in keys:
try:
data[key] = event.contents[key][0].value # type: ignore
except KeyError:
pass
if "dtend" not in data:
data["dtend"] = data["dtstart"]
if "duration" in data:
try:
data["dtend"] += data["duration"]
except (ValueError, TypeError, AttributeError):
_logger.warn(
"Could not add duration %s to %s",
repr(data["duration"]),
repr(data["dtstart"]),
)
del data["duration"]
return cls.model_validate(data)