annotate gcalendar/calendar.py @ 631:f36d1a168be7

For issue 27, disable login dialog button during POST. This seems to prevent multiple logins most of the time. You can still bang on the enter key and sometimes get more through.
author Brian Neal <bgneal@gmail.com>
date Wed, 14 Nov 2012 20:57:05 -0600
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