annotate gcalendar/calendar.py @ 787:7e17b9e45356

Merge.
author Brian Neal <bgneal@gmail.com>
date Sat, 17 May 2014 12:17:14 -0500
parents ee87ea74d46b
children 9165edfb1709
rev   line source
gremmie@1 1 """
gremmie@1 2 This file contains the calendar class wich abstracts the Google gdata API for working with
gremmie@1 3 Google Calendars.
bgneal@458 4
gremmie@1 5 """
gremmie@1 6 import datetime
gremmie@1 7 import pytz
gremmie@1 8
gremmie@1 9 from django.utils.tzinfo import FixedOffset
bgneal@458 10 from gdata.calendar.client import CalendarClient
bgneal@458 11 from gdata.calendar.data import (CalendarEventEntry, CalendarEventFeed,
bgneal@458 12 CalendarWhere, When, EventWho)
bgneal@458 13 import atom.data
gremmie@1 14
gremmie@1 15 from gcalendar.models import Event
gremmie@1 16
gremmie@1 17
gremmie@1 18 class CalendarError(Exception):
bgneal@68 19 def __init__(self, msg):
bgneal@68 20 self.msg = msg
gremmie@1 21
gremmie@1 22 def __str__(self):
bgneal@68 23 return repr(self.msg)
gremmie@1 24
gremmie@1 25
gremmie@1 26 class Calendar(object):
gremmie@1 27 DATE_FMT = '%Y-%m-%d'
gremmie@1 28 DATE_TIME_FMT = DATE_FMT + 'T%H:%M:%S'
gremmie@1 29 DATE_TIME_TZ_FMT = DATE_TIME_FMT + '.000Z'
gremmie@1 30
bgneal@458 31 def __init__(self, source=None, calendar_id='default', access_token=None):
bgneal@458 32 self.client = CalendarClient(source=source, auth_token=access_token)
bgneal@458 33
bgneal@458 34 self.insert_feed = ('https://www.google.com/calendar/feeds/'
bgneal@458 35 '%s/private/full' % calendar_id)
gremmie@1 36 self.batch_feed = '%s/batch' % self.insert_feed
gremmie@1 37
gremmie@1 38 def sync_events(self, qs):
gremmie@1 39 request_feed = CalendarEventFeed()
gremmie@1 40 for model in qs:
gremmie@1 41 if model.status == Event.NEW_APRV:
gremmie@1 42 event = CalendarEventEntry()
gremmie@1 43 request_feed.AddInsert(entry=self._populate_event(model, event))
gremmie@1 44 elif model.status == Event.EDIT_APRV:
gremmie@1 45 event = self._retrieve_event(model)
gremmie@1 46 request_feed.AddUpdate(entry=self._populate_event(model, event))
gremmie@1 47 elif model.status == Event.DEL_APRV:
gremmie@1 48 event = self._retrieve_event(model)
gremmie@1 49 request_feed.AddDelete(entry=event)
gremmie@1 50 else:
gremmie@1 51 assert False, 'unexpected status in sync_events'
gremmie@1 52
bgneal@68 53 try:
bgneal@68 54 response_feed = self.client.ExecuteBatch(request_feed, self.batch_feed)
bgneal@68 55 except Exception, e:
bgneal@68 56 raise CalendarError('ExecuteBatch exception: %s' % e)
bgneal@68 57
gremmie@1 58 err_msgs = []
gremmie@1 59 for entry in response_feed.entry:
gremmie@1 60 i = int(entry.batch_id.text)
gremmie@1 61 code = int(entry.batch_status.code)
gremmie@1 62
gremmie@1 63 error = False
bgneal@458 64 if qs[i].status == Event.NEW_APRV:
bgneal@458 65 if code == 201:
bgneal@458 66 qs[i].status = Event.ON_CAL
bgneal@458 67 qs[i].google_id = entry.GetEditLink().href
bgneal@228 68 qs[i].google_url = entry.GetHtmlLink().href
bgneal@458 69 qs[i].save()
bgneal@458 70 qs[i].notify_on_calendar()
bgneal@458 71 else:
bgneal@458 72 error = True
bgneal@458 73
bgneal@458 74 elif qs[i].status == Event.EDIT_APRV:
bgneal@458 75 if code == 200:
gremmie@1 76 qs[i].status = Event.ON_CAL
gremmie@1 77 qs[i].save()
gremmie@1 78 else:
gremmie@1 79 error = True
bgneal@458 80
gremmie@1 81 elif qs[i].status == Event.DEL_APRV:
gremmie@1 82 if code == 200:
gremmie@1 83 qs[i].delete()
gremmie@1 84 else:
gremmie@1 85 error = True
gremmie@1 86
gremmie@1 87 if error:
bgneal@124 88 err_msgs.append('%s - (%d) %s' % (
bgneal@124 89 qs[i].what, code, entry.batch_status.reason))
gremmie@1 90
gremmie@1 91 if len(err_msgs) > 0:
bgneal@68 92 raise CalendarError(', '.join(err_msgs))
gremmie@1 93
gremmie@1 94 def _retrieve_event(self, model):
gremmie@1 95 try:
bgneal@458 96 event = self.client.GetEventEntry(model.google_id)
bgneal@68 97 except Exception, e:
bgneal@68 98 raise CalendarError('Could not retrieve event from Google: %s, %s' \
bgneal@68 99 % (model.what, e))
gremmie@1 100 return event
gremmie@1 101
gremmie@1 102 def _populate_event(self, model, event):
gremmie@1 103 """Populates a gdata event from an Event model object."""
bgneal@458 104 event.title = atom.data.Title(text=model.what)
bgneal@458 105 event.content = atom.data.Content(text=model.html)
bgneal@458 106 event.where = [CalendarWhere(value=model.where)]
bgneal@458 107 event.who = [EventWho(email=model.user.email)]
gremmie@1 108
gremmie@1 109 if model.all_day:
gremmie@1 110 start_time = self._make_time(model.start_date)
gremmie@1 111 if model.start_date == model.end_date:
gremmie@1 112 end_time = None
gremmie@1 113 else:
gremmie@1 114 end_time = self._make_time(model.end_date)
gremmie@1 115 else:
gremmie@1 116 start_time = self._make_time(model.start_date, model.start_time, model.time_zone)
gremmie@1 117 end_time = self._make_time(model.end_date, model.end_time, model.time_zone)
gremmie@1 118
bgneal@458 119 event.when = [When(start=start_time, end=end_time)]
gremmie@1 120 return event
bgneal@458 121
gremmie@1 122 def _make_time(self, date, time=None, tz_name=None):
gremmie@1 123 """
gremmie@1 124 Returns the gdata formatted date/time string given a date, optional time,
gremmie@1 125 and optional time zone name (e.g. 'US/Pacific'). If the time zone name is None,
gremmie@1 126 no time zone info will be added to the string.
gremmie@1 127 """
gremmie@1 128
gremmie@1 129 if time is not None:
gremmie@1 130 d = datetime.datetime.combine(date, time)
gremmie@1 131 else:
gremmie@1 132 d = datetime.datetime(date.year, date.month, date.day)
gremmie@1 133
gremmie@1 134 if time is None:
gremmie@1 135 s = d.strftime(self.DATE_FMT)
gremmie@1 136 elif tz_name is None:
gremmie@1 137 s = d.strftime(self.DATE_TIME_FMT)
gremmie@1 138 else:
gremmie@1 139 try:
gremmie@1 140 tz = pytz.timezone(tz_name)
gremmie@1 141 except pytz.UnknownTimeZoneError:
bgneal@68 142 raise CalendarError('Invalid time zone: %s' (tz_name,))
gremmie@1 143 local = tz.localize(d)
gremmie@1 144 zulu = local.astimezone(FixedOffset(0))
gremmie@1 145 s = zulu.strftime(self.DATE_TIME_TZ_FMT)
gremmie@1 146
gremmie@1 147 return s
gremmie@1 148