I am writing a Python script using gspread to read and write data to a Google Sheet. One of the tasks that this script has to do is set the background color of any empty cell in a given column to black. I am attempting to read the sheet data just once into a dictionary called data using data = sheet.get_all_records(), then formatting the cell based on the dictionary's data, however I am running into a problem. worksheet.format() seems to only accept cells in 'A1' notation (that's all that it shows in the docs about formatting cells, and any attempts at other formats throws errors), but the cell object that I have by calling sheet.find(userDict[column]), where userDict[] is an element of data, returns its row and column as an integer. Is there a nice way to have a cell object return its position in 'A1' notation? If not, I suspect the easiest way would be to convert cell.col to the appropriate character using ASCII values, but I'm hoping there's a more elegant way.
Thanks in advance!
I believe your goal is as follows.
For the specific column, you want to set the background color of the cells which are empty.
You want to achieve this using gspread for python.
In this case, how about the following flow?
Retrieve all values from the sheet.
Retrieve the row numbers of the cells which are empty.
Set the background color of the cells using batchUpdate method.
When this flow is reflected in the script, it becomes as follows.
Sample script:
client = gspread.authorize(credentials) # Please use your authorization script.
spreadsheetId = "###" # Please set your Spreadsheet ID.
sheetName = "Sheet1" # Please set the sheet name.
checkColumns = [1, 3] # Please set the column number. In this sample, 1 and 3 means columns "A" and "C", respectively.
# 1. Retrieve all values from the sheet.
spreadsheet = client.open_by_key(spreadsheetId)
sheet = spreadsheet.worksheet(sheetName)
data = sheet.get_all_values()
# 2. Retrieve the row numbers of the cells which are the empty.
transposed = [list(e) for e in zip(*data)]
rowNumbers = [[i for i, v in enumerate(transposed[e - 1]) if not v] for e in checkColumns]
# 3. Set background color of the cells using batchUpdate method.
requests = []
for i, e in enumerate(checkColumns):
if rowNumbers[i] != []:
for f in rowNumbers[i]:
requests.append({
"updateCells": {
"range": {"sheetId": sheet.id, "startRowIndex": f, "endRowIndex": f + 1, "startColumnIndex": e - 1, "endColumnIndex": e},
"rows": [{"values": [{"userEnteredFormat": {"backgroundColor": {"red": 0, "green": 0, "blue": 0}}}]}],
"fields": "userEnteredFormat.backgroundColor"
}
})
res = spreadsheet.batch_update({"requests": requests})
When this script is run, the background color of the empty cells of the specific columns is set as the black color.
References:
batch_update(body)
UpdateCellsRequest
Related
I have data in a google sheet with the following structure:
I'd like to use pygsheets in order to delete the rows that match date == '2022-01-02', or any given date that I want to delete.
Is there an easy way to do so by using pyghseets?
I believe your goal is as follows.
You want to search a value from the column "A" of a sheet. And, you want to delete the searched rows.
For example, when a value of 2022-01-02 is found at column "A" of row 3 in a sheet, you want to delete the row.
You want to achieve this using pygsheets for python.
In this case, how about the following sample script?
Sample script:
import pygsheets
client = ### # Please use your client.
spreadsheet_id = "###" # Please set your Spreadsheet ID.
sheet_name = "Sheet1" # Please set your sheet name.
search = "2022-01-02" # Please set the search value.
searchCol = 1 # Please set the search column. 1 is column "A".
sh = client.open_by_key(spreadsheet_id)
wks = sh.worksheet_by_title(sheet_name)
values = wks.get_all_values(value_render="FORMATTED_VALUE")
deleteRows = [i for i, r in enumerate(values) if r[searchCol - 1] == search]
if deleteRows == []:
exit()
reqs = [
{
"deleteDimension": {
"range": {
"sheetId": wks.id,
"startIndex": e,
"endIndex": e + 1,
"dimension": "ROWS",
}
}
}
for e in deleteRows
]
reqs.reverse()
client.sheet.batch_update(spreadsheet_id, reqs)
When this script is run, the value of search is searched from the column "A" of "Sheet1", and the searched rows are deleted.
Reference:
Sheet API Wrapper
https://developers.google.com/sheets/api/reference/rest
I was able to Create sheet and also clear sheet and some other operations
But I am not able to get "number of row and the column" that are already filled, such that I can add some more data in the sheet.
def nextAvailableRow(worksheet):
rowsUsed = len(filter(None, worksheet.col_values(1)))
return rowsUsed+1
This function should take in a worksheet as a parameter and output the next available row that is not used. It takes in all the values of the first column and then filters out all the empty rows to find how many rows have been used.
If you only want to insert new data after the last row with values, you can do it using the spreadsheets.values.append endpoint (Notice you can play with the API at "try this API" and there is also a Python example)
spreadsheet_id = 'your-spreadSheet-id'
ranges = "A1:A" # It will get the indo until the last row with data
value_render_option = "DIMENSION_UNSPECIFIED"
value_input_option = "USER_ENTERED"
# Body for the request
value_range_body = {
"values": [
[
"A11",
"B11"
],
[
"A12",
"B12"
]
],
"majorDimension": "DIMENSION_UNSPECIFIED"
}
request = service.spreadsheets().values()\
.append(spreadsheetId=spreadsheet_id, range=ranges, valueInputOption=value_input_option, body=value_range_body)
request.execute()
The outer array in values will represent the rows and the inner ones the columns as you can see in the example body in my code.
Notice: In another answer, someone is recommending you to use a third library, instead of Google Sheet's client library for Python. I would recommend you to use the Official Google library because it has the guarantee to be supported by Google.
I am trying to add a new row to an existing sheet with date columns. Some of the cells in the new row hyperlink to other sheets, this works fine. But there are other cells that I want to link_in_to from other sheets. I keep getting an "attributes are not allowed for this operation" error.
there was a comment from an old smartsheet community post indicating that cell links don't work in the 1.1 API. But we are well past that, and the 2.0 documentation implies that it should be possible.
Has anyone else seen this or solved it?
row_a.cells.append({
'column_id': status_columns['Exp Start'],
'value': None,
'linkInFromCell': {
'columnID': project_columns['Start'],
'rowID': project_rows[1],
'sheetID': map_of_sheets[this_project]},
})
The value property must be set to an ExplicitNull (so that it is serialized as null in the JSON body), like this:
cell = smart.models.Cell()
cell.column_id = col_id
cell.link_in_from_cell = cell_link
cell.value = smart.models.ExplicitNull()
row = smart.models.Row()
row.id = added_row.id
row.cells.append(cell)
action = smart.Sheets.update_rows(sheet.id, [row])
Check out test_regression.py in the tests/integration folder, test case test_link_in_from_cell shows the technique.
I am using Python. I need to add sheet in spreadsheets using Google API v4. I can create a sheet using batchUpdate with spreadsheet id and addSheet request (it returns shetId and creates empty sheet). But how can I add data in it?
data = {'requests': [
{
'addSheet':{
'properties':{'title': 'New sheet'}
}
}
]}
res = service.spreadsheets().batchUpdate(spreadsheetId=s_id, body=data).execute()
SHEET_ID = res['replies'][0]['addSheet']['properties']['sheetId']
You can add this code to write data in Google Sheet. In the document - Reading & Writing Cell Values
Spreadsheets can have multiple sheets, with each sheet having any number of rows or columns. A cell is a location at the intersection of a particular row and column, and may contain a data value. The Google Sheets API provides the spreadsheets.values collection to enable the simple reading and writing of values.
Writing to a single range
To write data to a single range, use a spreadsheets.values.update request:
values = [
[
# Cell values ...
],
# Additional rows ...
]
body = {
'values': values
}
result = service.spreadsheets().values().update(
spreadsheetId=spreadsheet_id, range=range_name,
valueInputOption=value_input_option, body=body).execute()
The body of the update request must be a ValueRange object, though the only required field is values. If range is specified, it must match the range in the URL. In the ValueRange, you can optionally specify its majorDimension. By default, ROWS is used. If COLUMNS is specified, each inner array is written to a column instead of a row.
Writing multiple ranges
If you want to write multiple discontinuous ranges, you can use a spreadsheets.values.batchUpdate request:
values = [
[
# Cell values
],
# Additional rows
]
data = [
{
'range': range_name,
'values': values
},
# Additional ranges to update ...
]
body = {
'valueInputOption': value_input_option,
'data': data
}
result = service.spreadsheets().values().batchUpdate(
spreadsheetId=spreadsheet_id, body=body).execute()
The body of the batchUpdate request must be a BatchUpdateValuesRequest object, which contains a ValueInputOption and a list of ValueRange objects (one for each written range). Each ValueRange object specifies its own range, majorDimension, and the data to input.
Hope this helps.
Figured out that after creating sheet inside spreadsheet you can access range 'nameofsheet!A1' i.e.
service.spreadsheets().values().update(spreadsheetId=s_id, range='New sheet!A1', body=data_i, valueInputOption='RAW').execute()
This request will post the data into new created sheet with name 'New sheet'
You could also try a 'more intuitive' library i wrote for google sheets api v4 , pygsheets as i found the default client bit cumbersome.
import pygsheets
gc = pygsheets.authorize()
# Open spreadsheet
sh = gc.open('my new ssheet')
# create a worksheet
sh.add_worksheet("new sheet",rows=50,cols=60)
# update data with 2d vector
sh.update_cells('A1:B10', values)
# or skip let it infer range from size of values
sh.update_cells('A1', values)
I need to add multiple (few hundreds) rows into google spreadsheet. Currently I'm doing it in a loop:
for row in rows
_api_client.InsertRow(row, _spreadsheet_key, _worksheet_id)
which is extremely slow, because rows are added one by one.
Is there any way to speed this up?
Ok, I finally used batch request. The idea is to send multiple changes in a one API request.
Firstly, I created a list of dictionaries, which will be used like rows_map[R][C] to get value of cell at row R and column C.
rows_map = [
{
1: row['first_column']
2: row['second']
3: row['and_last']
}
for row i rows
]
Then I get all the cells from the worksheet
query = gdata.spreadsheet.service.CellQuery()
query.return_empty = 'true'
cells = _api_client.GetCellsFeed(self._key, wksht_id=self._raw_events_worksheet_id, query=query)
And create batch request to modify multiple cells at a time.
batch_request = gdata.spreadsheet.SpreadsheetsCellsFeed()
Then I can modify (or in my case rewrite all the values) the spreadsheet.
for cell_entry in cells.entry:
row = int(cell_entry.cell.row) - 2
col = int(cell_entry.cell.col)
if 0 <= row < len(events_map):
cell_entry.cell.inputValue = rows_map[row][col]
else:
cell_entry.cell.inputValue = ''
batch_request.AddUpdate(cell_entry)
And send all the changes in only one request:
_api_client.ExecuteBatch(batch_request, cells.GetBatchLink().href)
NOTES:
Batch request are possible only with Cell Queries. There is no such mechanism to be used with List Queries.
query.return_empty = 'true' is mandatory. Otherwise API will return only cells which are not empty.