Parsing Google Calendar events with Python

This post will show you how to use Python to parse Google Calendar events. Google’s API documentation is good but we haven’t found a realistic example anywhere. So we are sharing what we have. In this post, you will learn to look for all-day events with the word ‘PTO’ in the event title.


Overview

Here are the steps we will perform:

a) Enable the Google Calendar API and download the client secret file
b) Install the Python module google-api-python-client
c) Connect to your Google calendar
d) Fetch all the calendars shared with you
e) Retrieve all-day events for a given time range
f) Filter the events with the words PTO in them


Detailed steps

a) Enable the Google Calendar API and download the client secret
I followed the instructions in Step 1 of this guide to turn on the Google Calendar API. I set my application name to Google Calendar - Raw Python and I named my client secret file client_secret_google_calendar.json. I placed my client secret file in ~/.credentials/. You need these details when you login.

b) Install the Python module google-api-python-client
This is easy: pip install -U google-api-python-client

c) Connect to your Google calendar
Let us try connecting to your Google calendar. I know the script looks large but it is largely boilerplate that I just copied from Google’s quickstart guide. You can blindly copy it too and make just 3 edits: replace two global variables ( CLIENT_SECRET_FILE, APPLICATION_NAME with your details) and change the location of credentials file. If all goes well, you will be prompted to give permission to give your application access to your calendar (first time only).

from __future__ import print_function
import httplib2
import os
 
# from apiclient import discovery
# Commented above import statement and replaced it below because of 
# reader Vishnukumar's comment
# Src: https://stackoverflow.com/a/30811628
 
import googleapiclient.discovery as discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
 
import datetime
 
try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None
 
# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/calendar-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
CLIENT_SECRET_FILE = 'client_secret_google_calendar.json'
APPLICATION_NAME = 'Google Calendar - Raw Python'
 
 
def get_credentials():
    """Gets valid user credentials from storage.
 
    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.
 
    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'calendar-python-quickstart.json')
 
    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else: # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials
 
def main():
    """Shows basic usage of the Google Calendar API.
 
    Creates a Google Calendar API service object and outputs a list of the next
    10 events on the user's calendar.
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('calendar', 'v3', http=http)

d) Fetch all the calendars shared with you
Let us first get all the calendars shared with you. We’ll query each of them for events in the next step. BTW, we filter calendars using the string ‘@qxf2.com’ since we are interested only in the calendars of our colleagues.

    # This code is to fetch the calendar ids shared with me
    # Src: https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list
    page_token = None
    calendar_ids = []
    while True:
        calendar_list = service.calendarList().list(pageToken=page_token).execute()
        for calendar_list_entry in calendar_list['items']:
            if '@qxf2.com' in calendar_list_entry['id']:
                calendar_ids.append(calendar_list_entry['id'])
        page_token = calendar_list.get('nextPageToken')
        if not page_token:
            break

e) Retrieve all-day events for a given time range
Now, we can filter events any way we like. We are going to use a combination of start-date, end-date and all-day events. For this example, I have hard-coded the dates. You can always make them command line parameters.

    # This code is to look for all-day events in each calendar for the month of September
    # Src: https://developers.google.com/google-apps/calendar/v3/reference/events/list
    # You need to get this from command line
    # Bother about it later!
    start_date = datetime.datetime(
        2017, 10, 30, 00, 00, 00, 0).isoformat() + 'Z'
    end_date = datetime.datetime(2017, 12, 01, 23, 59, 59, 0).isoformat() + 'Z'
 
    for calendar_id in calendar_ids:
        count = 0
        print('\n----%s:\n' % calendar_id)
        eventsResult = service.events().list(
            calendarId=calendar_id,
            timeMin=start_date,
            timeMax=end_date,
            singleEvents=True,
            orderBy='startTime').execute()
        events = eventsResult.get('items', [])

f) Filter the events with the words PTO in them
Now, we simply loop through all the (all-day) events we collected in the previous step and see if the string ‘PTO’ exists in the title.

        if not events:
            print('No upcoming events found.')
        for event in events:
            if event.has_key('summary'):
                if 'PTO' in event['summary']:
                    count += 1
                    start = event['start'].get(
                        'dateTime', event['start'].get('date'))
                    print(start, event['summary'])
        print('Total days off for %s is %d' % (calendar_id, count))

g) Putting it all together:
This is how the complete script looks. To run, simply execute python sample_google_calendar.py

"""
05-Oct-2017: Scratch script for blog post about Google Calendar API
This script is a sample and NOT indicative of Qxf2's programming habits.
 
This script will:
a) Connect to Google Calendar
b) Get calendar ids for all Qxf2 employees
c) Execute a search in a given hardcoded timeframe for all employees
d) List any event with the word PTO in the summary
 
To setup, I followed this reference:
https://developers.google.com/google-apps/calendar/quickstart/python
 
References:
1. https://developers.google.com/google-apps/calendar/quickstart/python
2. https://developers.google.com/google-apps/calendar/v3/reference/events/list
3. https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list
"""
 
 
from __future__ import print_function
import httplib2
import os
 
# from apiclient import discovery
# Commented above import statement and replaced it below because of
# reader Vishnukumar's comment
# Src: https://stackoverflow.com/a/30811628
 
import googleapiclient.discovery as discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
 
import datetime
 
try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None
 
# If modifying these scopes, delete your previously saved credentials
# at ~/.credentials/calendar-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/calendar.readonly'
CLIENT_SECRET_FILE = 'client_secret_google_calendar.json'
APPLICATION_NAME = 'Google Calendar - Raw Python'
 
 
def get_credentials():
    """Gets valid user credentials from storage.
 
    If nothing has been stored, or if the stored credentials are invalid,
    the OAuth2 flow is completed to obtain the new credentials.
 
    Returns:
        Credentials, the obtained credential.
    """
    home_dir = os.path.expanduser('~')
    credential_dir = os.path.join(home_dir, '.credentials')
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir,
                                   'calendar-python-quickstart.json')
 
    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else:  # Needed only for compatibility with Python 2.6
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials
 
 
def main():
    """Shows basic usage of the Google Calendar API.
 
    Creates a Google Calendar API service object and outputs a list of the next
    10 events on the user's calendar.
    """
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('calendar', 'v3', http=http)
 
    # This code is to fetch the calendar ids shared with me
    # Src: https://developers.google.com/google-apps/calendar/v3/reference/calendarList/list
    page_token = None
    calendar_ids = []
    while True:
        calendar_list = service.calendarList().list(pageToken=page_token).execute()
        for calendar_list_entry in calendar_list['items']:
            if '@qxf2.com' in calendar_list_entry['id']:
                calendar_ids.append(calendar_list_entry['id'])
        page_token = calendar_list.get('nextPageToken')
        if not page_token:
            break
 
    # This code is to look for all-day events in each calendar for the month of September
    # Src: https://developers.google.com/google-apps/calendar/v3/reference/events/list
    # You need to get this from command line
    # Bother about it later!
    start_date = datetime.datetime(
        2017, 10, 01, 00, 00, 00, 0).isoformat() + 'Z'
    end_date = datetime.datetime(2017, 12, 30, 23, 59, 59, 0).isoformat() + 'Z'
 
    for calendar_id in calendar_ids:
        count = 0
        print('\n----%s:\n' % calendar_id)
        eventsResult = service.events().list(
            calendarId=calendar_id,
            timeMin=start_date,
            timeMax=end_date,
            singleEvents=True,
            orderBy='startTime').execute()
        events = eventsResult.get('items', [])
        if not events:
            print('No upcoming events found.')
        for event in events:
            if event.has_key('summary'):
                if 'PTO' in event['summary']:
                    count += 1
                    start = event['start'].get(
                        'dateTime', event['start'].get('date'))
                    print(start, event['summary'])
        print('Total days off for %s is %d' % (calendar_id, count))
 
 
if __name__ == '__main__':
    main()

If you liked what you read, know more about Qxf2.


References

1. Google’s Calendar API Quickstart
2. API documentation for /events/list
3. API documentation for /calendarList/list


8 thoughts on “Parsing Google Calendar events with Python

  1. Thanks for this. Made it really easy to put together a simple tool to summarize where the time went over the past week based on google calendar entries. All the best, Soham

  2. Thank you for this post.

    I get the following error when runnig the full example with either python2.7 or python3.7

    File “gcal2.py”, line 106
    start_date = datetime.datetime(2017, 09, 01, 00, 00, 00, 0).isoformat() + ‘Z’
    ^

  3. This works locally. Its not working on server side. Can you help and sort me out from my struggle. ?

  4. I just received the error:
    Traceback (most recent call last):
    File “/home/bert/quick2.py”, line 133, in
    main()
    File “/home/bert/quick2.py”, line 123, in main
    if event.has_key(‘summary’):
    AttributeError: ‘dict’ object has no attribute ‘has_key’

    I managed to resolve this with exchanging
    if event.has_key(‘summary’):
    with
    if ‘summary’ not in event:

Leave a Reply

Your email address will not be published. Required fields are marked *