feat: enhance Google Calendar event update functionality

- Introduced helper functions `_is_date_only` and `_build_time_body` to streamline the construction of event start and end times for all-day and timed events.
- Refactored the `create_update_calendar_event_tool` to utilize the new helper functions, improving code readability and maintainability.
- Updated the Google Calendar sync service to ensure proper handling of calendar IDs with a default fallback to "primary".
- Modified the ApprovalCard component to simplify the construction of event update arguments, enhancing clarity and reducing redundancy.
This commit is contained in:
Anish Sarkar 2026-03-25 20:35:23 +05:30
parent c3d5c865fd
commit bbd5ee8a19
4 changed files with 55 additions and 31 deletions

View file

@ -14,6 +14,20 @@ from app.services.google_calendar import GoogleCalendarToolMetadataService
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _is_date_only(value: str) -> bool:
"""Return True when *value* looks like a bare date (YYYY-MM-DD) with no time component."""
return len(value) <= 10 and "T" not in value
def _build_time_body(value: str, context: dict[str, Any] | Any) -> dict[str, str]:
"""Build a Google Calendar start/end body using ``date`` for all-day
events and ``dateTime`` for timed events."""
if _is_date_only(value):
return {"date": value}
tz = context.get("timezone", "UTC") if isinstance(context, dict) else "UTC"
return {"dateTime": value, "timeZone": tz}
def create_update_calendar_event_tool( def create_update_calendar_event_tool(
db_session: AsyncSession | None = None, db_session: AsyncSession | None = None,
search_space_id: int | None = None, search_space_id: int | None = None,
@ -255,25 +269,13 @@ def create_update_calendar_event_tool(
if final_new_summary is not None: if final_new_summary is not None:
update_body["summary"] = final_new_summary update_body["summary"] = final_new_summary
if final_new_start_datetime is not None: if final_new_start_datetime is not None:
tz = ( update_body["start"] = _build_time_body(
context.get("timezone", "UTC") final_new_start_datetime, context
if isinstance(context, dict)
else "UTC"
) )
update_body["start"] = {
"dateTime": final_new_start_datetime,
"timeZone": tz,
}
if final_new_end_datetime is not None: if final_new_end_datetime is not None:
tz = ( update_body["end"] = _build_time_body(
context.get("timezone", "UTC") final_new_end_datetime, context
if isinstance(context, dict)
else "UTC"
) )
update_body["end"] = {
"dateTime": final_new_end_datetime,
"timeZone": tz,
}
if final_new_description is not None: if final_new_description is not None:
update_body["description"] = final_new_description update_body["description"] = final_new_description
if final_new_location is not None: if final_new_location is not None:

View file

@ -209,8 +209,8 @@ class GoogleCalendarKBSyncService:
) )
calendar_id = (document.document_metadata or {}).get( calendar_id = (document.document_metadata or {}).get(
"calendar_id", "primary" "calendar_id"
) ) or "primary"
live_event = await loop.run_in_executor( live_event = await loop.run_in_executor(
None, None,
lambda: ( lambda: (

View file

@ -185,7 +185,7 @@ function DateTimePickerField({
type="time" type="time"
value={time} value={time}
onChange={handleTimeChange} onChange={handleTimeChange}
className="w-[120px] text-sm shrink-0 pl-1.5 [&::-webkit-calendar-picker-indicator]:order-first [&::-webkit-calendar-picker-indicator]:mr-1" className="w-[120px] text-sm shrink-0 appearance-none [&::-webkit-calendar-picker-indicator]:hidden [&::-webkit-calendar-picker-indicator]:appearance-none"
/> />
</div> </div>
); );

View file

@ -253,6 +253,12 @@ function ApprovalCard({
String(effectiveNewDescription ?? "") !== (event?.description ?? ""); String(effectiveNewDescription ?? "") !== (event?.description ?? "");
const buildFinalArgs = useCallback(() => { const buildFinalArgs = useCallback(() => {
const base = {
event_id: event?.event_id,
document_id: event?.document_id,
connector_id: account?.id,
};
if (pendingEdits) { if (pendingEdits) {
const attendeesArr = pendingEdits.attendees const attendeesArr = pendingEdits.attendees
? pendingEdits.attendees ? pendingEdits.attendees
@ -260,22 +266,38 @@ function ApprovalCard({
.map((e) => e.trim()) .map((e) => e.trim())
.filter(Boolean) .filter(Boolean)
: null; : null;
const origAttendees = event?.attendees?.map((a) => a.email) ?? [];
return { return {
event_id: event?.event_id, ...base,
document_id: event?.document_id, new_summary:
connector_id: account?.id, pendingEdits.summary && pendingEdits.summary !== (event?.summary ?? "")
new_summary: pendingEdits.summary || null, ? pendingEdits.summary
new_description: pendingEdits.description || null, : null,
new_start_datetime: pendingEdits.start_datetime || null, new_description:
new_end_datetime: pendingEdits.end_datetime || null, pendingEdits.description !== (event?.description ?? "")
new_location: pendingEdits.location || null, ? pendingEdits.description || null
new_attendees: attendeesArr, : null,
new_start_datetime:
pendingEdits.start_datetime && pendingEdits.start_datetime !== (event?.start ?? "")
? pendingEdits.start_datetime
: null,
new_end_datetime:
pendingEdits.end_datetime && pendingEdits.end_datetime !== (event?.end ?? "")
? pendingEdits.end_datetime
: null,
new_location:
pendingEdits.location !== (event?.location ?? "")
? pendingEdits.location || null
: null,
new_attendees:
attendeesArr && attendeesArr.join(",") !== origAttendees.join(",")
? attendeesArr
: null,
}; };
} }
return { return {
event_id: event?.event_id, ...base,
document_id: event?.document_id,
connector_id: account?.id,
new_summary: actionArgs.new_summary ?? null, new_summary: actionArgs.new_summary ?? null,
new_description: actionArgs.new_description ?? null, new_description: actionArgs.new_description ?? null,
new_start_datetime: actionArgs.new_start_datetime ?? null, new_start_datetime: actionArgs.new_start_datetime ?? null,