reservations: Refactor sync_google_calendar to use class

This commit is contained in:
Adam Goldsmith 2024-08-19 18:47:44 -04:00
parent 06fd819acf
commit deb1165afc

View File

@ -1,6 +1,7 @@
import logging
from datetime import date, datetime
from http import HTTPStatus
from typing import TYPE_CHECKING
from django.conf import settings
from django.utils import timezone
@ -9,6 +10,9 @@ from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
if TYPE_CHECKING:
from googleapiclient._apis.calendar.v3 import CalendarResource, Event
from cmsmanage.django_q2_helper import q_task_group
from reservations.models import ExternalReservation, Reservation, Resource
@ -26,9 +30,22 @@ def parse_google_calendar_datetime(dt) -> date | datetime:
raise Exception("Google Calendar event with out a start/end date/dateTime")
def update_calendar_event(
service, resource: Resource, existing_event, reservation: Reservation
):
class GoogleCalendarSynchronizer:
service: "CalendarResource"
def __init__(self) -> None:
self.service = build(
"calendar",
"v3",
credentials=service_account.Credentials.from_service_account_file(
settings.GOOGLE_SERVICE_ACCOUNT_FILE,
scopes=SCOPES,
),
)
def update_calendar_event(
self, resource: Resource, existing_event: "Event", reservation: Reservation
):
changes = reservation.make_google_calendar_event()
# skip update if no changes are needed
if (
@ -42,17 +59,16 @@ def update_calendar_event(
):
logger.debug("Updating event")
new_event = existing_event | changes
service.events().update(
self.service.events().update(
calendarId=resource.google_calendar,
eventId=reservation.google_calendar_event_id,
body=new_event,
).execute()
def insert_calendar_event(service, resource: Resource, reservation: Reservation):
def insert_calendar_event(self, resource: Resource, reservation: Reservation):
new_gcal_event = reservation.make_google_calendar_event()
created_event = (
service.events()
self.service.events()
.insert(
calendarId=resource.google_calendar,
body=new_gcal_event,
@ -62,16 +78,15 @@ def insert_calendar_event(service, resource: Resource, reservation: Reservation)
reservation.google_calendar_event_id = created_event["id"]
reservation.save()
def insert_or_update_calendar_event(
service, resource: Resource, reservation: Reservation
):
def insert_or_update_calendar_event(
self, resource: Resource, reservation: Reservation
):
if not reservation.google_calendar_event_id:
logger.info(
"Event in database has no Google Calendar event ID: inserting | %s",
reservation.google_calendar_event_id,
)
insert_calendar_event(service, resource, reservation)
self.insert_calendar_event(resource, reservation)
else:
# this event was in Google Calendar at some point (possibly for a different
@ -83,30 +98,29 @@ def insert_or_update_calendar_event(
)
try:
event = (
service.events()
self.service.events()
.get(
calendarId=resource.google_calendar,
eventId=reservation.google_calendar_event_id,
)
.execute()
)
update_calendar_event(service, resource, event, reservation)
self.update_calendar_event(resource, event, reservation)
except HttpError as error:
if error.status_code == HTTPStatus.NOT_FOUND:
logger.info(
"Event in database not in Google Calendar: inserting | %s",
reservation.google_calendar_event_id,
)
insert_calendar_event(service, resource, reservation)
self.insert_calendar_event(resource, reservation)
else:
raise
def sync_resource_from_google_calendar(
service, resource: Resource, now: datetime
) -> set[str]:
def sync_resource_from_google_calendar(
self, resource: Resource, now: datetime
) -> set[str]:
request = (
service.events()
self.service.events()
.list(
calendarId=resource.google_calendar,
timeMin=now.isoformat(timespec="seconds"),
@ -136,14 +150,14 @@ def sync_resource_from_google_calendar(
"Event in Google Calendar found in database: checking for update | %s",
event["id"],
)
update_calendar_event(service, resource, event, reservation)
self.update_calendar_event(resource, event, reservation)
except Reservation.DoesNotExist:
# reservation deleted in database, so remove from Google Calendar
logger.info(
"Event in Google Calendar not found in database: deleting | %s",
event["id"],
)
service.events().delete(
self.service.events().delete(
calendarId=resource.google_calendar,
eventId=event["id"],
sendUpdates="none",
@ -167,15 +181,13 @@ def sync_resource_from_google_calendar(
return {event["id"] for event in events}
def sync_reservation_from_database(service, reservation: Reservation):
def sync_reservation_from_database(self, reservation: Reservation):
for resource in reservation.resources.all():
insert_or_update_calendar_event(service, resource, reservation)
self.insert_or_update_calendar_event(resource, reservation)
def sync_resource_from_database(
service, resource: Resource, now: datetime, existing_event_ids: set[str]
):
def sync_resource_from_database(
self, resource: Resource, now: datetime, existing_event_ids: set[str]
):
reservations = (
resource.reservation_set.filter(end__gt=now)
# skip events we already pulled from Google Calendar during this sync
@ -192,30 +204,21 @@ def sync_resource_from_database(
reservation.delete()
else:
insert_or_update_calendar_event(service, resource, reservation)
self.insert_or_update_calendar_event(resource, reservation)
def sync_resource(service, resource: Resource, now: datetime):
def sync_resource(self, resource: Resource, now: datetime):
logger.info(
"Checking calendar %s for resource %s", resource.google_calendar, resource
)
existing_event_ids = sync_resource_from_google_calendar(service, resource, now)
sync_resource_from_database(service, resource, now, existing_event_ids)
existing_event_ids = self.sync_resource_from_google_calendar(resource, now)
self.sync_resource_from_database(resource, now, existing_event_ids)
@q_task_group("Sync Reservations with Google Calendar")
def sync_reservations_with_google_calendar():
service = build(
"calendar",
"v3",
credentials=service_account.Credentials.from_service_account_file(
settings.GOOGLE_SERVICE_ACCOUNT_FILE,
scopes=SCOPES,
),
)
synchronizer = GoogleCalendarSynchronizer()
now = timezone.now()
for resource in Resource.objects.all():
sync_resource(service, resource, now)
synchronizer.sync_resource(resource, now)