How to create Google Apps Script? - python

I use service account and it's my code:
test.py
import httplib2
from apiclient import discovery
from googleapiclient.http import MediaFileUpload
from oauth2client.service_account import ServiceAccountCredentials
SCOPE = 'https://www.googleapis.com/auth/drive'
KEY_DICT = {...}
credentials = ServiceAccountCredentials.from_json_keyfile_dict(KEY_DICT, SCOPE)
http = credentials.authorize(httplib2.Http())
drive = discovery.build('drive', 'v3', http=http)
script = discovery.build('script', 'v1', http=http)
file_name = 'test.gs'
file_metadata = {
'name': file_name,
'mimeType': 'application/vnd.google-apps.script'
}
media = MediaFileUpload(file_name, mimetype='text/plain')
file = drive.files().create(body=file_metadata, media_body=media, fields='id').execute()
script_id = file.get('id')
request = {
"function": 'getFoldersUnderRoot',
}
response = script.scripts().run(body=request, scriptId=script_id).execute()
print response['response'].get('result')
test.gs
function getFoldersUnderRoot() {
var root = DriveApp.getRootFolder();
var folders = root.getFolders();
var folderSet = {};
while (folders.hasNext()) {
var folder = folders.next();
folderSet[folder.getId()] = folder.getName();
}
return folderSet;
}
Then I get the following error:
Traceback (most recent call last):
File "./test.py", line 37, in <module>
response = script.scripts().run(body=request, scriptId=script_id).execute()
File "site-packages/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
File "site-packages/googleapiclient/http.py", line 840, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://script.googleapis.com/v1/scripts/1LI...4y0:run?alt=json returned "Request contains an invalid argument.">
What am I doing wrong? Maybe I should deploy the script? Maybe this works only web UI. I didn't find how to do it via dev console.

Unfortunately there are known issues with using service accounts and the Google App Scripts API. You can read more about this issue here.
The work-around is to user client credentials to generate a refresh token manually, and then use the refresh token as a means of getting an authorization token.

Related

Google Sheets API Wont Let Me Write Data into My Google Sheet

Code is below. this isnt the full code but the basis of it. Im trying to take data from Twitter's API and Write it to my Google Sheets API. Below is the Code.
from googleapiclient import discovery
from google.oauth2 import service_account
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
SERVICE_ACCOUNT_FILE = 'twitter.json' #json File should be in the same folder as this Python Script.
SCOPES = ['https://www.googleapis.com/auth/spreadsheets']
creds = None
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# The ID and range of a sample spreadsheet.
SAMPLE_SPREADSHEET_ID = '1f4iqVHljytmRSC2EZcApEiA2lE1cHa1o6Fr4s3t0AKg'
#SAMPLE_RANGE_NAME = 'Class Data!A2:A60'
service = build('sheets', 'v4', credentials=creds)
sheet = service.spreadsheets()
# Call the Sheets API
result = sheet.values().get(spreadsheetId=SAMPLE_SPREADSHEET_ID,
range='twitterData!A1:A180').execute()
#ERROR HERE BELOW
request = sheet.values().update(spreadsheetId = SAMPLE_SPREADSHEET_ID,
range = 'twitterData!B2:B180', valueInputOption='USER_ENTERED', body={"values": 8}).execute()
the one Line im getting an Error for has the comment above it Error Below. and this is the Error Message:
Traceback (most recent call last):
File "C:\Users\John Doe\Desktop\Code\Python\Twitter\test.py", line 27, in <module>
request = sheet.values().update(spreadsheetId = SAMPLE_SPREADSHEET_ID,
File "C:\Users\John Doe\AppData\Local\Programs\Python\Python38-32\lib\site-packages\googleapiclient\_helpers.py", line 130, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Users\John Doe\AppData\Local\Programs\Python\Python38-32\lib\site-packages\googleapiclient\http.py", line 938, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 400 when requesting https://sheets.googleapis.com/v4/spreadsheets/1f4iqVHljytmRSC2EZcApEiA2lE1cHa1o6Fr4s3t0AKg/values/twitterData%21B2%3AB180?valueInputOption=USER_ENTERED&alt=json returned "Invalid value at 'data.values' (type.googleapis.com/google.protobuf.ListValue), 8". Details: "[{'#type': 'type.googleapis.com/google.rpc.BadRequest', 'fieldViolations': [{'field': 'data.values', 'description': "Invalid value at 'data.values' (type.googleapis.com/google.protobuf.ListValue), 8"}]}]">
Ive used Google Sheets API before. Im not sure why this code isnt working. Its just that one line.
I think that the reason for your current issue of "Invalid value at 'data.values' (type.googleapis.com/google.protobuf.ListValue), 8" is due to body={"values": 8}. In this case, it is required to use a 2-dimensional array. So, please modify it as follows.
From:
request = sheet.values().update(spreadsheetId = SAMPLE_SPREADSHEET_ID,
range = 'twitterData!B2:B180', valueInputOption='USER_ENTERED', body={"values": 8}).execute()
To:
request = sheet.values().update(spreadsheetId = SAMPLE_SPREADSHEET_ID, range = 'twitterData!B2:B180', valueInputOption='USER_ENTERED', body={"values": [[8]]}).execute()
In this modification, body={"values": 8} is modified to body={"values": [[8]]}.
By this modification, 8 is put to the cell "B2" of "twitterData" sheet.
Reference:
Method: spreadsheets.values.update

Returned "Access to model denied." from Google AI Platform

I made a Tensorflow model and uploaded files to GCP storage.
I tried to get prediction results from Google AI Platform. But it showed errors.
I made a serviceAccount for model 'vgg_16' with below roles.
roles/iam.securityAdmin
roles/iam.securityReviewer
roles/ml.admin
roles/ml.developer
roles/ml.modelOwner
roles/ml.modelUser
roles/ml.viewer
roles/owner
roles/viewer
also, billing is activated for my project.
My codes:
from googleapiclient import discovery
from oauth2client.client import GoogleCredentials
from oauth2client.service_account import ServiceAccountCredentials
import numpy
import base64
import logging
from IPython.display import display, Image
from google.cloud import storage
cropped_image = "8-android.widget.TextView.png"
display(Image(cropped_image))
PROJECT_ID = '6111116488'
MODEL_NAME = 'vgg_16'
VERSION_NAME ='v1'
def explicit():
# Explicitly use service account credentials by specifying the private key
# file.
storage_client = storage.Client.from_service_account_json(
'D://setup//Development//sfsfc1111.json')
# Make an authenticated API request
buckets = list(storage_client.list_buckets())
print(buckets)
def call_ml_service(img_str):
parent = 'projects/{}/models/{}/versions/{}'.format(PROJECT_ID, MODEL_NAME,VERSION_NAME)
pred = None
request_dict = {
"instances": [
{
"image": {
"b64": "%s"%img_str
}
}
]
}
try:
credentials = ServiceAccountCredentials.from_json_keyfile_name('D://setup//Development//sfsfc1111.json')
#credentials = GoogleCredentials.get_application_default()
cloudml_svc = discovery.build('ml', 'v1', credentials=credentials)
request = cloudml_svc.projects().predict(name=parent, body=request_dict)
response = request.execute()
print(response)
#pred = response['predictions'][0]['scores']
#pred = numpy.asarray(pred)
except:
logging.exception("Something went wrong!")
return pred
# base64 encode the same image
with open(cropped_image, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read())
#print(encoded_string)
explicit()
# See what ML Engine thinks
online_prediction = call_ml_service(encoded_string)
print(online_prediction)
But, 403 "Access to model denied." was occurred.
error codes:
(tensorflow1.14) D:\work\models\research\slim>python inference_google_aiplatform.py
<IPython.core.display.Image object>
[<Bucket: android-ios-new-vcm>]
ERROR:root:Something went wrong!
Traceback (most recent call last):
File "inference_google_aiplatform.py", line 48, in call_ml_service
response = request.execute()
File "C:\Users\admin\Anaconda3\envs\tensorflow1.14\lib\site-packages\googleapiclient\_helpers.py", line 134, in positional_wrapper
return wrapped(*args, **kwargs)
File "C:\Users\admin\Anaconda3\envs\tensorflow1.14\lib\site-packages\googleapiclient\http.py", line 907, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: <HttpError 403 when requesting https://ml.googleapis.com/v1/projects/6111116488/models/vgg_16/versions/v1:predict?alt=json returned "Access to model denied.">
None
Can anyone help me?

Working with Gmail API from Google AppEngine

In GAE standard environment, I'm struggling with registering the watch() call against Gmail API for the Pub/Sub push notification using google-api-python-client.
Here is the relevant excerpt from my code:
import googleapiclient.discovery
from oauth2client import service_account
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
SERVICE_ACCOUNT_FILE = '<My-project>-<short-ID>.json'
credentials = service_account.ServiceAccountCredentials.from_json_keyfile_name(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
gmail = googleapiclient.discovery.build('gmail', 'v1', credentials=credentials)
watchRequest = {
'labelIds' : ['INBOX'],
'topicName' : 'projects/<my-project>/topics/<my-topic>'
}
gmail.users().watch(userId='<email-I-need-to-watch>', body=watchRequest).execute()
After firing-off this part of the code I get:
Traceback (most recent call last):
File "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 240, in Handle
handler = _config_handle.add_wsgi_middleware(self._LoadHandler())
File "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 299, in _LoadHandler
handler, path, err = LoadObject(self._handler)
File "/base/alloc/tmpfs/dynamic_runtimes/python27/54c5883f70296ec8_unzipped/python27_lib/versions/1/google/appengine/runtime/wsgi.py", line 85, in LoadObject
obj = __import__(path[0])
File "/base/data/home/apps/e~<my-project>-191008/20180124t154459.407164278206739455/main.py", line 68, in <module>
gmail.users().watch(userId='<email-I-need-to-watch>', body=watchRequest).execute()
File "/base/data/home/apps/e~<my-project>/20180124t154459.407164278206739455/lib/oauth2client/_helpers.py", line 133, in positional_wrapper
return wrapped(*args, **kwargs)
File "/base/data/home/apps/e~<my-project>/20180124t154459.407164278206739455/lib/googleapiclient/http.py", line 844, in execute
raise HttpError(resp, content, uri=self.uri)
HttpError: <HttpError 400 when requesting https://www.googleapis.com/gmail/v1/users/<email-I-need-to-watch>/watch?alt=json returned "Bad Request">
In regards to the authentication and authorization, here is what I have done so far:
I've created a Pub/Sub topic and this is the one I'm passing into the watch() request
I use G-Suite and the email inbox I intend to watch is part of my G-Suite business domain.
For this task I use a service account with enabled G-Suite Domain-wide Delegation. I've downloaded the .json service account file which I'm supplying in order to acquire the oauth2client.service_account.Credentials object (I see the access and refresh tokens being exchanged successfully in the logs). The json service file is placed in the same folder as my main.py script (root of my project).
In my G-Suite administration panel I've enabled the api access to the service account from 2. with the scope of https://www.googleapis.com/auth/gmail.modify. I'm using the gmail.modify access level as I intend to read, write and send both emails and drafts.
Is there something I'm missing out in my code or in the authentication and authorization steps?
Problem solved. I was missing the part of the code for impersonating a user from my domain in order to read his/her mailbox (as explained here).
The corrected code looks like this:
import googleapiclient.discovery
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/gmail.modify']
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES
)
credentials = credentials.with_subject('<email-I-need-to-watch>')
gmail = googleapiclient.discovery.build('gmail', 'v1', credentials=credentials)
watchRequest = {
'labelIds' : ['INBOX'],
'topicName' : 'projects/<my-project>/topics/<my-topic>'
}
gmail.users().watch(userId='me', body=watchRequest).execute()

Gmail API Push Notifications gives Error 403

I am trying to get push notifications regarding all the new inbox messages I receive in my email. I have configured the pubsub client as mentioned in the pubsub doc. Following is the code:
import httplib2
from apiclient import discovery
from oauth2client import client as oauth2client
from lib.common.models.main.users import User
from oauth2client.client import SignedJwtAssertionCredentials,\
AccessTokenRefreshError
PUBSUB_SCOPES = ['https://www.googleapis.com/auth/pubsub']
def create_pubsub_client(http=None):
credentials = oauth2client.GoogleCredentials.get_application_default()
if credentials.create_scoped_required():
credentials = credentials.create_scoped(PUBSUB_SCOPES)
if not http:
http = httplib2.Http()
credentials.authorize(http)
return discovery.build('pubsub', 'v1', http=http)
pubsub = create_pubsub_client()
topic = 'projects/<project-name>/topics/<Topic-name>'
policy = {
'policy': {
'bindings': [{
'role': 'roles/pubsub.publisher',
'members': ['serviceAccount:<my-service-account>'],
}],
}
}
resp = pubsub.projects().topics().setIamPolicy(resource=topic, body=policy).execute()
request = {
'labelIds': ['INBOX'],
'topicName': 'projects/<project-name>/topics/<Topic-name>'
}
f = file('link-to.p12', 'rb')
key = f.read()
f.close()
credentials = SignedJwtAssertionCredentials(
'service-account-email',
key,
scope='https://mail.google.com/',
)
user = User.query.filter(User.id == 143).first()
accesskey = user.get_access_key(True)
credentials.access_token = accesskey['key']
service = discovery.build('gmail','v1',credentials=credentials)
service.users().watch(userId='me', body=request).execute()
When I run the above Program, I encounter the following error:
Traceback (most recent call last):
File "/home/kmittal/workspace/engine/workers/newPubSubClient.py", line 82, in
service.users().watch(userId='me', body=request).execute()
File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 135, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/googleapiclient/http.py", line 723, in execute
raise HttpError(resp, content, uri=self.uri)
googleapiclient.errors.HttpError: https://www.googleapis.com/gmail/v1/users/me/watch?alt=json returned "Invalid topicName does not match projects/SOME_ANOTHER_PROJECT/topics/*">
As per this other answer, it looks like you can only create a watch for topics that are part of your own project, not another project. The documentation seems to back this up as it implies you must specify myproject (your own project ID):
The topic name can be any name you choose under your project (i.e.
matching projects/myproject/topics/*, where myproject is the Project
ID listed for your project in the Google Developers Console).

403 Forbidden error google plus python

There are my codes:
import pprint
import httplib2
from apiclient.discovery import build
from oauth2client.client import OAuth2WebServerFlow
SCOPES = ['https://www.googleapis.com/auth/plus.me',
'https://www.googleapis.com/auth/plus.stream.write']
REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob'
CLIENT_ID = "my client id"
CLIENT_SECRET = "my client secret"
flow = OAuth2WebServerFlow(client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPES,
redirect_uri=REDIRECT_URI)
auth_uri = flow.step1_get_authorize_url()
print 'Please paste this URL in your browser to authenticate this program.'
print auth_uri
code = raw_input('Enter the code it gives you here: ')
credentials = flow.step2_exchange(code)
http = httplib2.Http()
http = credentials.authorize(http)
service = build('plusDomains', 'v1', http=http)
user_id = 'me'
print('Insert activity')
result = service.activities().insert(
userId = user_id,
body = {
'object' : {
'originalContent' : 'Happy Monday! #caseofthemondays'
},
'access' : {
'items' : [{
'type' : 'domain'
}],
'domainRestricted': True
}
}).execute()
print('result = %s' % pprint.pformat(result))
and there are my error:
Traceback (most recent call last):
File "/home/karl/workspace/googleplus/google_plus/google_plus_pic.py", line 44, in <module>
'domainRestricted': False
File "/usr/local/lib/python2.7/dist-packages/oauth2client/util.py", line 128, in positional_wrapper
return wrapped(*args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/apiclient/http.py", line 680, in execute
raise HttpError(resp, content, uri=self.uri)
apiclient.errors.HttpError: <HttpError 403 when requesting https://www.googleapis.com/plusDomains/v1/people/me/activities?alt=json returned "Forbidden">
I have read a lot of information,but I don't know how to solve the problem.
Where is the bug?
To use the Google+ Domains API, you need to make sure that the Domain for the user you are acting on behalf of has been set up with the right permissions for your app. These instructions are under the "Delegate domain-wide authority to your service account" section of Step 1 of the quick-start guide.
Specifically, you need to associate your app's Client ID with the scopes your app will use in the control panel for the domain. The domain administrator is the only person who can do this--so if you are working with another domain, be sure you get in contact with that person. Also, the scopes listed in the control panel must match EXACTLY with the scopes you request in your app.

Categories