From b547f47f5438f3c25031a484219c25f26ffedbee Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Wed, 25 Mar 2026 12:48:40 -0400 Subject: [PATCH] feat(calendar): update Event model and patch caldav.create_event with uid param Co-Authored-By: Claude Sonnet 4.6 --- src/fabledassistant/models/event.py | 9 +++++++-- src/fabledassistant/services/caldav.py | 6 ++++++ tests/test_events_model.py | 26 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 tests/test_events_model.py diff --git a/src/fabledassistant/models/event.py b/src/fabledassistant/models/event.py index d6d6282..d8b4473 100644 --- a/src/fabledassistant/models/event.py +++ b/src/fabledassistant/models/event.py @@ -24,6 +24,8 @@ class Event(Base): all_day: Mapped[bool] = mapped_column(Boolean, default=False) description: Mapped[str] = mapped_column(Text, default="") location: Mapped[str] = mapped_column(Text, default="") + caldav_uid: Mapped[str] = mapped_column(Text, default="") + color: Mapped[str] = mapped_column(Text, default="") recurrence: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column( DateTime(timezone=True), default=lambda: datetime.now(timezone.utc) @@ -37,7 +39,9 @@ class Event(Base): def to_dict(self) -> dict: return { "id": self.id, + "user_id": self.user_id, "uid": self.uid, + "caldav_uid": self.caldav_uid, "project_id": self.project_id, "title": self.title, "start_dt": self.start_dt.isoformat() if self.start_dt else None, @@ -45,7 +49,8 @@ class Event(Base): "all_day": self.all_day, "description": self.description, "location": self.location, + "color": self.color, "recurrence": self.recurrence, - "created_at": self.created_at.isoformat(), - "updated_at": self.updated_at.isoformat(), + "created_at": self.created_at.isoformat() if self.created_at else None, + "updated_at": self.updated_at.isoformat() if self.updated_at else None, } diff --git a/src/fabledassistant/services/caldav.py b/src/fabledassistant/services/caldav.py index 69afd1b..12a0ec7 100644 --- a/src/fabledassistant/services/caldav.py +++ b/src/fabledassistant/services/caldav.py @@ -176,6 +176,7 @@ async def create_event( reminder_minutes: int | None = None, attendees: list[str] | None = None, calendar_name: str | None = None, + uid: str | None = None, ) -> dict: """Create a calendar event. @@ -193,6 +194,11 @@ async def create_event( cal.add("version", "2.0") event = icalendar.Event() + if uid: + # Remove auto-generated UID if the library added one, then inject ours + if "UID" in event: + del event["UID"] + event.add("uid", uid) event.add("summary", title) if all_day: diff --git a/tests/test_events_model.py b/tests/test_events_model.py new file mode 100644 index 0000000..6b981a8 --- /dev/null +++ b/tests/test_events_model.py @@ -0,0 +1,26 @@ +def test_event_model_has_new_columns(): + from fabledassistant.models.event import Event + cols = {c.key for c in Event.__table__.columns} + assert "caldav_uid" in cols + assert "color" in cols + + +def test_event_to_dict_includes_new_fields(): + from fabledassistant.models.event import Event + from datetime import datetime, timezone + e = Event( + user_id=1, uid="test-uid", title="Test", + start_dt=datetime(2026, 3, 25, 10, 0, tzinfo=timezone.utc), + caldav_uid="sync-uid", color="#6366f1", + ) + d = e.to_dict() + assert d["caldav_uid"] == "sync-uid" + assert d["color"] == "#6366f1" + + +def test_caldav_create_event_accepts_uid_param(): + """caldav.create_event signature must accept an optional uid param.""" + import inspect + from fabledassistant.services.caldav import create_event + sig = inspect.signature(create_event) + assert "uid" in sig.parameters