annotate gpp/gcalendar/calendar.py @ 19:aa2b41c5212b

First steps at scratching out a home page. Will need to develop some template tags now to fill it out.
author Brian Neal <bgneal@gmail.com>
date Sun, 19 Apr 2009 20:10:00 +0000
parents dbd703f7d63a
children cce1fc3f8752
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.
gremmie@1 4 """
gremmie@1 5 import datetime
gremmie@1 6 import pytz
gremmie@1 7
gremmie@1 8 from django.utils.tzinfo import FixedOffset
gremmie@1 9 from gdata.calendar.service import CalendarService
gremmie@1 10 from gdata.calendar import CalendarEventFeed
gremmie@1 11 from gdata.calendar import CalendarEventEntry
gremmie@1 12 from gdata.calendar import Who
gremmie@1 13 from gdata.calendar import Where
gremmie@1 14 from gdata.calendar import When
gremmie@1 15 from gdata.service import BadAuthentication
gremmie@1 16 import atom
gremmie@1 17
gremmie@1 18 from gcalendar.models import Event
gremmie@1 19
gremmie@1 20
gremmie@1 21 class CalendarError(Exception):
gremmie@1 22 def __init__(self, errs):
gremmie@1 23 self.errs = errs
gremmie@1 24
gremmie@1 25 def __str__(self):
gremmie@1 26 return repr(self.errs)
gremmie@1 27
gremmie@1 28
gremmie@1 29 class Calendar(object):
gremmie@1 30 DATE_FMT = '%Y-%m-%d'
gremmie@1 31 DATE_TIME_FMT = DATE_FMT + 'T%H:%M:%S'
gremmie@1 32 DATE_TIME_TZ_FMT = DATE_TIME_FMT + '.000Z'
gremmie@1 33
gremmie@1 34 def __init__(self, email, password, calendar_id='default'):
gremmie@1 35 self.client = CalendarService()
gremmie@1 36 self.client.email = email
gremmie@1 37 self.client.password = password
gremmie@1 38 self.client.source = 'Google-Calendar_Python_GCalendar'
gremmie@1 39 self.insert_feed = '/calendar/feeds/%s/private/full' % calendar_id
gremmie@1 40 self.batch_feed = '%s/batch' % self.insert_feed
gremmie@1 41 try:
gremmie@1 42 self.client.ProgrammaticLogin()
gremmie@1 43 except BadAuthentication:
gremmie@1 44 raise CalendarError(['Incorrect password'])
gremmie@1 45 except Exception, e:
gremmie@1 46 raise CalendarError([e])
gremmie@1 47
gremmie@1 48 def sync_events(self, qs):
gremmie@1 49 request_feed = CalendarEventFeed()
gremmie@1 50 for model in qs:
gremmie@1 51 if model.status == Event.NEW_APRV:
gremmie@1 52 event = CalendarEventEntry()
gremmie@1 53 request_feed.AddInsert(entry=self._populate_event(model, event))
gremmie@1 54 elif model.status == Event.EDIT_APRV:
gremmie@1 55 event = self._retrieve_event(model)
gremmie@1 56 request_feed.AddUpdate(entry=self._populate_event(model, event))
gremmie@1 57 elif model.status == Event.DEL_APRV:
gremmie@1 58 event = self._retrieve_event(model)
gremmie@1 59 request_feed.AddDelete(entry=event)
gremmie@1 60 else:
gremmie@1 61 assert False, 'unexpected status in sync_events'
gremmie@1 62
gremmie@1 63 response_feed = self.client.ExecuteBatch(request_feed, self.batch_feed)
gremmie@1 64 err_msgs = []
gremmie@1 65 for entry in response_feed.entry:
gremmie@1 66 i = int(entry.batch_id.text)
gremmie@1 67 code = int(entry.batch_status.code)
gremmie@1 68
gremmie@1 69 error = False
gremmie@1 70 if qs[i].status == Event.NEW_APRV or qs[i].status == Event.EDIT_APRV:
gremmie@1 71 if (code == 201 and qs[i].status == Event.NEW_APRV) or \
gremmie@1 72 (code == 200 and qs[i].status == Event.EDIT_APRV):
gremmie@1 73 qs[i].google_id = entry.id.text
gremmie@1 74 qs[i].status = Event.ON_CAL
gremmie@1 75 qs[i].save()
gremmie@1 76 else:
gremmie@1 77 error = True
gremmie@1 78 elif qs[i].status == Event.DEL_APRV:
gremmie@1 79 if code == 200:
gremmie@1 80 qs[i].delete()
gremmie@1 81 else:
gremmie@1 82 error = True
gremmie@1 83
gremmie@1 84 if error:
gremmie@1 85 err_msgs.append('%s - (%d) %s' % \
gremmie@1 86 (qs[i].title, code, entry.batch_status.reason))
gremmie@1 87
gremmie@1 88 if len(err_msgs) > 0:
gremmie@1 89 raise CalendarError(err_msgs)
gremmie@1 90
gremmie@1 91 def _retrieve_event(self, model):
gremmie@1 92 try:
gremmie@1 93 event = self.client.GetCalendarEventEntry(model.google_id)
gremmie@1 94 except Exception:
gremmie@1 95 raise CalendarError(['Could not retrieve event from Google: %s' % model.what])
gremmie@1 96 return event
gremmie@1 97
gremmie@1 98 def _populate_event(self, model, event):
gremmie@1 99 """Populates a gdata event from an Event model object."""
gremmie@1 100 event.title = atom.Title(text=model.what)
gremmie@1 101 event.content = atom.Content(text=model.html)
gremmie@1 102 event.where = [Where(value_string=model.where)]
gremmie@1 103 event.who = [Who(name=model.user.username, email=model.user.email)]
gremmie@1 104
gremmie@1 105 if model.all_day:
gremmie@1 106 start_time = self._make_time(model.start_date)
gremmie@1 107 if model.start_date == model.end_date:
gremmie@1 108 end_time = None
gremmie@1 109 else:
gremmie@1 110 end_time = self._make_time(model.end_date)
gremmie@1 111 else:
gremmie@1 112 start_time = self._make_time(model.start_date, model.start_time, model.time_zone)
gremmie@1 113 end_time = self._make_time(model.end_date, model.end_time, model.time_zone)
gremmie@1 114
gremmie@1 115 event.when = [When(start_time=start_time, end_time=end_time)]
gremmie@1 116 return event
gremmie@1 117
gremmie@1 118 def _make_time(self, date, time=None, tz_name=None):
gremmie@1 119 """
gremmie@1 120 Returns the gdata formatted date/time string given a date, optional time,
gremmie@1 121 and optional time zone name (e.g. 'US/Pacific'). If the time zone name is None,
gremmie@1 122 no time zone info will be added to the string.
gremmie@1 123 """
gremmie@1 124
gremmie@1 125 if time is not None:
gremmie@1 126 d = datetime.datetime.combine(date, time)
gremmie@1 127 else:
gremmie@1 128 d = datetime.datetime(date.year, date.month, date.day)
gremmie@1 129
gremmie@1 130 if time is None:
gremmie@1 131 s = d.strftime(self.DATE_FMT)
gremmie@1 132 elif tz_name is None:
gremmie@1 133 s = d.strftime(self.DATE_TIME_FMT)
gremmie@1 134 else:
gremmie@1 135 try:
gremmie@1 136 tz = pytz.timezone(tz_name)
gremmie@1 137 except pytz.UnknownTimeZoneError:
gremmie@1 138 raise CalendarError(['Invalid time zone: %s' (tz_name,)])
gremmie@1 139 local = tz.localize(d)
gremmie@1 140 zulu = local.astimezone(FixedOffset(0))
gremmie@1 141 s = zulu.strftime(self.DATE_TIME_TZ_FMT)
gremmie@1 142
gremmie@1 143 return s
gremmie@1 144
gremmie@1 145 # vim: ts=4 sw=4