I'm trying to get familiar with the google calendar api. In the getting started guide they have this code example:
from __future__ import print_function
import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
if not events:
print('No upcoming events found.')
for event in events:
start = event['start'].get('dateTime', event['start'].get('date'))
print(start, event['summary'])
if __name__ == '__main__':
main()
In this example we automatically open a window to ask the user to access their calendar, if we don't already have access through the pickle file. The thing is I don't want this window to open automatically, I want to print a link instead that the user can click to authenticate. I have looked around in the documentation but can't seem to find anything usefull. I would appriciate any help i could get, thanks!
For authorizing process, you want to show only the URL. You don't want to automatically open the browser.
You want to achieve this using googleapis with python.
If my understanding is correct, how about this answer? Please think of this as just one of several possible answers.
In this case, please use Flow.from_client_secrets_file instead of InstalledAppFlow.from_client_secrets_file.
Modified script:
When your script is modified, please modify as follows.
From:
from google_auth_oauthlib.flow import InstalledAppFlow
To:
from google_auth_oauthlib.flow import Flow
and
From:
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
To:
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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:
# Create the flow using the client secrets file from the Google API
# Console.
flow = Flow.from_client_secrets_file('client_secret.json', SCOPES, redirect_uri='urn:ietf:wg:oauth:2.0:oob')
# Tell the user to go to the authorization URL.
auth_url, _ = flow.authorization_url(prompt='consent')
print('Please go to this URL: {}'.format(auth_url))
# The user will get an authorization code. This code is used to get the
# access token.
code = input('Enter the authorization code: ')
flow.fetch_token(code=code)
creds = flow.credentials
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
In this case, when you run the script under token.pickle is not existing, the URL for the authorization is displayed to the console. The browser is not opened. So please access to the URL by opening the browser and authorize the scopes. Then, please the copied authorization code to the console and input enter key. By this, the access token is retrieved and the file of token.pickle is created.
Note:
If an error related to the redirect uri occurs, please modify it to http://localhost and test it again.
Reference:
google_auth_oauthlib.flow module
If I misunderstood your question and this was not the direction you want, I apologize.
Added:
From I want to print a link instead that the user can click to authenticate in your question, I proposed above sample script.
From some way not to manually confirm authorization codes in your replying, I think that that above sample script is not suitable.
In this case, how about using the service account? When the service account is used, no authorization code is required. The script for using the service account is as follows.
Sample script:
from google.oauth2 import service_account
from googleapiclient.discovery import build
SERVICE_ACCOUNT_FILE = 'service-account-credentials.json' # Here, please set the creadential file of the service account.
SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
creds = service_account.Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
service = build('calendar', 'v3', credentials=creds)
Note:
In order to access to the Google calendar using the service account, at first, please share the Google calendar with the email of the service account. Please be careful this.
Reference:
Creating a service account
Related
the code was working but I had to revoke the temporary token because my app was still in testing, but now it shows on console:
"Enter the authorization code:" But What is the authorization code??
CLIENT_SECRET_FILE = 'client_secret_xxxxxxx.json'
API_NAME = 'drive'
API_VERSION = 'v3'
SCOPES = ['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/drive.file',
'https://www.googleapis.com/auth/drive.readonly',
'https://www.googleapis.com/auth/drive.metadata.readonly',
'https://www.googleapis.com/auth/drive.appdata',
'https://www.googleapis.com/auth/drive.metadata',
'https://www.googleapis.com/auth/drive.photos.readonly',]
service = Create_Service(CLIENT_SECRET_FILE, API_NAME, API_VERSION, SCOPES)
The authorization code is the response from the authorization as part of the fist step in the authorization process.
When your application runs you should ask the user to navigate to a web site where they will be shown the consent screen.
When the user consents to the request for authorization. Then a new browser window will open and the authorization code will appear in the url of the browser.
This code is then used by your application to exchange for an access token which will give your application access to request data from the users account.
I think that you should have a look at the official google drive sample. python quickstart authorizaotn code you will find here will work better then what you have now.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
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 os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
return
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
except HttpError as error:
# TODO(developer) - Handle errors from drive API.
print(f'An error occurred: {error}')
if __name__ == '__main__':
main()
I am trying to set up a program that will automatically change my YouTube banner. I thought it would be really cool to have if swap between multiple different banner images that people might make for me. But the implementation I found requires the user to input the authorization code from.
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.http import MediaFileUpload
CLIENT_SECRET_FILE = 'client_secret.json'
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRET_FILE, SCOPES)
credentials = flow.run_console()
youtube = build('youtube', 'v3', api_key=apikey)
request = youtube.channelBanners().insert(
channelId="",
body={},
media_body=MediaFileUpload("image.jpg")
)
response = request.execute()
print(response)
Short hand: Is there a good way to update a YouTube banner with a basic script running on a server?
If you run something like this locally once and authorize it. it will store the user credentials in token.json. As long as you upload that along with your code to your server the next time it runs it should just load the authorization from that.
from __future__ import print_function
import os.path
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/youtube.force-ssl']
def main():
"""Shows basic usage of the YouTube API.
Prints the names and ids of the first 10 files the user has access to.
"""
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 os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
try:
service = build('youtube', 'v3', credentials=creds)
# Call the Youtube v3 API
request = service.channelBanners().insert(
channelId="",
body={},
media_body=MediaFileUpload("image.jpg")
)
response = request.execute()
print(response)
if __name__ == '__main__':
main()
Using token.pickle is quite frustrating, as every couple of weeks the token expires, and then I need to manually delete it from my source files in code, so it can regenerate itself, and then I need to re-authenticate it from my account.
Is there a way I can just create a new service without it? I know it's possible for Google sheets files. This is what that looks like:
def get_g_sheets_service():
SERVICE_ACCOUNT_FILE = 'key.json'
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
creds = None
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
SAMPLE_SPREADSHEET_ID = 'ID_GOES_HERE'
service = build('sheets', 'v4', credentials=creds)
return service
but, the way to get a service for the calendar API looks like this:
import datetime
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/calendar']
CREDENTIALS_FILE = 'path_to_file/credentials.json'
def get_calendar_service():
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
CREDENTIALS_FILE, SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds)
return service
Notice the token.pickle file?
How can I not deal with it?
why is the token.pickle expring?
The token.pickle should contain an access token and a refresh token which was created with the user consented to your application accessing their data. Access tokens are used to access user data and expire after an hour, refresh tokens are use to request a new access token when it has expired. This is done automatically via the client library you are using. Refresh tokens for the most part should not be expiring see: experation.
You need to be sure you are always storing the most recent refresh token.
If your application is still in the testing phase refresh tokens will expire after seven days. see: experation
A Google Cloud Platform project with an OAuth consent screen configured for an external user type and a publishing status of "Testing" is issued a refresh token expiring in 7 days.
The solution is to go to google cloud console under the oauth2 consent screen and set your application to production.
service accounts
If this google calendar is part of google workspace. then your workspace admin could grant a service account domain wide delegation and allow you to impersonate a user on the domain with the service account. This form for authorization is much easer and will not have the same expiration token issues as the authorization is configured via workspace.
Service accounts only work though google calendar with workspace domain accounts.
I'm currently working on a project that uses the Google Calendar API. I would like the program that I'm creating to print out a "Success" when it successfully connects to the API or a "Failed" when it does not. Also, wondering if there's any way to specifically print out the API response code?
Have you reviewed this Google Calendar API tutorial for Python?
From the snippet below, you should check for errors/exceptions when generating credentials and instantiating the client. I have marked them as TODO #1, #2, and #3.
Please refer to the example for additional context. Good luck!
def main():
"""Shows basic usage of the Google Calendar API.
Prints the start and name of the next 10 events on the user's calendar.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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()) # TODO #1: Add exception handling here (and really the entire block) to capture authentication/authorization issues
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('calendar', 'v3', credentials=creds) # TODO #2: Add exception handling here to capture client side issues
# Call the Calendar API
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
print('Getting the upcoming 10 events')
# TODO #3: Add exception handling here in the actual API call
events_result = service.events().list(calendarId='primary', timeMin=now,
maxResults=10, singleEvents=True,
orderBy='startTime').execute()
events = events_result.get('items', [])
I enabled the Google Drive API in my personal GCP, set up my OAuth2 consent screen, and downloaded my credentials.json file. I'm now running this code from the Python quickstart in Colab:
from __future__ import print_function
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
# If modifying these scopes, delete the file token.pickle.
SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly']
def main():
"""Shows basic usage of the Drive v3 API.
Prints the names and ids of the first 10 files the user has access to.
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.pickle'):
with open('token.pickle', 'rb') as token:
creds = pickle.load(token)
# 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:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.pickle', 'wb') as token:
pickle.dump(creds, token)
service = build('drive', 'v3', credentials=creds)
# Call the Drive v3 API
results = service.files().list(
pageSize=10, fields="nextPageToken, files(id, name)").execute()
items = results.get('files', [])
if not items:
print('No files found.')
else:
print('Files:')
for item in items:
print(u'{0} ({1})'.format(item['name'], item['id']))
if __name__ == '__main__':
main()
When I execute the code, I am prompted to login via Google's OAuth sign-in page, to give my program access to my Google account. I click Allow, then immediately see this page:
I've redone this without a VPN or firewall with no luck, and I don't know if this is a problem with my OAuth or creds setup in the Project. Thoughts?
Solved by running in PyCharm. VSCode also works. For whatever reason, Colab continues to throw the error.
Provide a solution:
When run_local_server() opening browser, copy the url and use private browsing to login with this link.
It work for me.