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
|