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()
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 = 'XXX#YYY.iam.gserviceaccount.com'
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 :)
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 = "app-calendar#xxxxxxxxx.iam.gserviceaccount.com"
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
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="CALENDARID#group.calendar.google.com",
body={
"summary": "Foo",
"description": "Bar",
"start": {"dateTime": start_datetime.isoformat()},
"end": {
"dateTime": (start_datetime + timedelta(minutes=15)).isoformat()
},
},
)
.execute()
)
print(event)
create_event()
2022
credits to #joey Coder(i would have added this as comment but its too long)
If you want your website or app to make events or calendars without have to use the Google accounts of the users you should use service accounts.
In https://console.cloud.google.com/ choose your project or start new one.
In the navigation menu choose "APIs & Services"
enable new APIs and then look up "calendar API", enable the API
Under "APIs & Services">"Credentials", select "Create Credentials" and click on "service account", fill in the desired name, and continue. Set role as owner(or other desired)(owner gives full access you you might want to switch to something less powerful). Click "Done"
This will redirect you to the credentials page.
Under the "Service accounts" click on the desired account(this will redirect you to the IAM & Admin panel)
Under the tab "Keys" click "ADD KEY" and select json, this will download a json file to your computer.
in the Calendar page in google
get and add the calendar ID to the admin panel under AgendaClients "CalendarId"
add the service account to the people shared as admin (make changes to events)
This is how it looks like in my django project:
signals.py
import datetime
import json
import os
from django.db.models.signals import post_delete, post_save
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
from oauth2client.service_account import ServiceAccountCredentials
from .models import Event
# If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/calendar"]
def get_service(refresh=False):
credentials = ServiceAccountCredentials.from_json_keyfile_dict(
json.loads(os.environ.get("client_secret")), scopes=SCOPES
)
# or if you have a file
# credentials = ServiceAccountCredentials.from_json_keyfile_name(
# filename="file.json", scopes=SCOPES
# )
service = build("calendar", "v3", credentials=credentials)
return service
def handle_event(sender, created, instance, **kwargs):
"""this function creates the events in the google agenda and updates them if changed in the website"""
service = get_service()
event = instance
if not event.end_date:
event.end_date = event.start_date
if not event.end_time and event.start_time:
event.end_time = event.start_time
elif not event.end_time:
event.end_time = datetime.datetime.min.time()
if not event.start_time:
event.start_time = datetime.datetime.min.time()
if event.end_date < event.start_date:
event.end_date, event.start_date = event.start_date, event.end_date
queryset = Event.objects.filter(
id=event.id
) # https://stackoverflow.com/questions/1555060/how-to-save-a-model-without-sending-a-signal
# this is used so that we can update the google event within this signal without reshooting this signal(signals shot every time an object is saved)
event = {
"summary": event.description,
"location": event.location or "",
"description": (event.description + " " + event.summary),
"start": {
"dateTime": datetime.datetime.combine(
event.start_date, event.start_time
).isoformat(),
"timeZone": "Europe/Amsterdam",
},
"end": {
"dateTime": datetime.datetime.combine(
event.end_date, event.end_time
).isoformat(),
"timeZone": "Europe/Amsterdam",
},
"recurrence": [],
"reminders": {},
}
if created or not instance.google_link:
try:
event = (
service.events()
.insert(
calendarId=os.environ.get("calendarId"),
body=event,
)
.execute()
)
queryset.update(google_link=event["id"])
except HttpError as error:
# print("An error occurred: %s" % error)
pass
else:
try:
event = (
service.events()
.update(
calendarId=os.environ.get("calendarId"),
body=event,
eventId=instance.google_link,
)
.execute()
)
queryset.update(google_link=event["id"])
except HttpError as error:
# print("An error occurred: %s" % error)
pass
# print("#############ADDED NEW #############")
def delete_event(sender, instance, **kwargs):
"""this function deletes an event from google agenda when deleted in the website"""
try:
service = get_service()
service.events().delete(
calendarId=os.environ.get("CalendarId"),
eventId=instance.google_link,
).execute()
except:
pass
post_save.connect(handle_event, sender=Event)
post_delete.connect(delete_event, sender=Event)
models.py
class Event(models.Model):
summary = models.CharField(max_length=50)
description = models.CharField(max_length=50, null=True, blank=True)
start_date = models.DateField()
start_time = models.TimeField(null=True, blank=True)
end_date = models.DateField(null=True, blank=True)
end_time = models.TimeField(null=True, blank=True)
location = models.CharField(max_length=50, null=True, blank=True)
google_link = models.CharField(max_length=150, null=True, blank=True)
# google link is used to edit events in google if you change them in your website
Feel free to ask any questions or point out anything
Related
I've been trying to make a script that update the YouTube title with the video views, like the Tom Scott video. But I keep getting this error with the authentication.
I get the code to authenticate it but it just says I don't have enough permission.
<HttpError 400 when requesting https://youtube.googleapis.com/youtube/v3/videos?part=%28%27snippet%2Cstatus%2Clocalizations%27%2C%29&alt=json returned "'('snippet'". Details: "[{'message': "'('snippet'", 'domain': 'youtube.part', 'reason': 'unknownPart', 'location': 'part', 'locationType': 'parameter'}]"> File "C:\Users\erik\Documents\PYTHON CODE\view_changer\example.py", line 49, in main response_update = request_update.execute().
Here's my code:
import os
import google_auth_oauthlib.flow
import googleapiclient.discovery
import googleapiclient.errors
scopes = ["https://www.googleapis.com/auth/youtube"]
api_key = "..."
video_1_part_view = 'statistics'
video_1_id_view = 'hVuoPgZJ3Yw'
video_1_part_update= "snippet,status,localizations",
video_1_part_body_update= { "id": "1y94SadSsMU",
"localizations": {"es": { "title": "no hay nada a ver aqui",
"description": "Esta descripcion es en espaƱol."
}
}
}
def main():
# Disable OAuthlib's HTTPS verification when running locally.
# *DO NOT* leave this option enabled in production.
os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1"
api_service_name = "youtube"
api_version = "v3"
client_secrets_file = (r"C:\Users\erik\Documents\PYTHON CODE\view_changer\client_secret_379587650859-bbl63je7sh6kva19l33auonkledt09a9.apps.googleusercontent.com.json")
# Get credentials and create an API client
flow = google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file(client_secrets_file, scopes)
credentials = flow.run_console()
youtube = googleapiclient.discovery.build(api_service_name, api_version, credentials=credentials)
#check views
request_view= youtube.videos().list( part = video_1_part_view, id = video_1_id_view )
respons_view = request_view.execute()
views = respons_view['items'][0]['statistics']['viewCount']
print (views)
#update video
request_update = youtube.videos().update(part= video_1_part_update, body = video_1_part_body_update)
response_update = request_update.execute()
print(response_update)
main()
# if __name__ == "__main__":
# main()
If the problem is not in the code I might have an other idea of what it might be. When I was first setting up the OAuth, I didn't know I needed to pick what the user would be giving permission to (I know rookie mistake(: ), but since I updated it, when I ask for permission after going to the authentication link the code gives me back, it doesn't ask permission for all the things I told it to do.
According to the official documentation, the request parameter part of the Videos.list API endpoint is defined as:
part (string)
The part parameter specifies a comma-separated list of one or more video resource properties that the API response will include.
[...]
But you're having that request parameter set as:
('snippet,status,localizations',).
That's because (though your code above doesn't show it) you have the variable video_1_part_view defined as:
video_1_part_view = "snippet,status,localizations",
(Do notice the trailing comma). That makes video_1_part_view a Python tuple, which is not what you need to send to the API endpoint.
The same is true for your variable video_1_part_update.
Just remove the trailing commas and things will work OK.
I am currently trying to automatically update a slide in a Google Slide presentation with the Slides API and when I run the code it does not seem to update.
My current code is:
from datetime import datetime
from datetime import timedelta
from oauth2client.client import GoogleCredentials
from google.oauth2 import service_account
from googleapiclient.discovery import build
from oauth2client.service_account import ServiceAccountCredentials
from apiclient import discovery
import httplib2
import json
import requests
import string
credentials = service_account.Credentials.from_service_account_file("credentials.json")
service = build('slides', 'v1', credentials=credentials)
#Getting the lunch for the next day
tomorrow = datetime.now() + timedelta(days=1)
url = tomorrow.strftime("https://udas.nutrislice.com/menu/api/digest/school/upper-dauphin-high/menu-type/lunch/date/%Y/%m/%d/")
data = requests.get(url)
test = data.json()['menu_items']
#Converting the lunch to look nice and printing it
lunchtext = json.dumps(test, indent=4, sort_keys=True, separators=('\n', ':'))
newlunchtext = lunchtext.replace(r'"', ' ')
newlunchtext = newlunchtext.replace(r'[', r'"')
newlunchtext = newlunchtext.replace(r']', r'"')
print(newlunchtext)
#Preparing to make the request
PRESENTATION_ID = '1DE0NxsRH6nWxlZGsOP-IzZD-qodw_0YLcMKE0QaQ7vE'
OBJECT_ID = 'g6d3de38d16_0_115'
requests = [
{
'insertText': {
'text': newlunchtext
},
'objectId': OBJECT_ID,
'insertionIndex': 0,
},
]
body = {
'requests': requests
}
update = service.presentations().batchUpdate(presentationId = PRESENTATION_ID, body = body).execute
print(update)
When I run it I get a response that looks like this:
"
French Toast Sticks & Sausage
Pulled Pork BBQ on Bun
McCain Crispy Tater Tots
Garden Salad
"
<bound method HttpRequest.execute of <googleapiclient.http.HttpRequest object at 0x105519c70>>
I am not sure why it is not working as I have the correct objectId and presentationId.
Answer:
There are a couple of issues with your code: the request JSON and the execution of the batchUpdate
More Information:
Firstly, as per the request documentation of the Slides API, both objectId and insertionIndex are representations that should be included inside the insertText resource. This needs to be changed as such:
requests = [
{
'insertText': {
'text': newlunchtext,
'objectId': OBJECT_ID,
'insertionIndex': 0,
},
},
]
The other issue is that yo uare not executing the batchUpdate only referencing it. You need to add parentheses in order to call the execute() function:
update = service.presentations().batchUpdate(presentationId = PRESENTATION_ID, body = body).execute()
As a side note - do remember that your service account also needs to have edit access to the Slide for it to be able to make changes.
References:
Google Slides API - Method: presentations.batchUpdate
Google Slides API - batchUpdate: Requests
Google Slides API - Python Quickstart - Set up the sample
I need to get the list of all files and folders in google drive owned by a user. For some reasons file.list method doesn't return nextPageToken in response and I see only few results, since I can't go to the next page.
I have tried API Client Library for python and API explorer, but I receive only several records per user.
My code
users = ['user1#domain.com', 'user2#domain.com']
drive_array = []
if users:
for item in users:
page_token_drive = None
query = "'%s' in owners" % (item)
while True:
drive_result = service_drive.files().list(q=query, corpora='domain', includeTeamDriveItems=False,
supportsTeamDrives=False, fields='nextPageToken, files(id,owners)',
pageToken=page_token_drive).execute()
drive_array.extend(drive_result.get('files', []))
page_token_drive = drive_result.get('nextPageToken', None)
if not page_token_drive:
break
I expect to get id of a file and owners array for all files owned by the user
[
{
"id": "12344",
"owners": [
{
"kind": "drive#user",
"displayName": "User1 User1",
"photoLink": "https://lg",
"me": false,
"permissionId": "1234556",
"emailAddress": "user1#domain.com"
}
]
},
{
"id": "09875",
"owners": [
{
"kind": "drive#user",
"displayName": "User1 User1",
"photoLink": "https://lh5",
"me": false,
"permissionId": "56565665655656566",
"emailAddress": "user1#domain.com"
}
]
}
]
According to the documentation, to authorize requests to G Suite APIs, you need to use OAuth 2.0 and you basically have two options (or flows if you want to stick with the official terminology):
User authorization with a consent screen (e.g OAuth 2.0 for installed applications)
Domain-wide delegation for server to server applications (e.g. Using OAuth 2.0 for Server to Server Applications)
With the first option, once that the user completes the flow, you can only access to its resources. So if you want to list all the contents of drive for different users in the G Suite domain, you need to use the second option.
I also recommend you to use the python client pagination feature to manage the listing of files.
Here is a working example of option 1 (Python 3.6)
import os
import pickle
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
SCOPES = ['https://www.googleapis.com/auth/drive', ]
users = ['user1#domain.eu', 'user2#domain.eu']
# we check if we save the credentials in the past and we reuse them
if not os.path.exists('credentials.dat'):
# no credentials found, we run the standard auth flow
flow = InstalledAppFlow.from_client_secrets_file('client_id.json', SCOPES)
credentials = flow.run_local_server()
with open('credentials.dat', 'wb') as credentials_dat:
pickle.dump(credentials, credentials_dat)
else:
with open('credentials.dat', 'rb') as credentials_dat:
credentials = pickle.load(credentials_dat)
if credentials.expired:
credentials.refresh(Request())
drive_sdk = build('drive', 'v3', credentials=credentials)
# drive files API
drive_files_api = drive_sdk.files()
for item in users:
query = "'{}' in owners".format(item)
drive_list_params = {
'q': query,
'corpora': 'domain',
'includeTeamDriveItems': False,
'supportsTeamDrives': False,
'fields': 'files(id,owners),nextPageToken',
}
# first request
files_list_req = drive_files_api.list(**drive_list_params)
while files_list_req is not None:
drive_file_list = files_list_req.execute()
print(drive_file_list.get('files', []))
# pagination handling
files_list_req = drive_files_api.list_next(files_list_req, drive_file_list)
If you run this, you will be prompted for authorization and the script will run on your drive listing files owned by other users and shared with you.
If you want to use the server-to-server flow with domain-wide delegation to list all the files (not just the ones shared with you), here is another working sample.
from googleapiclient.discovery import build
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/drive', ]
users = ['user1#domain.eu', 'user2#domain.eu']
credentials = service_account.Credentials.from_service_account_file('client_secret.json', scopes=SCOPES)
for item in users:
delegated_credentials = credentials.with_subject(item)
drive_sdk = build('drive', 'v3', credentials=delegated_credentials)
# drive files API
drive_files_api = drive_sdk.files()
drive_list_params = {
'corpora': 'domain',
'includeTeamDriveItems': False,
'supportsTeamDrives': False,
'fields': 'files(id,owners),nextPageToken',
}
# first request
files_list_req = drive_files_api.list(**drive_list_params)
while files_list_req is not None:
drive_file_list = files_list_req.execute()
print(drive_file_list.get('files', []))
# pagination handling
files_list_req = drive_files_api.list_next(files_list_req, drive_file_list)
We are trying to import our existing users into our B2C tenant. For this, we have been trying to use the azure-graphrbac python library.
I have followed this guide to register an application to be used with the graph api.
I'm using the below code to try and create a user:
from azure.graphrbac import GraphRbacManagementClient
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac.models import UserCreateParameters, PasswordProfile
credentials = ServicePrincipalCredentials(
client_id="<CLIENT ID>",
secret="<SECRET>",
tenant="<TENANT ID>"
)
tenant_id = '<myb2ctenant>.onmicrosoft.com'
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
ucp = UserCreateParameters(
user_principal_name="my#mail.com",
account_enabled=True,
display_name='Martin T',
mail_nickname='<mymail>',
additional_properties={
"signInNames": [{"type": "emailAddress", "value": "<mymail>"}]
},
user_type="LocalAccount",
password_profile=PasswordProfile(
password='<somepassword>',
force_change_password_next_login=True
)
)
user = graphrbac_client.users.create(ucp)
I've made sure that the client id, secret and tenant id are correct. However, I keep getting this error:
GraphErrorException: Access Token missing or malformed.
Does anyone have an idea as to what I might be doing wrong?
As Laurent said, you need define resource. The default resource is https://management.core.windows.net/. In your scenario, you want to create a user, the resource is https://graph.windows.net.
Your code also has some mistake, I modify it. The following code works for me.
from azure.graphrbac import GraphRbacManagementClient
from azure.common.credentials import ServicePrincipalCredentials
from azure.graphrbac.models import UserCreateParameters, PasswordProfile
credentials = ServicePrincipalCredentials(
client_id="",
secret="",
resource="https://graph.windows.net",
tenant = ''
)
tenant_id = ''
graphrbac_client = GraphRbacManagementClient(
credentials,
tenant_id
)
ucp = UserCreateParameters(
user_principal_name="",
account_enabled=True,
display_name='Martin T',
##I test in my lab, if I use this line, I will get error log and could not create a user.
#additional_properties={
# "signInNames": [{"type": "emailAddress", "value": ""}]
#},
##user_type only support Member or Guest, see this link https://learn.microsoft.com/en-us/python/api/azure.graphrbac.models.usercreateparameters?view=azure-python
user_type="Member",
mail_nickname = 'shuitest',
password_profile=PasswordProfile(
password='',
force_change_password_next_login=True
)
)
user = graphrbac_client.users.create(ucp)
See SDK in this link.
You service principal authentication needs to define "resource":
https://learn.microsoft.com/en-us/python/api/overview/azure/activedirectory
credentials = UserPassCredentials(
'user#domain.com', # Your user
'my_password', # Your password
resource="https://graph.windows.net"
)
I have a python script that is going to run a cron operation.
It needs to find a list of events for the current day, and then perform some other action depending on the event.
So far, I am able to make a request to the Calendar API, and I am getting a list of events for the current day.
When I loop through the list of event items, the "summary" key for the item is missing.
I need this field so that I can determine what the event is for.
The data in each event item is coming back like this with no "summary" key
{
"status": "confirmed",
"kind": "calendar#event",
"end": {
"date": "2017-07-29"
},
"iCalUID": "0000000000000#google.com",
"updated": "2017-06-20T00:00:00.000Z",
"start": {
"date": "2017-07-24"
},
"etag": "\"0000000000000000\"",
"id": "0000000000000"
}
In the Google docs found here https://developers.google.com/google-apps/calendar/v3/reference/events#resource it shows the "summary" key should be returned with the event.
Since this script is going to run automatically, I setup a Google Service account to authorize the request to the API so that a user doesn't have to authorize it manually. Here is a sample of the script that I'm using
# -*- coding: utf-8 -*-
from oauth2client.service_account import ServiceAccountCredentials
from httplib2 import Http
from apiclient.discovery import build
import datetime
try:
scopes = ['https://www.googleapis.com/auth/calendar']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'/path/to/credentials/filename.json', scopes=scopes)
http_auth = credentials.authorize(Http())
startTime = str(datetime.datetime.now().date()) + 'T00:00:00-07:00'
endTime = str(datetime.datetime.now().date()) + 'T23:59:00-07:00'
calendar = build('calendar', 'v3', http=http_auth)
calId = 'calendar_id_string'
response = calendar.events().list(calendarId=calId,
timeMin=startTime, timeMax=endTime).execute()
items = response.get('items',[])
for item in items:
summary = item.get('summary','') # summary is blank!!
except Exception, ex:
print ex
Thank you for the help
The reason why the event "summary" was not returned to the client is because of a calendar permission that was set for an email account.
The email account came from the credentials JSON file that was created when I made the Service Account.
When the calendar was shared with the email account, the permission was set to "See only free/busy (hide details)" instead of "See all event details" by the admin.
With the "See only free/busy (hide details)" permission, a user can only see if the calendar is free or busy at a given time, but no event details are returned.
By changing this permission to "See all event details" then all of the event details will be returned back to the client, including the "summary" key which is what I need.
See here for more details:
https://developers.google.com/google-apps/calendar/concepts/sharing
https://developers.google.com/api-client-library/python/auth/service-accounts
Only the Calendar Owner can change the permissions on a calendar. If you are the owner then,
Log into Google Calendar
Click on the Settings Gear button
Click Settings > Calendars > [Your Calendar Name] > Share this Calendar
Find the email account and change the permission setting
Click Save
Otherwise, you will need to contact the owner about changing the permission.
If you have a G suite account, and have your own domain name, then the administrator of the domain must authorize the client following these instructions here.
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#delegatingauthority
Note - In order to authorize the client, you must have already created a service account with the "Enable G Suite Domain-wide Delegation" checked. Then give the client_id to the administrator along with the scope, so it can be authorized by the administrator.
https://developers.google.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests
After the Service account was enabled with the "G Suite Domain-wide Delegation," and the client and scope was authorized by the domain administrator, then you should be able to see all of the event details after making the api call. Here is an example below. Hope this helps others
# -*- coding: utf-8 -*-
from oauth2client.service_account import ServiceAccountCredentials
from httplib2 import Http
from apiclient.discovery import build
import datetime
try:
# updated the scopes with "calendar.readonly"
scopes = ['https://www.googleapis.com/auth/calendar.readonly']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'/path/to/credentials/filename.json', scopes=scopes)
# create a credential object with an authorized user with read access to the calendar
delegated_credentials = credentials.create_delegated('authorized_user_email#your_domain.com')
http_auth = delegated_credentials.authorize(Http())
startTime = str(datetime.datetime.now().date()) + 'T00:00:00-07:00'
endTime = str(datetime.datetime.now().date()) + 'T23:59:00-07:00'
calendar = build('calendar', 'v3', http=http_auth)
calId = 'calendar_id_string'
response = calendar.events().list(calendarId=calId, timeMin=startTime, timeMax=endTime).execute()
items = response.get('items',[])
for item in items:
summary = item.get('summary','') # Event summary exists!! Yea!!
except Exception, ex:
print ex