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