I have a bunch of data that I routinely use a Python script to back up into a Google Sheets sheet. It's currently at 5385 rows filled with 6041 total. I know that if I try to upload more than 6041 rows the update will fail, but I know that I can fix this by opening up the sheet, scrolling all the way to the bottom, and then clicking "All 1000 more rows at bottom" a few times.
Is there a way for googleapiclient to automatically make sure that there's room in the sheet?
Edit:
cells = 'Backup!A{}:{}{}'.format(start_ind, self._excel_column_index(len(headers)), start_ind + len(to_cache) + 1)
values = self._excel_serialize_arb_array(to_cache, headers)
data = {'values':values}
self.sheet.values().update(spreadsheetId=self.spreadsheet_ids['FOO'],
range=cells, valueInputOption='USER_ENTERED',
body=data).execute()
You would need to update the properties of the Sheet itself or create a sheet with the properties of more than 1000 rows.
Note: I build the code over apps script before your edit with your code.
/**
* Add a new sheet with some properties.
* #param {string} yourspreadsheetId The spreadsheet ID.
*/
// This funcion would add a sheet with 10,000 rows that would be empty and that they can be filled
function addSheet() {
const spreadsheetId = "yourspreeadsheetID";
var requests = [{
'addSheet': {
'properties': {
'title': 'Deposits',
'gridProperties': {
'rowCount': 100000,
'columnCount': 2
},
'tabColor': {
'red': 1.0,
'green': 0.3,
'blue': 0.4
}
}
}
}];
var response =
Sheets.Spreadsheets.batchUpdate({'requests': requests}, spreadsheetId);
Logger.log('Created sheet with ID: ' +
response.replies[0].addSheet.properties.sheetId);
}
// This function is to write all 10,000 rows bypassing the 1000 limit that you might be having
function myFunction() {
const spreadsheetId = "yourspreadsheetID";
// Spreadsheet ID.
const max = 10000;
const data = [];
for (let i = 0; i < max; i++) {
data.push({range: `Deposits!A${i + 1}`, values: [[`A${i + 1}`]]});
}
Sheets.Spreadsheets.Values.batchUpdate({data: data, valueInputOption: "USER_ENTERED"}, spreadsheetId);
}
I run the first function to create the Sheet with the properties using the "batchUpdate" and was able to add 10,000 strings to it.
I would assume that on Python you need to increase the number of rows in the sheet using an UpdateSheetPropertiesRequest or InsertDimensionRequest check it here: Sheet API update properties!
Here's my solution coded up.
def get_and_update_sheet_properties(self, min_rows):
''' Ensure we have enough rows for our arbs cache. '''
sized_sheets = self.sheet.get(spreadsheetId=self.spreadsheet_ids['FOO'],
fields="sheets(properties(title,gridProperties(columnCount,rowCount)))").execute()
arb_sheet = [sized_sheet for sized_sheet in sized_sheets['sheets'] if sized_sheet['properties']['title'] == 'BAR'][0]
cur_rows = arb_sheet['properties']['gridProperties']['rowCount']
padded_rows = min_rows - (min_rows % 100) + 200
if padded_rows > cur_rows:
requests = {
"updateSheetProperties": {
"properties": {
"sheetId": 1922941665,
'gridProperties': {
'rowCount': padded_rows,
'columnCount': 26
},
},
"fields": "gridProperties",
}
}
body = {
'requests': requests
}
self.sheet.batchUpdate(spreadsheetId=self.spreadsheet_ids['FOO'], body=body).execute()
return True
else:
return False
Related
I am trying to write a 0 into a predefined range of cells in google sheets. Using gspread I am able to blank clear all the cells I want but I am unable to figure out how to batch update all the cells to one value. Iterating through the cells and updating them one by one is too slow. I'm sure its just a simple formatting error but I can't find documentation on the proper way
sheet.batch_clear(['A2:A50']) #works fine
sheet.batch_update(['B2:B50'], 0) #does not work
In your situation, I thought that when the batch_update method of Class Spreadsheet is used, your goal can be achieved by one API call. When this is reflected in a sample script using gspread, 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 your sheet name.
spreadsheet = client.open_by_key(spreadsheetId)
sheet_id = spreadsheet.worksheet(sheetName).id
requests = {
"requests": [
{
"repeatCell": {
"cell": {
"userEnteredValue": {
"numberValue": 0
}
},
"range": {
"sheetId": sheet_id,
"startRowIndex": 1,
"endRowIndex": 50,
"startColumnIndex": 1,
"endColumnIndex": 2
},
"fields": "userEnteredValue"
}
}
]
}
spreadsheet.batch_update(requests)
When this script is run, the value of 0 is put to the cells B2:B50.
In this script, the A1Notation of B2:B50 is used as the GridRange.
References:
batch_update(body)
RepeatCellRequest
GridRange
I've been trying to configure the cell horizontal alignment format of a google spreadsheet in python. I've read the original google developers information and have also checked other examples from people facing the same issue, but none of them applies to my example. The following code is the one I use to configure my spreadsheet request body. What I want to do is to create a new spreadsheet in which the horizontal alignment of all the cells is centered. As a result, when a user types anything in any cell, it will be centered automatically. Any tips?
spreadsheet_body = {
'properties': {
# spreadsheet name
'title': sheet_name,
"defaultFormat":{
"horizontalAlignment":'CENTER'
}
},
'sheets': [{
'properties': {
# worksheet name
'title': 'Φύλλο1',
'gridProperties': {
# row\column number
'rowCount': 100,
'columnCount': 20
},
},
'data': [{'rowData': [{'values': header_row}]}] # Added
}
]
}
request = service.spreadsheets().create(body=spreadsheet_body)
response = request.execute()
I believe your goal is as follows.
When a new Spreadsheet is created using the method of spreadsheets.create of Sheets API, you want to set the horizontal alignment of the cells.
You want to achieve this using googleapis with python.
Issue and workaround:
Unfortunately, "defaultFormat":{"horizontalAlignment":'CENTER'} of Spreadsheet property cannot be used. This is the current specification. This has already been mentioned ziganotschka's answer.
When I saw your script, the created new Spreadsheet has one sheet. In this case, when this sheet is created, the horizontal alignment of all cells in the sheet can be set as CENTER. But in this case, when a new sheet is created, the default format is used. Please be careful about this. So this answer is a workaround.
When this workaround is reflected in your script, it becomes as follows.
Modified script:
rowCount = 100
columnCount = 20
rowData = []
for r in range(1, rowCount):
temp = []
for c in range(0, columnCount):
temp.append({'userEnteredFormat': {'horizontalAlignment': 'CENTER'}})
rowData.append({'values': temp})
hlen = len(header_row)
if hlen < columnCount:
for c in range(0, columnCount - hlen):
header_row.append({'userEnteredFormat': {'horizontalAlignment': 'CENTER'}})
rowData.insert(0, {'values': header_row})
spreadsheet_body = {
'properties': {
# spreadsheet name
'title': sheet_name
},
'sheets': [{
'properties': {
# worksheet name
'title': 'Φύλλο1',
'gridProperties': {
# row\column number
'rowCount': rowCount,
'columnCount': columnCount
},
},
'data': [{'rowData': rowData}]
}]}
request = service.spreadsheets().create(body=spreadsheet_body)
response = request.execute()
Reference:
Method: spreadsheets.create
Under the resource SpreadsheetProperties it is specified:
defaultFormat
This field is read-only.
So unfortunately it is not possible to create with the Sheets API a spreadsheet where all cells are centered automatically - just like it is not possible to do it via the UI.
I do have a scraped data which i overwriting google sheet daily with it.
The point here that I'm unable to find an option where i can set number of rows and cols for the existing google sheet.
I noticed that can be done only for new created sheet according to documentation but i don't know how to do it for existing sheet!
def api(key):
myfilt = [list of lists]
columns = [name of columns]
gc = gspread.service_account(filename='Auth.json')
sh = gc.open_by_key(key)
worksheet = sh.sheet1
worksheet.clear()
head = worksheet.insert_row(columns, 1)
res = worksheet.insert_rows(myfilt, 2)
api("MyAPIHere")
My target here is to predefined number of rows according to len(myfilt) and number of cols according to len(cols)
I believe your goal as follows.
You want to change the max row and column number of the existing sheet in the Google Spreadsheet.
You want to achieve this using gspread with python.
You have already been able to get and put values for Google Spreadsheet using Sheets API.
Points for achieving your goal:
In this case, it is required to use the method of "spreadsheets.batchUpdate" in Sheets API. And I would like to propose the following flow.
Insert one row.
Insert one column.
Delete rows from 2 to end.
Delete columns from 2 to end.
Insert rows. In this case, you can set the number of rows you want to insert.
Insert columns. In this case, you can set the number of columns you want to insert.
1 and 2 are used for avoiding the error. Because when the DeleteDimensionRequest is run for the sheet which has only one row or one column, an error occurs.
When above flow is reflected to the script using gspread, it becomes as follows.
Sample script:
Please set the Spreadsheet ID and sheet name.
spreadsheetId = "###" # Please set the Spreadsheet ID.
sheetName = "###" # Please set the sheet name.
client = gspread.authorize(credentials)
spreadsheet = client.open_by_key(spreadsheetId)
# worksheet = spreadsheet.worksheet(sheetName)
sheetId = spreadsheet.worksheet(sheetName)._properties['sheetId']
rows = len(myfilt)
columns = len(cols)
req = {
"requests": [
{
"insertDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 0,
"endIndex": 1,
"dimension": "ROWS"
}
}
},
{
"insertDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 0,
"endIndex": 1,
"dimension": "COLUMNS"
}
}
},
{
"deleteDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 1,
"dimension": "ROWS"
}
}
},
{
"deleteDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 1,
"dimension": "COLUMNS"
}
}
},
{
"insertDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 0,
"endIndex": rows - 1,
"dimension": "ROWS"
}
}
},
{
"insertDimension": {
"range": {
"sheetId": sheetId,
"startIndex": 0,
"endIndex": columns - 1,
"dimension": "COLUMNS"
}
}
}
]
}
res = spreadsheet.batch_update(req)
print(res)
References:
Method: spreadsheets.batchUpdate
DeleteDimensionRequest
InsertDimensionRequest
batch_update(body)
I used the following to solve my issue as well:
worksheet.clear() # to clear the sheet firstly.
head = worksheet.insert_row(header, 1) # inserting the header at first row
res = worksheet.insert_rows(mydata, 2) # inserting my data.
worksheet.resize(rows=len(mydata) + 1, cols=len(header)) # resize according to length of cols and rows.
What I'm trying to do is getting each column in Source worksheet and pasting them to Target_sheet in another worksheet, each pasting action should start from the third row though (Ex: A3:A, B3:B ...)
However I get error such as:
ata.values[1731]","description": "Invalid value at 'data.values[1731]' (type.googleapis.com/google.protobuf.ListValue), \"x23232x2x2x442x42x42x42\""
},
{
"field": "data.values[1732]",
"description": "Invalid value at 'data.values[1732]' (type.googleapis.com/google.protobuf.ListValue), \"x242x42x42x42x42x442x427\""
},
{
"field": "data.values[1733]",
"description": "Invalid value at 'data.values[1733]' (type.googleapis.com/google.protobuf.ListValue), \"x42x424242x42454555x56666\""
}
.
.
.
My code:
sh = client.open('Target')
sh.values_clear("Target_sheet!A3:J10000")
source = client.open('Source')
source_col_numbers = source.sheet1.col_count
i = 1
# creating a holder for the values in Source.sheet1
columns = {}
#getting the values in each column at the Source.sheet1
while i <= source_col_numbers:
columns[i] = list(filter(None , source.sheet1.col_values(i)))
i += 1
# will use this variable to iterate between columns in the Target.Target_sheet
charn=ord("A")
#updating the columns in the Target with values from Source
b=1
while b <= source_col_numbers:
sh.values_update(
"Target_sheet!"+chr(charn)+"3:"+chr(charn)
,
params={
'valueInputOption': 'USER_ENTERED'
} ,
body={
'values': columns[b]
}
)
charn+=1
b+=1
#carlesgg97 tried with get_value but still getting error I mentioned under your comment:
target_worksheet.values_clear("Target!A3:J10000")
source = client.open('Source')
source_col_numbers = source.sheet1.col_count
source_values=source.values_get('Sheet1!A:J')
last_column=chr(source_col_numbers)
target_worksheet.values_update(
"Target!A3:"+last_column ,
params={ 'valueInputOption': 'USER_ENTERED' },
body={ 'values': source_values }
)
Using col_values you obtain the values of the column, in a list. However, when using the values_update method, the body requires the "values" property to be a list of lists (see: col_values).
Further to that, I believe the task you are attempting to accomplish can be done in a much more simpler way, using values_get. An example that would move the range A1:J9997 to A4:J10000 (moved 3 rows down) would look as follows:
sh = client.open('Target')
sh.values_clear("Target_sheet!A3:J10000")
response = sh.values_get("A1:J9997")
spreadsheet.values_update(
'Target_sheet!A3:J10000',
params = {
'valueInputOption': 'USER_ENTERED'
},
body = {
'values': response['values']
}
)
A bit of a general question - I am looking for ways to refresh a graph on a Django page based on user choices. The page has a graph, a few drop boxes where you can select parameters and a refresh button. Currently, I can capture the selections via ajax to my Django view and generate new data from database for the graph. I now need to feed that newly-generated data back into the graph and refresh it without a page refresh. Could anyone recommend the best methods of doing this?
Use JQuery to refresh graph without refreshing page.
I am using chart.js to create graph. first create a graph and on change event get updated data using Ajax URL call and assign values to chart data sets.
/** Graph Start Here */
window.chart = null;
$(document).on('change', '.graph-year-earning', function () {
var year = $(this).val();
$.get($('.graph-ajaxload-context').data('href'), { 'year': year, 'number': Math.floor(Math.random() * (1000000 - 10 + 1) + 10) }, function (response) {
window.chart.data.labels = response.labels;
window.chart.data.datasets[0].soldProductLabel = response.product_sold_label;
window.chart.data.datasets[0].totalCommissionLabel = response.monthly_commission_label;
window.chart.data.datasets[0].dataLabel = response.your_share_label;
if (response.total_commission == 0) {
window.chart.options.scales.yAxes[0].ticks.suggestedMin = 0;
window.chart.options.scales.yAxes[0].ticks.suggestedMax = 140000;
} else {
window.chart.options.scales.yAxes[0].ticks.suggestedMin = '';
window.chart.options.scales.yAxes[0].ticks.suggestedMax = '';
}
$.each(response.data, function (index, value) {
window.chart.data.datasets[0].soldProduct[index] = value[2];
window.chart.data.datasets[0].data[index] = Math.round(value[0]);
});
window.chart.update();
$(".txt-total-commission-by-year").html(response.total_commission)
$('.graph-ajaxload-context .inline-loader').hide();
});
});
if ($('.graph-ajaxload-context').length > 0) {
showLoader()
$('.graph-year-earning').trigger('change');
var ctx = $('#userEarningGraph');
window.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [{
soldProductLabel: '',
soldProduct: [],
dataLabel: '',
data: [],
backgroundColor: '#ADAEB1',
hoverBackgroundColor: '#48C6B9'
}]
},
options: {
legend: {
display: false
},
scales: {
yAxes: [{
ticks: {
beginAtZero: true,
maxTicksLimit: 8,
userCallback: function (value, index, values) {
value = value.toString();
value = value.split(/(?=(?:...)*$)/);
value = value.join(',');
var currency_code = ' ₩'
if ($('.graph-ajaxload-context').data('currency-code') && $('.graph-ajaxload-context').data('currency-code') != 'None') {
currency_code = $('.graph-ajaxload-context').data('currency-code')
}
return value + ' ' + currency_code;
}
},
}]
},
tooltips: {
mode: 'label',
callbacks: {
label: function (tooltipItem, data) {
var soldProduct = data.datasets[tooltipItem.datasetIndex].soldProduct[tooltipItem.index];
var soldProductLabel = data.datasets[tooltipItem.datasetIndex].soldProductLabel;
var dataPro = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
var dataLabel = data.datasets[tooltipItem.datasetIndex].dataLabel;
return [soldProductLabel + ':' + soldProduct, dataLabel + ':' + dataPro + ' ₩',];
}
}
}
}
});
}
$(document).on('click', '.showgraph', function (e) {
$('.graph-year-earning').trigger('change');
});
/** Graph End Here */