Google Calendar Integration with Django

17,455

Solution 1

Just a note here: Although the code works, as per https://github.com/googleapis/google-auth-library-python/blob/7a8641a7f0718c0dce413436f23691e8590face1/docs/index.rst, oauth2client has been deprecated recently in favour of google-auth library - https://github.com/googleapis/google-auth-library-python/tree/edfe24602051969e32917e82bcedd2bace43e260

You can find the documentation of the new library here - https://google-auth.readthedocs.io/en/latest/user-guide.html

To use the new library, the code can be written as

import datetime
from datetime import timedelta

import pytz

from google.oauth2 import service_account

from googleapiclient.discovery import build

service_account_email = "[email protected]"
SCOPES = ["https://www.googleapis.com/auth/calendar"]

credentials = service_account.Credentials.from_service_account_file('google_calendar_credential.json')
scoped_credentials = credentials.with_scopes(SCOPES)


def build_service():
    service = build("calendar", "v3", credentials=scoped_credentials)
    return service


def create_event():
    service = build_service()

    start_datetime = datetime.datetime.now(tz=pytz.utc)
    event = (
        service.events()
        .insert(
            calendarId="primary",
            body={
                "summary": "Foo 2",
                "description": "Bar",
                "start": {"dateTime": start_datetime.isoformat()},
                "end": {
                    "dateTime": (start_datetime + timedelta(minutes=15)).isoformat()
                },
            },
        )
        .execute()
    )

    print(event)

create_event()

As I do not have enough reputation to post this as comment, I am posting this as a separate post

Solution 2

Researching a lot of different approaches I found out that server-to-server authentication is what I wanted. This way no user has to explicitly give permissions and acquired auth-tokens don't have to be renewed. Instead, using a service account, a server can make calls itself.

Before you can start coding, you have to setup such a service account and add it to your calendar that you want the service account to access. Google has written down the three steps to create an account here. Afterwards, go to https://calendar.google.com, locate on the left side of the screen the calendar you want to share with your new service account and click the triangle next to it. From the drop-down menu choose calendar settings. This takes you to a screen where you'll find the calendar-ID which you'll need later (so write it down) and also displays a tab at the top to share access to the calendar. As "person" insert the email address from the service account, give it the respective permissions and click save (if you don't click save the service account won't be added).

The code for this is actually pretty elegant:

import os
from datetime import timedelta
import datetime
import pytz

import httplib2
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

service_account_email = '[email protected]'

CLIENT_SECRET_FILE = 'creds.p12'

SCOPES = 'https://www.googleapis.com/auth/calendar'
scopes = [SCOPES]

def build_service():
    credentials = ServiceAccountCredentials.from_p12_keyfile(
        service_account_email=service_account_email,
        filename=CLIENT_SECRET_FILE,
        scopes=SCOPES
    )

    http = credentials.authorize(httplib2.Http())

    service = build('calendar', 'v3', http=http)

    return service


def create_event():
    service = build_service()

    start_datetime = datetime.datetime.now(tz=pytz.utc)
    event = service.events().insert(calendarId='<YOUR EMAIL HERE>@gmail.com', body={
        'summary': 'Foo',
        'description': 'Bar',
        'start': {'dateTime': start_datetime.isoformat()},
        'end': {'dateTime': (start_datetime + timedelta(minutes=15)).isoformat()},
    }).execute()

    print(event)

I'm using oauth2client version 2.2.0 (pip install oauth2client).

I hope this answer helps :)

Solution 3

As this post was quite a while ago I wanted to share my 2020 version of it. Thanks for this post. Helped me a lot to achieve my goal.

import datetime
from datetime import timedelta

import pytz
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials

service_account_email = "INSERT_HERE"
SCOPES = ["https://www.googleapis.com/auth/calendar"]
credentials = ServiceAccountCredentials.from_json_keyfile_name(
    filename="FILENAME.json", scopes=SCOPES
)


def build_service():
    service = build("calendar", "v3", credentials=credentials)
    return service


def create_event():
    service = build_service()

    start_datetime = datetime.datetime.now(tz=pytz.utc)
    event = (
        service.events()
        .insert(
            calendarId="[email protected]",
            body={
                "summary": "Foo",
                "description": "Bar",
                "start": {"dateTime": start_datetime.isoformat()},
                "end": {
                    "dateTime": (start_datetime + timedelta(minutes=15)).isoformat()
                },
            },
        )
        .execute()
    )

    print(event)

create_event()
Share:
17,455
pasql
Author by

pasql

ReactJS + Redux @ Workpath. Previously worked with iOS and Django. Join us at Workpath: https://www.workpath.com/jobs

Updated on July 28, 2022

Comments

  • pasql
    pasql over 1 year

    Is there a fully fledged Django-based example of a Google Calendar integration? I was reading through Google's example page but their link at the bottom is outdated.

    I'm specifically struggeling with the refresh token as Google's examples focus solely on how to get the access token. That's what I have so far:

    @staff_member_required
    def authorize_access(request):
        return redirect(get_flow(request).step1_get_authorize_url())
    
    @staff_member_required
    def oauth2_callback(request):
        credentials = get_flow(request).step2_exchange(request.GET['code'])
    
        store = get_store()
        store.put(credentials)
        credentials.set_store(store)
    
        return redirect('...')
    
    def get_flow(request):
        flow = client.flow_from_clientsecrets(os.path.join(CREDENTIAL_DIR, CLIENT_SECRET_FILE),
                                          SCOPES,
                                          redirect_uri='%s://%s/google-calendar/oauth2-callback/' % (request.META['wsgi.url_scheme'], request.META['HTTP_HOST'],))
        flow.params['access_type'] = 'offline'
        flow.params['approval_prompt'] = 'force'
    
        return flow
    
    def get_store():
        return oauth2client.file.Storage(os.path.join(CREDENTIAL_DIR, ACCESS_TOKEN_FILE))
    
    def has_valid_api_credentials():
        credentials = get_store().get()
        return credentials is not None
    
    def build_service():
        credentials = get_store().get()
    
        if not credentials:
            return None
        elif credentials.access_token_expired:
            http = credentials.refresh(httplib2.Http())
            http = get_store().get().authorize(http)
        else:
            http = credentials.authorize(httplib2.Http())
    
        service = discovery.build('calendar', 'v3', http=http)
    
        return service
    
    def create_events(rental_request):
        service = build_service()
    
        event = service.events().insert(...).execute()
    
  • Joao Ponte
    Joao Ponte over 5 years
    Thank you very much! Nowadays, google uses JSON key format as default, so your code can be adapted from credentials = ServiceAccountCredentials.from_p12_keyfile(service_account_e‌​mail=service_account‌​_email, filename=CLIENT_SECRET_FILE,scopes=SCOPES) to credentials = ServiceAccountCredentials.from_json_keyfile_name(filename=<T‌​he filename of your key in JSON format>,scopes=SCOPES)
  • saran3h
    saran3h over 4 years
    Is the from address same as the calendarId value OR the service_account_email ? How can I set a specific from address so that person recieving mail can reply to that address rather than the service_account_email?
  • rmindzstar
    rmindzstar over 3 years
    @pasql is this viable in 2020?
  • Joey Coder
    Joey Coder about 3 years
    Thank you @pasql . Solved problem!
  • sumit4613
    sumit4613 almost 3 years
    can you specify what is exactly CALENDARID here and how can we get this.