Reading JSON in Google Sheets with Google Script - javascript

I am currently pulling data from an external API to Google Sheets and wanted to get a certain part of the data to show from the JSON. The only part that should be added to the cell is $440.00 but currently it shows {"data":[{"cost":"$440.00"}],"success":true,"totalNumRows":1}. Is there a way to just pull that?
// Add menu to run program
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Get Crobo Cost')
.addItem('Display daily cost', 'callCrobo')
.addToUi();
}
// Function to call Crobo
function callCrobo() {
// Call Crobo daily spend
var apiKey = '****omitted****'
var response = UrlFetchApp.fetch("http://cis.crobo.com/stats/stats.json?api_key=" + apiKey + "&start_date=2018-03-12&end_date=2018-03-12&field[]=Stat.revenue")
Logger.log(response.getContentText());
// Parse to JSON reply
var json = response.getContentText();
var obj = JSON.parse(json);
Logger.log(obj.data);
//Gets hold of ActiveSheet in current workbook
var sheet = SpreadsheetApp.getActiveSheet();
// Set value in cell A1
sheet.getRange(sheet.getLastRow() + 1,1).setValue([response]);
}
The logger's current output is:
[18-03-13 17:22:02:377 EDT] {"data":
[{"cost":"$440.00"}],"success":true,"totalNumRows":1}
[18-03-13 17:22:02:378 EDT] undefined
Thank you

Related

How do I get Snipe-IT API to retrieve data using a Google App Script and populate it on a Google Sheet?

I am attempting to create a visualization of the data we have on Snipe-IT Asset Management on Google Data Studio. To do so, I am creating a Google Sheets spreadsheet with an App Script extension that will communicate with the Snipe-IT API, retrieve the data, and populate it on the Google Sheet. So far, I've been able to get the API to populate some of our data on the terminal but not on the spreadsheet itself.
I wrote a simple script that should list out the assets assigned to one particular user, for testing purposes. Again, the script works fine, it populates the data I need on the terminal but not on the Google Sheet. Here is my exact code (excluding SETUP section details):
//SETUP
serverURL = 'SERVER-URL'; (ignore)
apiKey = 'API-KEY' (ignore)
function onOpen(e) {
createCommandsMenu();
}
function createCommandsMenu() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Run Script')
.addItem('Get Assets By Department', 'runGetAssetsByDepartment')
.addToUi();
}
function testGetAssetsByUser(){
getAssetsByUser("1745")
}
//Get assets for a user by id
//Returns a list of assets by id
function getAssetsByUser(userID) {
var url = serverURL + 'api/v1/users/' + userID + '/assets';
var headers = {
"Authorization" : "Bearer " + apiKey
};
var options = {
"method" : "GET",
"contentType" : "application/json",
"headers" : headers
};
var response = JSON.parse(UrlFetchApp.fetch(url, options));
var rows = response.rows;
var assets = []
for (var i=0; i<rows.length; i++) {
var row = rows[i];
if (row.category.name == "Laptop" || row.category.name == "Desktop" || row.category.name == "2-in-1") {
var asset = row.id
assets.push(asset)
}
}
return assets
}
console.log(getAssetsByUser(1745));
There are several ways to write values into an spreadsheet
The basics
Grab the spreadsheet
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
This works in bounded projects and add-ons
Grab the destination sheet
var sheet = spreadsheet.getSheetByName('put here the sheet name');
You might use SpreadsheetApp.getActiveSheet() if your spreadsheet has only one sheet, but for functions being called from a custom menu this one is risky.
Write values into the destination sheet
Preparation: Make a 2D Array
var output = assets.map(v => [v]);
Do: Write values into the destination sheet
sheet.getRange(1,1,output.length, 1).setValues(output);
Resources
https://developers.google.com/apps-script/guides/sheets

Fetch data from multiple sheets using Google app script

Trying to fetch data from multiple spreadsheet's. All Sheet's are stored in same folder. Have to fetch data in one master sheet from only specific files by file name. I have written below script. It's working fine if we enter only one file name in specified range (in master sheet tab) (getSheetByName) but showing error while trying to fetch data for multiple files.
Showing error - "TypeError: Cannot read property 'length' of undefined"
Below is Script -
function get_compiled_data() {
var filelist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileName");
var filelists = filelist.getRange("A2:A").getValues();
var folder = DriveApp.getFolderById("1li9hBP_W5gPkb_ASKqin4j1ZGEn1Gvji");
var fileindex = folder.getFilesByName(filelists);
var file;
var filetype;
var sheetID;
var collect_data = [];
var data;
while (fileindex.hasNext()) {
file = fileindex.next();
filetype = file.getMimeType();
if (filetype === "application/vnd.google-apps.spreadsheet"){
sheetID = file.getId();
data = getData(sheetID);
data = data.map(function(r){return r.concat([file.getName()]);});
collect_data = collect_data.concat(data);
}
}
var target = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Compiled_Data");
target.getRange("A2:AX").clearContent();
target.getRange(2, 1, collect_data.length, collect_data[0].length).setValues(collect_data);
}
function getData (sheetID) {
var sheet = SpreadsheetApp.openById(sheetID);
var tab = sheet.getSheets()[0];
var data = tab.getRange("A3:AX" + tab.getLastRow()).getValues();
return data;
}
Issue:
You are providing a 2D array to getFilesByName(name), when you should be providing a string. Because of this, the code never enters the while block and collect_data remains an empty array, causing collect_data[0] to be undefined, and producing the observed error when trying to access its length.
When you were looking for a single file name you were probably using getValue(), which retrieves a single value, which can be a string, which can be used in getFilesByName. getValues() returns a 2D array instead, so you should adapt your code so that it iterates through each value returned by getValues().
Solution:
Edit the getValues() line and wrap all the actions made from getFilesByName inside a loop the following way:
function get_compiled_data() {
var filelist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("FileName");
var filelists = filelist.getRange(2, 1, filelist.getLastRow() - 1).getValues();
var folder = DriveApp.getFolderById("FOLDER-ID");
filelists.forEach(fileName => {
var fileindex = folder.getFilesByName(fileName[0]);
// Rest of code
});
});
Reference:
getValues()
getValue()

Google sheets scripts function UrlFetchApp.fetch does not run from .onEdit(e) but works from editor

I have created a google sheet with a lot of info for a beach volleyball cup and I want to call an API I have created when a checkbox is checked in this sheet.
function onEdit(e){
const ui = SpreadsheetApp.getUi();
const spreadsheets = SpreadsheetApp.getActive();
const configSheet = spreadsheets.getSheetByName("Config")
var tourneyId = String(configSheet.getRange(2,4).getValue())
var tourneyTitle = String(configSheet.getRange(2,5).getValue())
var sheet = spreadsheets.getActiveSheet()
if (sheet.getName() == "LiveScore"){
var actRng = sheet.getActiveRange();
var editColumn = actRng.getColumn();
var rowIndex = actRng.getRowIndex();
actRng = actRng.getCell(1, 1);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
if(editColumn == 7 && rowIndex != 1){
onStartBroadcastClicked(actRng, ui, sheet, rowIndex, editColumn, tourneyTitle);
}
}
}
There is never any problems with this part as I see it. But when i get into the function onStartBroadcastClicked:
function onStartBroadcastClicked(actRng, ui, sheet, rowIndex, editColumn, tourneyTitle){
var homeTeam = String(sheet.getRange(rowIndex, 14).getValue());
... // more setting variables
var endTime = new Date(startTime.getTime() + MILLIS_PER_MATCH);
if(actRng.isChecked()){
var response = ui.alert("You are about to start a new broadcast. Are you sure?" +
"\n Title: " + title, ui.ButtonSet.YES_NO);
if (response == ui.Button.YES) {
var httpRequest = "https://someUrl";
var options =
{
'method':'POST',
'contentType': 'application/json',
'payload' : JSON.stringify({
"title" : title,
... // setting all variables
"description" : description
}),
'muteHttpExceptions' : true,
'headers' : {
"Authorization": "Basic " + Utilities.base64Encode(USERNAME + ":" + PASSWORD)
}
};
ui.alert("Waiting.......")
var result = UrlFetchApp.fetch(httpRequest, options);
ui.alert(result.getContentText())
The issue is that it always gets to the line ui.alert("Waiting......."), but when triggered from the checkbox, it never succeeds the http POST request. If I click play inside the editor, it succeeds and I got the response in the alertbox.
Could it be some timeout or some autosave issues? Does anyone have any idea if where to keep looking? I've been stuck here for some time now and I would be really happy if anyone can point me to the correct direction.
The modification point of your issue is to use the installable trigger of OnEdit event. When the methods which are required to authorize used at the simple trigger, the error occurs. This situation makes us think that it seems the script doesn't work.
In order to avoid this error, please use the installable triggers of OnEdit event trigger.
As an important point, before you install the trigger, please rename the function name of onEdit() to other name. And install the renamed function name as the OnEdit event trigger. By this, the duplicate run of onEdit() can be prevented. If onEdit() function is installed as the installable trigger, when a cell is edited, the function is run 2 times. Ref.
By above settings, when the cell is edited, UrlFetchApp.fetch() works.
References:
Simple Triggers
Installable Triggers
Asynchronous Processing using Event Triggers
I was able to get a script to work with a trigger if I created the script from script.google.com and call the Google Sheet and tab from the script. I'm manually entered in my API calls per cell within a specified Row:
function fetchUrls() {
var spreadsheetId = "ENTER GOOGLE SHEET ID";
var spreadsheet = SpreadsheetApp.openById(spreadsheetId);
var sheet = spreadsheet.getSheetByName("ENTER SHEET NAME");
var range = sheet.getRange("ENTER RANGE OR FULL COLUMN"); // specify the range of cells in column B
var urls = range.getValues(); // get the values of the cells and store them
in an array
var cache = CacheService.getScriptCache();
for (var i = 0; i < urls.length; i++) {
if(urls[i][0] != "") { // check if the current cell is not empty
var url = urls[i][0];
var result = cache.get(url);
if(!result) {
var response = UrlFetchApp.fetch(url);
result = response.getContentText();
cache.put(url, result, 21600);
}
sheet.getRange(i+1,3).setValue(result); // set the value of the current cell to the result of the API call in column C
}
}
}

Using Google sheet script to push data from HTML form to sheet not working when sheet is not open

I am using a Google App script to send data from an HTML form to a Google spreadsheet. This works perfectly when I have the spreadsheet open in my browser; the data I put in the form is submitted to the sheet. On the other hand, when I close the spreadsheet and fill out the form, noting is submitted to the sheet.
Here is my script.
// 1. Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
// 2. Run > setup
//
// 3. Publish > Deploy as web app
// - enter Project Version name and click 'Save New Version'
// - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
//
// 4. Copy the 'Current web app URL' and post this in your form/script action
//
// 5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
return handleResponse(e);
}
function doPost(e){
return handleResponse(e);
}
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
I suspect that there is no "active spreadsheet" when you have the spreadsheet closed.
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
I recommend that you hardcode the spreadsheet id into your code.

Error: "Cannot call method getRange of null" in google spreadsheet

I'm trying to insert data to google spreadsheet using GET method. I found the code below, followed the instructions, and created new sheet named "Sheet1", but when I'm trying to insert data through GET method, I get an error says: "Cannot call method getRange of null". What could be the problem? Does anyone have another idea how to insert data to google spreadsheet using GET method?
Thanks!
// Usage
// 1. Enter sheet name where data is to be written below
var SHEET_NAME = "Sheet1";
// 2. Run > setup
//
// 3. Publish > Deploy as web app
// - enter Project Version name and click 'Save New Version'
// - set security level and enable service (most likely execute as 'me' and access 'anyone, even anonymously)
//
// 4. Copy the 'Current web app URL' and post this in your form/script action
//
// 5. Insert column names on your destination sheet matching the parameter names of the data you are passing in (exactly matching case)
var SCRIPT_PROP = PropertiesService.getScriptProperties(); // new property service
// If you don't want to expose either GET or POST methods you can comment out the appropriate function
function doGet(e){
return handleResponse(e);
}
function doPost(e){
return handleResponse(e);
}
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
// loop through the header columns
for (i in headers){
if (headers[i] == "Timestamp"){ // special case if you include a 'Timestamp' column
row.push(new Date());
} else { // else use header name to get data
row.push(e.parameter[headers[i]]);
}
}
// more efficient to set values as [][] array than individually
sheet.getRange(nextRow, 1, 1, row.length).setValues([row]);
// return json success results
return ContentService
.createTextOutput(JSON.stringify({"result":"success", "row": nextRow}))
.setMimeType(ContentService.MimeType.JSON);
} catch(e){
// if error return this
return ContentService
.createTextOutput(JSON.stringify({"result":"error", "error": e}))
.setMimeType(ContentService.MimeType.JSON);
} finally { //release lock
lock.releaseLock();
}
}
function setup() {
var doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
Looks like page SHEET_NAME does not exist
and sheet is null here
var sheet = doc.getSheetByName(SHEET_NAME);
I would recommend you to check if sheet was initialized
function handleResponse(e) {
// shortly after my original solution Google announced the LockService[1]
// this prevents concurrent access overwritting data
// [1] http://googleappsdeveloper.blogspot.co.uk/2011/10/concurrency-and-google-apps-script.html
// we want a public lock, one that locks for all invocations
var lock = LockService.getPublicLock();
lock.waitLock(30000); // wait 30 seconds before conceding defeat.
try {
// next set where we write the data - you could write to multiple/alternate destinations
var doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
var sheet = doc.getSheetByName(SHEET_NAME);
if(sheet != null) {
// we'll assume header is in row 1 but you can override with header_row in GET/POST data
var headRow = e.parameter.header_row || 1;
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0];
var nextRow = sheet.getLastRow()+1; // get next row
var row = [];
==== code skipped =====
if you doesn't care about sheet name, use
var sheet = doc.getSheets()[0];

Categories