Wednesday, 9 September 2015

Python: Office 365 alerts on Linux

It is possible to use Microsoft's Office 365 with native Linux clients for mail and calendar:

However, neither have proven reliable. The web interface is good but being browser based has meeting and new mail alert limitations. Fortunately, Office 365 has a REST API...

Some familiarity with Python is assumed.

Office 365 API 🔗

Links:

The API is not final at time of writing:

This documentation covers features that are currently in preview. For information about working with preview features, see Preview developer features on the Office 365 platform.

Reading the Calendar 🔗

This Python code uses the Calendar API to read events up to a specified number of minutes in the future:

def find_events():
    now = datetime.datetime.utcnow()
    start_date_time = now.isoformat()
    future = now + datetime.timedelta(minutes=delay_mins)
    end_date_time = future.isoformat()
    query = 'startDateTime=' + start_date_time + '&endDateTime=' + end_date_time + '&$select=Subject,Start,End,Location'
    url = 'https://outlook.office.com/api/v1.0/me/calendarview?'
    print 'GET', url
    r = requests.get(url, auth=(user, pw))
    r.raise_for_status()
    return r.json()

The service returns any Calendar events that intersect with the startDateTime and endDateTime range.

The Python Requests library is used in this sample.

Displaying Alerts 🔗

The notify-send program is commonly used to display messages in the notification area in Linux desktop systems. This application can be invoked from Python:

def send_message(message):
    subprocess.Popen(['notify-send', message])

The message will appear and fade without user intervention or focus stealing.

Notification Script 🔗

Here is a command-line proof-of-concept script:

import subprocess
import requests
import getpass
import datetime
import threading
import time

user = raw_input("User: ")
pw = getpass.getpass()

delay_mins = 5


def find_events():
    now = datetime.datetime.utcnow()
    start_date_time = now.isoformat()
    future = now + datetime.timedelta(minutes=delay_mins)
    end_date_time = future.isoformat()
    query = 'startDateTime=' + start_date_time + '&endDateTime=' + end_date_time + '&$select=Subject,Start,End,Location'
    url = 'https://outlook.office.com/api/v1.0/me/calendarview?'
    print 'GET', url
    r = requests.get(url, auth=(user, pw))
    r.raise_for_status()
    return r.json()


def send_message(message):
    subprocess.Popen(['notify-send', message])


def raise_message_at(delay_seconds, message):
    t = threading.Timer(delay_seconds, lambda: send_message(message))
    t.start()
    return t


def process_event(evt):
    start_str = evt['Start']
    start = datetime.datetime.strptime(start_str, '%Y-%m-%dT%H:%M:%SZ')
    now = datetime.datetime.utcnow()
    if start < now:
        return
    delay = (start - datetime.datetime.utcnow())
    location = evt['Location']
    location_name = location['DisplayName']
    subject = evt['Subject']
    print 'Alerting', subject, ' in ', delay
    raise_message_at(delay.seconds, location_name + ': ' + subject)


while True:
    try:
        response = find_events()
        print response
        events = response['value']
        for event in events:
            process_event(event)
    except Exception as e:
        print e

    time.sleep(delay_mins * 60)

There is much room for improvement.

No comments:

Post a Comment

All comments are moderated