another gcal try without chunking
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user