using google api eith django

Viewed 105

I'm trying to connect events withing py project to google calendar. my application doesn't used google login as it is intended for only a small group of people. I've been looking for hours on how to get it done and it doesnt work. Any help is appreciated.

models.py

class AgendaClient(models.Model): # used to store info(same as enviroment variables)
    name = models.CharField(max_length=30, null=True, blank=True, unique=True)
    json = models.TextField(blank=True, null=True)

class Event(models.Model):
    summary = models.CharField(max_length=50, choices=EVENT_CHOICES)
    description = models.CharField(max_length=50, null=True, blank=True)
    start_date = models.DateField()
    google_link = models.CharField(max_length=150, null=True, blank=True)

signals.py

import datetime
import json

from django.db.models.signals import post_delete, post_save
from google.auth.transport.requests import Request
from google.cloud import storage
from google.oauth2 import service_account
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from users.models import Lid

from .models import AgendaClient, Event, NIEvent

# If modifying these scopes, delete the file token.json.
try:
    SCOPES = (AgendaClient.objects.get(name='SCOPES').json).strip("][").split(', ')
except: pass
def get_service(refresh = False):
    '''this functions gets and builds the service using the token and the client_secret'''
    creds = None
    # The file token.json stores the user's access and refresh tokens, and is
    # created automatically when the authorization flow completes for the first
    # time.
    if len(AgendaClient.objects.filter(name='token'))==1:
            creds = Credentials.from_authorized_user_info(json.loads(AgendaClient.objects.get(name='token').json), SCOPES)
    else:
        # pprint(json.loads(AgendaClient.objects.get(name='service_account').json))
        creds = service_account.Credentials.from_service_account_info(json.loads(AgendaClient.objects.get(name='service_account').json), scopes=SCOPES)

    # Explicitly use service account credentials by specifying the private key
    # file.
    # If there are no (valid) credentials available, let the user log in.
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            # pprint(json.loads(AgendaClient.objects.get(name='client_secret').json))
            flow = InstalledAppFlow.from_client_config(
                    json.loads(AgendaClient.objects.get(name='client_secret').json), SCOPES)
            creds = flow.run_local_server(port=0)
            # creds = service_account.Credentials.from_service_account_info(json.loads(AgendaClient.objects.get(name='service_account').json), scopes=SCOPES)

    # Save the credentials for the next run
    AgendaClient.objects.update_or_create(name='token', defaults = {'json':creds.to_json()})
    if not refresh:
        service = build('calendar', 'v3', credentials=creds)
        return service
    
def handle_event(sender,created, instance=None,  **kwargs):
    """this function creates the events in the google agenda and updates them if changed in the website
    """
    service = get_service()
    evs = instance
    event ={
    'summary': evs.description,
    'location': evs.location or "",
    'description': (evs.description+' '+ evs.summary),
    'start': {
        'dateTime': datetime.datetime.combine(evs.start_date,evs.start_time).isoformat(),
        'timeZone': 'Europe/Amsterdam',
    },
    'end': {
        'dateTime':datetime.datetime.combine(evs.end_date,evs.end_time).isoformat() ,
        'timeZone': 'Europe/Amsterdam',
    },
    'recurrence': [],'reminders': {}}

    if created or not instance.google_link:
        try:
            event = service.events().insert(calendarId=AgendaClient.objects.get(name='calendarId').json, body=event).execute()
            instance.google_link = event['id']
            instance.save()
        except HttpError as error:
            print('An error occurred: %s' % error)
            pass
    else:  
        try:
            # print(type(AgendaClient.objects.get(name='calendarId').json))
            event = service.events().update(calendarId=AgendaClient.objects.get(name='calendarId').json, body=event, eventId = instance.google_link).execute()
        except HttpError as error:
            print('An error occurred: %s' % error)
            pass

post_save.connect(handle_event, sender=Event)

Ideally, I would use a service account so that users dont have to log in to google. Maybe someone knows a better solution than this but essentially the website should be able to add events to a calendar that users can follow but not add events to each user's calendar.

2 Answers

The main cause of your issue is you appear to be trying to mix service account authorization and installed application authorizaotn in the same code.

As far as the service accounts part of your code goes remember for service accounts with Google calendar with a google workspace account. Make sure you have properly configured the delegation to the user on your domain you want the service account to delegate as.

from google.oauth2 import service_account

SCOPES = ['https://www.googleapis.com/auth/sqlservice.admin']
SERVICE_ACCOUNT_FILE = '/path/to/service.json'

credentials = service_account.Credentials.from_service_account_file(
        SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('user@example.org')
Related