eventplanner_gcal: PEP coding style
This commit is contained in:
parent
aaf1528426
commit
e2c8915321
Binary file not shown.
|
@ -6,15 +6,15 @@ import time
|
|||
from eventplanner.models import Event, EventParticipation
|
||||
from eventplanner_gcal.models import GCalMapping, GCalPushChannel, UserGCalCoupling
|
||||
from apiclient.http import BatchHttpRequest
|
||||
|
||||
from builtins import str as text # python2 and python3
|
||||
from django.conf import settings
|
||||
|
||||
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"""
|
||||
from oauth2client.file import Storage
|
||||
from apiclient.discovery import build
|
||||
|
@ -24,11 +24,9 @@ def createGCalServiceObject():
|
|||
storage = Storage(gcal_settings['credentials_file'])
|
||||
credentials = storage.get()
|
||||
|
||||
print("credentials", credentials)
|
||||
if credentials is None or credentials.invalid == True:
|
||||
# flow = client.flow_from_clientsecrets(CLIENT_SEICRET_FILE, SCOPES)
|
||||
|
||||
print("invalid credentials for gcal")
|
||||
logger.debug("Credentials", credentials)
|
||||
if credentials is None or credentials.invalid is True:
|
||||
# flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
|
||||
logger.error("Unable to initialize Google Calendar coupling. Check your settings!")
|
||||
return None
|
||||
|
||||
|
@ -37,28 +35,27 @@ def createGCalServiceObject():
|
|||
res = build(serviceName='calendar', version='v3',
|
||||
http=http, developerKey=gcal_settings['developerKey'])
|
||||
|
||||
print("res", res)
|
||||
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
|
||||
|
||||
|
||||
def getServiceObject():
|
||||
if getServiceObject.__serviceObject is None:
|
||||
getServiceObject.__serviceObject = createGCalServiceObject()
|
||||
def get_service_object():
|
||||
if get_service_object.__serviceObject is None:
|
||||
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):
|
||||
"""Builds a attendees object that is inserted into the GCal event.
|
||||
Attendees are all users that have a google mail address. """
|
||||
def build_gcal_attendees_obj(event):
|
||||
"""Builds an attendees object that is inserted into the GCal event.
|
||||
Attendees are all users that have a google mail address."""
|
||||
result = []
|
||||
|
||||
for userMapping in UserGCalCoupling.objects.all():
|
||||
|
@ -68,22 +65,25 @@ def buildGCalAttendeesObj(event):
|
|||
# participation = EventParticipation.get_or_create( u, event )
|
||||
try:
|
||||
participation = EventParticipation.objects.get(event=event, user=u)
|
||||
localStatus = participation.status
|
||||
localComment = participation.comment
|
||||
local_status = participation.status
|
||||
local_comment = participation.comment
|
||||
except EventParticipation.DoesNotExist:
|
||||
localStatus = "-"
|
||||
localComment = ""
|
||||
local_status = "-"
|
||||
local_comment = ""
|
||||
|
||||
status = "needsAction"
|
||||
if localStatus == "?": status = "tentative"
|
||||
if localStatus == 'Yes': status = "accepted"
|
||||
if localStatus == 'No': status = "declined"
|
||||
if local_status == "?":
|
||||
status = "tentative"
|
||||
elif local_status == 'Yes':
|
||||
status = "accepted"
|
||||
elif local_status == 'No':
|
||||
status = "declined"
|
||||
|
||||
o = {
|
||||
'id': userMapping.email,
|
||||
'email': userMapping.email,
|
||||
'displayName': u.username,
|
||||
'comment': localComment,
|
||||
'comment': local_comment,
|
||||
'responseStatus': status,
|
||||
}
|
||||
result.append(o)
|
||||
|
@ -91,201 +91,204 @@ def buildGCalAttendeesObj(event):
|
|||
return result
|
||||
|
||||
|
||||
def buildGCalEvent(event, timezone="Europe/Berlin"):
|
||||
def build_gcal_event(event, timezone="Europe/Berlin"):
|
||||
""" Builds a GCal event using a local event. """
|
||||
|
||||
def createDateTimeObj(date, time):
|
||||
if time is None:
|
||||
return {'date': unicode(date), 'timeZone': timezone}
|
||||
def create_date_time_obj(date, time_obj):
|
||||
if time_obj is None:
|
||||
return {'date': text(date), 'timeZone': timezone}
|
||||
else:
|
||||
return {'dateTime': unicode(date) + 'T' + unicode(time), 'timeZone': timezone}
|
||||
return {'dateTime': text(date) + 'T' + text(time_obj), 'timeZone': timezone}
|
||||
|
||||
startDate = event.date
|
||||
endDate = event.end_date
|
||||
if endDate is None: endDate = startDate
|
||||
start_date = event.date
|
||||
end_date = event.end_date
|
||||
if end_date is None:
|
||||
end_date = start_date
|
||||
|
||||
startTime = event.meeting_time
|
||||
if startTime is None: startTime = event.time
|
||||
start_time = event.meeting_time
|
||||
if start_time is None:
|
||||
start_time = event.time
|
||||
|
||||
if startTime is None:
|
||||
endTime = None
|
||||
if start_time is None:
|
||||
end_time = None
|
||||
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:
|
||||
# Map location has the following format: latitude,longitude,zoomlevel
|
||||
# the first two are needed
|
||||
s = event.map_location.split(",")
|
||||
gLocation = unicode("%s,%s" % (s[0], s[1]))
|
||||
g_location = text("%s,%s" % (s[0], s[1]))
|
||||
|
||||
return {
|
||||
'summary': unicode(settings.GCAL_COUPLING['eventPrefix'] + event.title),
|
||||
'description': unicode(event.desc),
|
||||
'location': gLocation,
|
||||
'start': createDateTimeObj(startDate, startTime),
|
||||
'end': createDateTimeObj(endDate, endTime),
|
||||
'summary': text(settings.GCAL_COUPLING['eventPrefix'] + event.title),
|
||||
'description': text(event.desc),
|
||||
'location': g_location,
|
||||
'start': create_date_time_obj(start_date, start_time),
|
||||
'end': create_date_time_obj(end_date, end_time),
|
||||
'extendedProperties': {
|
||||
'private': {
|
||||
'blechreizEvent': 'true',
|
||||
'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"""
|
||||
if exception is not None:
|
||||
print("response " + str(response))
|
||||
logger.error("on_gcal_event_created: Exception " + str(exception))
|
||||
raise exception
|
||||
|
||||
googleId = response['id']
|
||||
djangoId = response['extendedProperties']['private']['blechreizID']
|
||||
mapping = GCalMapping(gcal_id=googleId, event=Event.objects.get(pk=djangoId))
|
||||
google_id = response['id']
|
||||
django_id = response['extendedProperties']['private']['blechreizID']
|
||||
mapping = GCalMapping(gcal_id=google_id, event=Event.objects.get(pk=django_id))
|
||||
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
|
||||
events that have been created by this script."""
|
||||
|
||||
if fromNow:
|
||||
if from_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:
|
||||
minTime = '2000-01-01T00:00:00-00:00'
|
||||
min_time = '2000-01-01T00:00:00-00:00'
|
||||
|
||||
events = service.events().list(
|
||||
calendarId='primary',
|
||||
singleEvents=True,
|
||||
maxResults=1000,
|
||||
orderBy='startTime',
|
||||
timeMin=minTime,
|
||||
timeMin=min_time,
|
||||
timeMax='2100-01-01T00:00:00-00:00',
|
||||
privateExtendedProperty='blechreizEvent=true',
|
||||
).execute()
|
||||
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"""
|
||||
googleEvent = buildGCalEvent(event, timezone)
|
||||
return service.events().insert(calendarId='primary', body=googleEvent)
|
||||
google_event = build_gcal_event(event, timezone)
|
||||
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"""
|
||||
googleEvent = buildGCalEvent(event, timezone)
|
||||
google_event = build_gcal_event(event, timezone)
|
||||
try:
|
||||
mapping = GCalMapping.objects.get(event=event)
|
||||
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"""
|
||||
mapping = GCalMapping.objects.get(event=event)
|
||||
gcalId = mapping.gcal_id
|
||||
gcal_id = mapping.gcal_id
|
||||
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"""
|
||||
|
||||
if service is None:
|
||||
service = getServiceObject()
|
||||
service = get_service_object()
|
||||
|
||||
gcalIds = [ev['id'] for ev in getAllGCalEvents(service)]
|
||||
l = len(gcalIds)
|
||||
if l == 0:
|
||||
return l
|
||||
gcal_ids = [ev['id'] for ev in get_all_gcal_events(service)]
|
||||
num_ids = len(gcal_ids)
|
||||
if num_ids == 0:
|
||||
return num_ids
|
||||
|
||||
batch = BatchHttpRequest()
|
||||
for id in gcalIds:
|
||||
batch.add(service.events().delete(calendarId='primary', eventId=id))
|
||||
for ev_id in gcal_ids:
|
||||
batch.add(service.events().delete(calendarId='primary', eventId=ev_id))
|
||||
batch.execute()
|
||||
|
||||
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
|
||||
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()
|
||||
eventsAtGoogle_googleID = set()
|
||||
for gcalEv in allEvents:
|
||||
eventsAtGoogle_djangoID.add(int(gcalEv['extendedProperties']['private']['blechreizID']))
|
||||
eventsAtGoogle_googleID.add(gcalEv['id'])
|
||||
events_at_google_django_id = set()
|
||||
events_at_google_google_id = set()
|
||||
for gcal_ev in all_events:
|
||||
events_at_google_django_id.add(int(gcal_ev['extendedProperties']['private']['blechreizID']))
|
||||
events_at_google_google_id.add(gcal_ev['id'])
|
||||
|
||||
localEvents_djangoID = set(Event.objects.all().values_list('pk', flat=True))
|
||||
localEvents_googleID = set(GCalMapping.objects.all().values_list('gcal_id', flat=True))
|
||||
local_events_django_id = set(Event.objects.all().values_list('pk', flat=True))
|
||||
local_events_google_id = set(GCalMapping.objects.all().values_list('gcal_id', flat=True))
|
||||
|
||||
eventsToCreate_djangoID = localEvents_djangoID - eventsAtGoogle_djangoID
|
||||
eventsToDelete_googleID = eventsAtGoogle_googleID - localEvents_googleID
|
||||
events_to_create_django_id = local_events_django_id - events_at_google_django_id
|
||||
events_to_delete_google_id = events_at_google_google_id - local_events_google_id
|
||||
|
||||
batch = BatchHttpRequest()
|
||||
|
||||
batchIsEmpty = True
|
||||
for eventDjangoID in eventsToCreate_djangoID:
|
||||
batch.add(createGCalEvent(service, Event.objects.get(pk=eventDjangoID)), callback=onGcalEventCreated)
|
||||
batchIsEmpty = False
|
||||
batch_is_empty = True
|
||||
for event_django_id in events_to_create_django_id:
|
||||
batch.add(create_gcal_event(service, Event.objects.get(pk=event_django_id)), callback=on_gcal_event_created)
|
||||
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))
|
||||
batchIsEmpty = False
|
||||
batch_is_empty = False
|
||||
|
||||
for gcalEv in allEvents:
|
||||
eventDjangoID = int(gcalEv['extendedProperties']['private']['blechreizID'])
|
||||
for gcal_ev in all_events:
|
||||
event_django_id = int(gcal_ev['extendedProperties']['private']['blechreizID'])
|
||||
try:
|
||||
djangoEv = Event.objects.get(pk=eventDjangoID)
|
||||
if 'attendees' not in gcalEv:
|
||||
gcalEv['attendees'] = []
|
||||
django_ev = Event.objects.get(pk=event_django_id)
|
||||
if 'attendees' not in gcal_ev:
|
||||
gcal_ev['attendees'] = []
|
||||
|
||||
if gcalEv['attendees'] != buildGCalAttendeesObj(djangoEv):
|
||||
batch.add(updateGCalEvent(service, djangoEv))
|
||||
batchIsEmpty = False
|
||||
if gcal_ev['attendees'] != build_gcal_attendees_obj(django_ev):
|
||||
batch.add(update_gcal_event(service, django_ev))
|
||||
batch_is_empty = False
|
||||
except Event.DoesNotExist:
|
||||
pass
|
||||
|
||||
if not batchIsEmpty:
|
||||
if not batch_is_empty:
|
||||
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. """
|
||||
|
||||
if service is None:
|
||||
service = getServiceObject()
|
||||
service = get_service_object()
|
||||
|
||||
newStatusReceived = False
|
||||
allEvents = getAllGCalEvents(service, fromNow=True)
|
||||
for e in allEvents:
|
||||
localId = e['extendedProperties']['private']['blechreizID']
|
||||
localEvent = Event.objects.get(pk=localId)
|
||||
new_status_received = False
|
||||
all_events = get_all_gcal_events(service, from_now=True)
|
||||
for e in all_events:
|
||||
local_id = e['extendedProperties']['private']['blechreizID']
|
||||
local_event = Event.objects.get(pk=local_id)
|
||||
for a in e['attendees']:
|
||||
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:
|
||||
part.comment = a['comment']
|
||||
|
||||
|
@ -307,15 +310,15 @@ def syncFromGoogleToLocal(service=None):
|
|||
# and an endless loop is entered
|
||||
if prev.status != part.status or prev.comment != part.comment:
|
||||
part.save()
|
||||
newStatusReceived = True
|
||||
new_status_received = True
|
||||
|
||||
return newStatusReceived
|
||||
return new_status_received
|
||||
|
||||
|
||||
# ------------------------------------- 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
|
||||
channel, which has a certain time to live. This method checks that a valid channel exists:
|
||||
- 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 service is None:
|
||||
service = getServiceObject()
|
||||
service = get_service_object()
|
||||
|
||||
if renewBeforeExpiry is None:
|
||||
renewBeforeExpiry = 0.8 * timeToLive
|
||||
if renew_before_expiry is None:
|
||||
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
|
||||
try:
|
||||
dbChannel = GCalPushChannel.objects.get(address=callbackUrl)
|
||||
gChannel = dbChannel.toGChannel()
|
||||
db_channel = GCalPushChannel.objects.get(address=callback_url)
|
||||
g_channel = db_channel.to_google_channel()
|
||||
|
||||
# if expiration time between 0 and two days: stop and create new channel
|
||||
curTime = int(time.time() * 1000)
|
||||
if gChannel.expiration > curTime:
|
||||
cur_time = int(time.time() * 1000)
|
||||
if g_channel.expiration > cur_time:
|
||||
# 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"
|
||||
print("Renewing Google Calendar Subscription: " + callbackUrl)
|
||||
GCalPushChannel.stop(service, gChannel)
|
||||
GCalPushChannel.createNew(callbackUrl, service, timeToLive)
|
||||
logger.info("Renewing Google Calendar Subscription: " + callback_url)
|
||||
GCalPushChannel.stop(service, g_channel)
|
||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||
else:
|
||||
print("Channel active until %d " % (gChannel.expiration,))
|
||||
logger.info("Channel active until %d " % (g_channel.expiration,))
|
||||
else:
|
||||
logger.info("Google calendar subscription had expired - getting new subscription")
|
||||
# to get back in sync again we have to decide which data to take
|
||||
# so we use the local data as reference
|
||||
syncFromLocalToGoogle(service)
|
||||
GCalPushChannel.createNew(callbackUrl, service, timeToLive)
|
||||
sync_from_local_to_google(service)
|
||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||
|
||||
except GCalPushChannel.DoesNotExist:
|
||||
# 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
|
||||
# so we use the local data as reference
|
||||
syncFromLocalToGoogle(service)
|
||||
GCalPushChannel.createNew(callbackUrl, service, timeToLive)
|
||||
sync_from_local_to_google(service)
|
||||
GCalPushChannel.create_new(callback_url, service, time_to_live)
|
||||
|
||||
|
||||
def stopAllGCalSubscriptions(service=None):
|
||||
def stop_all_gcal_subscriptions(service=None):
|
||||
"""Stops the channel subscription """
|
||||
|
||||
if service is None:
|
||||
service = getServiceObject()
|
||||
service = get_service_object()
|
||||
|
||||
for dbChannel in GCalPushChannel.objects.all():
|
||||
print("Stopping %s expiry at %d " % (dbChannel.id, dbChannel.expiration))
|
||||
GCalPushChannel.stop(service, dbChannel.toGChannel())
|
||||
logger.info("Stopping %s expiry at %d " % (dbChannel.id, dbChannel.expiration))
|
||||
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:
|
||||
service = getServiceObject()
|
||||
service = get_service_object()
|
||||
|
||||
allChannels = GCalPushChannel.objects.all()
|
||||
if len(allChannels) == 0:
|
||||
all_channels = GCalPushChannel.objects.all()
|
||||
if len(all_channels) == 0:
|
||||
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. "
|
||||
"All channels are closed and one new is created. ")
|
||||
stopAllGCalSubscriptions(service)
|
||||
checkGCalSubscription()
|
||||
allChannels = GCalPushChannel.objects.all()
|
||||
stop_all_gcal_subscriptions(service)
|
||||
check_gcal_subscription()
|
||||
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"
|
||||
"Got (%s,%s,%s) "
|
||||
"expected (%s,%s,%s) "
|
||||
"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)
|
||||
GCalPushChannel.stop(service, channelToStop.toGChannel())
|
||||
channel_to_stop = GCalPushChannel(id=channel_id, resource_id=resource_id, token=token)
|
||||
GCalPushChannel.stop(service, channel_to_stop.to_google_channel())
|
||||
|
||||
return False
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
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):
|
||||
help = 'Checks if the GCal notification channel is still active'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
print ( "Checking Subscription")
|
||||
checkGCalSubscription()
|
||||
print("Checking Subscription")
|
||||
check_gcal_subscription()
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
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):
|
||||
help = 'Delete all events in the google calendar created by this app'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
print ("Deleting all GCal Events.")
|
||||
nrOfDeletedEvents = deleteAllGCalEvents()
|
||||
print ("Deleted %d events. To Restore them from local database run gcal_sync" % (nrOfDeletedEvents, ) )
|
||||
print("Deleting all GCal Events.")
|
||||
nr_of_deleted_events = delete_all_gcal_events()
|
||||
print("Deleted %d events. To Restore them from local database run gcal_sync" % (nr_of_deleted_events,))
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
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):
|
||||
help = 'Stops all GCal subscriptions'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
stopAllGCalSubscriptions()
|
||||
stop_all_gcal_subscriptions()
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
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):
|
||||
help = 'Synchronize Google Calendar with locally stored Events'
|
||||
|
||||
def handle_noargs(self, **options):
|
||||
print ( "Running Sync")
|
||||
created, deleted = syncFromLocalToGoogle()
|
||||
print ( "Created %d and deleted %d events" % (created,deleted) )
|
||||
print("Running Sync")
|
||||
created, deleted = sync_from_local_to_google()
|
||||
print("Created %d and deleted %d events" % (created, deleted))
|
||||
|
|
|
@ -32,28 +32,28 @@ class GCalPushChannel(models.Model):
|
|||
resource_id = models.CharField(max_length=128)
|
||||
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)
|
||||
|
||||
@staticmethod
|
||||
def fromGChannel(gChannel):
|
||||
return GCalPushChannel(id=gChannel.id,
|
||||
address=gChannel.address,
|
||||
token=gChannel.token,
|
||||
expiration=gChannel.expiration,
|
||||
resource_id=gChannel.resource_id)
|
||||
def from_google_channel(google_channel):
|
||||
return GCalPushChannel(id=google_channel.id,
|
||||
address=google_channel.address,
|
||||
token=google_channel.token,
|
||||
expiration=google_channel.expiration,
|
||||
resource_id=google_channel.resource_id)
|
||||
|
||||
@staticmethod
|
||||
def createNew(callbackUrl, service, ttl=None):
|
||||
gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callbackUrl, params={'ttl': int(ttl)})
|
||||
def create_new(callback_url, service, ttl=None):
|
||||
gChannel = Channel('web_hook', str(uuid.uuid4()), 'blechreizGcal', callback_url, params={'ttl': int(ttl)})
|
||||
response = service.events().watch(calendarId='primary', body=gChannel.body()).execute()
|
||||
gChannel.update(response)
|
||||
|
||||
dbChannel = GCalPushChannel.fromGChannel(gChannel)
|
||||
dbChannel = GCalPushChannel.from_google_channel(gChannel)
|
||||
dbChannel.save()
|
||||
|
||||
@staticmethod
|
||||
def stop(service, gChannel):
|
||||
channelService = service.channels()
|
||||
channelService.stop(body=gChannel.body()).execute()
|
||||
GCalPushChannel.fromGChannel(gChannel).delete()
|
||||
def stop(service, google_channel):
|
||||
channel_service = service.channels()
|
||||
channel_service.stop(body=google_channel.body()).execute()
|
||||
GCalPushChannel.from_google_channel(google_channel).delete()
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from django.db.models.signals import post_save, pre_delete
|
||||
from django.dispatch import receiver
|
||||
from eventplanner.models import Event, EventParticipation
|
||||
from eventplanner_gcal.google_sync import getServiceObject, \
|
||||
createGCalEvent, deleteGCalEvent, updateGCalEvent, onGcalEventCreated
|
||||
from eventplanner_gcal.google_sync import get_service_object, \
|
||||
create_gcal_event, delete_gcal_event, update_gcal_event, on_gcal_event_created
|
||||
|
||||
import logging
|
||||
|
||||
|
@ -22,13 +22,13 @@ def event_post_save_handler(**kwargs):
|
|||
try:
|
||||
if created:
|
||||
logger.info("Creating Gcal event")
|
||||
response = createGCalEvent(getServiceObject(), event).execute()
|
||||
onGcalEventCreated(None, response, None)
|
||||
response = create_gcal_event(get_service_object(), event).execute()
|
||||
on_gcal_event_created(None, response, None)
|
||||
else:
|
||||
logger.info("Updating Gcal event")
|
||||
updateGCalEvent(getServiceObject(), event).execute()
|
||||
except:
|
||||
logger.error("Error updating Gcal event")
|
||||
update_gcal_event(get_service_object(), event).execute()
|
||||
except Exception as e:
|
||||
logger.error("Error updating Gcal event" + str(e))
|
||||
|
||||
|
||||
@receiver(pre_delete, sender=Event)
|
||||
|
@ -36,9 +36,9 @@ def event_pre_delete_handler(**kwargs):
|
|||
try:
|
||||
event = kwargs['instance']
|
||||
logger.info("Deleting GCAL event")
|
||||
deleteGCalEvent(getServiceObject(), event).execute()
|
||||
except:
|
||||
logger.error("Error deleting GCAL event")
|
||||
delete_gcal_event(get_service_object(), event).execute()
|
||||
except Exception as e:
|
||||
logger.error("Error deleting GCAL event" + str(e))
|
||||
|
||||
|
||||
@receiver(post_save, sender=EventParticipation)
|
||||
|
@ -46,6 +46,6 @@ def participation_post_save_handler(**kwargs):
|
|||
try:
|
||||
participation = kwargs['instance']
|
||||
logger.info("Participation post save -> update gcal")
|
||||
updateGCalEvent(getServiceObject(), participation.event).execute()
|
||||
except:
|
||||
logger.error("Error deleting GCAL event")
|
||||
update_gcal_event(get_service_object(), participation.event).execute()
|
||||
except Exception as e:
|
||||
logger.error("Error deleting GCAL event" + str(e))
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
from django.conf.urls import url
|
||||
|
||||
from .views import runSync, gcalApiCallback, manage
|
||||
from .views import run_sync, gcal_api_callback, manage
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^runSync$', runSync),
|
||||
url(r'^gcalApiCallback$', gcalApiCallback),
|
||||
url(r'^runSync$', run_sync),
|
||||
url(r'^gcalApiCallback$', gcal_api_callback),
|
||||
url(r'^manage$', manage),
|
||||
]
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
from django.shortcuts import redirect
|
||||
from eventplanner_gcal.google_sync import syncFromGoogleToLocal, syncFromLocalToGoogle
|
||||
from django.shortcuts import redirect
|
||||
from eventplanner_gcal.google_sync import sync_from_google_to_local, sync_from_local_to_google
|
||||
from django.http import HttpResponse
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
from pprint import pformat
|
||||
from eventplanner_gcal.google_sync import checkIfGoogleCallbackIsValid
|
||||
from eventplanner_gcal.google_sync import check_if_google_callback_is_valid
|
||||
from eventplanner_gcal.models import UserGCalCoupling
|
||||
from django.shortcuts import render
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger( __name__ )
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def runSync( request ):
|
||||
syncFromLocalToGoogle()
|
||||
def run_sync(request):
|
||||
sync_from_local_to_google()
|
||||
return redirect("/")
|
||||
|
||||
|
||||
def manage( request ):
|
||||
|
||||
def manage(request):
|
||||
if request.method == 'POST':
|
||||
if request.POST['activate'] == "1":
|
||||
UserGCalCoupling.objects.filter( user=request.user ).delete()
|
||||
c = UserGCalCoupling( user=request.user, email = request.POST['email'] )
|
||||
UserGCalCoupling.objects.filter(user=request.user).delete()
|
||||
c = UserGCalCoupling(user=request.user, email=request.POST['email'])
|
||||
c.save()
|
||||
syncFromLocalToGoogle()
|
||||
sync_from_local_to_google()
|
||||
else:
|
||||
UserGCalCoupling.objects.filter( user=request.user ).delete()
|
||||
syncFromLocalToGoogle()
|
||||
UserGCalCoupling.objects.filter(user=request.user).delete()
|
||||
sync_from_local_to_google()
|
||||
|
||||
context = {}
|
||||
userCoupling = UserGCalCoupling.objects.filter( user = request.user )
|
||||
context['enabled'] = len(userCoupling)
|
||||
assert( len(userCoupling) < 2 )
|
||||
if len(userCoupling) == 1:
|
||||
context['mail'] = userCoupling[0].email
|
||||
user_coupling = UserGCalCoupling.objects.filter(user=request.user)
|
||||
context['enabled'] = len(user_coupling)
|
||||
assert (len(user_coupling) < 2)
|
||||
if len(user_coupling) == 1:
|
||||
context['mail'] = user_coupling[0].email
|
||||
|
||||
return render( request, 'eventplanner_gcal/management.html', context )
|
||||
return render(request, 'eventplanner_gcal/management.html', context)
|
||||
|
||||
|
||||
@csrf_exempt
|
||||
def gcalApiCallback( request ):
|
||||
def gcal_api_callback(request):
|
||||
token = ""
|
||||
if 'HTTP_X_GOOG_CHANNEL_TOKEN' in request.META: token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
|
||||
channelID = ""
|
||||
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']
|
||||
if 'HTTP_X_GOOG_CHANNEL_TOKEN' in request.META:
|
||||
token = request.META['HTTP_X_GOOG_CHANNEL_TOKEN']
|
||||
|
||||
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:
|
||||
return HttpResponse('<h1>Old Channel - no update triggered</h1>')
|
||||
|
||||
|
||||
logger.info( "Received Google Callback with the following headers Token: %s ID %s ResID %s " % ( token, channelID, resourceID ) )
|
||||
result = syncFromGoogleToLocal()
|
||||
logger.info("Finished processing callback from GCal - New Information present: %d " %(result, ) )
|
||||
logger.info("Received Google Callback with the following headers Token: "
|
||||
"%s ID %s ResID %s " % (token, channel_id, resource_id))
|
||||
result = sync_from_google_to_local()
|
||||
logger.info("Finished processing callback from GCal - New Information present: %d " % (result,))
|
||||
return HttpResponse('<h1>Callback successful</h1>')
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue