another gcal try without chunking

This commit is contained in:
2026-04-09 17:27:59 +02:00
parent bd048a245b
commit cd735ef028

View File

@@ -79,13 +79,6 @@ def create_gcal_service_object():
return None
def _invalidate_service_on_error(exc):
"""Reset the cached service object so the next call retries credential loading."""
global _service_object
logger.warning(f"Invalidating cached GCal service due to error: {exc}")
_service_object = None
def get_service_object():
"""Get or create the Google Calendar service object."""
global _service_object
@@ -204,24 +197,29 @@ def build_gcal_event(event, timezone="Europe/Berlin"):
# ------------------------------ Callback Functions ------------------------------------------------
def on_gcal_event_created(request_id, response, exception=None):
"""Callback function for created events to enter new gcal id in the mapping table."""
if exception is not None:
logger.error(f"Error creating GCal event: {exception}")
return # Don't raise — let the batch continue processing other events
def _save_gcal_mapping_from_response(response):
"""Save a GCal mapping from an insert response."""
google_id = response["id"]
django_id = response["extendedProperties"]["private"]["blechreizID"]
try:
event = Event.objects.get(pk=django_id)
mapping = GCalMapping(gcal_id=google_id, event=event)
mapping.save()
GCalMapping.objects.update_or_create(
event=event, defaults={"gcal_id": google_id}
)
logger.info(f"Created mapping: GCal {google_id} <-> Event {django_id}")
except Event.DoesNotExist:
logger.error(f"Event {django_id} not found when creating GCal mapping")
def on_gcal_event_created(request_id, response, exception=None):
"""Callback function for batch delete_all — kept for backwards compat."""
if exception is not None:
logger.error(f"Error creating GCal event: {exception}")
return
_save_gcal_mapping_from_response(response)
# ------------------------------ GCal Api Calls -------------------------------------------------
@@ -328,51 +326,12 @@ def delete_all_gcal_events(service=None):
batch.execute()
except Exception as e:
logger.error(f"Error deleting GCal events: {e}")
status = getattr(e, 'status_code', None) or getattr(e, 'resp', {}).get('status')
if str(status) in ('401', '403'):
_invalidate_service_on_error(e)
GCalMapping.objects.all().delete()
return count
def _execute_in_chunks(service, request_callback_pairs, chunk_size=30, delay=12):
"""
Execute API requests in small batches with a sleep between chunks.
Google Calendar API allows 500 requests per 100 seconds per user (~5 req/s).
Default: 30 requests per chunk, 12 s sleep → ~2.5 req/s average.
request_callback_pairs: list of (request, callback_or_None)
"""
total = len(request_callback_pairs)
for i in range(0, total, chunk_size):
chunk = request_callback_pairs[i : i + chunk_size]
batch = service.new_batch_http_request()
for req, cb in chunk:
if cb is not None:
batch.add(req, callback=cb)
else:
batch.add(req)
try:
batch.execute()
except Exception as e:
logger.error(f"Error executing batch chunk {i // chunk_size + 1}: {e}")
status = getattr(e, "status_code", None) or getattr(e, "resp", {}).get("status")
if str(status) in ("401", "403"):
_invalidate_service_on_error(e)
return # auth broken, no point continuing
if i + chunk_size < total:
logger.info(
f"Chunk {i // chunk_size + 1} done "
f"({min(i + chunk_size, total)}/{total}), "
f"sleeping {delay}s to stay within rate limits..."
)
time.sleep(delay)
def sync_from_local_to_google(service=None):
"""
Creates a google event for each local event (if it does not exist yet) and
@@ -425,13 +384,13 @@ def sync_from_local_to_google(service=None):
if django_id not in local_events_django_id
}
# --- Deletes (usually few, single batch is fine) ---
if events_to_delete_google_id:
delete_pairs = [
(service.events().delete(calendarId="primary", eventId=gcal_id), None)
for gcal_id in events_to_delete_google_id
]
_execute_in_chunks(service, delete_pairs)
# --- Deletes: one by one ---
for gcal_id in events_to_delete_google_id:
try:
service.events().delete(calendarId="primary", eventId=gcal_id).execute()
GCalMapping.objects.filter(gcal_id=gcal_id).delete()
except Exception as e:
logger.error(f"Failed to delete GCal event {gcal_id}: {e}")
# --- Creates: future events first (soonest upcoming), then past events ---
today = datetime.date.today()
@@ -445,23 +404,20 @@ def sync_from_local_to_google(service=None):
.order_by("-date")
.values_list("pk", flat=True)
)
ordered_create_ids = future_ids + past_ids
ordered_create_ids = future_ids # + past_ids
create_pairs = []
for event_django_id in ordered_create_ids:
try:
event = Event.objects.get(pk=event_django_id)
create_pairs.append(
(create_gcal_event_request(service, event), on_gcal_event_created)
)
request = create_gcal_event_request(service, event)
response = request.execute()
_save_gcal_mapping_from_response(response)
except Event.DoesNotExist:
pass
if create_pairs:
_execute_in_chunks(service, create_pairs)
except Exception as e:
logger.error(f"Failed to create GCal event for Event {event_django_id}: {e}")
# --- Updates: attendee status changes ---
update_pairs = []
for gcal_ev in all_events:
try:
event_django_id = int(
@@ -473,14 +429,13 @@ def sync_from_local_to_google(service=None):
local_attendees = build_gcal_attendees_obj(django_ev)
if gcal_attendees != local_attendees:
update_pairs.append((update_gcal_event_request(service, django_ev), None))
update_gcal_event_request(service, django_ev).execute()
except Event.DoesNotExist:
pass
except (KeyError, ValueError):
pass
if update_pairs:
_execute_in_chunks(service, update_pairs)
except Exception as e:
logger.error(f"Failed to update GCal event: {e}")
return len(events_to_create_django_id), len(events_to_delete_google_id)