eventplanner_gcal: PEP coding style

This commit is contained in:
Martin Bauer 2019-01-05 13:04:20 +01:00
parent aaf1528426
commit e2c8915321
10 changed files with 243 additions and 232 deletions

Binary file not shown.

View File

@ -6,15 +6,15 @@ import time
from eventplanner.models import Event, EventParticipation from eventplanner.models import Event, EventParticipation
from eventplanner_gcal.models import GCalMapping, GCalPushChannel, UserGCalCoupling from eventplanner_gcal.models import GCalMapping, GCalPushChannel, UserGCalCoupling
from apiclient.http import BatchHttpRequest from apiclient.http import BatchHttpRequest
from builtins import str as text # python2 and python3
from django.conf import settings from django.conf import settings
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# --------------------- Authentication using oauth2 -------------------------------------------- # ---------------------------------- Authentication using oauth2 -----------------------------------------------------
def createGCalServiceObject(): def create_gcal_service_object():
"""Creates a Google API service object. This object is required whenever a Google API call is made""" """Creates a Google API service object. This object is required whenever a Google API call is made"""
from oauth2client.file import Storage from oauth2client.file import Storage
from apiclient.discovery import build from apiclient.discovery import build
@ -24,11 +24,9 @@ def createGCalServiceObject():
storage = Storage(gcal_settings['credentials_file']) storage = Storage(gcal_settings['credentials_file'])
credentials = storage.get() credentials = storage.get()
print("credentials", credentials) logger.debug("Credentials", credentials)
if credentials is None or credentials.invalid == True: if credentials is None or credentials.invalid is True:
# flow = client.flow_from_clientsecrets(CLIENT_SEICRET_FILE, SCOPES) # flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
print("invalid credentials for gcal")
logger.error("Unable to initialize Google Calendar coupling. Check your settings!") logger.error("Unable to initialize Google Calendar coupling. Check your settings!")
return None return None
@ -37,28 +35,27 @@ def createGCalServiceObject():
res = build(serviceName='calendar', version='v3', res = build(serviceName='calendar', version='v3',
http=http, developerKey=gcal_settings['developerKey']) http=http, developerKey=gcal_settings['developerKey'])
print("res", res)
if res is None: if res is None:
logger.error("Authentication at google API failed. Check your settings!") logger.error("Authentication at Google API failed. Check your settings!")
return res return res
def getServiceObject(): def get_service_object():
if getServiceObject.__serviceObject is None: if get_service_object.__serviceObject is None:
getServiceObject.__serviceObject = createGCalServiceObject() get_service_object.__serviceObject = create_gcal_service_object()
return getServiceObject.__serviceObject return get_service_object.__serviceObject
getServiceObject.__serviceObject = None get_service_object.__serviceObject = None
# --------------------- Building GCal event representation ------------------------------------ # --------------------- Building GCal event representation ----------------------------------------------------------
def buildGCalAttendeesObj(event): def build_gcal_attendees_obj(event):
"""Builds a attendees object that is inserted into the GCal event. """Builds an attendees object that is inserted into the GCal event.
Attendees are all users that have a google mail address. """ Attendees are all users that have a google mail address."""
result = [] result = []
for userMapping in UserGCalCoupling.objects.all(): for userMapping in UserGCalCoupling.objects.all():
@ -68,22 +65,25 @@ def buildGCalAttendeesObj(event):
# participation = EventParticipation.get_or_create( u, event ) # participation = EventParticipation.get_or_create( u, event )
try: try:
participation = EventParticipation.objects.get(event=event, user=u) participation = EventParticipation.objects.get(event=event, user=u)
localStatus = participation.status local_status = participation.status
localComment = participation.comment local_comment = participation.comment
except EventParticipation.DoesNotExist: except EventParticipation.DoesNotExist:
localStatus = "-" local_status = "-"
localComment = "" local_comment = ""
status = "needsAction" status = "needsAction"
if localStatus == "?": status = "tentative" if local_status == "?":
if localStatus == 'Yes': status = "accepted" status = "tentative"
if localStatus == 'No': status = "declined" elif local_status == 'Yes':
status = "accepted"
elif local_status == 'No':
status = "declined"
o = { o = {
'id': userMapping.email, 'id': userMapping.email,
'email': userMapping.email, 'email': userMapping.email,
'displayName': u.username, 'displayName': u.username,
'comment': localComment, 'comment': local_comment,
'responseStatus': status, 'responseStatus': status,
} }
result.append(o) result.append(o)
@ -91,201 +91,204 @@ def buildGCalAttendeesObj(event):
return result return result
def buildGCalEvent(event, timezone="Europe/Berlin"): def build_gcal_event(event, timezone="Europe/Berlin"):
""" Builds a GCal event using a local event. """ """ Builds a GCal event using a local event. """
def createDateTimeObj(date, time): def create_date_time_obj(date, time_obj):
if time is None: if time_obj is None:
return {'date': unicode(date), 'timeZone': timezone} return {'date': text(date), 'timeZone': timezone}
else: else:
return {'dateTime': unicode(date) + 'T' + unicode(time), 'timeZone': timezone} return {'dateTime': text(date) + 'T' + text(time_obj), 'timeZone': timezone}
startDate = event.date start_date = event.date
endDate = event.end_date end_date = event.end_date
if endDate is None: endDate = startDate if end_date is None:
end_date = start_date
startTime = event.meeting_time start_time = event.meeting_time
if startTime is None: startTime = event.time if start_time is None:
start_time = event.time
if startTime is None: if start_time is None:
endTime = None end_time = None
else: else:
endTime = datetime.time(22, 30) end_time = datetime.time(22, 30)
gLocation = unicode(event.location) g_location = text(event.location)
if event.map_location: if event.map_location:
# Map location has the following format: latitude,longitude,zoomlevel # Map location has the following format: latitude,longitude,zoomlevel
# the first two are needed # the first two are needed
s = event.map_location.split(",") s = event.map_location.split(",")
gLocation = unicode("%s,%s" % (s[0], s[1])) g_location = text("%s,%s" % (s[0], s[1]))
return { return {
'summary': unicode(settings.GCAL_COUPLING['eventPrefix'] + event.title), 'summary': text(settings.GCAL_COUPLING['eventPrefix'] + event.title),
'description': unicode(event.desc), 'description': text(event.desc),
'location': gLocation, 'location': g_location,
'start': createDateTimeObj(startDate, startTime), 'start': create_date_time_obj(start_date, start_time),
'end': createDateTimeObj(endDate, endTime), 'end': create_date_time_obj(end_date, end_time),
'extendedProperties': { 'extendedProperties': {
'private': { 'private': {
'blechreizEvent': 'true', 'blechreizEvent': 'true',
'blechreizID': event.id, 'blechreizID': event.id,
} }
}, },
'attendees': buildGCalAttendeesObj(event), 'attendees': build_gcal_attendees_obj(event),
} }
# ------------------------------ Callback Functions ------------------------------------------------ # ------------------------------ Callback Functions -------------------------------------------------------------------
def onGcalEventCreated(request_id, response, exception=None): def on_gcal_event_created(_, response, exception=None):
"""Callback function for created events to enter new gcal id in the mapping table""" """Callback function for created events to enter new gcal id in the mapping table"""
if exception is not None: if exception is not None:
print("response " + str(response)) logger.error("on_gcal_event_created: Exception " + str(exception))
raise exception raise exception
googleId = response['id'] google_id = response['id']
djangoId = response['extendedProperties']['private']['blechreizID'] django_id = response['extendedProperties']['private']['blechreizID']
mapping = GCalMapping(gcal_id=googleId, event=Event.objects.get(pk=djangoId)) mapping = GCalMapping(gcal_id=google_id, event=Event.objects.get(pk=django_id))
mapping.save() mapping.save()
# ------------------------------ GCal Api Calls ------------------------------------------------- # ------------------------------ GCal Api Calls --------------------------------------------------------------------
def getAllGCalEvents(service, fromNow=False): def get_all_gcal_events(service, from_now=False):
"""Retrieves all gcal events with custom property blechreizEvent=True i.e. all """Retrieves all gcal events with custom property blechreizEvent=True i.e. all
events that have been created by this script.""" events that have been created by this script."""
if fromNow: if from_now:
now = datetime.datetime.now() now = datetime.datetime.now()
minTime = now.strftime("%Y-%m-%dT%H:%M:%S-00:00") min_time = now.strftime("%Y-%m-%dT%H:%M:%S-00:00")
else: else:
minTime = '2000-01-01T00:00:00-00:00' min_time = '2000-01-01T00:00:00-00:00'
events = service.events().list( events = service.events().list(
calendarId='primary', calendarId='primary',
singleEvents=True, singleEvents=True,
maxResults=1000, maxResults=1000,
orderBy='startTime', orderBy='startTime',
timeMin=minTime, timeMin=min_time,
timeMax='2100-01-01T00:00:00-00:00', timeMax='2100-01-01T00:00:00-00:00',
privateExtendedProperty='blechreizEvent=true', privateExtendedProperty='blechreizEvent=true',
).execute() ).execute()
return events['items'] return events['items']
def createGCalEvent(service, event, timezone="Europe/Berlin"): def create_gcal_event(service, event, timezone="Europe/Berlin"):
"""Creates a new gcal event using a local event""" """Creates a new gcal event using a local event"""
googleEvent = buildGCalEvent(event, timezone) google_event = build_gcal_event(event, timezone)
return service.events().insert(calendarId='primary', body=googleEvent) return service.events().insert(calendarId='primary', body=google_event)
def updateGCalEvent(service, event, timezone="Europe/Berlin"): def update_gcal_event(service, event, timezone="Europe/Berlin"):
"""Updates an existing gcal event, using a local event""" """Updates an existing gcal event, using a local event"""
googleEvent = buildGCalEvent(event, timezone) google_event = build_gcal_event(event, timezone)
try: try:
mapping = GCalMapping.objects.get(event=event) mapping = GCalMapping.objects.get(event=event)
except GCalMapping.DoesNotExist: except GCalMapping.DoesNotExist:
return createGCalEvent(service, event, timezone) return create_gcal_event(service, event, timezone)
return service.events().patch(calendarId='primary', eventId=mapping.gcal_id, body=googleEvent) return service.events().patch(calendarId='primary', eventId=mapping.gcal_id, body=google_event)
def deleteGCalEvent(service, event): def delete_gcal_event(service, event):
"""Deletes gcal that belongs to the given local event""" """Deletes gcal that belongs to the given local event"""
mapping = GCalMapping.objects.get(event=event) mapping = GCalMapping.objects.get(event=event)
gcalId = mapping.gcal_id gcal_id = mapping.gcal_id
mapping.delete() mapping.delete()
return service.events().delete(calendarId='primary', eventId=gcalId) return service.events().delete(calendarId='primary', eventId=gcal_id)
# ------------------------------------- Synchronization ---------------------------------------------------- # ------------------------------------- Synchronization -------------------------------------------------------------
def deleteAllGCalEvents(service=None): def delete_all_gcal_events(service=None):
"""Deletes all gcal events that have been created by this script""" """Deletes all gcal events that have been created by this script"""
if service is None: if service is None:
service = getServiceObject() service = get_service_object()
gcalIds = [ev['id'] for ev in getAllGCalEvents(service)] gcal_ids = [ev['id'] for ev in get_all_gcal_events(service)]
l = len(gcalIds) num_ids = len(gcal_ids)
if l == 0: if num_ids == 0:
return l return num_ids
batch = BatchHttpRequest() batch = BatchHttpRequest()
for id in gcalIds: for ev_id in gcal_ids:
batch.add(service.events().delete(calendarId='primary', eventId=id)) batch.add(service.events().delete(calendarId='primary', eventId=ev_id))
batch.execute() batch.execute()
GCalMapping.objects.all().delete() GCalMapping.objects.all().delete()
return l return num_ids
def syncFromLocalToGoogle(service=None): def sync_from_local_to_google(service=None):
""" Creates a google event for each local event (if it does not exist yet) and deletes all google events """ Creates a google event for each local event (if it does not exist yet) and deletes all google events
that are not found in local database. Updates participation info of gcal events using local data that are not found in local database. Updates participation info of gcal events using local data
""" """
if service is None: service = getServiceObject() if service is None:
service = get_service_object()
allEvents = getAllGCalEvents(service) all_events = get_all_gcal_events(service)
eventsAtGoogle_djangoID = set() events_at_google_django_id = set()
eventsAtGoogle_googleID = set() events_at_google_google_id = set()
for gcalEv in allEvents: for gcal_ev in all_events:
eventsAtGoogle_djangoID.add(int(gcalEv['extendedProperties']['private']['blechreizID'])) events_at_google_django_id.add(int(gcal_ev['extendedProperties']['private']['blechreizID']))
eventsAtGoogle_googleID.add(gcalEv['id']) events_at_google_google_id.add(gcal_ev['id'])
localEvents_djangoID = set(Event.objects.all().values_list('pk', flat=True)) local_events_django_id = set(Event.objects.all().values_list('pk', flat=True))
localEvents_googleID = set(GCalMapping.objects.all().values_list('gcal_id', flat=True)) local_events_google_id = set(GCalMapping.objects.all().values_list('gcal_id', flat=True))
eventsToCreate_djangoID = localEvents_djangoID - eventsAtGoogle_djangoID events_to_create_django_id = local_events_django_id - events_at_google_django_id
eventsToDelete_googleID = eventsAtGoogle_googleID - localEvents_googleID events_to_delete_google_id = events_at_google_google_id - local_events_google_id
batch = BatchHttpRequest() batch = BatchHttpRequest()
batchIsEmpty = True batch_is_empty = True
for eventDjangoID in eventsToCreate_djangoID: for event_django_id in events_to_create_django_id:
batch.add(createGCalEvent(service, Event.objects.get(pk=eventDjangoID)), callback=onGcalEventCreated) batch.add(create_gcal_event(service, Event.objects.get(pk=event_django_id)), callback=on_gcal_event_created)
batchIsEmpty = False batch_is_empty = False
for eventGoogleID in eventsToDelete_googleID: for eventGoogleID in events_to_delete_google_id:
batch.add(service.events().delete(calendarId='primary', eventId=eventGoogleID)) batch.add(service.events().delete(calendarId='primary', eventId=eventGoogleID))
batchIsEmpty = False batch_is_empty = False
for gcalEv in allEvents: for gcal_ev in all_events:
eventDjangoID = int(gcalEv['extendedProperties']['private']['blechreizID']) event_django_id = int(gcal_ev['extendedProperties']['private']['blechreizID'])
try: try:
djangoEv = Event.objects.get(pk=eventDjangoID) django_ev = Event.objects.get(pk=event_django_id)
if 'attendees' not in gcalEv: if 'attendees' not in gcal_ev:
gcalEv['attendees'] = [] gcal_ev['attendees'] = []
if gcalEv['attendees'] != buildGCalAttendeesObj(djangoEv): if gcal_ev['attendees'] != build_gcal_attendees_obj(django_ev):
batch.add(updateGCalEvent(service, djangoEv)) batch.add(update_gcal_event(service, django_ev))
batchIsEmpty = False batch_is_empty = False
except Event.DoesNotExist: except Event.DoesNotExist:
pass pass
if not batchIsEmpty: if not batch_is_empty:
batch.execute() batch.execute()
return len(eventsToCreate_djangoID), len(eventsToDelete_googleID) return len(events_to_create_django_id), len(events_to_delete_google_id)
def syncFromGoogleToLocal(service=None): def sync_from_google_to_local(service=None):
"""Retrieves only participation infos for all events and updates local database if anything has changed. """ """Retrieves only participation infos for all events and updates local database if anything has changed. """
if service is None: if service is None:
service = getServiceObject() service = get_service_object()
newStatusReceived = False new_status_received = False
allEvents = getAllGCalEvents(service, fromNow=True) all_events = get_all_gcal_events(service, from_now=True)
for e in allEvents: for e in all_events:
localId = e['extendedProperties']['private']['blechreizID'] local_id = e['extendedProperties']['private']['blechreizID']
localEvent = Event.objects.get(pk=localId) local_event = Event.objects.get(pk=local_id)
for a in e['attendees']: for a in e['attendees']:
user = UserGCalCoupling.objects.get(email=a['email']).user user = UserGCalCoupling.objects.get(email=a['email']).user
part = EventParticipation.get_or_create(user, localEvent) part = EventParticipation.get_or_create(user, local_event)
if 'comment' in a: if 'comment' in a:
part.comment = a['comment'] part.comment = a['comment']
@ -307,15 +310,15 @@ def syncFromGoogleToLocal(service=None):
# and an endless loop is entered # and an endless loop is entered
if prev.status != part.status or prev.comment != part.comment: if prev.status != part.status or prev.comment != part.comment:
part.save() part.save()
newStatusReceived = True new_status_received = True
return newStatusReceived return new_status_received
# ------------------------------------- Synchronization ---------------------------------------------------- # ------------------------------------- Synchronization ----------------------------------------------------
def checkGCalSubscription(service=None, timeToLive=14 * 24 * 3600, renewBeforeExpiry=None): def check_gcal_subscription(service=None, time_to_live=14 * 24 * 3600, renew_before_expiry=None):
"""Google offers a push service if any event information has changed. This works using a so called """Google offers a push service if any event information has changed. This works using a so called
channel, which has a certain time to live. This method checks that a valid channel exists: channel, which has a certain time to live. This method checks that a valid channel exists:
- if none exists a new one is created - if none exists a new one is created
@ -323,84 +326,84 @@ def checkGCalSubscription(service=None, timeToLive=14 * 24 * 3600, renewBeforeEx
- if channel has already expired a sync is triggered and a new channel is created - if channel has already expired a sync is triggered and a new channel is created
""" """
if service is None: if service is None:
service = getServiceObject() service = get_service_object()
if renewBeforeExpiry is None: if renew_before_expiry is None:
renewBeforeExpiry = 0.8 * timeToLive renew_before_expiry = 0.8 * time_to_live
callbackUrl = settings.GCAL_COUPLING['push_url'] callback_url = settings.GCAL_COUPLING['push_url']
# Test if a channel already exists for this callbackURL # Test if a channel already exists for this callbackURL
try: try:
dbChannel = GCalPushChannel.objects.get(address=callbackUrl) db_channel = GCalPushChannel.objects.get(address=callback_url)
gChannel = dbChannel.toGChannel() g_channel = db_channel.to_google_channel()
# if expiration time between 0 and two days: stop and create new channel # if expiration time between 0 and two days: stop and create new channel
curTime = int(time.time() * 1000) cur_time = int(time.time() * 1000)
if gChannel.expiration > curTime: if g_channel.expiration > cur_time:
# not yet expired # not yet expired
if curTime + renewBeforeExpiry * 1000 > gChannel.expiration: if cur_time + renew_before_expiry * 1000 > g_channel.expiration:
# will expire in less than "renewBeforeExpiry" # will expire in less than "renewBeforeExpiry"
print("Renewing Google Calendar Subscription: " + callbackUrl) logger.info("Renewing Google Calendar Subscription: " + callback_url)
GCalPushChannel.stop(service, gChannel) GCalPushChannel.stop(service, g_channel)
GCalPushChannel.createNew(callbackUrl, service, timeToLive) GCalPushChannel.create_new(callback_url, service, time_to_live)
else: else:
print("Channel active until %d " % (gChannel.expiration,)) logger.info("Channel active until %d " % (g_channel.expiration,))
else: else:
logger.info("Google calendar subscription had expired - getting new subscription") logger.info("Google calendar subscription had expired - getting new subscription")
# to get back in sync again we have to decide which data to take # to get back in sync again we have to decide which data to take
# so we use the local data as reference # so we use the local data as reference
syncFromLocalToGoogle(service) sync_from_local_to_google(service)
GCalPushChannel.createNew(callbackUrl, service, timeToLive) GCalPushChannel.create_new(callback_url, service, time_to_live)
except GCalPushChannel.DoesNotExist: except GCalPushChannel.DoesNotExist:
# create new channel and save it in database # create new channel and save it in database
logger.info("No CGalCallback Channel exists yet for: " + callbackUrl) logger.info("No CGalCallback Channel exists yet for: " + callback_url)
# to get back in sync again we have to decide which data to take # to get back in sync again we have to decide which data to take
# so we use the local data as reference # so we use the local data as reference
syncFromLocalToGoogle(service) sync_from_local_to_google(service)
GCalPushChannel.createNew(callbackUrl, service, timeToLive) GCalPushChannel.create_new(callback_url, service, time_to_live)
def stopAllGCalSubscriptions(service=None): def stop_all_gcal_subscriptions(service=None):
"""Stops the channel subscription """ """Stops the channel subscription """
if service is None: if service is None:
service = getServiceObject() service = get_service_object()
for dbChannel in GCalPushChannel.objects.all(): for dbChannel in GCalPushChannel.objects.all():
print("Stopping %s expiry at %d " % (dbChannel.id, dbChannel.expiration)) logger.info("Stopping %s expiry at %d " % (dbChannel.id, dbChannel.expiration))
GCalPushChannel.stop(service, dbChannel.toGChannel()) GCalPushChannel.stop(service, dbChannel.to_google_channel())
def checkIfGoogleCallbackIsValid(token, channelID, resourceID, service=None): def check_if_google_callback_is_valid(token, channel_id, resource_id, service=None):
if service is None: if service is None:
service = getServiceObject() service = get_service_object()
allChannels = GCalPushChannel.objects.all() all_channels = GCalPushChannel.objects.all()
if len(allChannels) == 0: if len(all_channels) == 0:
return False # no known subscriptions -> callback has to be from an old channel return False # no known subscriptions -> callback has to be from an old channel
if len(allChannels) > 1: if len(all_channels) > 1:
logger.warning("Multiple GCal subscriptions! This is strange and probably an error. " logger.warning("Multiple GCal subscriptions! This is strange and probably an error. "
"All channels are closed and one new is created. ") "All channels are closed and one new is created. ")
stopAllGCalSubscriptions(service) stop_all_gcal_subscriptions(service)
checkGCalSubscription() check_gcal_subscription()
allChannels = GCalPushChannel.objects.all() all_channels = GCalPushChannel.objects.all()
assert (len(allChannels) == 1) assert (len(all_channels) == 1)
theChannel = allChannels[0] the_channel = all_channels[0]
if channelID != theChannel.id or resourceID != theChannel.resource_id or token != theChannel.token: if channel_id != the_channel.id or resource_id != the_channel.resource_id or token != the_channel.token:
logger.warning("Got GCal Response from an unexpected Channel" logger.warning("Got GCal Response from an unexpected Channel"
"Got (%s,%s,%s) " "Got (%s,%s,%s) "
"expected (%s,%s,%s) " "expected (%s,%s,%s) "
"Old Channel is stopped." "Old Channel is stopped."
% (channelID, resourceID, token, theChannel.id, theChannel.resource_id, theChannel.token)) % (channel_id, resource_id, token, the_channel.id, the_channel.resource_id, the_channel.token))
channelToStop = GCalPushChannel(id=channelID, resource_id=resourceID, token=token) channel_to_stop = GCalPushChannel(id=channel_id, resource_id=resource_id, token=token)
GCalPushChannel.stop(service, channelToStop.toGChannel()) GCalPushChannel.stop(service, channel_to_stop.to_google_channel())
return False return False

View File

@ -1,9 +1,11 @@
from django.core.management.base import NoArgsCommand from django.core.management.base import NoArgsCommand
from eventplanner_gcal.google_sync import checkGCalSubscription from eventplanner_gcal.google_sync import check_gcal_subscription
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = 'Checks if the GCal notification channel is still active' help = 'Checks if the GCal notification channel is still active'
def handle_noargs(self, **options): def handle_noargs(self, **options):
print ( "Checking Subscription") print("Checking Subscription")
checkGCalSubscription() check_gcal_subscription()

View File

@ -1,9 +1,11 @@
from django.core.management.base import NoArgsCommand from django.core.management.base import NoArgsCommand
from eventplanner_gcal.google_sync import deleteAllGCalEvents from eventplanner_gcal.google_sync import delete_all_gcal_events
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = 'Delete all events in the google calendar created by this app' help = 'Delete all events in the google calendar created by this app'
def handle_noargs(self, **options): def handle_noargs(self, **options):
print ("Deleting all GCal Events.") print("Deleting all GCal Events.")
nrOfDeletedEvents = deleteAllGCalEvents() nr_of_deleted_events = delete_all_gcal_events()
print ("Deleted %d events. To Restore them from local database run gcal_sync" % (nrOfDeletedEvents, ) ) print("Deleted %d events. To Restore them from local database run gcal_sync" % (nr_of_deleted_events,))

View File

@ -1,8 +1,10 @@
from django.core.management.base import NoArgsCommand from django.core.management.base import NoArgsCommand
from eventplanner_gcal.google_sync import stopAllGCalSubscriptions from eventplanner_gcal.google_sync import stop_all_gcal_subscriptions
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = 'Stops all GCal subscriptions' help = 'Stops all GCal subscriptions'
def handle_noargs(self, **options): def handle_noargs(self, **options):
stopAllGCalSubscriptions() stop_all_gcal_subscriptions()

View File

@ -1,10 +1,12 @@
from django.core.management.base import NoArgsCommand from django.core.management.base import NoArgsCommand
from eventplanner_gcal.google_sync import syncFromLocalToGoogle from eventplanner_gcal.google_sync import sync_from_local_to_google
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = 'Synchronize Google Calendar with locally stored Events' help = 'Synchronize Google Calendar with locally stored Events'
def handle_noargs(self, **options): def handle_noargs(self, **options):
print ( "Running Sync") print("Running Sync")
created, deleted = syncFromLocalToGoogle() created, deleted = sync_from_local_to_google()
print ( "Created %d and deleted %d events" % (created,deleted) ) print("Created %d and deleted %d events" % (created, deleted))

View File

@ -32,28 +32,28 @@ class GCalPushChannel(models.Model):
resource_id = models.CharField(max_length=128) resource_id = models.CharField(max_length=128)
expiration = models.IntegerField() expiration = models.IntegerField()
def toGChannel(self): def to_google_channel(self):
return Channel('web_hook', self.id, self.token, self.address, self.expiration, resource_id=self.resource_id) return Channel('web_hook', self.id, self.token, self.address, self.expiration, resource_id=self.resource_id)
@staticmethod @staticmethod
def fromGChannel(gChannel): def from_google_channel(google_channel):
return GCalPushChannel(id=gChannel.id, return GCalPushChannel(id=google_channel.id,
address=gChannel.address, address=google_channel.address,
token=gChannel.token, token=google_channel.token,
expiration=gChannel.expiration, expiration=google_channel.expiration,
resource_id=gChannel.resource_id) resource_id=google_channel.resource_id)
@staticmethod @staticmethod
def createNew(callbackUrl, service, ttl=None): def create_new(callback_url, service, ttl=None):
gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callbackUrl, params={'ttl': int(ttl)}) gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callback_url, params={'ttl': int(ttl)})
response = service.events().watch(calendarId='primary', body=gChannel.body()).execute() response = service.events().watch(calendarId='primary', body=gChannel.body()).execute()
gChannel.update(response) gChannel.update(response)
dbChannel = GCalPushChannel.fromGChannel(gChannel) dbChannel = GCalPushChannel.from_google_channel(gChannel)
dbChannel.save() dbChannel.save()
@staticmethod @staticmethod
def stop(service, gChannel): def stop(service, google_channel):
channelService = service.channels() channel_service = service.channels()
channelService.stop(body=gChannel.body()).execute() channel_service.stop(body=google_channel.body()).execute()
GCalPushChannel.fromGChannel(gChannel).delete() GCalPushChannel.from_google_channel(google_channel).delete()

View File

@ -1,8 +1,8 @@
from django.db.models.signals import post_save, pre_delete from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver from django.dispatch import receiver
from eventplanner.models import Event, EventParticipation from eventplanner.models import Event, EventParticipation
from eventplanner_gcal.google_sync import getServiceObject, \ from eventplanner_gcal.google_sync import get_service_object, \
createGCalEvent, deleteGCalEvent, updateGCalEvent, onGcalEventCreated create_gcal_event, delete_gcal_event, update_gcal_event, on_gcal_event_created
import logging import logging
@ -22,13 +22,13 @@ def event_post_save_handler(**kwargs):
try: try:
if created: if created:
logger.info("Creating Gcal event") logger.info("Creating Gcal event")
response = createGCalEvent(getServiceObject(), event).execute() response = create_gcal_event(get_service_object(), event).execute()
onGcalEventCreated(None, response, None) on_gcal_event_created(None, response, None)
else: else:
logger.info("Updating Gcal event") logger.info("Updating Gcal event")
updateGCalEvent(getServiceObject(), event).execute() update_gcal_event(get_service_object(), event).execute()
except: except Exception as e:
logger.error("Error updating Gcal event") logger.error("Error updating Gcal event" + str(e))
@receiver(pre_delete, sender=Event) @receiver(pre_delete, sender=Event)
@ -36,9 +36,9 @@ def event_pre_delete_handler(**kwargs):
try: try:
event = kwargs['instance'] event = kwargs['instance']
logger.info("Deleting GCAL event") logger.info("Deleting GCAL event")
deleteGCalEvent(getServiceObject(), event).execute() delete_gcal_event(get_service_object(), event).execute()
except: except Exception as e:
logger.error("Error deleting GCAL event") logger.error("Error deleting GCAL event" + str(e))
@receiver(post_save, sender=EventParticipation) @receiver(post_save, sender=EventParticipation)
@ -46,6 +46,6 @@ def participation_post_save_handler(**kwargs):
try: try:
participation = kwargs['instance'] participation = kwargs['instance']
logger.info("Participation post save -> update gcal") logger.info("Participation post save -> update gcal")
updateGCalEvent(getServiceObject(), participation.event).execute() update_gcal_event(get_service_object(), participation.event).execute()
except: except Exception as e:
logger.error("Error deleting GCAL event") logger.error("Error deleting GCAL event" + str(e))

View File

@ -1,9 +1,9 @@
from django.conf.urls import url from django.conf.urls import url
from .views import runSync, gcalApiCallback, manage from .views import run_sync, gcal_api_callback, manage
urlpatterns = [ urlpatterns = [
url(r'^runSync$', runSync), url(r'^runSync$', run_sync),
url(r'^gcalApiCallback$', gcalApiCallback), url(r'^gcalApiCallback$', gcal_api_callback),
url(r'^manage$', manage), url(r'^manage$', manage),
] ]

View File

@ -1,63 +1,63 @@
from django.shortcuts import redirect from django.shortcuts import redirect
from eventplanner_gcal.google_sync import syncFromGoogleToLocal, syncFromLocalToGoogle from eventplanner_gcal.google_sync import sync_from_google_to_local, sync_from_local_to_google
from django.http import HttpResponse from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from pprint import pformat from eventplanner_gcal.google_sync import check_if_google_callback_is_valid
from eventplanner_gcal.google_sync import checkIfGoogleCallbackIsValid
from eventplanner_gcal.models import UserGCalCoupling from eventplanner_gcal.models import UserGCalCoupling
from django.shortcuts import render from django.shortcuts import render
import logging import logging
logger = logging.getLogger( __name__ ) logger = logging.getLogger(__name__)
def runSync( request ): def run_sync(request):
syncFromLocalToGoogle() sync_from_local_to_google()
return redirect("/") return redirect("/")
def manage( request ): def manage(request):
if request.method == 'POST': if request.method == 'POST':
if request.POST['activate'] == "1": if request.POST['activate'] == "1":
UserGCalCoupling.objects.filter( user=request.user ).delete() UserGCalCoupling.objects.filter(user=request.user).delete()
c = UserGCalCoupling( user=request.user, email = request.POST['email'] ) c = UserGCalCoupling(user=request.user, email=request.POST['email'])
c.save() c.save()
syncFromLocalToGoogle() sync_from_local_to_google()
else: else:
UserGCalCoupling.objects.filter( user=request.user ).delete() UserGCalCoupling.objects.filter(user=request.user).delete()
syncFromLocalToGoogle() sync_from_local_to_google()
context = {} context = {}
userCoupling = UserGCalCoupling.objects.filter( user = request.user ) user_coupling = UserGCalCoupling.objects.filter(user=request.user)
context['enabled'] = len(userCoupling) context['enabled'] = len(user_coupling)
assert( len(userCoupling) < 2 ) assert (len(user_coupling) < 2)
if len(userCoupling) == 1: if len(user_coupling) == 1:
context['mail'] = userCoupling[0].email context['mail'] = user_coupling[0].email
return render( request, 'eventplanner_gcal/management.html', context ) return render(request, 'eventplanner_gcal/management.html', context)
@csrf_exempt @csrf_exempt
def gcalApiCallback( request ): def gcal_api_callback(request):
token = "" token = ""
if 'HTTP_X_GOOG_CHANNEL_TOKEN' in request.META: token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN'] if 'HTTP_X_GOOG_CHANNEL_TOKEN' in request.META:
channelID = "" token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
if 'HTTP_X_GOOG_CHANNEL_ID' in request.META: channelID = request.META['HTTP_X_GOOG_CHANNEL_ID']
resourceID = ""
if 'HTTP_X_GOOG_RESOURCE_ID' in request.META: resourceID = request.META['HTTP_X_GOOG_RESOURCE_ID']
valid = checkIfGoogleCallbackIsValid( token, channelID, resourceID) channel_id = ""
if 'HTTP_X_GOOG_CHANNEL_ID' in request.META:
channel_id = request.META['HTTP_X_GOOG_CHANNEL_ID']
resource_id = ""
if 'HTTP_X_GOOG_RESOURCE_ID' in request.META:
resource_id = request.META['HTTP_X_GOOG_RESOURCE_ID']
valid = check_if_google_callback_is_valid(token, channel_id, resource_id)
if not valid: if not valid:
return HttpResponse('<h1>Old Channel - no update triggered</h1>') return HttpResponse('<h1>Old Channel - no update triggered</h1>')
logger.info("Received Google Callback with the following headers Token: "
logger.info( "Received Google Callback with the following headers Token: %s ID %s ResID %s " % ( token, channelID, resourceID ) ) "%s ID %s ResID %s " % (token, channel_id, resource_id))
result = syncFromGoogleToLocal() result = sync_from_google_to_local()
logger.info("Finished processing callback from GCal - New Information present: %d " %(result, ) ) logger.info("Finished processing callback from GCal - New Information present: %d " % (result,))
return HttpResponse('<h1>Callback successful</h1>') return HttpResponse('<h1>Callback successful</h1>')