gpsread Batch Update Multiple Sheets - python

Is there a way to Batch Update multiple sheets at once? I am trying to protect every worksheet in a workbook and I am doing:
def protectSheet(sheet):
body = {
"requests": [
{
"addProtectedRange": {
"protectedRange": {
"range": {
"sheetId": sheet._properties['sheetId'],
},
"warningOnly": True
}
}
}
]
}
try:
workbook.batch_update(body)
except gspread.exceptions.APIError:
logger.warning("%s is already protected" % sheet._properties['title'])
for worksheet in workbook.worksheets():
protectSheet(worksheet)
But is there a way to make a single call to protect every sheet in a workbook?
Edit: Alternatively is there a way to tell if a worksheet is protected?

I believe your goal as follows.
In your question, there are following 2 questions.
is there a way to make a single call to protect every sheet in a workbook?
is there a way to tell if a worksheet is protected?.
You want to achieve above using gspread with python.
Answer for question 1:
The following sample script protects all sheets in the Spreadsheet by one API call. But for example, a sheet in the Spreadsheet has already been protected, an error occurs. So please be careful this.
Sample script:
spreadsheetId = "###" # Please set the Spreadsheet ID.
client = gspread.authorize(credentials)
ss = client.open_by_key(spreadsheetId)
worksheets = ss.worksheets()
requests = [
{
"addProtectedRange": {
"protectedRange": {
"range": {
"sheetId": e.id,
},
"warningOnly": True
}
}
} for e in worksheets]
request_body = {"requests": requests}
res = ss.batch_update(request_body)
print(res)
Answer for question 2:
The following sample script retrieves all protected sheets in the Spreadsheet by one API call. In order to retrieve all protected sheets and ranges by one API call, it is required to use the method of "spreadsheets.get". Unfortunately, it seems that there is no method of "spreadsheets.get" in gspread. So I used the access token from credentials for gspread.
When you run the script, the sheets IDs of protected sheets are retrieved.
Sample script:
import requests # This is used.
spreadsheetId = "###" # Please set the Spreadsheet ID.
# client = gspread.authorize(credentials)
access_token = credentials.access_token
endpoint = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "?fields=sheets.protectedRanges.range"
headers = {'Authorization': 'Bearer ' + access_token}
res = requests.get(endpoint, headers=headers)
obj = res.json()
sheetIds = []
for e in obj['sheets']:
if 'protectedRanges' in e:
for f in e.get('protectedRanges'):
if 'range' in f and 'startRowIndex' not in f.get('range') and 'endRowIndex' not in f.get('range') and 'startColumnIndex' not in f.get('range') and 'endColumnIndex' not in f.get('range'):
if 'sheetId' not in f.get('range'):
sheetIds.append(0)
else:
sheetIds.append(f.get('range')['sheetId'])
print(sheetIds)
Other pattern:
As other pattern, the following sample script retrieves all unprotected sheets in the Spreadsheet.
Sample script:
import requests # This is used.
spreadsheetId = "###" # Please set the Spreadsheet ID.
# client = gspread.authorize(credentials)
access_token = credentials.access_token
endpoint = "https://sheets.googleapis.com/v4/spreadsheets/" + spreadsheetId + "?fields=sheets.properties%2Csheets.protectedRanges.range"
headers = {'Authorization': 'Bearer ' + access_token}
res = requests.get(endpoint, headers=headers)
obj = res.json()
sheetIds = []
for e in obj['sheets']:
if 'protectedRanges' in e:
for f in e.get('protectedRanges'):
if 'range' not in f or ('range' in f and (
'startRowIndex' in f.get('range') or
'endRowIndex' in f.get('range') or
'startColumnIndex' in f.get('range') or
'endColumnIndex' in f.get('range'))):
sheetIds.append(f.get('range')['sheetId'])
else:
sheetIds.append(e['properties']['sheetId'])
print(sheetIds)
Reference:
spreadsheets.get

Related

pagination for nested Json

I have a paginated API and I'm trying to go through all the data available and save it into a list. However, the nature of my API is that it's nested here's an example of how it looks like.
"data": [{"type": "general-Type", "id": 1, "attributes": {"firstname": "Kevin", "lastname": "Wolf", "emailaddress": "kevinwolf#gmail.com"}}]
Thus when I save it into a list the last part of the data aka "attributes" looks like a dictionary causing the following error:
sample_data.extend(sample_data['data'])
AttributeError: 'dict' object has no attribute 'extend'
I'm new to this so any help on how to successfully do this request would be helpful
Thank you in advance
If it helps here's my code:
the request limit is 10,000 that's why I set the limit to 10,000 increments
sample_data = []
offset = 0
limit = 10000
while True:
print("----")
url = f"https://results.us.sampledata.com/api/reporting/v0.1.0/samples?offset={offset}&page[size]={limit}"
headers = {"Content-Type": "application/json", "Accept-Charset": "UTF-8", "x-apikey-token": "sampletoken"}
print("Requesting", url)
response = requests.get(url, data={"sample": "data"}, headers=headers)
sample_data = response.json()
if len(sample_data['data']) == 0:
# If not, exit the loop
break
# If we did find records, add them
# to our list and then move on to the next offset
sample_data.extend(sample_data['data'])
offset = offset + 10000
As #8349697 already said your problem is that you use the same name sample_data to keep two different structures.
First you create list sample_data = [] but later you overwrite it with dictionary sample_data = response.json() but next you want to use original list sample_data to add values from dict sample_data
You should use different names like
page_data = response.json()
if not page_data['data']: # if len(page_data['data']) == 0:
break
sample_data.extend(page_data['data'])
Minimal code with other changes - but I couldn't test it with your url.
import requests
sample_data = []
headers = {
"Content-Type": "application/json",
"Accept-Charset": "UTF-8",
"x-apikey-token": "sampletoken"
}
data = {
"sample": "data"
}
params = {
"offset": 0,
"page[size]": 10000,
}
url = "https://results.us.sampledata.com/api/reporting/v0.1.0/samples"
while True:
print("----")
#url = f"https://results.us.sampledata.com/api/reporting/v0.1.0/samples?offset={offset}&page[size]={limit}"
#print("Requesting", url)
print('Offset:', params['offset'])
response = requests.get(url, params=params, data=data, headers=headers)
page_data = response.json()
if (not 'data' in page_data) or (not page_data['data']):
break
sample_data.extend(page_data['data'])
params['offset'] += 10000

Delete (remove) column in google sheet over gspread Python like sheet.delete_row

Is there a method like worksheet.delete_row in gspread google-sheet?
I tried:
delete = sheet.range('A1:A1000')
for cell in delete:
cell.value = ""
sheet.update_cells(delete)
but that only delete all values, not column.
Can anybode help me?
Answer:
There is no method in gspread to delete an entire column, like Workbook.delete_row, however you can do this with a batch update.
Code sample:
spreadsheetId = "your-spreadsheet-id"
sheetId = "id-of-sheet-to-delete-column-from"
sh = client.open_by_key(spreadsheetId)
request = {
"requests": [
{
"deleteDimension": {
"range": {
"sheetId": sheetId,
"dimension": "COLUMNS",
"startIndex": 0,
"endIndex": 1
}
}
}
]
}
result = sh.batch_update(request)
This sample will delete column A, but make sure to change the startIndex and endIndex to be of the column range you wish to delete.
Edit:
If you do not know the sheetId of a given sheet, you can get it using the following:
sheetName = "theSheetName"
sheetId = sh.worksheet(sheetName)._properties["sheetId"]
Note that this is not needed for the original sheet of a Spreadsheet, as this will always be 0.
References:
Method: spreadsheets.batchUpdate | Sheets API | Google Developers
API References - gspread 3.4.0 documentation - batch_update(body)
Update 2020-04-15:
This script was merged with gspread master today from pull request #759 as method delete_column().
The method will be available in the next release v3.5.0.
A method for delete_columns() was also added as a parallel method to the existing delete_rows() from pull request #761.

how to read (or parse) google spreadsheet colums note(comment) using python

I've a Google Spreadsheet and there is a two column
id , name
1 aaaa
and there a note for Colum name and row 1 that means in the cell(2,2)
I am able to read data for tow column of the spreadsheet but could not read the note of that cell.
I am using gspread library for reading data from the spreadsheet.
import gspread
from gspread_dataframe import get_as_dataframe
scope = ['https://www.googleapis.com/auth/drive', 'https://www.googleapis.com/auth/spreadsheets']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'jasonfileNlocation.json',
scope)
google_spreadsheet_connection = gspread.authorize(credentials)
wks = google_spreadsheet_connection.open("spreadsheet_name")
worksheet = wks.get_worksheet(0)
df = get_as_dataframe(worksheet, evaluate_formulas=True, index='false')
for better understanding i have added below image
Any help will be appriciated
You want to retrieve notes of the cells on the Spreadsheet.
You want to achieve this using the Service account and Python.
In your script, ServiceAccountCredentials is used as from oauth2client.service_account import ServiceAccountCredentials.
You have already been able to put and get values for Spreadsheet using Sheets API with gspread.
If my understanding is correct, how about this answer? Unfortunately, it seems that the notes cannot be retrieved by gspread. So how about using google-api-python-client? In this answer, the notes of the cells are retrieved using the method of spreadsheets.get in Sheets API with google-api-python-client.
Modified script:
In this modification, your script was modified.
import gspread
import httplib2
from apiclient import discovery
from gspread_dataframe import get_as_dataframe
from oauth2client.service_account import ServiceAccountCredentials
scope = ['https://www.googleapis.com/auth/drive',
'https://www.googleapis.com/auth/spreadsheets']
credentials = ServiceAccountCredentials.from_json_keyfile_name(
'jasonfileNlocation.json',
scope)
google_spreadsheet_connection = gspread.authorize(credentials)
wks = google_spreadsheet_connection.open("spreadsheet_name")
worksheet = wks.get_worksheet(0)
df = get_as_dataframe(worksheet, evaluate_formulas=True, index='false')
# I added below script.
service = discovery.build(
'sheets', 'v4', http=credentials.authorize(httplib2.Http()))
spreadsheet_id = '###' # Please set the Spreadsheet ID here.
ranges = ['Sheet1!A2:B'] # For example, when you want to retrieve the note from the cells "A2:B" of "Sheet1", please use this.
fields = 'sheets(data(rowData(values(note,userEnteredValue))))'
request = service.spreadsheets().get(
spreadsheetId=spreadsheet_id, ranges=ranges, fields=fields)
response = request.execute()
print(response)
I could understand that ID is the value of the column "A", and you want to the values of EMPID and `name together with the notes.
Result:
{
"sheets": [
{
"data": [
{
"rowData": [
{
"values": [
{"userEnteredValue": {"numberValue": 1}},
{"note": "sample note1", "userEnteredValue": {"stringValue": "name1"}}
]
},
{
"values": [
{"userEnteredValue": {"numberValue": 2}},
{"note": "sample note2", "userEnteredValue": {"stringValue": "name2"}}
]
},
,
,
,
]
}
]
}
]
}
The index of rowData is the row number.
The index of values is the column number.
Note:
In this sample, the note of the cell "B2" in the sheet of "Sheet1" is retrieved. If you want to retrieve other cells, please modify this.
In this modified script, gspread can be also used.
References:
google-api-python-client
Method: spreadsheets.get
If I misunderstood your question and this was not the direction you want, I apologize.

Invalid JSON payload received. Unknown name at 'requests[1].paste_data.data': Cannot find field

UPDATE:
I used the api explorer on the google bactch update page and was able to successfully update the spreadsheet but when I replicate this in my code I am still getting an error. The API explorer did work with the json format.
<HttpError 400 when requesting
Invalid JSON payload received. Unknown name "value_input_option" at 'requests[0]': Cannot find field.
Invalid JSON payload received. Unknown name "data" at 'requests[0]': Cannot find field.">
scope = ['https://spreadsheets.google.com/feeds',
'https://www.googleapis.com/auth/drive']
creds =
ServiceAccountCredentials.from_json_keyfile_name('client_secret.json', scope)
service = discovery.build('sheets', 'v4', credentials=creds)
client = gspread.authorize(creds)
# find workbook by name and open the first sheet
# make sure you use the right name here
sheet = client.open("forklift").sheet1
# extract and print all of the values
# list_of_hashes = sheet.get_all_records()
spreadsheet_id = '112345'
batch_update_spreadsheet_request_body = {
"requests": [
{
"valueInputOption": "RAW",
"data": [
{
"majorDimension": "ROWS",
"range": "43:48",
"values": [
["www.colossalpoint.com","colossal point llc"]
]
}
]
}
],
"includeSpreadsheetInResponse": True,
"responseIncludeGridData": True
}
request = service.spreadsheets().batchUpdate(spreadsheetId=spreadsheet_id,
body=batch_update_spreadsheet_request_body)
response = request.execute()

python gspread updating multiple cells from reponse body

I am using this python script to take a response from Progresso API:
http://docs.progresso.apiary.io/#reference/behaviour/behaviour-events-collection/get-behaviour-events
from urllib2 import Request, urlopen
import smtplib import gspread
from oauth2client.service_account import ServiceAccountCredentialseaders = {
'Authorization': 'Bearer [CURRENT_TOKEN]'
}
request = Request('https://private-anon-ae5edf57e7-progresso.apiary-
mock.com/BMEvents/?Behaviour=new', headers=headers)
response_body = urlopen(request).read()
scope = ['https://spreadsheets.google.com/feeds']
credentials = ServiceAccountCredentials.from_json_keyfile_name('ProgressoAPI-
2f6ecaa6635c.json', scope)
gc = gspread.authorize(credentials)
wks = gc.open("Progresso Test").sheet1
wks.clear()
cell_list = wks.range('A1:H20')
for cell in cell_list:
cell.value = response_body
wks.update_cells(cell_list)
I know the cell.value = response body is wrong and I don't know how I can get it right - I am stuck.
it appears in every cell like this:
"{
""BehaviourEntryId"": 13798177,
""LearnerId"": 245277,
""LearnerCode"": ""2009-0080"",
""RegGroup"": ""U6-RWE"",
""Behaviour"": ""Negative"",
""IncidentDate"": ""2017-02-07"",
""Subject"": ""BE"",
""Location"": ""CLS"",
""Published"": ""Yes"",
""Creator"": ""DhDr"",
""Editor"": null,
""Assignee"": ""DiRo"",
""Status"": ""Completed"",
""Details"": [
{
""Category"": ""CL"",
""Type"": ""CLatt"",
""Severity"": ""S2"",
""point"": 0
},
{
""Category"": ""CL"",
""Type"": ""CLBEH"",
""Severity"": ""S2"",
""point"": 2
}
],
""Comments"": [
{
""BehaviourEntryCommentId"": 5648278,
""Confidential"": true,
""Comment"": ""Asked to go to the toilet and went to the one furthest away just to waste time.""
},
{
""BehaviourEntryCommentId"": 5648279,
""Confidential"": false,
""Comment"": ""Spat gum out on floor""
},
{
""BehaviourEntryCommentId"": 5648280,
""Confidential"": false,
""Comment"": ""Was rude to memeber of Staff""
}
],
""Actions"": [
""HTO"",
""ISO""
]
}"
How do I separate the text to how I want in the cell range and bulk update it?
If you mean something like two columns with one row being "BehaviourEntryId" and the other row being 13798177, you can try something like this:
import json
response = json.loads(response_body) #decode the json response string, returns a dict
response_pairs = list(response.items)
for i in range(1, len(response_body)+1):
current_pair = response_pairs[i-1]
current_key = current_pair[0]
current_value = current_pair[1]
wks.update_acell('A{}'.format(i), current_key)
wks.update_acell('B{}'.format(i), current_value)

Categories