43
loading...
This website collects cookies to deliver better user experience
Some tricky configuration is required, and the existing Python-specific documentation is sparse and buggy (or simply not written for a production setting). My eventual solution involves using a Google Cloud Platform service account and a custom Heroku buildpack
I have also created a blog example repo on my GitHub that includes all of the steps in this entire tutorial, so feel free to clone that repo and have a look at the code. You'll still need to do a lot of person configuration to get it working, including creating your own service account, google calendar, Heroku project, .env files and Heroku config vars, etc.
.py
file) to contain all of the Calendar API access methods. Python automatically exports, but you must explicitly import them into any code that needs access.main_app/
folder, create a file called calendar_API.py
. In that blank file:touch main_app/calendar_API.py
def test_calendar():
print("RUNNING TEST_CALENDAR()")
test_event1 = {"start": {"date": "2022-01-01"}, "end": {"date": "2022-01-07"}, "summary":"test event 1"}
test_event2 = {"start": {"date": "2022-02-01"}, "end": {"date": "2022-02-07"}, "summary":"test event 2"}
events = [test_event1, test_event2]
return events
main_app/urls.py
and add the following the following item to the urlpatterns[]
array below the existing "home" route. This will give us two pages in total, our "Home" and our "Demo" which will demonstrate the connection to Google Calendar APIpath('demo/', views.demo, name='demo'),
views.py
, import the module method at the top of the file using:from .calendar_API import test_calendar
def demo(request):
results = test_calendar()
context = {"results": results}
return render(request, 'demo.html', context)
touch main_app/templates/demo.html
demo.html
file in VSCode:<!DOCTYPE html>
<html>
<head> </head>
<body>
<h1>Demo</h1>
<ul>
{% for result in results %}
<li>{{result.start.date}}{% if result.end.date %}-{% endif%}{{result.end.date}}: {{result.summary}}</li>
{% endfor %}
</ul>
</body>
</html>
home.html
to include a link to this new demo.html
. Add the following line after the <h1>Home</h1>
line:<a href="{% url 'demo' %}">Connect to Google Calendar</a>
pip install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib
pip3 freeze > requirements.txt
env
):python3 manage.py runserver
These instructions draw heavily on this amazing Stack Overflow answer
example/.env
. # Google API
CAL_ID={{{THE CAL ID YOU COPY-PASTED FROM YOUR GOOGLE CALENDAR SETTINGS PAGE}}}
touch google-credentials.json
{
"type": "service_account",
"project_id": "example",
"private_key_id": "BLAHBALH",
"private_key": "-----BEGIN PRIVATE KEY-----\nSUUUUUUPER------LONG-------STRING\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "1234567890987654321",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/username%40example.iam.gserviceaccount.com"
}
.gitignore
file# this hidden folder contains your local, virtual environment
.env
# this hidden file contains sensitive keys and environmental config vars
# if you've named your primary project folder something other than
# 'example' please adjust the following line as needed
example/.env
# this token contains your sensitive google service account info
google-credentials.json
example/.env
and the newly created google-credentials.json
appear greyed out in VSCodecalendar_API.py
, and replace the existing code with everything in the following block (basically replacing our test logging function with the real API calls)
from decouple import config
from google.oauth2 import service_account
import googleapiclient.discovery
import datetime
CAL_ID = config('CAL_ID')
SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = './google-credentials.json'
def test_calendar():
print("RUNNING TEST_CALENDAR()")
credentials = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = googleapiclient.discovery.build('calendar', 'v3', credentials=credentials)
# CREATE A NEW EVENT
new_event = {
'summary': "Ben Hammond Tech's Super Awesome Event",
'location': 'Denver, CO USA',
'description': 'https://benhammond.tech',
'start': {
'date': f"{datetime.date.today()}",
'timeZone': 'America/New_York',
},
'end': {
'date': f"{datetime.date.today() + datetime.timedelta(days=3)}",
'timeZone': 'America/New_York',
},
}
service.events().insert(calendarId=CAL_ID, body=new_event).execute()
print('Event created')
# GET ALL EXISTING EVENTS
events_result = service.events().list(calendarId=CAL_ID, maxResults=2500).execute()
events = events_result.get('items', [])
# LOG THEM ALL OUT IN DEV TOOLS CONSOLE
for e in events:
print(e)
#uncomment the following lines to delete each existing item in the calendar
#event_id = e['id']
# service.events().delete(calendarId=CAL_ID, eventId=event_id).execute()
return events
env
):python3 manage.py run server
and viewing localhost:8000 in browser should display the same button as before. However, now clicking the button should trigger the series of API calls we added above:These steps come mainly from this excellent DevDojo.com blog post
https://github.com/buyersight/heroku-google-application-credentials-buildpack
into the text input box labeled "Enter Buildpack URL".env
file, along with some addition Heroku only entries.CAL_ID
, and in the corresponding VALUE box, paste the string that occurs after the =
in your local .env. In my case it looks like this [email protected]
. Make sure there are no quotation marks or spaces before and after the stringGOOGLE_APPLICATION_CREDENTIALS
, and in the corresponding VALUE box paste google-credentials.json
GOOGLE_CREDENTIALS
, and in the corresponding VALUE box, paste the entire JSON object. You can literally copy all of the code that is now in your local google-credentials.json
, starting with a {
and ending with a }
git add .
git commit -m "getting ready for deploy"
git push heroku main
(or master
if you haven't yet switched your local git to default to the main
branch)heroku run bash
(wait a few seconds for it to log in)ls
google-credentials.json
listed on the floor of your project. Yay!exit
will bring you back onto your local machineAs noted in the previous article Deploying Django to Heroku, it can be helpful to configure your Django project and Heroku to give more detailed error logging. Check out the instructions on this stackoverflow where they explain adding DEBUG_PROPAGATE_EXCEPTIONS = True
and a LOGGING = { ... }
library to their settings.py
Sundial Photo by Marian Kroell on Unsplash