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
I want to find out what conditions produce remarkable software. A few years ago, I chose to work as the first professional tester at a startup. I successfully won credibility for testers and established a world-class team. I have lead the testing for early versions of multiple products. Today, I run Qxf2 Services. Qxf2 provides software testing services for startups. If you are interested in what Qxf2 offers or simply want to talk about testing, you can contact me at: [email protected]. I like testing, math, chess and dogs.
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
Hi Arunkumar,
It is an great post which clearly explain how to get it working with google api and specially for calendar.
Future reader: please use below to import build.
`from googleapiclient.discovery import build`
Ref: please send the answer from `Jesse Webb` https://stackoverflow.com/questions/18267749/importerror-no-module-named-apiclient-discovery
Thank you for bringing the update to my notice, Vishnukumar. I have updated the script to be
import googleapiclient.discovery as discovery
.I have also given you credit in the commented out code.
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’
^
I just found out that integer literals starting with 0 are interpreted as octal numbers and the digit 8 is not allowed in an octal number. So can you strip the leading zeros from the date string where its greater than 7 and try again. Eg: “(2017, 9, 01, 00, 00, 00, 0)”.
https://stackoverflow.com/questions/50290476/why-python-shows-invalid-token-for-datetime2018-01-01-10-08-00?noredirect=1&lq=1
This works locally. Its not working on server side. Can you help and sort me out from my struggle. ?
Hi,
Sure. What is the issue that you face?
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: