TL;DR: I can reproduce a sequence of creating → updating → deleting → creating a Google Calendar entry via the API, and the last "creating" fails because of a "duplicate identified". Since the event with that identifier was deleted, I do not understand why reusing the ID raises an error. Is this a bug?
Consider the following code fragment (explanation below the code):
import googleapiclient.discovery
import google.oauth2
from loguru import logger as log
import arrow
# ID of the Google Calendar
CALENDAR_ID = "YYY@group.calendar.google.com"
# prepare session in Google Calendar
# see:
# - https://developers.google.com/identity/protocols/oauth2/service-account
# - https://developers.google.com/calendar/api/v3/reference/events/update
# - https://developers.google.com/calendar/api/guides/create-events#python
# - https://developers.google.com/calendar/api/v3/reference/events
creds = google.oauth2.service_account.Credentials.from_service_account_file(
"creds.json", scopes=["https://www.googleapis.com/auth/calendar"]
)
delegated_credentials = creds.with_subject("XXX")
service = googleapiclient.discovery.build("calendar", "v3", credentials=delegated_credentials)
# get all existing events from the calendar from today on
existing_lessons_in_calendar = service.events().list(calendarId=CALENDAR_ID, timeMin=arrow.now().floor('day')).execute()
# extract the actual lessons
existing_lessons_in_calendar = [lesson for lesson in existing_lessons_in_calendar["items"]]
# create a bag for the ids of the above
all_ids_from_calendar = [l["id"].lower() for l in existing_lessons_in_calendar]
log.debug(all_ids_from_calendar)
# create or update an entry in calendar from promote
# for SO: imagine we have a proper list of objects
for lesson in lessons_from_pronote:
# lesson_id = lesson.id.lower()
lesson_id = 'aaaaaa' # note
entry = {
"id": lesson_id,
"summary": lesson.subject.name,
"start": {
"dateTime": arrow.get(lesson.start, 'Europe/Paris').isoformat(),
},
"end": {
"dateTime": arrow.get(lesson.end, 'Europe/Paris').isoformat(),
},
}
log.debug(entry)
# check if the entry already exists in the calendar
if lesson_id in all_ids_from_calendar:
log.debug(f"UPDATING {entry['summary']} on {entry['start']['dateTime']}")
service.events().update(
calendarId=CALENDAR_ID,
eventId=lesson_id,
body=entry,
).execute()
else:
log.debug(f"CREATING {entry['summary']} on {entry['start']['dateTime']}")
service.events().insert(
calendarId=CALENDAR_ID,
body=entry,
).execute()
What it does is to either create (.insert()) or update (.update()) an event in the calendar, depending on whether it is already present or not. To this, I check if the id of the event I want to add is one of the existing events.
When running this code with a fresh ID (aaaaaa in the case above) I get:
- first run: an event is added (created)
- second run: the event is updated (with the same values for the sake of this test)
- I then delete the entry from the calendar web page
- the next run fails with
2022-09-11 13:04:29.292 | DEBUG | __main__:<module>:64 - []
2022-09-11 13:04:29.417 | DEBUG | __main__:<module>:80 - {'id': 'aaaaaa', 'summary': 'ENSEIGN.SCIENTIFIQUE', 'start': {'dateTime': '2022-09-13T10:05:00+02:00'}, 'end':
{'dateTime': '2022-09-13T10:55:00+02:00'}}
2022-09-11 13:04:29.419 | DEBUG | __main__:<module>:89 - CREATING ENSEIGN.SCIENTIFIQUE on 2022-09-13T10:05:00+02:00
Traceback (most recent call last):
File "d:\W\dev-perso\pronote\pronote.py", line 93, in <module>
).execute()
File "C:\Python310\lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Python310\lib\site-packages\googleapiclient\http.py", line 938, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 409 when requesting https://www.googleapis.com/calendar/v3/calendars/YYY%40group.calendar.google.com/events?alt=json returned "The requested identifier already exists.". Details: "[{'domain': 'global', 'reason': 'duplicate', 'message': 'The requested identifier already exists.'}]">
As you can see in the first line of the log, after the event has been deleted via the web interface, the list of existing events in the calendar is empty.
Despite this, the creation I rejected because the Id allegedly already exists.
This looks like a bug to me but I wanted to make sure I do not make a mistake somewhere. The documentation is quite clear about the nature of this identifier:
the ID must be unique per calendar
Note: I initially thought that this was some kind of "eventual consistency" on Google side, where the deletion would not have had the time to correctly replicate on all servers. 24 hours later, the problem persists.