Skip to content

Commit

Permalink
Merge pull request #19 from txstate/master
Browse files Browse the repository at this point in the history
Recurring Event Support
  • Loading branch information
Rachel Sanders committed Oct 20, 2014
2 parents 41ec575 + 420a3e4 commit d87c146
Show file tree
Hide file tree
Showing 11 changed files with 2,050 additions and 126 deletions.
21 changes: 20 additions & 1 deletion docs/exchange2010.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Exchange2010CalendarEvent
=========================

.. autoclass:: Exchange2010CalendarEvent
:members: create, update, cancel, resend_invitations, move_to
:members: create, update, cancel, resend_invitations, move_to, get_occurrence, get_master

.. attribute:: id

Expand Down Expand Up @@ -122,6 +122,25 @@ Exchange2010CalendarEvent

Returns a :class:`ExchangeEventAttendee` object.

.. attribute:: recurrence

A property to set the recurrence type for the event. Possible values are: 'daily', 'weekly', 'monthly', 'yearly'.

.. attribute:: recurrence_interval

A property to set the recurrence interval for the event. This should be an int and applies to the following types of recurring events: 'daily', 'weekly', 'monthly'.
It should be a value between 1 and 999 for 'daily'.
It should be a value between 1 and 99 for 'weekly' and 'monthly'.

.. attribute:: recurrence_end_date

Should be a datetime.date() object which specifies the end of the recurrence.

.. attribute:: recurrence_days

Used in a weekly recurrence to specify which days of the week to schedule the event. This should be a
string of days separated by spaces. ex. "Monday Wednesday"

.. method:: add_attendee(attendees, required=True)

Adds new attendees to the event.
Expand Down
6 changes: 3 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,15 @@ For all other errors, we throw a ``pyexchange.exceptions.FailedExchangeException
Listing events
``````````````

To list events between two dates, simply do:
To list events between two dates, simply do::

events = my_calendar.list_events(
start=datetime(2014, 10, 1, 11, 0, 0, tzinfo=timezone("US/Eastern")),
end=datetime(2014, 10, 29, 11, 0, 0, tzinfo=timezone("US/Eastern")),
details=True
)

This will return a list of Event objects that are between start and end. If no results are found, it will return an empty list (it intentionally will not throw an Exception.)
This will return a list of Event objects that are between start and end. If no results are found, it will return an empty list (it intentionally will not throw an Exception.)::

for event in calendar_list.events:
print "{start} {stop} - {subject}".format(
Expand All @@ -195,7 +195,7 @@ This will return a list of Event objects that are between start and end. If no r
subject=event.subject
)

The third argument, 'details', is optional. By default (if details is not specified, or details=False), it will return most of the fields within an event. The full details for the Organizer or Attendees field are not populated by default by Exchange. If these fields are required in your usage, then pass details=True with the request to make a second lookup for these values. The further details can also be loaded after the fact using the load_all_details() function, as below:
The third argument, 'details', is optional. By default (if details is not specified, or details=False), it will return most of the fields within an event. The full details for the Organizer or Attendees field are not populated by default by Exchange. If these fields are required in your usage, then pass details=True with the request to make a second lookup for these values. The further details can also be loaded after the fact using the load_all_details() function, as below::

events = my_calendar.list_events(start, end)
events.load_all_details()
Expand Down
33 changes: 30 additions & 3 deletions pyexchange/base/calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,31 @@ class BaseExchangeCalendarEvent(object):
reminder_minutes_before_start = None
is_all_day = None

recurrence = None
recurrence_end_date = None
recurrence_days = None
recurrence_interval = None

_type = None

_attendees = {} # people attending
_resources = {} # conference rooms attending

_track_dirty_attributes = False
_dirty_attributes = set() # any attributes that have changed, and we need to update in Exchange

# these attributes can be pickled, or output as JSON
DATA_ATTRIBUTES = [u'_id', u'subject', u'start', u'end', u'location', u'html_body', u'text_body', u'organizer',
u'_attendees', u'_resources', u'reminder_minutes_before_start', u'is_all_day']
DATA_ATTRIBUTES = [
u'_id', u'subject', u'start', u'end', u'location', u'html_body', u'text_body', u'organizer',
u'_attendees', u'_resources', u'reminder_minutes_before_start', u'is_all_day',
'recurrence', 'recurrence_interval', 'recurrence_days', 'recurrence_day',
]

RECURRENCE_ATTRIBUTES = [
'recurrence', 'recurrence_end_date', 'recurrence_days', 'recurrence_interval',
]

WEEKLY_DAYS = [u'Sunday', u'Monday', u'Tuesday', u'Wednesday', u'Thursday', u'Friday', u'Saturday']

def __init__(self, service, id=None, calendar_id=u'calendar', xml=None, **kwargs):
self.service = service
Expand Down Expand Up @@ -105,6 +121,11 @@ def body(self):
""" **Read-only.** Returns either the html_body or the text_body property, whichever is set. """
return self.html_body or self.text_body or None

@property
def type(self):
""" **Read-only.** This is an attribute pulled from an event in the exchange store. """
return self._type

@property
def attendees(self):
"""
Expand Down Expand Up @@ -288,7 +309,7 @@ def validate(self):
raise ValueError("Event has no end date")

if self.end < self.start:
raise ValueError("End date is after start date")
raise ValueError("Start date is after end date")

if self.reminder_minutes_before_start and not isinstance(self.reminder_minutes_before_start, int):
raise TypeError("reminder_minutes_before_start must be of type int")
Expand All @@ -308,6 +329,12 @@ def cancel(self):
def resend_invitations(self):
raise NotImplementedError

def get_master(self):
raise NotImplementedError

def get_occurrance(self, instance_index):
raise NotImplementedError

def as_json(self):
""" Output ourselves as JSON """
return json.dumps(self.__getstate__())
Expand Down
7 changes: 7 additions & 0 deletions pyexchange/base/soap.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ def _parse_date(self, date_string):

return date

def _parse_date_only_naive(self, date_string):
date = datetime.strptime(date_string[0:10], self.EXCHANGE_DATE_FORMAT[0:8])

return date.date()

def _xpath_to_dict(self, element, property_map, namespace_map):
"""
property_map = {
Expand Down Expand Up @@ -106,6 +111,8 @@ def _xpath_to_dict(self, element, property_map, namespace_map):

if cast_as == u'datetime':
result_for_node.append(self._parse_date(node.text))
elif cast_as == u'date_only_naive':
result_for_node.append(self._parse_date_only_naive(node.text))
elif cast_as == u'int':
result_for_node.append(int(node.text))
elif cast_as == u'bool':
Expand Down
4 changes: 4 additions & 0 deletions pyexchange/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ class ExchangeInternalServerTransientErrorException(FailedExchangeException):
"""Raised when an internal server error occurs in Exchange and the request can actually be retried."""
pass


class InvalidEventType(Exception):
"""Raised when a method for an event gets called on the wrong type of event."""
pass
Loading

0 comments on commit d87c146

Please sign in to comment.