I'm trying to pull data out of an API from a third party and inserting into Google Sheets. However this third party only allows 3 requests per minute, so I'm trying to use a Utilities.sleep feature inside the function I'm building for this request.
My sheet looks like this:
It has the two inputs necessary for the function I'm using (this below):
function GET_DETAILS_RECEITA(CNPJ,sleep_seconds) {
Utilities.sleep(sleep_seconds*1000);
var fields = 'nome,fantasia,email,telefone';
var baseUrl = 'https://www.receitaws.com.br/v1/cnpj/';
var queryUrl = baseUrl + CNPJ;
if (CNPJ == '') {
return 'Give me CNPJ...';
}
var response = UrlFetchApp.fetch(queryUrl);
var json = response.getContentText();
var place = JSON.parse(json);
return [[ place.nome,
place.fantasia,
place.telefone,
place.email,
]];
}
Technically it should work but for some reason I'm getting a return only in the first one.
The error I'm getting is very generic "Erro: Erro interno ao executar a função personalizada." (something like "Error: Internal error in the execution of personalized function").
Any ideas?
From https://developers.google.com/apps-script/guides/sheets/functions
A custom function call must return within 30 seconds. If it does not, the cell will display an error: Internal error executing the custom function.
Considering the above, it's not a good idea to use sleep on a custom function that will be used as intended by the OP. Instead use a custom menu or the Script Editor to execute a script.
In order to minimize changes to your function, you could use a function that read/write the values to the spreadsheet and pass the required arguments to GET_DETAILS_RECEITA
I would consider using something like this in a dialog. You can pass an extra parameter in the set interval as long as your using Chrome.
<script>
var CNPJ='what ever';
window.onload=function(){setInterval(getDetails,25000,CNPJ);}
function getDetails(CNPJ){
google.script.run.GET_DETAILS_RECEITA(CNPJ)
}
</script>
And if you want a callback then use with withSuccessHandler();
Related
I've setup a google app script that would be triggered from an external system. This script would fetch the details from the third party system and add them to google sheet row.
function doPost(request) {
try{
var jsonString = request.postData.getDataAsString(); //get the request from KF as JSON String
setLog("\n postData*********************"+jsonString+"************************************* \n");
setLog("before the row append");
ss.appendRow([jsonString["Name"], jsonString["Age"], jsonString["Contact"]]);
setLog("After the row append");
var returnJson = '{"status": "success"}';
//used to send the return value to the calling function
setLog("/n returnJson****************************"+returnJson+"************************************* /n")
return ContentService.createTextOutput(returnJson).setMimeType(ContentService.MimeType.JSON);
}
There's absolutely no errors or warnings, but somehow it keeps adding the blank rows into the sheet.
Note: setLog() is a function where I print the values into google doc for debugging.
Maybe the reason your script is not working has to do with the value of jsonString.
I could not find any reference to request.postData.getDataAsString() inside GAS Documentation, so maybe you are trying to call a method on an object which does not support it, which would not raise an Error, but would return undefined.
One quick way to debug this would be to LOG the value (using your custom function or Logger.log(jsonString)) BEFORE you call .appendRow(). Then, you can verify if your variable has the value you expect it to have.
On the other hand, my suggestion is to use this method:
var jsonString = JSON.parse(request.postData.contents) //Gets the content of your request, then parses it
This method is present in the Documentation, and has been consistently working on all of my projects.
I think you should sort the coulmns with google app script. Write this code after ss.appendRow. The column will be sorted and all blank rows gets down.
// Sorts the sheet by the first column, ascending
ss.sort(1)
or if errors try this one also
var fl = SpreadsheetApp.getActiveSpreadsheet();
var sheet = fl.getSheets()[0];
fl.sort(1)
I've written a custom function [=ROUTEPLAN(origin,destination,mode,departuretime)] in the Google Sheets script editor. The function assigns a unique ID to the request, calls the Google Maps Directions API, passes as params the arguments as listed in the function, parses the JSON and extracts the duration, end latitude and end longitude for each step of the journey, and then appends a row for each step, with the request ID for the whole journey, the sequential step number, the duration, end latitude and end longitude:
function ROUTEPLAN() {
//Call the google route planner api
//(variables for api declared here but removed for brevity)
var routeResponse = UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/directions/json?origin=" + origin
+ "&destination=" + destination
+ "&mode=" + mode +
"®ion=uk&departure-time=" + departuretime
+ "&key=MYAPIKEY")
//Assign a unique ID to this request
var requestID = Date.now() + Math.random();
//Parse JSON from routeResponse
var json = routeResponse.getContentText();
var data = JSON.parse(json);
//Insert the RequestID, step number, duration, end Latitude and end Longitude for each step of the journey into the RouteDetails sheet
var steps = data["routes"][0]["legs"][0]["steps"];
for (i = 0; i < steps.length; i++) {
var stepID = i + 1;
var duration = steps[i]["duration"]["value"];
var endLat = steps[i]["end_location"]["lat"];
var endLng = steps[i]["end_location"]["lng"];
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("RouteDetails")
sheet.appendRow([requestID,stepID,duration,endLat,endLng]);
}
}
Or at least that's what I want it to do. It worked fine until I tinkered with it, and now I'm getting an ERROR when I call the function in the spreadsheet, telling me I don't have permission to call appendRow. I know why this is happening (although I don't understand why it wasn't happening before), but I cannot work out what I'm supposed to do about it.
If appendRow exists, there must be some circumstance in which it can be used to write data the sheet, but I can't figure out the circumstances in which permission to write to the sheet would be granted.
The purpose of the sheet is to provide data to a chatbot (the chatbot app has read & write permissions to the sheet). I'm not intending to provide access beyond that (i.e. i'm not intending to publish this for wider use). I've tried going down the installable trigger route, but despite following all the instructions that made absolutely no difference to the outcome. From the limited understanding I gained from reading about API Executables, that doesn't seem to be an option either.
Can anyone tell me how to solve this? Thank you :-)
A custom function can not modify the structure of the spreadsheet, so calling appendRow() is not allowed as stated in the documentation:
A custom function cannot affect cells other than those it returns a value to. In other words, a custom function cannot edit arbitrary cells, only the cells it is called from and their adjacent cells. To edit arbitrary cells, use a custom menu to run a function instead
If you want to return multiple rows from your function, it needs to return a two dimensional array. Note however that custom functions have the same limitation as native functions of not being able to overwrite content i.e. if you try to return two rows but the row below is already filled the function will error out.
I wanted to view my log from the sheet front end, I tried following workaround but it seems getLog() function is not working.
// log adding function
function addLogs()
{
Logger.log("Adding my test log");
}
// log display function
function viewLog()
{
var logs = Logger.getLog(); // issue is here
Browser.msgBox(logs); // gives empty
var htmlOutput = HtmlService
.createHtmlOutput(logs)
.setSandboxMode(HtmlService.SandboxMode.IFRAME).setHeight(500);
SpreadsheetApp.getUi().showModalDialog(htmlOutput, 'Sheet Log'); // gives empty
}
Try combining your functions together as a nested function. When you run the code, you have added to the log in one function, and called an empty log in the other.
You will either need to add to the log and call it in the same function or nest the functions together so that when you run the topmost function, it runs all processes.
Did you call the function addLogs() at all? It's not going to run on it's own and hence the log will be empty at the beginning. Correct behaviour.
I'm trying to write a script that allows me to execute commands as soon as Google finishes making calculations (i.e. I'm trying to add to a script to Google docs that imitates some VBA "Calculate" functionalities).
The script is conceived to work by converting a range into a string and looking for the substring "Loading..." (or "#VALUE!" or "#N/A") in that string. The "while" loop is supposed to sleep until the unwanted substrings are no longer found in the string.
I'm using the following spreadsheet as a sandbox, and the code seems to work okay in the sandbox just searching for "Loading...":
https://docs.google.com/spreadsheet/ccc?key=0AkK50_KKCI_pdHJvQXdnTmpiOWM4Rk5PV2k5OUNudVE#gid=0
In other contexts, however, I have cells whose values may return as "#VALUE!" or "#N/A" for reasons other than the fact that Google is still loading/thinking/calculating. What's the way around this?
function onEdit() {
Refresh();
};
function Refresh () {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var sheet2 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
// set the range wherever you want to make sure loading is done
var range = sheet.getRange('A:A')
var values = range.getValues();
var string = values.toString();
var loading = "Loading";
do
{
var randomWait = Math.floor(Math.random()*100+50);
Utilities.sleep(randomWait);
}
while (string.search(loading) ==! null);
range.copyTo(sheet2.getRange('A1'), {contentsOnly:true});
customMsgBox();
};
function customMsgBox() {
Browser.msgBox("Data refreshed.");
};
rather than using a while loop to "sleep" you should add an event handler to your document which captures the update/refresh event and then runs whatever math/processing you need.
Here's a good place to start reading about events: https://developers.google.com/apps-script/understanding_events
but if you search the api documents for eventhandler you can get some example code fast...
Q: why is e.parameter.wfId undefined (in the log) after running the script below (as a web-app)
I call script with this URL
https://script.google.com/a/macros/gappspro.com/exec?service=my-webapp-key
without a parameter (&wfId=somecharacters)
function doGet(e) {
var app = UiApp.createApplication().setTitle('Workflow Builder');
var mainGrid = app.createGrid(2,1).setId('FILE_doGet_mainGrid');
app.add(mainGrid);
var wfId = '1234567890' // FILE.doGet.randomString();
mainGrid.setWidget(1,0, app.createTextBox().setValue(wfId).setId('wfId').setName('wfId'));
var handler = app.createServerHandler('func');
handler.addCallbackElement(mainGrid);
Logger.log(e.parameter.wfId);
return app;
}
function func(e) {
return x;
}
I am trying to implement the workflow script from chapter 8 of james ferreira’s book Enterprise Application Essentials and in the add Docs section i ran into the problem that e.parameter.wfId in line “var wfRowArray = FILE.ssOps.getWfRowFromSS(e.parameter.wfId), “ is undefined when running the script. (on page 134 in the book, not the PDF).
In the example above i brought the code back to the essence of what is causing the error,...for me.
e.parameter.wfId is only available in your func(e) function, not in the doGet. the variable e represents the elements of the callBackElement catch by the handler function.
If I have understood your question correctly, this is behaving as expected.
You say "h-ttps://script.google.com/a/macros/gappspro.com/exec?service=my-webapp-key without a parameter (&wfId=somecharacters)"
So, I believe you are not passing any URL parameters to the script URL and therefore you get them as undefined.
If you call your script with the URL parameters, say
h-ttps://script.google.com/a/macros/gappspro.com/exec?service=my-webapp&wfId=somecharacters
then you can expect to see e.parameter.wfld