comparison gcalendar/calendar.py @ 581:ee87ea74d46b

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