2

I'm on MS Outlook 2010 and am trying to make the lives of HR easier.

Given a list of users (all of whom obviously have outlook accounts), I'd like to load their free/busy status for a given date in the future.

If I can get a list of users, that'll be useful as well, but not necessary.

Once I've processed the data, I'd like to programatically send appointment requests.

Since I'm on Outlook 2010, apparently I can't use the REST interface provided in the latest servers (as far as I know). I would love to get access to this info over sql, but I don't think sql server allows that.

I'd like to avoid VB. I can do C# but would prefer python. I have install "win32com.client," but can't find examples of getting list of users or getting free/busy times for a given list of users.

Would love any suggestions.

Shahbaz
  • 10,395
  • 21
  • 54
  • 83

1 Answers1

1

A while ago, I wrote some code to get a person's free/busy status at a specific time. I imagine you could use this code as a starting point to get what you need. Enjoy!

# method for communicating with an exchange calendar to see if a person has
# a current appointment marking them as unreachable (i.e. on vacation, out
# sick, attending an all-day offsite meeting, etc).

import datetime
import time
import httplib
import urllib
import base64
import xml.dom.minidom

def is_available(email_address, timestamp):
    '''Query the calendar and see if this person is available at the
    given time.'''

    # assume they are available
    available = True

    # this is a template for the SOAP command which queries the calendar.
    # Address, StartTime and EndTime will be filled in later
    soapTemplate = '''
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <GetUserAvailabilityRequest xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
          <TimeZone xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
            <Bias>360</Bias>
            <StandardTime>
              <Bias>0</Bias>
              <Time>02:00:00</Time>
              <DayOrder>1</DayOrder>
              <Month>11</Month>
              <DayOfWeek>Sunday</DayOfWeek>
            </StandardTime>
            <DaylightTime>
              <Bias>-60</Bias>
              <Time>02:00:00</Time>
              <DayOrder>2</DayOrder>
              <Month>3</Month>
              <DayOfWeek>Sunday</DayOfWeek>
            </DaylightTime>
          </TimeZone>
          <MailboxDataArray>
            <MailboxData xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
              <Email>
                <Address>%s</Address>
              </Email>
              <AttendeeType>Required</AttendeeType>
            </MailboxData>
          </MailboxDataArray>
          <FreeBusyViewOptions xmlns="http://schemas.microsoft.com/exchange/services/2006/types">
            <TimeWindow>
              <StartTime>%s</StartTime>
              <EndTime>%s</EndTime>
            </TimeWindow>
            <MergedFreeBusyIntervalInMinutes>5</MergedFreeBusyIntervalInMinutes>
            <RequestedView>FreeBusy</RequestedView>
          </FreeBusyViewOptions>
        </GetUserAvailabilityRequest>
      </soap:Body>
    </soap:Envelope>
    '''.strip()

    # get a SOAP-compatible representation of the given timestamp, and
    # another timestamp for five minutes later.  (the Exchange server
    # requires that the two timestamps be at least five minutes apart.)
    startTime = soap_time(timestamp)
    fiveMinuteDelta = datetime.timedelta(minutes=5)
    endTime = soap_time(timestamp + fiveMinuteDelta)

    # fill in the soap template with times and email address
    soapMessage = soapTemplate % (email_address, startTime, endTime)

    # get the server response as a string
    soapResponse = soap_query(soapMessage)

    # parse the string into an xml node tree structure
    xmldoc = xml.dom.minidom.parseString(soapResponse)

    # do we have a root <Envelope> element?
    root = xmldoc.childNodes[0]
    if root.localName.lower() != 'envelope':
        # punt
        return available

    node = root

    # traverse the xml document, looking for this series of nodes
    element_names = ['body', 'GetUserAvailabilityResponse',
        'FreeBusyResponseArray', 'FreeBusyResponse', 'ResponseMessage']

    for element_name in element_names:
        new_node = find_node(node, element_name)
        if not new_node:
            return available
        node = new_node

    # see if we got a 'success' or 'error' response

    successResponse = False
    errorResponse   = False

    attribs = node.attributes
    for i in range(attribs.length):
        if attribs.item(i).name.lower() == 'responseclass':
            if attribs.item(i).value.lower() == 'success':
                successResponse = True
            elif attribs.item(i).value.lower() == 'error':
                errorResponse = True

    if not successResponse:
        return available

    # since we got a success response, keep traversing
    element_names = ['freeBusyView', 'CalendarEventArray']

    for element_name in element_names:
        new_node = find_node(node, element_name)
        if not new_node:
            return available
        node = new_node

    for item in node.childNodes:
        if item.localName and item.localName.lower() == 'calendarevent':
            for child in item.childNodes:
                if child.localName and child.localName.lower() == 'busytype':
                    if child.childNodes[0].nodeValue.lower() == 'oof':
                        # hallelujah!
                        available = False
                        break

    return available


def soap_query(message):
    '''Send the message to the calendar server and return the response.'''

    host = 'exchange.yourcompany.com' # your calendar server host name
    uri = '/EWS/Exchange.asmx' # your calendar uri
    username = 'foobar' # your calendar user name
    password = 'bazbaz' # your calendar password

    auth = base64.encodestring(username + ':' + password).strip()

    headers = { 'Authorization': 'Basic %s' % auth,
                'Content-type' : 'text/xml; charset="utf-8"',
                'User-Agent'   : 'python' }

    # make the connection
    conn = httplib.HTTPSConnection(host)

    # send the request
    conn.request('POST', uri, message, headers)

    # get the response
    resp = conn.getresponse()

    # get the various parts of the response
    body    = resp.read()
    headers = resp.msg
    version = resp.version
    status  = resp.status
    reason  = resp.reason

    conn.close()

    return body


def soap_time(timestamp):
    '''Return the timestamp as a SOAP-compatible time string.'''

    return timestamp.strftime('%Y-%m-%dT%H:%M:%S')


def find_node(node, name):
    '''Return the first child node with the given name, or return None
    if no children exist by that name.'''

    for child in node.childNodes:
        if child.localName:
            if child.localName.lower() == name.lower():
                return child

    return None
John Gordon
  • 29,573
  • 7
  • 33
  • 58
  • Is this style of query supported by 2010? This doesn't look like REST so not sure what to google for. – Shahbaz Nov 03 '15 at 14:44
  • I don't recall if our server was running 2010 when I wrote this. Have a look at http://stackoverflow.com/questions/20222941/retrieving-free-busy-status-from-microsoft-outlook-using-python – John Gordon Nov 04 '15 at 15:21