PageRenderTime 47ms CodeModel.GetById 14ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 1ms

/gdata/calendar/service.py

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