Mercurial > public > sg101
diff gcalendar/calendar.py @ 857:9165edfb1709
For issue #80, use new Google Calendar v3 API.
author | Brian Neal <bgneal@gmail.com> |
---|---|
date | Sat, 22 Nov 2014 13:27:13 -0600 |
parents | ee87ea74d46b |
children | 4dc6a452afd3 |
line wrap: on
line diff
--- a/gcalendar/calendar.py Thu Nov 20 18:47:54 2014 -0600 +++ b/gcalendar/calendar.py Sat Nov 22 13:27:13 2014 -0600 @@ -1,122 +1,120 @@ """ -This file contains the calendar class wich abstracts the Google gdata API for working with -Google Calendars. +This file contains the calendar class wich abstracts the Google API for working +with Google Calendars. """ import datetime import pytz +import httplib2 +from apiclient.discovery import build +from apiclient.http import BatchHttpRequest from django.utils.tzinfo import FixedOffset -from gdata.calendar.client import CalendarClient -from gdata.calendar.data import (CalendarEventEntry, CalendarEventFeed, - CalendarWhere, When, EventWho) -import atom.data from gcalendar.models import Event class CalendarError(Exception): - def __init__(self, msg): - self.msg = msg + """Calendar exception base class.""" - def __str__(self): - return repr(self.msg) + +def _make_err_msg(event, exception): + """Returns an error message string from the given Event and exception.""" + return '%s - %s' % (event.what, exception) class Calendar(object): - DATE_FMT = '%Y-%m-%d' - DATE_TIME_FMT = DATE_FMT + 'T%H:%M:%S' - DATE_TIME_TZ_FMT = DATE_TIME_FMT + '.000Z' + DATE_TIME_FMT = '%Y-%m-%dT%H:%M:%S' + DATE_TIME_TZ_FMT = DATE_TIME_FMT + 'Z' - def __init__(self, source=None, calendar_id='default', access_token=None): - self.client = CalendarClient(source=source, auth_token=access_token) - - self.insert_feed = ('https://www.google.com/calendar/feeds/' - '%s/private/full' % calendar_id) - self.batch_feed = '%s/batch' % self.insert_feed + def __init__(self, calendar_id='primary', credentials=None): + self.calendar_id = calendar_id + http = httplib2.Http() + if credentials: + http = credentials.authorize(http) + self.service = build('calendar', 'v3', http=http) def sync_events(self, qs): - request_feed = CalendarEventFeed() - for model in qs: - if model.status == Event.NEW_APRV: - event = CalendarEventEntry() - request_feed.AddInsert(entry=self._populate_event(model, event)) - elif model.status == Event.EDIT_APRV: - event = self._retrieve_event(model) - request_feed.AddUpdate(entry=self._populate_event(model, event)) - elif model.status == Event.DEL_APRV: - event = self._retrieve_event(model) - request_feed.AddDelete(entry=event) + """Process the pending events in a batch request to the Google calendar + API. + """ + batch = BatchHttpRequest() + + err_msgs = [] + def insert_callback(request_id, event, exception): + n = int(request_id) + if not exception: + qs[n].status = Event.ON_CAL + qs[n].google_id = event['id'] + qs[n].google_url = event['htmlLink'] + qs[n].save() + qs[n].notify_on_calendar() else: - assert False, 'unexpected status in sync_events' + err_msgs.append(_make_err_msg(qs[n], exception)) + + def update_callback(request_id, event, exception): + n = int(request_id) + if not exception: + qs[n].status = Event.ON_CAL + qs[n].save() + else: + err_msgs.append(_make_err_msg(qs[n], exception)) + + def delete_callback(request_id, response, exception): + n = int(request_id) + if not exception: + qs[n].delete() + else: + err_msgs.append(_make_err_msg(qs[n], exception)) + + for n, event in enumerate(qs): + if event.status == Event.NEW_APRV: + batch.add(self.service.events().insert(calendarId=self.calendar_id, + body=self._make_event(event)), + callback=insert_callback, + request_id=str(n)) + elif event.status == Event.EDIT_APRV: + batch.add(self.service.events().update(calendarId=self.calendar_id, + eventId=event.google_id, + body=self._make_event(event)), + callback=update_callback, + request_id=str(n)) + elif event.status == Event.DEL_APRV: + batch.add(self.service.events().delete(calendarId=self.calendar_id, + eventId=event.google_id), + callback=delete_callback, + request_id=str(n)) + else: + raise CalendarError("Invalid event status: %s" % event.status) try: - response_feed = self.client.ExecuteBatch(request_feed, self.batch_feed) - except Exception, e: - raise CalendarError('ExecuteBatch exception: %s' % e) + batch.execute() + except Exception as ex: + raise CalendarError('Batch exception: %s' % ex) - err_msgs = [] - for entry in response_feed.entry: - i = int(entry.batch_id.text) - code = int(entry.batch_status.code) - - error = False - if qs[i].status == Event.NEW_APRV: - if code == 201: - qs[i].status = Event.ON_CAL - qs[i].google_id = entry.GetEditLink().href - qs[i].google_url = entry.GetHtmlLink().href - qs[i].save() - qs[i].notify_on_calendar() - else: - error = True - - elif qs[i].status == Event.EDIT_APRV: - if code == 200: - qs[i].status = Event.ON_CAL - qs[i].save() - else: - error = True - - elif qs[i].status == Event.DEL_APRV: - if code == 200: - qs[i].delete() - else: - error = True - - if error: - err_msgs.append('%s - (%d) %s' % ( - qs[i].what, code, entry.batch_status.reason)) - - if len(err_msgs) > 0: + if err_msgs: raise CalendarError(', '.join(err_msgs)) - def _retrieve_event(self, model): - try: - event = self.client.GetEventEntry(model.google_id) - except Exception, e: - raise CalendarError('Could not retrieve event from Google: %s, %s' \ - % (model.what, e)) - return event - - def _populate_event(self, model, event): - """Populates a gdata event from an Event model object.""" - event.title = atom.data.Title(text=model.what) - event.content = atom.data.Content(text=model.html) - event.where = [CalendarWhere(value=model.where)] - event.who = [EventWho(email=model.user.email)] + def _make_event(self, model): + """Creates an event body from a model instance.""" + event = { + 'summary': model.what, + 'description': model.html, + 'location': model.where, + 'anyoneCanAddSelf': True, + } if model.all_day: - start_time = self._make_time(model.start_date) - if model.start_date == model.end_date: - end_time = None - else: - end_time = self._make_time(model.end_date) + start = {'date': model.start_date.isoformat()} + end = {'date': model.end_date.isoformat()} else: - start_time = self._make_time(model.start_date, model.start_time, model.time_zone) - end_time = self._make_time(model.end_date, model.end_time, model.time_zone) + start = {'dateTime': self._make_time(model.start_date, model.start_time, + model.time_zone)} + end = {'dateTime': self._make_time(model.end_date, model.end_time, + model.time_zone)} - event.when = [When(start=start_time, end=end_time)] + event['start'] = start + event['end'] = end return event def _make_time(self, date, time=None, tz_name=None): @@ -126,23 +124,20 @@ no time zone info will be added to the string. """ - if time is not None: + if time: d = datetime.datetime.combine(date, time) else: d = datetime.datetime(date.year, date.month, date.day) - if time is None: - s = d.strftime(self.DATE_FMT) - elif tz_name is None: + if not tz_name: s = d.strftime(self.DATE_TIME_FMT) else: try: tz = pytz.timezone(tz_name) except pytz.UnknownTimeZoneError: - raise CalendarError('Invalid time zone: %s' (tz_name,)) + raise CalendarError('Invalid time zone: %s' % tz_name) local = tz.localize(d) zulu = local.astimezone(FixedOffset(0)) s = zulu.strftime(self.DATE_TIME_TZ_FMT) return s -