2023-10-18 16:55:54 +00:00
|
|
|
"""
|
|
|
|
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
|
2023-10-18 16:55:54 +00:00
|
|
|
from datetime import datetime
|
|
|
|
from typing import Annotated, Self
|
|
|
|
|
2023-10-19 11:54:39 +00:00
|
|
|
from pydantic import BaseModel, ConfigDict, StringConstraints
|
2023-10-18 16:55:54 +00:00
|
|
|
from vobject.base import Component
|
|
|
|
|
2023-10-26 16:21:07 +00:00
|
|
|
_logger = logging.getLogger(__name__)
|
2023-10-19 11:54:39 +00:00
|
|
|
type StrippedStr = Annotated[str, StringConstraints(strip_whitespace=True)]
|
2023-10-18 16:55:54 +00:00
|
|
|
|
|
|
|
|
2023-10-26 16:21:07 +00:00
|
|
|
@functools.total_ordering
|
2023-10-18 16:55:54 +00:00
|
|
|
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: datetime = datetime.utcnow()
|
|
|
|
dtend: datetime = datetime.utcnow()
|
|
|
|
|
|
|
|
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)
|