pythongoogle-calendar-api

Update All Future Events Using Google Calendar API


I want to be able to use Google Calendar API to make modifications to an event and apply them to following events in the future.

I tried patching and updating, even using its own recurringEventId to make the patches and nothing seems to work. I am currently using the Python googleapiclient to make this work, is this even possible from an API side? Because it's definitely possible in the UI.

{'kind': 'calendar#event',
 'etag': 'etag',
 'id': '<recurr>_<time>',
 'status': 'confirmed',
 'htmlLink': 'https://www.google.com/calendar/event',
 'created': '2024-07-11T01:28:33.000Z',
 'updated': '2024-07-11T01:32:53.501Z',
 'summary': 'Test',
 'description': 'Content Here',
 'creator': {'email': 'kevin'},
 'organizer': {'email': 'calendar.google.com',
  'displayName': 'Pipeline Scheduler - Staging',
  'self': True},
  'start': {'dateTime': '2024-07-10T18:30:00-07:00',
   'timeZone': 'America/Los_Angeles'},
  'end': {'dateTime': '2024-07-10T19:30:00-07:00',
  'timeZone': 'America/Los_Angeles'},
 'recurringEventId': '<recurr>',
 'originalStartTime': {'dateTime': '2024-07-10T18:30:00-07:00',
  'timeZone': 'America/Los_Angeles'},
 'iCalUID': '<recurr>@google.com',
 'sequence': 3,
  'reminders': {'useDefault': True},
  'eventType': 'default'}

For an event like this, I am only able to patch or update a single event with the following command (not doing batching because it takes too long for our use-case):

Below is how we query for events: service.events().patch(calendarId=calendarId, eventId=event['id'], body=event).execute()

now = datetime.utcnow()
start = now.isoformat() + "Z"
end = datetime.combine(now.date(), time.max).isoformat() + "Z"

try:
    events_result = (
        service.events()
        .list(
            calendarId=calendar_id,
            timeMin=start,
            timeMax=end,
            singleEvents=True,
            orderBy="startTime"
        )
        .execute()
    )
    events = events_result.get("items", [])

    if not events:
        log.info("No events found today (UTC)")

    return events

except HttpError as e:
    log.error(f"Error fetching events: {e}")
    return []

Solution

  • Upon further experimenting, it turns out it's not possible to only update partial recurring events without updating them all. For this reason we must actually insert an event modify the RRule for the recurrent event to stop at the same time as the start of the modified event.

        recurr_event = service.events().get(calendarId=calendar_id, eventId=event['recurringEventId']).execute()
    rrule = recurr_event['recurrence']
    
        new_updated_event = {
        'start': event['start'],
        'end': event['end'],
        'description': event['description'],
        'summary': clean_summary(event['summary']),
        'recurrence': rrule
    }
    
    created_event = service.events().insert(calendarId=calendar_id, body=new_updated_event).execute()
    split_event_id = adapter_request.event_id.split('_')
    
    new_rrule = [f"{rrule[0]};UNTIL={datetime.strftime(start, form)}"]
    recurr_event['recurrence'] = new_rrule
    updated_event = service.events().update(calendarId=calendar_id, eventId=recurr_event['id'], body=recurr_event).execute()
    

    There is an edge case for recurring events where we must delete the event if originalStartTime is in the future, as well:

    if start >= datetime.fromisoformat(event['originalStartTime']['dateTime']):
        service.events().delete(calendarId=calendar_id, eventId=event.get('id')).execute()