from django.db import models from eventplanner.models import Event, EventParticipation from apiclient.discovery import build from apiclient.http import BatchHttpRequest from oauth2client.file import Storage import httplib2 from django.conf import settings import logging import datetime from django.contrib.auth.models import User logger = logging.getLogger(__name__) class GCalMapping( models.Model ): gcal_id = models.CharField( max_length=64 ) event = models.OneToOneField( Event, primary_key=True ) def init( gcal_settings ): """Creates a API service object required by the following synchronization functions""" storage = Storage( gcal_settings['credentials_file'] ) credentials = storage.get() if credentials is None or credentials.invalid == True: logger.error("Unable to initialize Google Calendar coupling. Check your settings!") return None http = httplib2.Http() http = credentials.authorize( http ) return build( serviceName='calendar', version='v3', http=http, developerKey=gcal_settings['developerKey'] ) service = init( settings.GCAL_COUPLING ) def createAttendeesObj( event ): result = [] for u in User.objects.all(): if u.email.endswith( "@gmail.com") or u.email.endswith("@googlemail.com"): participation = EventParticipation.get_or_create( u, event ) status = "tentative" if participation.status == 'Yes': status = "accepted" if participation.status == 'No' : status = "declined" o = { 'id': u.email, 'email': u.email, 'displayName': u.username, 'comment': participation.comment, 'responseStatus': status, } result.append( o ) return result def createEvent( event, timezone="Europe/Berlin" ): if service is None: logger.error("createEvent: Google API connection not configured") return def createDateTimeObj( date, time ): if time is None: return { 'date': unicode(date), 'timeZone': timezone } else: return { 'dateTime': unicode(date) + 'T' + unicode(time) , 'timeZone': timezone } startDate = event.date endDate = event.end_date if endDate is None: endDate = startDate startTime = event.meeting_time if startTime is None: startTime = event.time if startTime is None: endTime = None else: endTime = datetime.time( 22, 30 ) googleEvent = { 'summary': unicode(settings.GCAL_COUPLING['eventPrefix'] + event.title), 'description': unicode(event.desc), 'location': unicode(event.location), 'start': createDateTimeObj( startDate, startTime ), 'end' : createDateTimeObj( endDate, endTime ), 'extendedProperties': { 'private': { 'blechreizEvent': 'true', 'blechreizID': event.id, } }, 'attendees': createAttendeesObj( event ), } return service.events().insert(calendarId='primary', body=googleEvent) def getAllEvents(pageToken=None): events = service.events().list( calendarId='primary', singleEvents=True, maxResults=1000, orderBy='startTime', timeMin='2000-01-01T00:00:00-00:00', timeMax='2100-01-01T00:00:00-00:00', pageToken=pageToken, privateExtendedProperty='blechreizEvent=true', ).execute() return events['items'] def onGcalEventCreated( request_id, response, exception ): if exception is not None: print ( "response " + str( response ) ) raise exception googleId = response['id'] djangoId = response['extendedProperties']['private']['blechreizID'] mapping = GCalMapping( gcal_id = googleId, event = Event.objects.get( pk=djangoId ) ) mapping.save() def syncGCalEvents(): if service is None: logger.error("syncGCalEvents: Google API connection not configured") return allEvents = getAllEvents() eventsAtGoogle_djangoID = set() eventsAtGoogle_googleID = set() for gcalEv in allEvents: eventsAtGoogle_djangoID.add( int(gcalEv['extendedProperties']['private']['blechreizID'] ) ) eventsAtGoogle_googleID.add( gcalEv['id'] ) localEvents_djangoID = set( Event. objects.all().values_list('pk' , flat=True) ) localEvents_googleID = set( GCalMapping.objects.all().values_list('gcal_id', flat=True) ) eventsToCreate_djangoID = localEvents_djangoID - eventsAtGoogle_djangoID eventsToDelete_googleID = eventsAtGoogle_googleID - localEvents_googleID batch = BatchHttpRequest() batchIsEmpty = True for eventDjangoID in eventsToCreate_djangoID: batch.add( createEvent( Event.objects.get( pk=eventDjangoID ) ), callback=onGcalEventCreated ) batchIsEmpty=False for eventGoogleID in eventsToDelete_googleID: batch.add( service.events().delete(calendarId='primary', eventId=eventGoogleID) ) batchIsEmpty=False if not batchIsEmpty: batch.execute()