/gdata/calendar/service.py

http://radioappz.googlecode.com/ · Python · 595 lines · 490 code · 21 blank · 84 comment · 4 complexity · cbd1be6788901a38033c7106f1308acc MD5 · raw file

  1. #!/usr/bin/python
  2. #
  3. # Copyright (C) 2006 Google Inc.
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. """CalendarService extends the GDataService to streamline Google Calendar operations.
  17. CalendarService: Provides methods to query feeds and manipulate items. Extends
  18. GDataService.
  19. DictionaryToParamList: Function which converts a dictionary into a list of
  20. URL arguments (represented as strings). This is a
  21. utility function used in CRUD operations.
  22. """
  23. __author__ = 'api.vli (Vivian Li)'
  24. import urllib
  25. import gdata
  26. import atom.service
  27. import gdata.service
  28. import gdata.calendar
  29. import atom
  30. DEFAULT_BATCH_URL = ('http://www.google.com/calendar/feeds/default/private'
  31. '/full/batch')
  32. class Error(Exception):
  33. pass
  34. class RequestError(Error):
  35. pass
  36. class CalendarService(gdata.service.GDataService):
  37. """Client for the Google Calendar service."""
  38. def __init__(self, email=None, password=None, source=None,
  39. server='www.google.com', additional_headers=None, **kwargs):
  40. """Creates a client for the Google Calendar service.
  41. Args:
  42. email: string (optional) The user's email address, used for
  43. authentication.
  44. password: string (optional) The user's password.
  45. source: string (optional) The name of the user's application.
  46. server: string (optional) The name of the server to which a connection
  47. will be opened. Default value: 'www.google.com'.
  48. **kwargs: The other parameters to pass to gdata.service.GDataService
  49. constructor.
  50. """
  51. gdata.service.GDataService.__init__(
  52. self, email=email, password=password, service='cl', source=source,
  53. server=server, additional_headers=additional_headers, **kwargs)
  54. def GetCalendarEventFeed(self, uri='/calendar/feeds/default/private/full'):
  55. return self.Get(uri, converter=gdata.calendar.CalendarEventFeedFromString)
  56. def GetCalendarEventEntry(self, uri):
  57. return self.Get(uri, converter=gdata.calendar.CalendarEventEntryFromString)
  58. def GetCalendarListFeed(self, uri='/calendar/feeds/default/allcalendars/full'):
  59. return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString)
  60. def GetAllCalendarsFeed(self, uri='/calendar/feeds/default/allcalendars/full'):
  61. return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString)
  62. def GetOwnCalendarsFeed(self, uri='/calendar/feeds/default/owncalendars/full'):
  63. return self.Get(uri, converter=gdata.calendar.CalendarListFeedFromString)
  64. def GetCalendarListEntry(self, uri):
  65. return self.Get(uri, converter=gdata.calendar.CalendarListEntryFromString)
  66. def GetCalendarAclFeed(self, uri='/calendar/feeds/default/acl/full'):
  67. return self.Get(uri, converter=gdata.calendar.CalendarAclFeedFromString)
  68. def GetCalendarAclEntry(self, uri):
  69. return self.Get(uri, converter=gdata.calendar.CalendarAclEntryFromString)
  70. def GetCalendarEventCommentFeed(self, uri):
  71. return self.Get(uri, converter=gdata.calendar.CalendarEventCommentFeedFromString)
  72. def GetCalendarEventCommentEntry(self, uri):
  73. return self.Get(uri, converter=gdata.calendar.CalendarEventCommentEntryFromString)
  74. def Query(self, uri, converter=None):
  75. """Performs a query and returns a resulting feed or entry.
  76. Args:
  77. feed: string The feed which is to be queried
  78. Returns:
  79. On success, a GDataFeed or Entry depending on which is sent from the
  80. server.
  81. On failure, a RequestError is raised of the form:
  82. {'status': HTTP status code from server,
  83. 'reason': HTTP reason from the server,
  84. 'body': HTTP body of the server's response}
  85. """
  86. if converter:
  87. result = self.Get(uri, converter=converter)
  88. else:
  89. result = self.Get(uri)
  90. return result
  91. def CalendarQuery(self, query):
  92. if isinstance(query, CalendarEventQuery):
  93. return self.Query(query.ToUri(),
  94. converter=gdata.calendar.CalendarEventFeedFromString)
  95. elif isinstance(query, CalendarListQuery):
  96. return self.Query(query.ToUri(),
  97. converter=gdata.calendar.CalendarListFeedFromString)
  98. elif isinstance(query, CalendarEventCommentQuery):
  99. return self.Query(query.ToUri(),
  100. converter=gdata.calendar.CalendarEventCommentFeedFromString)
  101. else:
  102. return self.Query(query.ToUri())
  103. def InsertEvent(self, new_event, insert_uri, url_params=None,
  104. escape_params=True):
  105. """Adds an event to Google Calendar.
  106. Args:
  107. new_event: atom.Entry or subclass A new event which is to be added to
  108. Google Calendar.
  109. insert_uri: the URL to post new events to the feed
  110. url_params: dict (optional) Additional URL parameters to be included
  111. in the insertion request.
  112. escape_params: boolean (optional) If true, the url_parameters will be
  113. escaped before they are included in the request.
  114. Returns:
  115. On successful insert, an entry containing the event created
  116. On failure, a RequestError is raised of the form:
  117. {'status': HTTP status code from server,
  118. 'reason': HTTP reason from the server,
  119. 'body': HTTP body of the server's response}
  120. """
  121. return self.Post(new_event, insert_uri, url_params=url_params,
  122. escape_params=escape_params,
  123. converter=gdata.calendar.CalendarEventEntryFromString)
  124. def InsertCalendarSubscription(self, calendar, url_params=None,
  125. escape_params=True):
  126. """Subscribes the authenticated user to the provided calendar.
  127. Args:
  128. calendar: The calendar to which the user should be subscribed.
  129. url_params: dict (optional) Additional URL parameters to be included
  130. in the insertion request.
  131. escape_params: boolean (optional) If true, the url_parameters will be
  132. escaped before they are included in the request.
  133. Returns:
  134. On successful insert, an entry containing the subscription created
  135. On failure, a RequestError is raised of the form:
  136. {'status': HTTP status code from server,
  137. 'reason': HTTP reason from the server,
  138. 'body': HTTP body of the server's response}
  139. """
  140. insert_uri = '/calendar/feeds/default/allcalendars/full'
  141. return self.Post(calendar, insert_uri, url_params=url_params,
  142. escape_params=escape_params,
  143. converter=gdata.calendar.CalendarListEntryFromString)
  144. def InsertCalendar(self, new_calendar, url_params=None,
  145. escape_params=True):
  146. """Creates a new calendar.
  147. Args:
  148. new_calendar: The calendar to be created
  149. url_params: dict (optional) Additional URL parameters to be included
  150. in the insertion request.
  151. escape_params: boolean (optional) If true, the url_parameters will be
  152. escaped before they are included in the request.
  153. Returns:
  154. On successful insert, an entry containing the calendar created
  155. On failure, a RequestError is raised of the form:
  156. {'status': HTTP status code from server,
  157. 'reason': HTTP reason from the server,
  158. 'body': HTTP body of the server's response}
  159. """
  160. insert_uri = '/calendar/feeds/default/owncalendars/full'
  161. response = self.Post(new_calendar, insert_uri, url_params=url_params,
  162. escape_params=escape_params,
  163. converter=gdata.calendar.CalendarListEntryFromString)
  164. return response
  165. def UpdateCalendar(self, calendar, url_params=None,
  166. escape_params=True):
  167. """Updates a calendar.
  168. Args:
  169. calendar: The calendar which should be updated
  170. url_params: dict (optional) Additional URL parameters to be included
  171. in the insertion request.
  172. escape_params: boolean (optional) If true, the url_parameters will be
  173. escaped before they are included in the request.
  174. Returns:
  175. On successful insert, an entry containing the calendar created
  176. On failure, a RequestError is raised of the form:
  177. {'status': HTTP status code from server,
  178. 'reason': HTTP reason from the server,
  179. 'body': HTTP body of the server's response}
  180. """
  181. update_uri = calendar.GetEditLink().href
  182. response = self.Put(data=calendar, uri=update_uri, url_params=url_params,
  183. escape_params=escape_params,
  184. converter=gdata.calendar.CalendarListEntryFromString)
  185. return response
  186. def InsertAclEntry(self, new_entry, insert_uri, url_params=None,
  187. escape_params=True):
  188. """Adds an ACL entry (rule) to Google Calendar.
  189. Args:
  190. new_entry: atom.Entry or subclass A new ACL entry which is to be added to
  191. Google Calendar.
  192. insert_uri: the URL to post new entries to the ACL feed
  193. url_params: dict (optional) Additional URL parameters to be included
  194. in the insertion request.
  195. escape_params: boolean (optional) If true, the url_parameters will be
  196. escaped before they are included in the request.
  197. Returns:
  198. On successful insert, an entry containing the ACL entry created
  199. On failure, a RequestError is raised of the form:
  200. {'status': HTTP status code from server,
  201. 'reason': HTTP reason from the server,
  202. 'body': HTTP body of the server's response}
  203. """
  204. return self.Post(new_entry, insert_uri, url_params=url_params,
  205. escape_params=escape_params,
  206. converter=gdata.calendar.CalendarAclEntryFromString)
  207. def InsertEventComment(self, new_entry, insert_uri, url_params=None,
  208. escape_params=True):
  209. """Adds an entry to Google Calendar.
  210. Args:
  211. new_entry: atom.Entry or subclass A new entry which is to be added to
  212. Google Calendar.
  213. insert_uri: the URL to post new entrys to the feed
  214. url_params: dict (optional) Additional URL parameters to be included
  215. in the insertion request.
  216. escape_params: boolean (optional) If true, the url_parameters will be
  217. escaped before they are included in the request.
  218. Returns:
  219. On successful insert, an entry containing the comment created
  220. On failure, a RequestError is raised of the form:
  221. {'status': HTTP status code from server,
  222. 'reason': HTTP reason from the server,
  223. 'body': HTTP body of the server's response}
  224. """
  225. return self.Post(new_entry, insert_uri, url_params=url_params,
  226. escape_params=escape_params,
  227. converter=gdata.calendar.CalendarEventCommentEntryFromString)
  228. def _RemoveStandardUrlPrefix(self, url):
  229. url_prefix = 'http://%s/' % self.server
  230. if url.startswith(url_prefix):
  231. return url[len(url_prefix) - 1:]
  232. return url
  233. def DeleteEvent(self, edit_uri, extra_headers=None,
  234. url_params=None, escape_params=True):
  235. """Removes an event with the specified ID from Google Calendar.
  236. Args:
  237. edit_uri: string The edit URL of the entry to be deleted. Example:
  238. 'http://www.google.com/calendar/feeds/default/private/full/abx'
  239. url_params: dict (optional) Additional URL parameters to be included
  240. in the deletion request.
  241. escape_params: boolean (optional) If true, the url_parameters will be
  242. escaped before they are included in the request.
  243. Returns:
  244. On successful delete, a httplib.HTTPResponse containing the server's
  245. response to the DELETE request.
  246. On failure, a RequestError is raised of the form:
  247. {'status': HTTP status code from server,
  248. 'reason': HTTP reason from the server,
  249. 'body': HTTP body of the server's response}
  250. """
  251. edit_uri = self._RemoveStandardUrlPrefix(edit_uri)
  252. return self.Delete('%s' % edit_uri,
  253. url_params=url_params, escape_params=escape_params)
  254. def DeleteAclEntry(self, edit_uri, extra_headers=None,
  255. url_params=None, escape_params=True):
  256. """Removes an ACL entry at the given edit_uri from Google Calendar.
  257. Args:
  258. edit_uri: string The edit URL of the entry to be deleted. Example:
  259. 'http://www.google.com/calendar/feeds/liz%40gmail.com/acl/full/default'
  260. url_params: dict (optional) Additional URL parameters to be included
  261. in the deletion request.
  262. escape_params: boolean (optional) If true, the url_parameters will be
  263. escaped before they are included in the request.
  264. Returns:
  265. On successful delete, a httplib.HTTPResponse containing the server's
  266. response to the DELETE request.
  267. On failure, a RequestError is raised of the form:
  268. {'status': HTTP status code from server,
  269. 'reason': HTTP reason from the server,
  270. 'body': HTTP body of the server's response}
  271. """
  272. edit_uri = self._RemoveStandardUrlPrefix(edit_uri)
  273. return self.Delete('%s' % edit_uri,
  274. url_params=url_params, escape_params=escape_params)
  275. def DeleteCalendarEntry(self, edit_uri, extra_headers=None,
  276. url_params=None, escape_params=True):
  277. """Removes a calendar entry at the given edit_uri from Google Calendar.
  278. Args:
  279. edit_uri: string The edit URL of the entry to be deleted. Example:
  280. 'http://www.google.com/calendar/feeds/default/allcalendars/abcdef@group.calendar.google.com'
  281. url_params: dict (optional) Additional URL parameters to be included
  282. in the deletion request.
  283. escape_params: boolean (optional) If true, the url_parameters will be
  284. escaped before they are included in the request.
  285. Returns:
  286. On successful delete, True is returned
  287. On failure, a RequestError is raised of the form:
  288. {'status': HTTP status code from server,
  289. 'reason': HTTP reason from the server,
  290. 'body': HTTP body of the server's response}
  291. """
  292. return self.Delete(edit_uri, url_params=url_params,
  293. escape_params=escape_params)
  294. def UpdateEvent(self, edit_uri, updated_event, url_params=None,
  295. escape_params=True):
  296. """Updates an existing event.
  297. Args:
  298. edit_uri: string The edit link URI for the element being updated
  299. updated_event: string, atom.Entry, or subclass containing
  300. the Atom Entry which will replace the event which is
  301. stored at the edit_url
  302. url_params: dict (optional) Additional URL parameters to be included
  303. in the update request.
  304. escape_params: boolean (optional) If true, the url_parameters will be
  305. escaped before they are included in the request.
  306. Returns:
  307. On successful update, a httplib.HTTPResponse containing the server's
  308. response to the PUT request.
  309. On failure, a RequestError is raised of the form:
  310. {'status': HTTP status code from server,
  311. 'reason': HTTP reason from the server,
  312. 'body': HTTP body of the server's response}
  313. """
  314. edit_uri = self._RemoveStandardUrlPrefix(edit_uri)
  315. return self.Put(updated_event, '%s' % edit_uri,
  316. url_params=url_params,
  317. escape_params=escape_params,
  318. converter=gdata.calendar.CalendarEventEntryFromString)
  319. def UpdateAclEntry(self, edit_uri, updated_rule, url_params=None,
  320. escape_params=True):
  321. """Updates an existing ACL rule.
  322. Args:
  323. edit_uri: string The edit link URI for the element being updated
  324. updated_rule: string, atom.Entry, or subclass containing
  325. the Atom Entry which will replace the event which is
  326. stored at the edit_url
  327. url_params: dict (optional) Additional URL parameters to be included
  328. in the update request.
  329. escape_params: boolean (optional) If true, the url_parameters will be
  330. escaped before they are included in the request.
  331. Returns:
  332. On successful update, a httplib.HTTPResponse containing the server's
  333. response to the PUT request.
  334. On failure, a RequestError is raised of the form:
  335. {'status': HTTP status code from server,
  336. 'reason': HTTP reason from the server,
  337. 'body': HTTP body of the server's response}
  338. """
  339. edit_uri = self._RemoveStandardUrlPrefix(edit_uri)
  340. return self.Put(updated_rule, '%s' % edit_uri,
  341. url_params=url_params,
  342. escape_params=escape_params,
  343. converter=gdata.calendar.CalendarAclEntryFromString)
  344. def ExecuteBatch(self, batch_feed, url,
  345. converter=gdata.calendar.CalendarEventFeedFromString):
  346. """Sends a batch request feed to the server.
  347. The batch request needs to be sent to the batch URL for a particular
  348. calendar. You can find the URL by calling GetBatchLink().href on the
  349. CalendarEventFeed.
  350. Args:
  351. batch_feed: gdata.calendar.CalendarEventFeed A feed containing batch
  352. request entries. Each entry contains the operation to be performed
  353. on the data contained in the entry. For example an entry with an
  354. operation type of insert will be used as if the individual entry
  355. had been inserted.
  356. url: str The batch URL for the Calendar to which these operations should
  357. be applied.
  358. converter: Function (optional) The function used to convert the server's
  359. response to an object. The default value is
  360. CalendarEventFeedFromString.
  361. Returns:
  362. The results of the batch request's execution on the server. If the
  363. default converter is used, this is stored in a CalendarEventFeed.
  364. """
  365. return self.Post(batch_feed, url, converter=converter)
  366. class CalendarEventQuery(gdata.service.Query):
  367. def __init__(self, user='default', visibility='private', projection='full',
  368. text_query=None, params=None, categories=None):
  369. gdata.service.Query.__init__(self,
  370. feed='http://www.google.com/calendar/feeds/%s/%s/%s' % (
  371. urllib.quote(user),
  372. urllib.quote(visibility),
  373. urllib.quote(projection)),
  374. text_query=text_query, params=params, categories=categories)
  375. def _GetStartMin(self):
  376. if 'start-min' in self.keys():
  377. return self['start-min']
  378. else:
  379. return None
  380. def _SetStartMin(self, val):
  381. self['start-min'] = val
  382. start_min = property(_GetStartMin, _SetStartMin,
  383. doc="""The start-min query parameter""")
  384. def _GetStartMax(self):
  385. if 'start-max' in self.keys():
  386. return self['start-max']
  387. else:
  388. return None
  389. def _SetStartMax(self, val):
  390. self['start-max'] = val
  391. start_max = property(_GetStartMax, _SetStartMax,
  392. doc="""The start-max query parameter""")
  393. def _GetOrderBy(self):
  394. if 'orderby' in self.keys():
  395. return self['orderby']
  396. else:
  397. return None
  398. def _SetOrderBy(self, val):
  399. if val is not 'lastmodified' and val is not 'starttime':
  400. raise Error, "Order By must be either 'lastmodified' or 'starttime'"
  401. self['orderby'] = val
  402. orderby = property(_GetOrderBy, _SetOrderBy,
  403. doc="""The orderby query parameter""")
  404. def _GetSortOrder(self):
  405. if 'sortorder' in self.keys():
  406. return self['sortorder']
  407. else:
  408. return None
  409. def _SetSortOrder(self, val):
  410. if (val is not 'ascending' and val is not 'descending'
  411. and val is not 'a' and val is not 'd' and val is not 'ascend'
  412. and val is not 'descend'):
  413. raise Error, "Sort order must be either ascending, ascend, " + (
  414. "a or descending, descend, or d")
  415. self['sortorder'] = val
  416. sortorder = property(_GetSortOrder, _SetSortOrder,
  417. doc="""The sortorder query parameter""")
  418. def _GetSingleEvents(self):
  419. if 'singleevents' in self.keys():
  420. return self['singleevents']
  421. else:
  422. return None
  423. def _SetSingleEvents(self, val):
  424. self['singleevents'] = val
  425. singleevents = property(_GetSingleEvents, _SetSingleEvents,
  426. doc="""The singleevents query parameter""")
  427. def _GetFutureEvents(self):
  428. if 'futureevents' in self.keys():
  429. return self['futureevents']
  430. else:
  431. return None
  432. def _SetFutureEvents(self, val):
  433. self['futureevents'] = val
  434. futureevents = property(_GetFutureEvents, _SetFutureEvents,
  435. doc="""The futureevents query parameter""")
  436. def _GetRecurrenceExpansionStart(self):
  437. if 'recurrence-expansion-start' in self.keys():
  438. return self['recurrence-expansion-start']
  439. else:
  440. return None
  441. def _SetRecurrenceExpansionStart(self, val):
  442. self['recurrence-expansion-start'] = val
  443. recurrence_expansion_start = property(_GetRecurrenceExpansionStart,
  444. _SetRecurrenceExpansionStart,
  445. doc="""The recurrence-expansion-start query parameter""")
  446. def _GetRecurrenceExpansionEnd(self):
  447. if 'recurrence-expansion-end' in self.keys():
  448. return self['recurrence-expansion-end']
  449. else:
  450. return None
  451. def _SetRecurrenceExpansionEnd(self, val):
  452. self['recurrence-expansion-end'] = val
  453. recurrence_expansion_end = property(_GetRecurrenceExpansionEnd,
  454. _SetRecurrenceExpansionEnd,
  455. doc="""The recurrence-expansion-end query parameter""")
  456. def _SetTimezone(self, val):
  457. self['ctz'] = val
  458. def _GetTimezone(self):
  459. if 'ctz' in self.keys():
  460. return self['ctz']
  461. else:
  462. return None
  463. ctz = property(_GetTimezone, _SetTimezone,
  464. doc="""The ctz query parameter which sets report time on the server.""")
  465. class CalendarListQuery(gdata.service.Query):
  466. """Queries the Google Calendar meta feed"""
  467. def __init__(self, userId=None, text_query=None,
  468. params=None, categories=None):
  469. if userId is None:
  470. userId = 'default'
  471. gdata.service.Query.__init__(self, feed='http://www.google.com/calendar/feeds/'
  472. +userId,
  473. text_query=text_query, params=params,
  474. categories=categories)
  475. class CalendarEventCommentQuery(gdata.service.Query):
  476. """Queries the Google Calendar event comments feed"""
  477. def __init__(self, feed=None):
  478. gdata.service.Query.__init__(self, feed=feed)