apply ruff formatting
This commit is contained in:
@@ -1 +1 @@
|
||||
# Make the calendar directory a Python package
|
||||
# Make the calendar directory a Python package
|
||||
|
||||
@@ -25,26 +25,30 @@ from core.server import server
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _parse_reminders_json(reminders_input: Optional[Union[str, List[Dict[str, Any]]]], function_name: str) -> List[Dict[str, Any]]:
|
||||
def _parse_reminders_json(
|
||||
reminders_input: Optional[Union[str, List[Dict[str, Any]]]], function_name: str
|
||||
) -> List[Dict[str, Any]]:
|
||||
"""
|
||||
Parse reminders from JSON string or list object and validate them.
|
||||
|
||||
|
||||
Args:
|
||||
reminders_input: JSON string containing reminder objects or list of reminder objects
|
||||
function_name: Name of calling function for logging
|
||||
|
||||
|
||||
Returns:
|
||||
List of validated reminder objects
|
||||
"""
|
||||
if not reminders_input:
|
||||
return []
|
||||
|
||||
|
||||
# Handle both string (JSON) and list inputs
|
||||
if isinstance(reminders_input, str):
|
||||
try:
|
||||
reminders = json.loads(reminders_input)
|
||||
if not isinstance(reminders, list):
|
||||
logger.warning(f"[{function_name}] Reminders must be a JSON array, got {type(reminders).__name__}")
|
||||
logger.warning(
|
||||
f"[{function_name}] Reminders must be a JSON array, got {type(reminders).__name__}"
|
||||
)
|
||||
return []
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f"[{function_name}] Invalid JSON for reminders: {e}")
|
||||
@@ -52,35 +56,46 @@ def _parse_reminders_json(reminders_input: Optional[Union[str, List[Dict[str, An
|
||||
elif isinstance(reminders_input, list):
|
||||
reminders = reminders_input
|
||||
else:
|
||||
logger.warning(f"[{function_name}] Reminders must be a JSON string or list, got {type(reminders_input).__name__}")
|
||||
logger.warning(
|
||||
f"[{function_name}] Reminders must be a JSON string or list, got {type(reminders_input).__name__}"
|
||||
)
|
||||
return []
|
||||
|
||||
|
||||
# Validate reminders
|
||||
if len(reminders) > 5:
|
||||
logger.warning(f"[{function_name}] More than 5 reminders provided, truncating to first 5")
|
||||
logger.warning(
|
||||
f"[{function_name}] More than 5 reminders provided, truncating to first 5"
|
||||
)
|
||||
reminders = reminders[:5]
|
||||
|
||||
|
||||
validated_reminders = []
|
||||
for reminder in reminders:
|
||||
if not isinstance(reminder, dict) or "method" not in reminder or "minutes" not in reminder:
|
||||
logger.warning(f"[{function_name}] Invalid reminder format: {reminder}, skipping")
|
||||
if (
|
||||
not isinstance(reminder, dict)
|
||||
or "method" not in reminder
|
||||
or "minutes" not in reminder
|
||||
):
|
||||
logger.warning(
|
||||
f"[{function_name}] Invalid reminder format: {reminder}, skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
method = reminder["method"].lower()
|
||||
if method not in ["popup", "email"]:
|
||||
logger.warning(f"[{function_name}] Invalid reminder method '{method}', must be 'popup' or 'email', skipping")
|
||||
logger.warning(
|
||||
f"[{function_name}] Invalid reminder method '{method}', must be 'popup' or 'email', skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
minutes = reminder["minutes"]
|
||||
if not isinstance(minutes, int) or minutes < 0 or minutes > 40320:
|
||||
logger.warning(f"[{function_name}] Invalid reminder minutes '{minutes}', must be integer 0-40320, skipping")
|
||||
logger.warning(
|
||||
f"[{function_name}] Invalid reminder minutes '{minutes}', must be integer 0-40320, skipping"
|
||||
)
|
||||
continue
|
||||
|
||||
validated_reminders.append({
|
||||
"method": method,
|
||||
"minutes": minutes
|
||||
})
|
||||
|
||||
|
||||
validated_reminders.append({"method": method, "minutes": minutes})
|
||||
|
||||
return validated_reminders
|
||||
|
||||
|
||||
@@ -136,7 +151,11 @@ def _apply_visibility_if_valid(
|
||||
)
|
||||
|
||||
|
||||
def _preserve_existing_fields(event_body: Dict[str, Any], existing_event: Dict[str, Any], field_mappings: Dict[str, Any]) -> None:
|
||||
def _preserve_existing_fields(
|
||||
event_body: Dict[str, Any],
|
||||
existing_event: Dict[str, Any],
|
||||
field_mappings: Dict[str, Any],
|
||||
) -> None:
|
||||
"""
|
||||
Helper function to preserve existing event fields when not explicitly provided.
|
||||
|
||||
@@ -153,21 +172,23 @@ def _preserve_existing_fields(event_body: Dict[str, Any], existing_event: Dict[s
|
||||
event_body[field_name] = new_value
|
||||
|
||||
|
||||
def _format_attendee_details(attendees: List[Dict[str, Any]], indent: str = " ") -> str:
|
||||
def _format_attendee_details(
|
||||
attendees: List[Dict[str, Any]], indent: str = " "
|
||||
) -> str:
|
||||
"""
|
||||
Format attendee details including response status, organizer, and optional flags.
|
||||
Format attendee details including response status, organizer, and optional flags.
|
||||
|
||||
Example output format:
|
||||
" user@example.com: accepted
|
||||
manager@example.com: declined (organizer)
|
||||
optional-person@example.com: tentative (optional)"
|
||||
Example output format:
|
||||
" user@example.com: accepted
|
||||
manager@example.com: declined (organizer)
|
||||
optional-person@example.com: tentative (optional)"
|
||||
|
||||
Args:
|
||||
attendees: List of attendee dictionaries from Google Calendar API
|
||||
indent: Indentation to use for newline-separated attendees (default: " ")
|
||||
Args:
|
||||
attendees: List of attendee dictionaries from Google Calendar API
|
||||
indent: Indentation to use for newline-separated attendees (default: " ")
|
||||
|
||||
Returns:
|
||||
Formatted string with attendee details, or "None" if no attendees
|
||||
Returns:
|
||||
Formatted string with attendee details, or "None" if no attendees
|
||||
"""
|
||||
if not attendees:
|
||||
return "None"
|
||||
@@ -190,7 +211,9 @@ def _format_attendee_details(attendees: List[Dict[str, Any]], indent: str = " "
|
||||
return f"\n{indent}".join(attendee_details_list)
|
||||
|
||||
|
||||
def _format_attachment_details(attachments: List[Dict[str, Any]], indent: str = " ") -> str:
|
||||
def _format_attachment_details(
|
||||
attachments: List[Dict[str, Any]], indent: str = " "
|
||||
) -> str:
|
||||
"""
|
||||
Format attachment details including file information.
|
||||
|
||||
@@ -301,7 +324,7 @@ async def list_calendars(service, user_google_email: str) -> str:
|
||||
return f"No calendars found for {user_google_email}."
|
||||
|
||||
calendars_summary_list = [
|
||||
f"- \"{cal.get('summary', 'No Summary')}\"{' (Primary)' if cal.get('primary') else ''} (ID: {cal['id']})"
|
||||
f'- "{cal.get("summary", "No Summary")}"{" (Primary)" if cal.get("primary") else ""} (ID: {cal["id"]})'
|
||||
for cal in items
|
||||
]
|
||||
text_output = (
|
||||
@@ -353,7 +376,9 @@ async def get_events(
|
||||
if event_id:
|
||||
logger.info(f"[get_events] Retrieving single event with ID: {event_id}")
|
||||
event = await asyncio.to_thread(
|
||||
lambda: service.events().get(calendarId=calendar_id, eventId=event_id).execute()
|
||||
lambda: service.events()
|
||||
.get(calendarId=calendar_id, eventId=event_id)
|
||||
.execute()
|
||||
)
|
||||
items = [event]
|
||||
else:
|
||||
@@ -398,9 +423,7 @@ async def get_events(
|
||||
request_params["q"] = query
|
||||
|
||||
events_result = await asyncio.to_thread(
|
||||
lambda: service.events()
|
||||
.list(**request_params)
|
||||
.execute()
|
||||
lambda: service.events().list(**request_params).execute()
|
||||
)
|
||||
items = events_result.get("items", [])
|
||||
if not items:
|
||||
@@ -419,30 +442,33 @@ async def get_events(
|
||||
description = item.get("description", "No Description")
|
||||
location = item.get("location", "No Location")
|
||||
attendees = item.get("attendees", [])
|
||||
attendee_emails = ", ".join([a.get("email", "") for a in attendees]) if attendees else "None"
|
||||
attendee_emails = (
|
||||
", ".join([a.get("email", "") for a in attendees]) if attendees else "None"
|
||||
)
|
||||
attendee_details_str = _format_attendee_details(attendees, indent=" ")
|
||||
|
||||
event_details = (
|
||||
f'Event Details:\n'
|
||||
f'- Title: {summary}\n'
|
||||
f'- Starts: {start}\n'
|
||||
f'- Ends: {end}\n'
|
||||
f'- Description: {description}\n'
|
||||
f'- Location: {location}\n'
|
||||
f'- Attendees: {attendee_emails}\n'
|
||||
f'- Attendee Details: {attendee_details_str}\n'
|
||||
f"Event Details:\n"
|
||||
f"- Title: {summary}\n"
|
||||
f"- Starts: {start}\n"
|
||||
f"- Ends: {end}\n"
|
||||
f"- Description: {description}\n"
|
||||
f"- Location: {location}\n"
|
||||
f"- Attendees: {attendee_emails}\n"
|
||||
f"- Attendee Details: {attendee_details_str}\n"
|
||||
)
|
||||
|
||||
if include_attachments:
|
||||
attachments = item.get("attachments", [])
|
||||
attachment_details_str = _format_attachment_details(attachments, indent=" ")
|
||||
event_details += f'- Attachments: {attachment_details_str}\n'
|
||||
attachment_details_str = _format_attachment_details(
|
||||
attachments, indent=" "
|
||||
)
|
||||
event_details += f"- Attachments: {attachment_details_str}\n"
|
||||
|
||||
event_details += (
|
||||
f'- Event ID: {event_id}\n'
|
||||
f'- Link: {link}'
|
||||
event_details += f"- Event ID: {event_id}\n- Link: {link}"
|
||||
logger.info(
|
||||
f"[get_events] Successfully retrieved detailed event {event_id} for {user_google_email}."
|
||||
)
|
||||
logger.info(f"[get_events] Successfully retrieved detailed event {event_id} for {user_google_email}.")
|
||||
return event_details
|
||||
|
||||
# Handle multiple events or single event with basic output
|
||||
@@ -453,29 +479,35 @@ async def get_events(
|
||||
end_time = item["end"].get("dateTime", item["end"].get("date"))
|
||||
link = item.get("htmlLink", "No Link")
|
||||
item_event_id = item.get("id", "No ID")
|
||||
|
||||
|
||||
if detailed:
|
||||
# Add detailed information for multiple events
|
||||
description = item.get("description", "No Description")
|
||||
location = item.get("location", "No Location")
|
||||
attendees = item.get("attendees", [])
|
||||
attendee_emails = ", ".join([a.get("email", "") for a in attendees]) if attendees else "None"
|
||||
attendee_emails = (
|
||||
", ".join([a.get("email", "") for a in attendees])
|
||||
if attendees
|
||||
else "None"
|
||||
)
|
||||
attendee_details_str = _format_attendee_details(attendees, indent=" ")
|
||||
|
||||
event_detail_parts = (
|
||||
f'- "{summary}" (Starts: {start_time}, Ends: {end_time})\n'
|
||||
f' Description: {description}\n'
|
||||
f' Location: {location}\n'
|
||||
f' Attendees: {attendee_emails}\n'
|
||||
f' Attendee Details: {attendee_details_str}\n'
|
||||
f" Description: {description}\n"
|
||||
f" Location: {location}\n"
|
||||
f" Attendees: {attendee_emails}\n"
|
||||
f" Attendee Details: {attendee_details_str}\n"
|
||||
)
|
||||
|
||||
if include_attachments:
|
||||
attachments = item.get("attachments", [])
|
||||
attachment_details_str = _format_attachment_details(attachments, indent=" ")
|
||||
event_detail_parts += f' Attachments: {attachment_details_str}\n'
|
||||
attachment_details_str = _format_attachment_details(
|
||||
attachments, indent=" "
|
||||
)
|
||||
event_detail_parts += f" Attachments: {attachment_details_str}\n"
|
||||
|
||||
event_detail_parts += f' ID: {item_event_id} | Link: {link}'
|
||||
event_detail_parts += f" ID: {item_event_id} | Link: {link}"
|
||||
event_details_list.append(event_detail_parts)
|
||||
else:
|
||||
# Basic output format
|
||||
@@ -485,14 +517,17 @@ async def get_events(
|
||||
|
||||
if event_id:
|
||||
# Single event basic output
|
||||
text_output = f"Successfully retrieved event from calendar '{calendar_id}' for {user_google_email}:\n" + "\n".join(event_details_list)
|
||||
text_output = (
|
||||
f"Successfully retrieved event from calendar '{calendar_id}' for {user_google_email}:\n"
|
||||
+ "\n".join(event_details_list)
|
||||
)
|
||||
else:
|
||||
# Multiple events output
|
||||
text_output = (
|
||||
f"Successfully retrieved {len(items)} events from calendar '{calendar_id}' for {user_google_email}:\n"
|
||||
+ "\n".join(event_details_list)
|
||||
)
|
||||
|
||||
|
||||
logger.info(f"Successfully retrieved {len(items)} events for {user_google_email}.")
|
||||
return text_output
|
||||
|
||||
@@ -547,18 +582,16 @@ async def create_event(
|
||||
logger.info(f"[create_event] Incoming attachments param: {attachments}")
|
||||
# If attachments value is a string, split by comma and strip whitespace
|
||||
if attachments and isinstance(attachments, str):
|
||||
attachments = [a.strip() for a in attachments.split(',') if a.strip()]
|
||||
logger.info(f"[create_event] Parsed attachments list from string: {attachments}")
|
||||
attachments = [a.strip() for a in attachments.split(",") if a.strip()]
|
||||
logger.info(
|
||||
f"[create_event] Parsed attachments list from string: {attachments}"
|
||||
)
|
||||
event_body: Dict[str, Any] = {
|
||||
"summary": summary,
|
||||
"start": (
|
||||
{"date": start_time}
|
||||
if "T" not in start_time
|
||||
else {"dateTime": start_time}
|
||||
),
|
||||
"end": (
|
||||
{"date": end_time} if "T" not in end_time else {"dateTime": end_time}
|
||||
{"date": start_time} if "T" not in start_time else {"dateTime": start_time}
|
||||
),
|
||||
"end": ({"date": end_time} if "T" not in end_time else {"dateTime": end_time}),
|
||||
}
|
||||
if location:
|
||||
event_body["location"] = location
|
||||
@@ -576,18 +609,20 @@ async def create_event(
|
||||
if reminders is not None or not use_default_reminders:
|
||||
# If custom reminders are provided, automatically disable default reminders
|
||||
effective_use_default = use_default_reminders and reminders is None
|
||||
|
||||
reminder_data = {
|
||||
"useDefault": effective_use_default
|
||||
}
|
||||
|
||||
reminder_data = {"useDefault": effective_use_default}
|
||||
if reminders is not None:
|
||||
validated_reminders = _parse_reminders_json(reminders, "create_event")
|
||||
if validated_reminders:
|
||||
reminder_data["overrides"] = validated_reminders
|
||||
logger.info(f"[create_event] Added {len(validated_reminders)} custom reminders")
|
||||
logger.info(
|
||||
f"[create_event] Added {len(validated_reminders)} custom reminders"
|
||||
)
|
||||
if use_default_reminders:
|
||||
logger.info("[create_event] Custom reminders provided - disabling default reminders")
|
||||
|
||||
logger.info(
|
||||
"[create_event] Custom reminders provided - disabling default reminders"
|
||||
)
|
||||
|
||||
event_body["reminders"] = reminder_data
|
||||
|
||||
# Handle transparency validation
|
||||
@@ -601,12 +636,12 @@ async def create_event(
|
||||
event_body["conferenceData"] = {
|
||||
"createRequest": {
|
||||
"requestId": request_id,
|
||||
"conferenceSolutionKey": {
|
||||
"type": "hangoutsMeet"
|
||||
}
|
||||
"conferenceSolutionKey": {"type": "hangoutsMeet"},
|
||||
}
|
||||
}
|
||||
logger.info(f"[create_event] Adding Google Meet conference with request ID: {request_id}")
|
||||
logger.info(
|
||||
f"[create_event] Adding Google Meet conference with request ID: {request_id}"
|
||||
)
|
||||
|
||||
if attachments:
|
||||
# Accept both file URLs and file IDs. If a URL, extract the fileId.
|
||||
@@ -622,10 +657,14 @@ async def create_event(
|
||||
# Match /d/<id>, /file/d/<id>, ?id=<id>
|
||||
match = re.search(r"(?:/d/|/file/d/|id=)([\w-]+)", att)
|
||||
file_id = match.group(1) if match else None
|
||||
logger.info(f"[create_event] Extracted file_id '{file_id}' from attachment URL '{att}'")
|
||||
logger.info(
|
||||
f"[create_event] Extracted file_id '{file_id}' from attachment URL '{att}'"
|
||||
)
|
||||
else:
|
||||
file_id = att
|
||||
logger.info(f"[create_event] Using direct file_id '{file_id}' for attachment")
|
||||
logger.info(
|
||||
f"[create_event] Using direct file_id '{file_id}' for attachment"
|
||||
)
|
||||
if file_id:
|
||||
file_url = f"https://drive.google.com/open?id={file_id}"
|
||||
mime_type = "application/vnd.google-apps.drive-sdk"
|
||||
@@ -634,34 +673,55 @@ async def create_event(
|
||||
if drive_service:
|
||||
try:
|
||||
file_metadata = await asyncio.to_thread(
|
||||
lambda: drive_service.files().get(fileId=file_id, fields="mimeType,name", supportsAllDrives=True).execute()
|
||||
lambda: drive_service.files()
|
||||
.get(
|
||||
fileId=file_id,
|
||||
fields="mimeType,name",
|
||||
supportsAllDrives=True,
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
mime_type = file_metadata.get("mimeType", mime_type)
|
||||
filename = file_metadata.get("name")
|
||||
if filename:
|
||||
title = filename
|
||||
logger.info(f"[create_event] Using filename '{filename}' as attachment title")
|
||||
logger.info(
|
||||
f"[create_event] Using filename '{filename}' as attachment title"
|
||||
)
|
||||
else:
|
||||
logger.info("[create_event] No filename found, using generic title")
|
||||
logger.info(
|
||||
"[create_event] No filename found, using generic title"
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not fetch metadata for file {file_id}: {e}")
|
||||
event_body["attachments"].append({
|
||||
"fileUrl": file_url,
|
||||
"title": title,
|
||||
"mimeType": mime_type,
|
||||
})
|
||||
logger.warning(
|
||||
f"Could not fetch metadata for file {file_id}: {e}"
|
||||
)
|
||||
event_body["attachments"].append(
|
||||
{
|
||||
"fileUrl": file_url,
|
||||
"title": title,
|
||||
"mimeType": mime_type,
|
||||
}
|
||||
)
|
||||
created_event = await asyncio.to_thread(
|
||||
lambda: service.events().insert(
|
||||
calendarId=calendar_id, body=event_body, supportsAttachments=True,
|
||||
conferenceDataVersion=1 if add_google_meet else 0
|
||||
).execute()
|
||||
lambda: service.events()
|
||||
.insert(
|
||||
calendarId=calendar_id,
|
||||
body=event_body,
|
||||
supportsAttachments=True,
|
||||
conferenceDataVersion=1 if add_google_meet else 0,
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
else:
|
||||
created_event = await asyncio.to_thread(
|
||||
lambda: service.events().insert(
|
||||
calendarId=calendar_id, body=event_body,
|
||||
conferenceDataVersion=1 if add_google_meet else 0
|
||||
).execute()
|
||||
lambda: service.events()
|
||||
.insert(
|
||||
calendarId=calendar_id,
|
||||
body=event_body,
|
||||
conferenceDataVersion=1 if add_google_meet else 0,
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
link = created_event.get("htmlLink", "No link available")
|
||||
confirmation_message = f"Successfully created event '{created_event.get('summary', summary)}' for {user_google_email}. Link: {link}"
|
||||
@@ -678,8 +738,8 @@ async def create_event(
|
||||
break
|
||||
|
||||
logger.info(
|
||||
f"Event created successfully for {user_google_email}. ID: {created_event.get('id')}, Link: {link}"
|
||||
)
|
||||
f"Event created successfully for {user_google_email}. ID: {created_event.get('id')}, Link: {link}"
|
||||
)
|
||||
return confirmation_message
|
||||
|
||||
|
||||
@@ -737,9 +797,7 @@ async def modify_event(
|
||||
event_body["summary"] = summary
|
||||
if start_time is not None:
|
||||
event_body["start"] = (
|
||||
{"date": start_time}
|
||||
if "T" not in start_time
|
||||
else {"dateTime": start_time}
|
||||
{"date": start_time} if "T" not in start_time else {"dateTime": start_time}
|
||||
)
|
||||
if timezone is not None and "dateTime" in event_body["start"]:
|
||||
event_body["start"]["timeZone"] = timezone
|
||||
@@ -755,7 +813,7 @@ async def modify_event(
|
||||
event_body["location"] = location
|
||||
if attendees is not None:
|
||||
event_body["attendees"] = [{"email": email} for email in attendees]
|
||||
|
||||
|
||||
# Handle reminders
|
||||
if reminders is not None or use_default_reminders is not None:
|
||||
reminder_data = {}
|
||||
@@ -764,25 +822,41 @@ async def modify_event(
|
||||
else:
|
||||
# Preserve existing event's useDefault value if not explicitly specified
|
||||
try:
|
||||
existing_event = service.events().get(calendarId=calendar_id, eventId=event_id).execute()
|
||||
reminder_data["useDefault"] = existing_event.get("reminders", {}).get("useDefault", True)
|
||||
existing_event = (
|
||||
service.events()
|
||||
.get(calendarId=calendar_id, eventId=event_id)
|
||||
.execute()
|
||||
)
|
||||
reminder_data["useDefault"] = existing_event.get("reminders", {}).get(
|
||||
"useDefault", True
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"[modify_event] Could not fetch existing event for reminders: {e}")
|
||||
reminder_data["useDefault"] = True # Fallback to True if unable to fetch
|
||||
|
||||
logger.warning(
|
||||
f"[modify_event] Could not fetch existing event for reminders: {e}"
|
||||
)
|
||||
reminder_data["useDefault"] = (
|
||||
True # Fallback to True if unable to fetch
|
||||
)
|
||||
|
||||
# If custom reminders are provided, automatically disable default reminders
|
||||
if reminders is not None:
|
||||
if reminder_data.get("useDefault", False):
|
||||
reminder_data["useDefault"] = False
|
||||
logger.info("[modify_event] Custom reminders provided - disabling default reminders")
|
||||
|
||||
logger.info(
|
||||
"[modify_event] Custom reminders provided - disabling default reminders"
|
||||
)
|
||||
|
||||
validated_reminders = _parse_reminders_json(reminders, "modify_event")
|
||||
if reminders and not validated_reminders:
|
||||
logger.warning("[modify_event] Reminders provided but failed validation. No custom reminders will be set.")
|
||||
logger.warning(
|
||||
"[modify_event] Reminders provided but failed validation. No custom reminders will be set."
|
||||
)
|
||||
elif validated_reminders:
|
||||
reminder_data["overrides"] = validated_reminders
|
||||
logger.info(f"[modify_event] Updated reminders with {len(validated_reminders)} custom reminders")
|
||||
|
||||
logger.info(
|
||||
f"[modify_event] Updated reminders with {len(validated_reminders)} custom reminders"
|
||||
)
|
||||
|
||||
event_body["reminders"] = reminder_data
|
||||
|
||||
# Handle transparency validation
|
||||
@@ -791,11 +865,7 @@ async def modify_event(
|
||||
# Handle visibility validation
|
||||
_apply_visibility_if_valid(event_body, visibility, "modify_event")
|
||||
|
||||
if (
|
||||
timezone is not None
|
||||
and "start" not in event_body
|
||||
and "end" not in event_body
|
||||
):
|
||||
if timezone is not None and "start" not in event_body and "end" not in event_body:
|
||||
# If timezone is provided but start/end times are not, we need to fetch the existing event
|
||||
# to apply the timezone correctly. This is a simplification; a full implementation
|
||||
# might handle this more robustly or require start/end with timezone.
|
||||
@@ -817,19 +887,25 @@ async def modify_event(
|
||||
# Get the existing event to preserve fields that aren't being updated
|
||||
try:
|
||||
existing_event = await asyncio.to_thread(
|
||||
lambda: service.events().get(calendarId=calendar_id, eventId=event_id).execute()
|
||||
lambda: service.events()
|
||||
.get(calendarId=calendar_id, eventId=event_id)
|
||||
.execute()
|
||||
)
|
||||
logger.info(
|
||||
"[modify_event] Successfully retrieved existing event before update"
|
||||
)
|
||||
|
||||
# Preserve existing fields if not provided in the update
|
||||
_preserve_existing_fields(event_body, existing_event, {
|
||||
"summary": summary,
|
||||
"description": description,
|
||||
"location": location,
|
||||
"attendees": attendees
|
||||
})
|
||||
_preserve_existing_fields(
|
||||
event_body,
|
||||
existing_event,
|
||||
{
|
||||
"summary": summary,
|
||||
"description": description,
|
||||
"location": location,
|
||||
"attendees": attendees,
|
||||
},
|
||||
)
|
||||
|
||||
# Handle Google Meet conference data
|
||||
if add_google_meet is not None:
|
||||
@@ -839,17 +915,17 @@ async def modify_event(
|
||||
event_body["conferenceData"] = {
|
||||
"createRequest": {
|
||||
"requestId": request_id,
|
||||
"conferenceSolutionKey": {
|
||||
"type": "hangoutsMeet"
|
||||
}
|
||||
"conferenceSolutionKey": {"type": "hangoutsMeet"},
|
||||
}
|
||||
}
|
||||
logger.info(f"[modify_event] Adding Google Meet conference with request ID: {request_id}")
|
||||
logger.info(
|
||||
f"[modify_event] Adding Google Meet conference with request ID: {request_id}"
|
||||
)
|
||||
else:
|
||||
# Remove Google Meet by setting conferenceData to empty
|
||||
event_body["conferenceData"] = {}
|
||||
logger.info("[modify_event] Removing Google Meet conference")
|
||||
elif 'conferenceData' in existing_event:
|
||||
elif "conferenceData" in existing_event:
|
||||
# Preserve existing conference data if not specified
|
||||
event_body["conferenceData"] = existing_event["conferenceData"]
|
||||
logger.info("[modify_event] Preserving existing conference data")
|
||||
@@ -869,7 +945,12 @@ async def modify_event(
|
||||
# Proceed with the update
|
||||
updated_event = await asyncio.to_thread(
|
||||
lambda: service.events()
|
||||
.update(calendarId=calendar_id, eventId=event_id, body=event_body, conferenceDataVersion=1)
|
||||
.update(
|
||||
calendarId=calendar_id,
|
||||
eventId=event_id,
|
||||
body=event_body,
|
||||
conferenceDataVersion=1,
|
||||
)
|
||||
.execute()
|
||||
)
|
||||
|
||||
@@ -898,7 +979,9 @@ async def modify_event(
|
||||
@server.tool()
|
||||
@handle_http_errors("delete_event", service_type="calendar")
|
||||
@require_google_service("calendar", "calendar_events")
|
||||
async def delete_event(service, user_google_email: str, event_id: str, calendar_id: str = "primary") -> str:
|
||||
async def delete_event(
|
||||
service, user_google_email: str, event_id: str, calendar_id: str = "primary"
|
||||
) -> str:
|
||||
"""
|
||||
Deletes an existing event.
|
||||
|
||||
@@ -922,11 +1005,11 @@ async def delete_event(service, user_google_email: str, event_id: str, calendar_
|
||||
# Try to get the event first to verify it exists
|
||||
try:
|
||||
await asyncio.to_thread(
|
||||
lambda: service.events().get(calendarId=calendar_id, eventId=event_id).execute()
|
||||
)
|
||||
logger.info(
|
||||
"[delete_event] Successfully verified event exists before deletion"
|
||||
lambda: service.events()
|
||||
.get(calendarId=calendar_id, eventId=event_id)
|
||||
.execute()
|
||||
)
|
||||
logger.info("[delete_event] Successfully verified event exists before deletion")
|
||||
except HttpError as get_error:
|
||||
if get_error.resp.status == 404:
|
||||
logger.error(
|
||||
@@ -941,7 +1024,9 @@ async def delete_event(service, user_google_email: str, event_id: str, calendar_
|
||||
|
||||
# Proceed with the deletion
|
||||
await asyncio.to_thread(
|
||||
lambda: service.events().delete(calendarId=calendar_id, eventId=event_id).execute()
|
||||
lambda: service.events()
|
||||
.delete(calendarId=calendar_id, eventId=event_id)
|
||||
.execute()
|
||||
)
|
||||
|
||||
confirmation_message = f"Successfully deleted event (ID: {event_id}) from calendar '{calendar_id}' for {user_google_email}."
|
||||
|
||||
Reference in New Issue
Block a user