Google App Script Timeout due to slowness - google-apps-script

This would be my first time posting, so I am sorry for the things I may or may not write.
I have a working Google Script which gets a file from an URL, parses the JSON then feed the data to a google Spreadsheet, one row at a time.
The problem that I have is that some of these files are large, 7000 rows large, which the script is unable to feed in the given time of 6 minutes. The speed of which it processes each row is quite slow, it is about 1 row every 1-3 seconds.
I believe that the problem is with the for clause, but I cannot seem to make it work.
I am not that experienced with this kind of scripting, and this was made from "inspirations"
Is there a way to speed up the for process in order to fill out the sheet faster, so it wont timeout?
SpreadsheetApp.getUi()
.createMenu('Update')
.addItem('Refresh Report', 'getData')
.addToUi();
}
function getData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
//var sheets = ss.getSheets();
var datasheet = ss.getSheetByName('Data');
var hasCreatedHeaders = false;
//Url
var url = 'URL HERE';
//Create header
var headers = {
'Content-Type': 'application/json',
'Authorization': 'xxx'
};
//Options
var options = {
'method': 'get',
'headers': headers
};
var response = UrlFetchApp.fetch(url, options);
var json = response.getContentText();
var data = JSON.parse(json);
var table = data.Table1;
datasheet.clear();
//foreach row
table.forEach(function(row) {
var cols = [];
var headers = [];
for (var prop in row.Columns) {
if (!hasCreatedHeaders)
headers.push(prop);
cols.push(row.Columns[prop]);
}
if (!hasCreatedHeaders) {
datasheet.appendRow(headers);
hasCreatedHeaders = true;
}
datasheet.appendRow(cols);
});
}

Try to change the loop table.forEach() this way:
//foreach row
var table_new = []; // <--- create a new empty array
table.forEach(function (row) {
var cols = [];
var headers = [];
for (var prop in row.Columns) {
if (!hasCreatedHeaders)
headers.push(prop);
cols.push(row.Columns[prop]);
}
if (!hasCreatedHeaders) {
// datasheet.appendRow(headers);
table_new.push(headers); // <--- add row to the array
hasCreatedHeaders = true;
}
// datasheet.appendRow(cols);
table_new.push(cols); // <--- add row to the array
});
// put the array on the sheet all at once
datasheet.getRange(1,1,table_new.length,table_new[0].length).setValues(table_new);
The problem is appendRow() method is quite time consuming. If you create the table as a 2D array you can put it on the sheet in one step with setValues() method. It's much faster.
References
Best practices

Related

How to POST / PUT webhook from Google Sheets when new rows are added

Disclaimer : I'm a newbie and doesn't know much. I understand webhooks a little bit, but doesn't know how to code.
Now that is out of the way, what I'm trying to do is, send data from a CRM to google sheets, do some calculations and send it back. I was able to figure out the first step of sending from CRM and doing the calculations.
Here is what I want to do..
I'd like to be able to send the updated data from googlesheet back to the CRM as a webhook when a new row is added to the google sheets.
So I have created a google sheet, I was able to add a trigger to run myFunction on Edit. My current myFunction code is at the end.
**Ideally it should send "id" and "updated_value" fetched from google sheets to the CRM via webhook (PUT method) as it will be updating a contact record.
Can anyone please share the code to add here or guide me how to write one? Thank you in advance**
This is my code so far and I'm currently finding resources to learn to add more lines to it
Update: One problem I'm facing now is that, I'm getting the 1000th row as the last row as "I've used array function in one column. So I'm not actually getting the non-empty last row.
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var range = sheet.getRange(lastRow, 1, 1, lastColumn);
var values = range.getValues();
var data = {
"id": values[0][1],
"f2130": values[0][3]
};
var options = {
'method': 'PUT',
'url': 'https://api.ontraport.com/1/Contacts',
'headers': {
'Content-Type': 'application/json',
'Api-Appid': 'xxxxxxxxxxxxxxx6',
'Api-Key': 'xxxxxxxxxxxxxxxH'
},
body: JSON.stringify({
"f2130": f2130,
"id": id
})
};
UrlFetchApp.fetch("https://api.ontraport.com/1/Contacts", options);
}
I finally able to figure it out by myself. Here is the code
Also you can read the steps here
https://damartech.com/how-to-send-and-receive-data-from-ontraport-to-google-sheet-and-back-without-zapier/
function opUpdater() {
// Replace `spreadsheetId` and `sheetName` with the ID and name of your Google Sheets document
const spreadsheetId = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
const sheetName = "xxxxxx";
// Get the sheet by ID and name
const sheet = SpreadsheetApp.openById(spreadsheetId).getSheetByName(sheetName);
// Get the number of rows in the sheet
const numRows = sheet.getLastRow();
// Initialize a variable to store the index of the last non-empty row
let lastNonEmptyRowIndex = 0;
// Loop through each row in the sheet
for (let i = 1; i <= numRows; i++) {
// Get the values in the current row
const rowValues = sheet.getRange(i, 1, 1, sheet.getLastColumn()).getValues()[0];
// Check if the row is empty
if (rowValues.every(cell => cell === "")) {
// If the row is not empty, store its index as the last non-empty row
lastNonEmptyRowIndex = i-1;
break;
}
}
// If a non-empty row was found, get its values
if (lastNonEmptyRowIndex > 0) {
const lastNonEmptyRowValues = sheet.getRange(lastNonEmptyRowIndex, 1, 1, sheet.getLastColumn()).getValues()[0];
// Define the variables that will be used in the request
// You can find the values from google sheet column number-1
var contact_id = parseInt(lastNonEmptyRowValues[1]);
var new_date = lastNonEmptyRowValues[3];
}
// Define the URL for the request
let url = "https://api.ontraport.com/1/Contacts";
// Define the options for the request
var options = {
"method": "put",
"headers": {
"Content-Type": "application/json",
"Accept": "application/json",
"Api-Appid": "xxxxxxxxxxxxxxxx",
"Api-Key": "xxxxxxxxxxxxxxxx"
},
"payload": JSON.stringify({
"id": contact_id,
"f2130": new_date
}),
"muteHttpExceptions": true
};
try {
const response = UrlFetchApp.fetch(url, options);
const responseBody = response.getContentText();
const data = JSON.parse(responseBody);
Logger.log(data.data.attrs.id);
Logger.log(data.data.attrs.f2130);
} catch (error) {
Logger.log(error.message);
Logger.log(error.response);
}
}

How to escape "/" character in Google Script

I'm trying to bring Wordpress data to Google Sheets.
The script below worked for almost all columns but I can't bring the emails and if I try to scape the "-" my script won't run.
function getPage(offset,per_page) {
// gets posts in chunks of per_page
var ui = SpreadsheetApp.getUi(); // used for error messages
var ss = SpreadsheetApp.getActiveSpreadsheet();
var options = {
'method': 'get',
"contentType" : "application/json",
'muteHttpExceptions': true
}
var apiHost = 'https://domain.com.br/wp-json'; // set to your own domain
url = apiHost + '/acf/v3/cadastros?per_page='+per_page+'&offset='+offset;
try {
var response = UrlFetchApp.fetch(url, options);
var data = JSON.parse(response)
// loop through the map and output to sheet
for (i = 0; i < data.length; i++) {
row=offset+i+2; // set the row to make sure it is below header and takes into account the paging
ss.getRange('A'+row).setValue(data[i].id);
ss.getRange('B'+row).setValue(data[i].acf.contato);
ss.getRange('C'+row).setValue(data[i].acf.e-mail); //this line is breaking everything I've tried to use "\", put quotes but nothing seems to work
ss.getRange('D'+row).setValue(data[i].acf.telefone);
ss.getRange('E'+row).setValue(data[i].acf.cnpj);
ss.getRange('F'+row).setValue(data[i].acf.endereco);
ss.getRange('G'+row).setValue(data[i].acf.principais_produtos);
ss.getRange('H'+row).setValue(data[i].acf.volume_disponivel);
ss.getRange('I'+row).setValue(data[i].acf.estoque_disponivel);
ss.getRange('J'+row).setValue(data[i].acf.aceite);
}
return data.length;
} catch(error) {
var result = ui.alert( error.toString());
}
return 0;
}
How do I fix this?
Quick and easy will be to replace data[i].acf.e-mail with data[i]['acf']['e-mail']

How can I write in Google Sheets my Firebase data?

I have a database in Firebase, and I want to get the data from there and put them in a Google SpreadSheet.
function getData() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Database");
var data = getFirebaseData('contacts');
var [rows, columns] = [sheet.getLastRow(), sheet.getLastColumn()];
var range = sheet.getRange(1,1,1,1);
Logger.log(data)
range.setValue(data)
}
function getFirebaseData(data){
var firebaseUrl = "https://XXXXX.firebaseio.com/";
var secret = 'XXXXXXXX';
var base = FirebaseApp.getDatabaseByUrl(firebaseUrl, secret);
var result = base.getData('contacts');
for(var i in data) {
Logger.log(data[i].eMail + ' ' + data[i].title);
return result;
}
}
and here the image:
No data is shown, and I cannot understand why
Your problem should be solved by completing several steps:
In your getFirebaseData() function, move the return statement outside of the loop;
Instead of looping over data, loop over result (currently, you iterate over each property of the "contacts" String);
Optionally, add checks for getData() returning null or invalid firebaseUrl (in the last case, getData() will cause an error, use try...catch to account for that);
Change base.getData('contacts') to base.getData(data) (isn't it
the reason you pass data to the function?);

Apps Script Error - Cannot find method getRange(number,number,(class),number)

I've written a custom Google Apps Script that will pull some data (2 columns wide, 50-100 rows long but this varies)in an array 2 from an API, parse it into JSON and then paste into a google sheet.
I can run the script from the editor and it works ok. But when I try to run it from a custom menu or when I run the debugger I get the following error:
'Cannot find method getRange(number,number,(class),number) (line 43)'
Line 43 is the last line of the code.
sheet.getRange(3,1,dataSet.length,2).setValues(rows);
It seems that the issue is that getRange is not able to use the variable of length of the dataset (number of rows) to set the number of rows to use in the range in which the data is to be pasted.
I cannot work out how to fix this - can anyone else see what I am doing wrong? Thanks for taking a look.
//custom menu
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('XXXX Data')
.addItem('Credit Limits','CREDITLIMITS')
.addToUi();
}
function CREDITLIMITS() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //get active spreadsheet
var sheet = ss.getActiveSheet();
// var sheet = ss.getSheetByName('data'); //get sheet by name from active spreadsheet
// URL and params for the API
var USERNAME = 'XXXXXXX';
var PASSWORD = 'XXXXXXXXXXXXX';
var url = 'https://api.XXXX.com/api/v1/XXX/?where=type=%27XXXXXXX%27'; // var url="http://example.com/feeds?type=json"; // Paste your JSON URL here
var authHeader = 'Basic ' + Utilities.base64Encode(USERNAME + ':' + PASSWORD);
var params = {'method': 'GET','muteHttpExceptions': true,'headers': {'Authorization': authHeader,} };
//call the XXXX API
var response = UrlFetchApp.fetch(url, params); // get api endpoint
var json = response.getContentText(); // get the response content as text
var dataAll = JSON.parse(json); //parse text into json
var dataSet = dataAll;
//create empty array to hold data points
var rows=[],
data;
//loop over the retrun events
for (i=0; i < dataSet.length; i++) {
data = dataSet[i];
//push a row of data as 2d array
rows.push([data.company, data.creditLimit]);
}
// clear any previous content
sheet.getRange(1,1,500,10).clearContent();
// write data to sheet
sheet.getRange(3,1,dataSet.length,2).setValues(rows);
}

The coordinates or dimensions of the range are invalid in JSon

I am working on google scripts which can call REST API and get the data in google spreadsheets. But different Json objects work and which I am using now does not work...
At first it was giving me an error as The coordinates or dimensions of the range are invalid. So looked to on at
"The coordinates or dimensions of the range are invalid" - Google Apps Script and JSON Data
And now the result is undefined..
Really appreciate if someone can help
function pullJSON(k) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = ss.getActiveSheet();
var url = "********"; // Paste your JSON URL here
var headers = {
"Content-Type": "application/json",
};
var options = {
"method": "GET",
"headers": headers
};
var response = UrlFetchApp.fetch(url, options); // get feed
var dataAll = JSON.parse(response.getContentText()); //
var dataSet = dataAll;
Logger.log(dataAll.length);
Logger.log(k);
Logger.log(url);
var rows = [],
data;
for (var k = 0; k < Object.keys(dataSet).length; k++) {
data = [Object.keys(dataSet)[k]];
Logger.log(data);
rows.push([data.total1]);} //your JSON entities here
dataRange = sheet.getRange( 1, 1,rows.length,1); // 3 Denotes total number of entites
dataRange.setValues(rows);
}
What you are doing at the moment is calling Object.keys() on dataSet.
This returns an array of strings, e.g. ["status", "data"].
Then you retrieve each of these keys separately and assign them as a one element array to data, so data looks like
["status"], and ["data"].
Since ["total1]" is an array of a string it doesn't have the "total1" attribute, just an element with the value equal to the name.
To get the actual total 1 value from each object within data you can
dataSet.data.forEach(function(x) {
rows.push([x.total1]);
});
instead of the for loop.