From 5f6910e5131b7f28f86c9ecdcc57fed8412ee3cd Mon Sep 17 00:00:00 2001 From: Jose Date: Wed, 8 Oct 2025 11:11:57 -0500 Subject: [PATCH] feat: Add calendar event data to transcript webhook payload (#689) * feat: add calendar event data to transcript webhook payload and implement get_by_id method * Update server/reflector/worker/webhook.py Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com> * Update server/reflector/worker/webhook.py Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com> * style: format conditional time fields with line breaks for better readability * docs: add calendar event fields to transcript.completed webhook payload schema --------- Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com> --- server/docs/webhook.md | 23 ++++++++++++++- server/reflector/db/calendar_events.py | 5 ++++ server/reflector/worker/webhook.py | 41 ++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/server/docs/webhook.md b/server/docs/webhook.md index 9fe88fb9..b103d655 100644 --- a/server/docs/webhook.md +++ b/server/docs/webhook.md @@ -14,7 +14,7 @@ Webhooks are configured at the room level with two fields: ### `transcript.completed` -Triggered when a transcript has been fully processed, including transcription, diarization, summarization, and topic detection. +Triggered when a transcript has been fully processed, including transcription, diarization, summarization, topic detection and calendar event integration. ### `test` @@ -128,6 +128,27 @@ This event includes a convenient URL for accessing the transcript: "room": { "id": "room-789", "name": "Product Team Room" + }, + "calendar_event": { + "id": "calendar-event-123", + "ics_uid": "event-123", + "title": "Q3 Product Planning Meeting", + "start_time": "2025-08-27T12:00:00Z", + "end_time": "2025-08-27T12:30:00Z", + "description": "Team discussed Q3 product roadmap, prioritizing mobile app features and API improvements.", + "location": "Conference Room 1", + "attendees": [ + { + "id": "participant-1", + "name": "John Doe", + "speaker": "Speaker 1" + }, + { + "id": "participant-2", + "name": "Jane Smith", + "speaker": "Speaker 2" + } + ] } } ``` diff --git a/server/reflector/db/calendar_events.py b/server/reflector/db/calendar_events.py index 4a88d126..3eddc3f1 100644 --- a/server/reflector/db/calendar_events.py +++ b/server/reflector/db/calendar_events.py @@ -104,6 +104,11 @@ class CalendarEventController: results = await get_database().fetch_all(query) return [CalendarEvent(**result) for result in results] + async def get_by_id(self, event_id: str) -> CalendarEvent | None: + query = calendar_events.select().where(calendar_events.c.id == event_id) + result = await get_database().fetch_one(query) + return CalendarEvent(**result) if result else None + async def get_by_ics_uid(self, room_id: str, ics_uid: str) -> CalendarEvent | None: query = calendar_events.select().where( sa.and_( diff --git a/server/reflector/worker/webhook.py b/server/reflector/worker/webhook.py index 64368b2e..57b294d8 100644 --- a/server/reflector/worker/webhook.py +++ b/server/reflector/worker/webhook.py @@ -11,6 +11,8 @@ import structlog from celery import shared_task from celery.utils.log import get_task_logger +from reflector.db.calendar_events import calendar_events_controller +from reflector.db.meetings import meetings_controller from reflector.db.rooms import rooms_controller from reflector.db.transcripts import transcripts_controller from reflector.pipelines.main_live_pipeline import asynctask @@ -84,6 +86,18 @@ async def send_transcript_webhook( } ) + # Fetch meeting and calendar event if they exist + calendar_event = None + try: + if transcript.meeting_id: + meeting = await meetings_controller.get_by_id(transcript.meeting_id) + if meeting and meeting.calendar_event_id: + calendar_event = await calendar_events_controller.get_by_id( + meeting.calendar_event_id + ) + except Exception as e: + logger.error("Error fetching meeting or calendar event", error=str(e)) + # Build webhook payload frontend_url = f"{settings.UI_BASE_URL}/transcripts/{transcript.id}" participants = [ @@ -116,6 +130,33 @@ async def send_transcript_webhook( }, } + # Always include calendar_event field, even if no event is present + payload_data["calendar_event"] = {} + + # Add calendar event data if present + if calendar_event: + calendar_data = { + "id": calendar_event.id, + "ics_uid": calendar_event.ics_uid, + "title": calendar_event.title, + "start_time": calendar_event.start_time.isoformat() + if calendar_event.start_time + else None, + "end_time": calendar_event.end_time.isoformat() + if calendar_event.end_time + else None, + } + + # Add optional fields only if they exist + if calendar_event.description: + calendar_data["description"] = calendar_event.description + if calendar_event.location: + calendar_data["location"] = calendar_event.location + if calendar_event.attendees: + calendar_data["attendees"] = calendar_event.attendees + + payload_data["calendar_event"] = calendar_data + # Convert to JSON payload_json = json.dumps(payload_data, separators=(",", ":")) payload_bytes = payload_json.encode("utf-8")