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
|