{"id":8165,"date":"2018-01-05T02:18:14","date_gmt":"2018-01-05T07:18:14","guid":{"rendered":"https:\/\/qxf2.com\/blog\/?p=8165"},"modified":"2019-01-31T08:16:59","modified_gmt":"2019-01-31T13:16:59","slug":"google-calendar-python","status":"publish","type":"post","link":"https:\/\/qxf2.com\/blog\/google-calendar-python\/","title":{"rendered":"Parsing Google Calendar events with Python"},"content":{"rendered":"<p>This post will show you how to use Python to parse Google Calendar events. Google&#8217;s API documentation is good but we haven&#8217;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 &#8216;PTO&#8217; in the event title. <\/p>\n<hr>\n<h3>Overview<\/h3>\n<p>Here are the steps we will perform:<\/p>\n<p>a) Enable the Google Calendar API and download the client secret file<br \/>\nb) Install the Python module google-api-python-client<br \/>\nc) Connect to your Google calendar<br \/>\nd) Fetch all the calendars shared with you<br \/>\ne) Retrieve all-day events for a given time range<br \/>\nf) Filter the events with the words PTO in them<\/p>\n<hr>\n<h3>Detailed steps<\/h3>\n<p><strong>a) Enable the Google Calendar API and download the client secret<\/strong><br \/>\nI followed the instructions in Step 1 of <a href=\"https:\/\/developers.google.com\/google-apps\/calendar\/quickstart\/python\">this guide<\/a> to turn on the Google Calendar API. I set my application name to <code>Google Calendar - Raw Python<\/code> and I named my client secret file <code>client_secret_google_calendar.json<\/code>. I placed my client secret file in <code>~\/.credentials\/<\/code>. You need these details when you login.<\/p>\n<p><strong>b) Install the Python module google-api-python-client<\/strong><br \/>\nThis is easy: <code>pip install -U google-api-python-client<\/code><\/p>\n<p><strong>c) Connect to your Google calendar<\/strong><br \/>\nLet us try connecting to your Google calendar. I know the script looks large but it is largely boilerplate that I just copied from Google&#8217;s quickstart guide. You can blindly copy it too and make just 3 edits: replace two global variables (<code> CLIENT_SECRET_FILE, APPLICATION_NAME<\/code> 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). <\/p>\n<pre lang=\"python\">\r\nfrom __future__ import print_function\r\nimport httplib2\r\nimport os\r\n\r\n# from apiclient import discovery\r\n# Commented above import statement and replaced it below because of \r\n# reader Vishnukumar's comment\r\n# Src: https:\/\/stackoverflow.com\/a\/30811628\r\n\r\nimport googleapiclient.discovery as discovery\r\nfrom oauth2client import client\r\nfrom oauth2client import tools\r\nfrom oauth2client.file import Storage\r\n\r\nimport datetime\r\n\r\ntry:\r\n    import argparse\r\n    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()\r\nexcept ImportError:\r\n    flags = None\r\n\r\n# If modifying these scopes, delete your previously saved credentials\r\n# at ~\/.credentials\/calendar-python-quickstart.json\r\nSCOPES = 'https:\/\/www.googleapis.com\/auth\/calendar.readonly'\r\nCLIENT_SECRET_FILE = 'client_secret_google_calendar.json'\r\nAPPLICATION_NAME = 'Google Calendar - Raw Python'\r\n\r\n\r\ndef get_credentials():\r\n    \"\"\"Gets valid user credentials from storage.\r\n\r\n    If nothing has been stored, or if the stored credentials are invalid,\r\n    the OAuth2 flow is completed to obtain the new credentials.\r\n\r\n    Returns:\r\n        Credentials, the obtained credential.\r\n    \"\"\"\r\n    home_dir = os.path.expanduser('~')\r\n    credential_dir = os.path.join(home_dir, '.credentials')\r\n    if not os.path.exists(credential_dir):\r\n        os.makedirs(credential_dir)\r\n    credential_path = os.path.join(credential_dir,\r\n                                   'calendar-python-quickstart.json')\r\n\r\n    store = Storage(credential_path)\r\n    credentials = store.get()\r\n    if not credentials or credentials.invalid:\r\n        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)\r\n        flow.user_agent = APPLICATION_NAME\r\n        if flags:\r\n            credentials = tools.run_flow(flow, store, flags)\r\n        else: # Needed only for compatibility with Python 2.6\r\n            credentials = tools.run(flow, store)\r\n        print('Storing credentials to ' + credential_path)\r\n    return credentials\r\n\r\ndef main():\r\n    \"\"\"Shows basic usage of the Google Calendar API.\r\n\r\n    Creates a Google Calendar API service object and outputs a list of the next\r\n    10 events on the user's calendar.\r\n    \"\"\"\r\n    credentials = get_credentials()\r\n    http = credentials.authorize(httplib2.Http())\r\n    service = discovery.build('calendar', 'v3', http=http)\r\n\r\n<\/pre>\n<p><strong>d) Fetch all the calendars shared with you<\/strong><br \/>\nLet us first get all the calendars shared with you. We&#8217;ll query each of them for events in the next step. BTW, we filter calendars using the string &#8216;@qxf2.com&#8217; since we are interested only in the calendars of our colleagues.<\/p>\n<pre lang=\"python\">\r\n    # This code is to fetch the calendar ids shared with me\r\n    # Src: https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/calendarList\/list\r\n    page_token = None\r\n    calendar_ids = []\r\n    while True:\r\n        calendar_list = service.calendarList().list(pageToken=page_token).execute()\r\n        for calendar_list_entry in calendar_list['items']:\r\n            if '@qxf2.com' in calendar_list_entry['id']:\r\n                calendar_ids.append(calendar_list_entry['id'])\r\n        page_token = calendar_list.get('nextPageToken')\r\n        if not page_token:\r\n            break\r\n<\/pre>\n<p><strong>e) Retrieve all-day events for a given time range<\/strong><br \/>\nNow, 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.<\/p>\n<pre lang=\"python\">\r\n    # This code is to look for all-day events in each calendar for the month of September\r\n    # Src: https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/events\/list\r\n    # You need to get this from command line\r\n    # Bother about it later!\r\n    start_date = datetime.datetime(\r\n        2017, 10, 30, 00, 00, 00, 0).isoformat() + 'Z'\r\n    end_date = datetime.datetime(2017, 12, 01, 23, 59, 59, 0).isoformat() + 'Z'\r\n\r\n    for calendar_id in calendar_ids:\r\n        count = 0\r\n        print('\\n----%s:\\n' % calendar_id)\r\n        eventsResult = service.events().list(\r\n            calendarId=calendar_id,\r\n            timeMin=start_date,\r\n            timeMax=end_date,\r\n            singleEvents=True,\r\n            orderBy='startTime').execute()\r\n        events = eventsResult.get('items', [])\r\n<\/pre>\n<p><strong>f) Filter the events with the words PTO in them<\/strong><br \/>\nNow, we simply loop through all the (all-day) events we collected in the previous step and see if the string &#8216;PTO&#8217; exists in the title.<\/p>\n<pre lang=\"python\">\r\n        if not events:\r\n            print('No upcoming events found.')\r\n        for event in events:\r\n            if event.has_key('summary'):\r\n                if 'PTO' in event['summary']:\r\n                    count += 1\r\n                    start = event['start'].get(\r\n                        'dateTime', event['start'].get('date'))\r\n                    print(start, event['summary'])\r\n        print('Total days off for %s is %d' % (calendar_id, count))\r\n<\/pre>\n<p><strong>g) Putting it all together:<\/strong><br \/>\nThis is how the complete script looks. To run, simply execute <strong>python sample_google_calendar.py<\/strong><\/p>\n<pre lang=\"python\">\r\n\"\"\"\r\n05-Oct-2017: Scratch script for blog post about Google Calendar API\r\nThis script is a sample and NOT indicative of Qxf2's programming habits.\r\n\r\nThis script will:\r\na) Connect to Google Calendar\r\nb) Get calendar ids for all Qxf2 employees\r\nc) Execute a search in a given hardcoded timeframe for all employees\r\nd) List any event with the word PTO in the summary\r\n\r\nTo setup, I followed this reference:\r\nhttps:\/\/developers.google.com\/google-apps\/calendar\/quickstart\/python\r\n\r\nReferences:\r\n1. https:\/\/developers.google.com\/google-apps\/calendar\/quickstart\/python\r\n2. https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/events\/list\r\n3. https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/calendarList\/list\r\n\"\"\"\r\n\r\n\r\nfrom __future__ import print_function\r\nimport httplib2\r\nimport os\r\n\r\n# from apiclient import discovery\r\n# Commented above import statement and replaced it below because of\r\n# reader Vishnukumar's comment\r\n# Src: https:\/\/stackoverflow.com\/a\/30811628\r\n\r\nimport googleapiclient.discovery as discovery\r\nfrom oauth2client import client\r\nfrom oauth2client import tools\r\nfrom oauth2client.file import Storage\r\n\r\nimport datetime\r\n\r\ntry:\r\n    import argparse\r\n    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()\r\nexcept ImportError:\r\n    flags = None\r\n\r\n# If modifying these scopes, delete your previously saved credentials\r\n# at ~\/.credentials\/calendar-python-quickstart.json\r\nSCOPES = 'https:\/\/www.googleapis.com\/auth\/calendar.readonly'\r\nCLIENT_SECRET_FILE = 'client_secret_google_calendar.json'\r\nAPPLICATION_NAME = 'Google Calendar - Raw Python'\r\n\r\n\r\ndef get_credentials():\r\n    \"\"\"Gets valid user credentials from storage.\r\n\r\n    If nothing has been stored, or if the stored credentials are invalid,\r\n    the OAuth2 flow is completed to obtain the new credentials.\r\n\r\n    Returns:\r\n        Credentials, the obtained credential.\r\n    \"\"\"\r\n    home_dir = os.path.expanduser('~')\r\n    credential_dir = os.path.join(home_dir, '.credentials')\r\n    if not os.path.exists(credential_dir):\r\n        os.makedirs(credential_dir)\r\n    credential_path = os.path.join(credential_dir,\r\n                                   'calendar-python-quickstart.json')\r\n\r\n    store = Storage(credential_path)\r\n    credentials = store.get()\r\n    if not credentials or credentials.invalid:\r\n        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)\r\n        flow.user_agent = APPLICATION_NAME\r\n        if flags:\r\n            credentials = tools.run_flow(flow, store, flags)\r\n        else:  # Needed only for compatibility with Python 2.6\r\n            credentials = tools.run(flow, store)\r\n        print('Storing credentials to ' + credential_path)\r\n    return credentials\r\n\r\n\r\ndef main():\r\n    \"\"\"Shows basic usage of the Google Calendar API.\r\n\r\n    Creates a Google Calendar API service object and outputs a list of the next\r\n    10 events on the user's calendar.\r\n    \"\"\"\r\n    credentials = get_credentials()\r\n    http = credentials.authorize(httplib2.Http())\r\n    service = discovery.build('calendar', 'v3', http=http)\r\n\r\n    # This code is to fetch the calendar ids shared with me\r\n    # Src: https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/calendarList\/list\r\n    page_token = None\r\n    calendar_ids = []\r\n    while True:\r\n        calendar_list = service.calendarList().list(pageToken=page_token).execute()\r\n        for calendar_list_entry in calendar_list['items']:\r\n            if '@qxf2.com' in calendar_list_entry['id']:\r\n                calendar_ids.append(calendar_list_entry['id'])\r\n        page_token = calendar_list.get('nextPageToken')\r\n        if not page_token:\r\n            break\r\n\r\n    # This code is to look for all-day events in each calendar for the month of September\r\n    # Src: https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/events\/list\r\n    # You need to get this from command line\r\n    # Bother about it later!\r\n    start_date = datetime.datetime(\r\n        2017, 10, 01, 00, 00, 00, 0).isoformat() + 'Z'\r\n    end_date = datetime.datetime(2017, 12, 30, 23, 59, 59, 0).isoformat() + 'Z'\r\n\r\n    for calendar_id in calendar_ids:\r\n        count = 0\r\n        print('\\n----%s:\\n' % calendar_id)\r\n        eventsResult = service.events().list(\r\n            calendarId=calendar_id,\r\n            timeMin=start_date,\r\n            timeMax=end_date,\r\n            singleEvents=True,\r\n            orderBy='startTime').execute()\r\n        events = eventsResult.get('items', [])\r\n        if not events:\r\n            print('No upcoming events found.')\r\n        for event in events:\r\n            if event.has_key('summary'):\r\n                if 'PTO' in event['summary']:\r\n                    count += 1\r\n                    start = event['start'].get(\r\n                        'dateTime', event['start'].get('date'))\r\n                    print(start, event['summary'])\r\n        print('Total days off for %s is %d' % (calendar_id, count))\r\n\r\n\r\nif __name__ == '__main__':\r\n    main()\r\n<\/pre>\n<p><strong>If you liked what you read, know more <a href=\"https:\/\/qxf2.com\/blog\/about-qxf2\/\">about Qxf2<\/a>.<\/strong><\/p>\n<hr>\n<h3>References<\/h3>\n<p>1. <a href=\"https:\/\/developers.google.com\/google-apps\/calendar\/quickstart\/python\"> Google&#8217;s Calendar API Quickstart<\/a><br \/>\n2. <a href=\"https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/events\/list\">API documentation for \/events\/list<\/a><br \/>\n3. <a href=\"https:\/\/developers.google.com\/google-apps\/calendar\/v3\/reference\/calendarList\/list\">API documentation for \/calendarList\/list<\/a><\/p>\n<hr>\n","protected":false},"excerpt":{"rendered":"<p>This post will show you how to use Python to parse Google Calendar events. Google&#8217;s API documentation is good but we haven&#8217;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 &#8216;PTO&#8217; in the event title. Overview Here are the steps we will [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[38,18],"tags":[],"class_list":["post-8165","post","type-post","status-publish","format-standard","hentry","category-automation","category-python"],"_links":{"self":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/8165","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/comments?post=8165"}],"version-history":[{"count":16,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/8165\/revisions"}],"predecessor-version":[{"id":15575,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/posts\/8165\/revisions\/15575"}],"wp:attachment":[{"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/media?parent=8165"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/categories?post=8165"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/qxf2.com\/blog\/wp-json\/wp\/v2\/tags?post=8165"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}