JavaScript: Pass Google Sheet cell value as Variable into POST URL - javascript

So I have this code that is supposed to fetch a project_id from a google sheet and append it into URL. I have added the plus (+) but still unable to pick the project_id from the google sheet. Posting will not be possible without this parameter. It is not throwing any error but still unable to post to the project's system. Here is the code: What am I not doing right?
function sendBudget(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// used getLastRow() function [1] to narrow the array to have only cells with data.
var range = sheet.getRange("AO2:AO"+sheet.getLastRow()).getValues();
var searchString = "3";
for (var i = 0; i<range.length; i++) {
if(range[i][0] == searchString) {
var lastRow = sheet.getRange(2+i,1,1,41).getValues();
var project_id = lastRow[0][40];
var data = {
'amount':lastRow[0][7]*10,
};
var payload = JSON.stringify(data);
var options = {
'method': 'POST',
'Content-Type': 'application/json',
'payload' : data,
};
var url = 'https://vnext.10000ft.com/api/v1/projects/'+ project_id +'/budget_items?item_type=TimeFeesDays&auth=token';
var response = UrlFetchApp.fetch(url , options);
if (response.getResponseCode() === 200) {
var json = JSON.parse(response);
var id = json["id"];
sheet.getRange(2+i, 41).setValue(4);
}
else {
sheet.getRange(2+i, 41).setValue(5);
}
}
}}

I realized that the project_id was picking the wrong cell, so all did change is the row number in the variable:
var project_id = lastRow[0][40];

Related

Search spreadsheet by column value, return rows then post the content to external API: JavaScript

I am new to javascript/google app script and I'm trying to find the best script to complete a task. I've got a google sheet where I need to check values in certain columns, and depending on a matching case it returns that row data that are to be posted through external API. Then the response (Ideally a project come to be set back to a different column of the same row.
I am not sure the most efficient way to search through the specific columns and grabbing not just that value but the entire row and pass them to external API.
Here is what I tried but failed miserably. Someone who can help me craft the code will please rescue me here.
function findTheLastRow(){
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("W2:W").getValues();
var searchString = "0";
if(searchString = "0")
{
var lastRow = lastRowData()
var data = {
'name':lastRow[2],
'client': lastRow[3],
'starts_at':lastRow[5],
'ends_at':lastRow[6],
'project_state':lastRow[4],
};
var payload = JSON.stringify(data);
var options = {
'method': 'POST',
'Content-Type': 'application/json',
'payload' : data,
};
var url = 'https:....';
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response);
var id = json ["id"];
}
You can use getLastRow() function [1] to narrow the array to have only cells with data. You have to take in account that getValues function [2] returns a 2D array every time. And you can use setValue function [3] to set the value on the cell with the match.
function findTheLastRow(){
var ui = SpreadsheetApp.getUi();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var range = sheet.getRange("W2:W"+sheet.getLastRow()).getValues();
var searchString = "0";
for (var i = 0; i<range.length; i++) {
if(range[i][0] == searchString) {
var lastRow = sheet.getRange(2+i,1,1,7).getValues(); //Assuming you only need 7 columns from each row
var data = {
'name':lastRow[0][2],
'client': lastRow[0][3],
'starts_at':lastRow[0][5],
'ends_at':lastRow[0][6],
'project_state':lastRow[0][4],
};
var payload = JSON.stringify(data);
var options = {
'method': 'POST',
'Content-Type': 'application/json',
'payload' : data,
};
var url = 'https:....';
var response = UrlFetchApp.fetch(url, options);
var json = JSON.parse(response);
var id = json["id"];
//Assuming you want id value in 8th column
sheet.getRange(2+i, 8).setValue(id);
}
}
}
[1] https://developers.google.com/apps-script/reference/spreadsheet/sheet#getlastrow
[2] https://developers.google.com/apps-script/reference/spreadsheet/range#getvalues
[3] https://developers.google.com/apps-script/reference/spreadsheet/range#setvaluevalue

How can I iterate through multiple URLs to fetch JSON response in Google Apps Script?

I am working with a third-party API (from company called Simpli.fi) in Google Apps Script to pull some data into a spreadsheet. I am able to authenticate my API call just fine and can pull all of my required data with one URL. The issue is that the way the URL to call this API is formatted is as follows:
https://app.simpli.fi/api/organizations/{CLIENT_ID}/{SOME_ENDPOINT}
It works when I plug in one client id and one endpoint, however I do not want to pull the data individually for each client with each data endpoint.
I wish to pull data on all of my clients and also wish to access multiple endpoints, such as "/audiences" or "/campaigns". I am hoping there is a way (similar to Promises in JavaScript) that I can iterate through multiple URLs to fetch all of the data from the API.
For now, I am simply focusing on pulling all the data I want from the "/audiences" endpoint for all of my clients. I have set up an array accessing my Google Sheet that contains all the client codes and have plugged this into the URL with a for loop, which works just fine:
// iterate through all URL endpoints and client codes
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('formatting');
var range = sheet.getRange(['B2:B']).getValues();
var clients = range.filter(String);
var urlOneArray = [];
for (var i = 0; i < clients.length; i++) {
var urlOne = [baseURL + clients[i] + '/audiences'];
for (var j = 0; j < urlOne.length; j++) {
urlOneArray = urlOne[j];
Logger.log(urlOneArray);
}
}
The above logs a list of each built out URL as desired.
After pushing all of the built-out URLs into the urlOneArray, I tried calling with UrlFetchApp.fetchAll:
for (i=0; i < urlOneArray.length; i++) {
var response = UrlFetchApp.fetchAll(urlOneArray[i], params);
Utilities.sleep(500);
Logger.log(response);
}
When trying to use this method, I receive this error:
"Cannot find method fetchAll(string,object). (line 35, file "Code")"
If there is a way to iterate through multiple URLs to gather all of the data from the API in one pull, I would really appreciate some pointers.
Here is the full script:
// authenticate API call
var X_USER_KEY = 'XXXX';
var X_APP_KEY = 'XXXX';
function simplifiService() {
var baseURL = 'https://app.simpli.fi/api/organizations';
// iterate through all URL endpoints and client codes
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('formatting');
var range = sheet.getRange(['B2:B']).getValues();
var clients = range.filter(String);
var urlOneArray = [];
for (var i = 0; i < clients.length; i++) {
var urlOne = [baseURL + clients[i] + '/audiences'];
for (var j = 0; j < urlOne.length; j++) {
urlOneArray = urlOne[j];
Logger.log(urlOneArray);
}
}
var params = {
method: 'GET',
headers: {
"x-app-key": X_APP_KEY,
"x-user-key": X_USER_KEY
},
muteHttpExceptions: true
}
for (i=0; i < urlOneArray.length; i++) {
var response = UrlFetchApp.fetchAll(urlOneArray[i], params);
Utilities.sleep(500);
Logger.log(response);
}
if (response.getResponseCode() === 200) {
var data = JSON.parse(response);
Logger.log(data);
} else {
Logger.log('Error: ' + response.getResponseCode());
}
getData(data);
}
// parse out JSON data
function getData(data) {
var date = new Date();
var geoFenceId = data.audiences;
var geoFenceName = data.audiences[0].name;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Campaign Data');
//sheet.appendRow([date, geoFenceId, geoFenceName]);
}
Issue:
Invalid syntax: UrlFetchApp.fetchAll(requests Object[]) accepts object array, while you're providing a string and a object as argument.
Solution:
Valid Syntax: Create a object array for each endpoint/client and provide it as a argument to fetchAll()
Snippet:
function copyParams() {//shallow clone params object
for (var i in params) {
this[i] = params[i];
}
}
var endPoints = ['/audiences', '/campaigns'];
var requests = [];
var url, obj;
clients.forEach(function(client) {
endPoints.forEach(function(endPoint) {
obj = new copyParams();
url = baseUrl + '/' + client[0] + endPoint;
obj.url = url;
requests.push(obj);
});
});
console.log(requests);
var responseArray = UrlFetchApp.fetchAll(requests);
console.log(responseArray);
References:
UrlFetchApp.fetchAll
Array#forEach

changing the width when running a function

I'm relatively new to coding, so thanks in advance for any assistance here.
I have a script that runs two functions on the same sheet.
Button2 contains a function (email_button2) that runs a SQL query and pulls info into columns A to D
Button1 also contains a function (email_button1) that runs a separate query and I want to pull this info in columns G to AA (21 columns)
Right now, when I click Button1, I get the following error: Incorrect range width, was 1 but should be 21
Any idea what I should change or add to my script?
function data_button() {
// Logger.log(e)
var thisDoc = SpreadsheetApp.getActiveSpreadsheet();
var helpers = thisDoc.getSheetByName("helper");
query =
helpers.getRange(3,2).getValue().split(String.fromCharCode(13)).join(" ").split(String.fromCharCode(10)).join(" ")
// lastrow = helpers.getRange("lastrow").getValue()
do_query(query,"data")
}
function email_button1() {
// Logger.log(e)
var thisDoc = SpreadsheetApp.getActiveSpreadsheet();
var helpers = thisDoc.getSheetByName("helper");
query =
helpers.getRange(4,2).getValue().split(String.fromCharCode(13)).join("
").split(String.fromCharCode(10)).join(" ")
// lastrow = helpers.getRange("lastrow").getValue()
do_query(query,"emails",11,7)
}
function email_button2() {
// Logger.log(e)
var thisDoc = SpreadsheetApp.getActiveSpreadsheet();
var helpers = thisDoc.getSheetByName("helper");
query =
helpers.getRange(5,2).getValue().split(String.fromCharCode(13)).join("
").split(String.fromCharCode(10)).join(" ")
// lastrow = helpers.getRange("lastrow").getValue()
do_query(query,"emails",11,1)
}
function do_query(query,sheetname,startRow,startCol){
//List of lists?
var url= "xxxxxxxxxxxxxx"
var q = {"query":query}
var options = {
'method' : 'post',
'payload' : q,
'muteHttpExceptions': true
};
var response = UrlFetchApp.fetch(url, options); // get feed
var response2 = response.getContentText()
var rows = response2.split(";;;")
var columnCount = rows[0].length
var data = [];
for(var i = 0; i < rows.length; i++){
var row = rows[i].split('|||');
data.push(row);
}
Logger.log(data)
var a = data[1]
var thisDoc = SpreadsheetApp.getActiveSpreadsheet();
var sheet2 = thisDoc.getSheetByName(sheetname);
sheet2.getRange(startRow,startCol,200,4).clear()
sheet2.getRange(startRow,startCol, data.length,
data[0].length).setValues(data);
//this line above designates column headers
//sheet2.getRange(12, 1, data[1].length, data[1] .
[0].length).setValues(data[1]);
//Logger.log(data[0].length)
//Logger.log(data[0][0].length)
//Logger.log(data[0])
// thisDoc.getSheetByName("helper").getRange("lastrow").setValue(data.length)
}

Run Google script on single sheet instead of entire spreadsheet

With my spreadsheet, I have 2 Google forms tied to 2 sheets. When a form gets submitted the script executes and does it's thing. However, I only want the script to execute based on a submission from a single sheet. As it is now, the script executes when either of the forms get submitted.
My two sheets are:
Job Submission
and Order Submission
Any advice?
// Work Order
// Get template from Google Docs and name it
var docTemplate = ""; // *** replace with your template ID ***
var docName = "Work Order";
var printerId = "";
function addDates() {
var date = new Date(); // your form date
var holiday = ["09/04/2017","10/09/2017","11/23/2017","12/24/2017","12/25/2017","01/01/2018"]; //Define holiday dates in MM/dd/yyyy
var days = 5; //No of days you want to add
date.setDate(date.getDate());
var counter = 0;
if(days > 0 ){
while (counter < days) {
date.setDate(date.getDate() + 1 );
var check = date.getDay();
var holidayCheck = holiday.indexOf(Utilities.formatDate(date, "EDT", "MM/dd/yyyy"));
if (check != 0 && check != 6 && holidayCheck == -1) {
counter++;
}
}
}
Logger.log(date) //for this example will give 08/16/2017
return date;
}
function createNewDoc(values) {
//Get information from form and set as variables
var email_address = "";
var job_name = values[1];
var order_count = values[2];
var order_form = values[7];
var print_services = values[3];
var priority = values[5];
var notes = values[6];
var formattedDate = Utilities.formatDate(new Date(), "EDT", "MM/dd/yyyy");
var expirationDate = Utilities.formatDate(addDates(), "EDT", "MM/dd/yyyy");
// Get document template, copy it as a new temp doc, and save the Doc's id
var copyId = DriveApp.getFileById(docTemplate)
.makeCopy(docName+' for '+job_name)
.getId();
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document's body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys,in our google doc template
copyBody.replaceText('keyJobName', job_name);
copyBody.replaceText('keyOrderCount', order_count);
copyBody.replaceText('keyOrderForm', order_form);
copyBody.replaceText('keyPrintServices', print_services);
copyBody.replaceText('keyPriority', priority);
copyBody.replaceText('keyNotes', notes);
copyBody.replaceText('keyDate', formattedDate);
copyBody.replaceText('keyDue', expirationDate);
// Save and close the temporary document
copyDoc.saveAndClose();
// Convert temporary document to PDF by using the getAs blob conversion
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
// Attach PDF and send the email
var subject = "New Job Submission - " + job_name;
var body = "Here is the work order for " + job_name + ". Job is due " + expirationDate + ".";
MailApp.sendEmail(email_address, subject, body, {htmlBody: body, attachments: pdf});
// Move file to folder
var file = DriveApp.getFileById(copyId);
DriveApp.getFolderById("").addFile(file);
file.getParents().next().removeFile(file);
var newDocName = docName + ' for ' + job_name;
return [copyId, newDocName];
}
function printGoogleDocument(copyId, docName) {
// For notes on ticket options see https://developers.google.com/cloud-print/docs/cdd?hl=en
var ticket = {
version: "1.0",
print: {
color: {
type: "STANDARD_COLOR"
},
duplex: {
type: "NO_DUPLEX"
},
}
};
var payload = {
"printerid" : "",
"content" : copyId,
"title" : docName,
"contentType" : "google.kix", // allows you to print google docs
"ticket" : JSON.stringify(ticket),
};
var response = UrlFetchApp.fetch('https://www.google.com/cloudprint/submit', {
method: "POST",
payload: payload,
headers: {
Authorization: 'Bearer ' + GoogleCloudPrint.getCloudPrintService().getAccessToken()
},
"muteHttpExceptions": true
});
// If successful, should show a job here: https://www.google.com/cloudprint/#jobs
response = JSON.parse(response);
if (response.success) {
Logger.log("%s", response.message);
} else {
Logger.log("Error Code: %s %s", response.errorCode, response.message);
}
return response;
}
// When Form Gets submitted
function onFormSubmit(e) {
var values = e.values;
var returnedDocValues = createNewDoc(values);
var copyId = returnedDocValues[0];
var docName= returnedDocValues[1];
printGoogleDocument(copyId, docName);
}
Edit:
I'm not sure how to do a complete or verifiable example since it's dependent on the form submission. I don't often work with javascript so I'm still learning.
Anyway, I updated my onFormSubmit function, however, my other functions have failed to execute. The script doesn't create the doc and in turn doesn't get sent to the google cloud print
// When Form Gets submitted
function onFormSubmit(e) {
// Initialize
var rng = e.range;
var sheet = rng.getSheet();
var name = sheet.getName();
// If the response was not submitted to the right sheet, exit.
if(name != "Job Submission") return;
var values = e.values;
var returnedDocValues = createNewDoc(values);
var copyId = returnedDocValues[0];
var docName= returnedDocValues[1];
printGoogleDocument(copyId, docName);
}
If your onFormSubmit function is on a script bounded to the spreadsheet, the event object includes a range object. You could use getSheet to get the sheet and then getName to get the sheet name.
Example:
function onFormSubmit(e){
// Initialize
var rng = e.range;
var sheet = rng.getSheet();
var name = sheet.getName();
// If the response was not submitted to the right sheet, exit.
if(name != "Responses 1") return;
//Otherwise continue
}
Going off of Ruben's suggestion, here is what I ended up with
function onFormSubmit(e) {
// Initialize
var name = e.range.getSheet().getName();
// If the response was not submitted to the right sheet, exit.
if (name != "Job Submission") return;
var values = e.values;
var returnedDocValues = createNewDoc(values);
var copyId = returnedDocValues[0];
var docName = returnedDocValues[1];
printGoogleDocument(copyId, docName);
}

Update and insert data instead of complete replacement

I use the code below to retrieve a XML and write the data to Sheet2.
But when i run the function again it loads the entire xml again into the sheet.
What i want to achieve, but don't know how:
1.
Get de XML and compare it with the data already in Sheet2, based on 2 colums:
stationID & stationTypeID
2.
When the two columns match, update the entire row. When the columns don't match, insert the new row on top.
function loadOutposts(){
var outposts= new Array();
var url = "https://api.eveonline.com/eve/ConquerableStationList.xml.aspx";
var parameters = {method : "get", payload : ""};
var xmlFeed = UrlFetchApp.fetch(url, parameters).getContentText();
var xml = XmlService.parse(xmlFeed);
if(xml) {
var rows=xml.getRootElement().getChild("result").getChild("rowset").getChildren("row");
for(var i = 0; i < rows.length; i++) {
outpost=[rows[i].getAttribute("stationID").getValue(),
rows[i].getAttribute("stationName").getValue(),
rows[i].getAttribute("stationTypeID").getValue(),
rows[i].getAttribute("solarSystemID").getValue(),
rows[i].getAttribute("corporationID").getValue(),
rows[i].getAttribute("corporationName").getValue()
]
outposts.push(outpost);
}
}
//return outposts;
SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2').getRange(1,1,outposts.length,outposts[0].length).setValues(outposts);
};
Thx for the help!
How about this modification?
Flow :
Data retrieved by UrlFetchApp.fetch() is compared to the existing data on Spreadsheet.
When the columns 1 and 2 for them are same, the existing data is updated.
When the data retrieved from URL is not included in the existing data, the data is inserted to the top of existing data. This existing data becomes new data.
Then, the data on Spreadsheet is updated by new data.
Modified script :
function loadOutposts(){
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet2');
var outposts= new Array();
var url = "https://api.eveonline.com/eve/ConquerableStationList.xml.aspx";
var parameters = {method : "get", payload : ""};
var xmlFeed = UrlFetchApp.fetch(url, parameters).getContentText();
var xml = XmlService.parse(xmlFeed);
if (xml) {
var rows=xml.getRootElement().getChild("result").getChild("rowset").getChildren("row");
for(var i = 0; i < rows.length; i++) {
outpost = [
rows[i].getAttribute("stationID").getValue(),
rows[i].getAttribute("stationName").getValue(),
rows[i].getAttribute("stationTypeID").getValue(),
rows[i].getAttribute("solarSystemID").getValue(),
rows[i].getAttribute("corporationID").getValue(),
rows[i].getAttribute("corporationName").getValue()
]
outposts.push(outpost);
}
// ----- Added script
if (ss.getLastRow() > 0) {
var currentdata = ss.getRange(2, 1, ss.getLastRow(), ss.getLastColumn()).getValues(); // Updated
currentdata.forEach(function(e1, i1){
var temp = [];
outposts.forEach(function(e2, i2){
if (e1[0] == e2[0] && e1[1] == e2[1]) {
currentdata[i1] = e2;
temp.push(i2);
}
});
for (var i in temp) {
outposts.splice(temp[i], 1);
}
});
Array.prototype.push.apply(outposts, currentdata);
}
// -----
}
ss.getRange(2,1,outposts.length,outposts[0].length).setValues(outposts); // Updated
};
If I misunderstand your question, I'm sorry.

Categories