For Loop for Event Object variables in Google App Script - google-apps-script

Again newbee in app scripting I am trying to do the following
How to store array values in a variable for event object while on form submit i.e I need to get all values from form responses column in a sheet J to AF from a single row into a variable
something like var value = j,k,l,m,n,o(obviously as string without any spl chars like ,.etc)
instinctively there should be better way get all the values in a variable? tried some loop since the range starts from in-between columns(J-AF) of the sheet cant get it right
function onFormSubmit(e) {
Logger.log("%s", JSON.stringify(e));
//Get information from form and set as variables
var amount = [e.values[9],e.values[10],e.values[11],e.values[12],e.values[13],e.values[14],...e.values[31]];
Logger.log('Lets see if its here:'+ amount);
}

The e object from onFormSubmit(e) has the following structure:
{
authMode: {... },
namedValues:
{ 'Question 2': [ 'Answer 2' ],
Timestamp: [ '3/2/2020 9:48:53' ],
Question: [ 'Answer' ] },
range: { columnEnd: 3, columnStart: 1, rowEnd: 6, rowStart: 6 },
source: {},
triggerUid: '3xxxxxx825600xxx',
values: [ '3/2/2020 9:48:53', 'Answer', 'Answer 2' ]
}
As you can see, there are a couple of properties that can be useful for your case. (namedVales and values).
It is up to you to choose which to use.
An example:
function onFormSubmit(e) {
// Get values from response and put them in named variables
var amountDict = {
'Question 2' : e.namedValues['Question 2'],
'Question' : e.namedValues['Question'],
'Timestamp' : e.namedValues.Timestamp,
moreCols:'...', colAB: e.namedValues[10]
};
// Log them
console.log(amountDict);
// Get values from response and put them in unnamed variables
var amountList = [e.values[0],e.values[2], '...', e.values[3]];
// Log them
console.log(amountList);
}
Output:

Try this:
Don't forget to change the sheet name if needed.
And create the trigger.
function onFormSubmit(e) {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet1');
sh.getRange(sh.getLastRow()+1,10,1,e.values.length).setValues([e.values]);
}

Related

Google form: create two rows with different information

Im trying to create a google form with 2 fields, one for "item" and the other for "quantity"
Since user might need to send miltiple items I want to create 1 form only and sort the information.
my Google form
So far I have managed to add a script that splits the information submitted in "item" into many rows, however, Im not able to do the same with the field "quantity"
I got this information from this post
This is my script:
function onFormSubmit(e) {
var ss = SpreadsheetApp.openByUrl("URL_here");
var sheet = ss.getSheetByName("FormResponses");
// Form Response retrieved from the event object
const formResponse = e.response;
var itemResponses = formResponse.getItemResponses();
// Add responses comma-separated included
var rowData = itemResponses.map(item => item.getResponse().toString());
rowData.splice(0, 0, formResponse.getTimestamp());
// Split into different rows afterwards
if (rowData[1].includes(',')) {
rowData[1].split(',').forEach(instanceName => {
let tmpRow = rowData.map(data => data);
tmpRow[1] = instanceName;
sheet.appendRow(tmpRow);
// Append to the sheet
});
}
else {
sheet.appendRow(rowData); // Append to the sheet
}
Current results:
Click here to see image
What I want to get:
Click here to see image
Thanks
When I saw your script, only the 2nd element of rowData is split with ,. I thought that this might be the reason for your issue. And, when appendRow is used in a loop, the process cost will become high. So, in your situation, how about the following modification?
From:
rowData.splice(0, 0, formResponse.getTimestamp());
// Split into different rows afterwards
if (rowData[1].includes(',')) {
rowData[1].split(',').forEach(instanceName => {
let tmpRow = rowData.map(data => data);
tmpRow[1] = instanceName;
sheet.appendRow(tmpRow);
// Append to the sheet
});
}
else {
sheet.appendRow(rowData); // Append to the sheet
}
To:
var date = formResponse.getTimestamp();
var values = rowData.map(v => v.includes(',') ? v.split(",") : [v]);
var res = values[0].map((_, c) => [date, ...values.map(r => r[c] || "")]);
sheet.getRange(sheet.getLastRow() + 1, 1, res.length, res[0].length).setValues(res);
Reference:
map()

How do you reference a column of data for input in a google sheets script editor?

I found a script to download prices from coin market cap, but in the script you have to individually enter the name of the coin in the script. Since I may want to change the names often, it seems it would be easier to "fetch" the names from a list in a column.
In the following code, you enter each name in line 21. Is there any way to edit the code to whatever names are listed in B2:B10 and have those names inputted in the script? so I can just edit column B instead of the script?
Thanks in advance
enter codefunction coin_price() {
const myGoogleSheetName =
// Change "Sheet1" to the name of your sheet where you want to run this.
SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1')
// Call CoinMarketCap and let them know who you are.
const coinMarketCapAPICall = {
method: 'GET',
uri: 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest',
qs: {
start: '1',
limit: '5000',
convert: 'USD',
},
headers: { 'X-CMC_PRO_API_KEY': 'YOUR_API_KEY_GOES_HERE' },
json: true,
gzip: true,
}
// Put the coin symbols that you want to follow here.
const myCoinSymbols = ['BTC', 'ETH', 'FIL', 'BTT', 'AMP', 'DOT']
// Let's itereate
for (let i = 0; i < myCoinSymbols.length; i++) {
const coinSymbol = myCoinSymbols[i]
const coinMarketCapUrl = `https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest?symbol=${coinSymbol}`
const result = UrlFetchApp.fetch(coinMarketCapUrl, coinMarketCapAPICall)
const txt = result.getContentText()
const d = JSON.parse(txt)
const row = i + 2
// Puts a column of at symbols into the sheet at B2.
myGoogleSheetName.getRange(row, 2).setValue(coinSymbol)
// Puts a column of current market price's in dollars into the sheet at B3.
myGoogleSheetName
.getRange(row, 3)
.setValue(d.data[coinSymbol].quote.USD.price)
You can put your symbols in columns B, from B2 to B7 for instance, and retrieve them by
var myCoinSymbols = myGoogleSheetName.getRange('B2:B7').getValues().join().split(',')
then remove this line myGoogleSheetName.getRange(row, 2).setValue(coinSymbol)

Copying Google Sheets table to Google Document - Error with Boolean cells

I am very new to programming google app script, just trying to make some custom function. Sorry if this question is too basic...
I was following a tutorial to copy a table from Google Sheets into Google document but the exact same code the instructor was using did not worked for me.
I was getting this error:
Exception: The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable.
The simplified version of the code is:
function fun4(){
var ss = SpreadsheetApp.openById('17-23aFf6mN5oQrKNwNDy3Zh24_foTN5mXzNkjvd3V5w');
var sheet = ss.getSheets()[0];
var doc = DocumentApp.create('Sample Sheet Data');
var body = doc.getBody();
var numLines = sheet.getLastRow();
var numColumns = sheet.getLastColumn();
var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();
console.log(rowData);
var table = body.appendTable(rowData); // ERROR IN THIS LINE
table.getRow(0).editAsText().setBold(true);
}
After some search I found that the problem was caused by the last column containing Boolean values and changed .getValues() to .getDisplayValues().
It is working now but I am very confused...
How it was working in the instructor code but not in mine?
Why did not work if the output looks to be in the same format (double array?)
Code: (Gives an error when I append the table to doc , but worked in the instructor video)
var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();
console.log(rowData);
Output:
11:36:05 AM Info
[ [ 'NAME', 'EMAIL', 'AGE', 'ACTIVE' ],
[ 'Alex', 'alex#gmail.com', 50, true ],
[ 'Brian', 'brian#gmail.com', 34, false ],
[ 'Julian', 'julian#gmail.com', 42, true ],
[ 'John', 'john#gmail.com', 24, false ] ]
Code:
var rowData = sheet.getRange(1, 1, numLines, numColumns).getDisplayValues();
console.log(rowData);
Output:
11:36:05 AM Info
[ [ 'NAME', 'EMAIL', 'AGE', 'ACTIVE' ],
[ 'Alex', 'alex#gmail.com', '50', 'TRUE' ],
[ 'Brian', 'brian#gmail.com', '34', 'FALSE' ],
[ 'Julian', 'julian#gmail.com', '42', 'TRUE' ],
[ 'John', 'john#gmail.com', '24', 'FALSE' ] ]
I believe your goal as follows.
You want to know the reason the following situation.
After some search I found that the problem was caused by the last column containing Boolean values and changed .getValues() to .getDisplayValues().
It is working now but I am very confused... How it was working in the instructor code but not in mine? Why did not work if the output looks to be in the same format (double array?)
Answer:
About the error at the script of var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues();, I thought that the reason of the issue might be due to using V8 runtime. Ref
When V8 runtime is enabled at the script editor, I confirmed that the error of The parameters (number[]) don't match the method signature for DocumentApp.Body.appendTable. occurred.
When V8 runtime is disabled at the script editor, I confirmed that no error occurred.
The table can be created using the value of var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues(); and your sample values.
From your question, unfortunately, I cannot understand about the instructor video of Code: (Gives an error when I append the table to doc , but worked in the instructor video). But, above situation, I guess that the instructor video might not use V8 runtime.
So, in your script, when you want to test whether var rowData = sheet.getRange(1, 1, numLines, numColumns).getValues(); works, how about disabling V8 runtime at the script editor as follows and testing it again?
Reference:
V8 Runtime Overview

Google Apps Scripting Issue

1) I am trying to create a complete script that will take input data from a google form/google sheet and use the top row as as field names for a Freshdesk ticket while taking the last row (most current) as the data to input as the ticket data ..
2) I have coded onFormSubmit portion so it creates the general ticket however i would the data from the backend to be used as the certain field data
function onFormSubmit(e) {
if ((typeof GasFreshdesk)==='undefined') {
eval(UrlFetchApp.fetch('https://raw.githubusercontent.com/zixia/gas-freshdesk/master/src/gas-freshdesk-lib.js').getContentText())
}
var MyFreshdesk = new GasFreshdesk('https://***.freshdesk.com', 'API KEY'); // REPLACE redacted with real key
// Custom fields passed in. Use this for now - to test (then replace later with other valid values).
var customFields = {"email": "firstnamelastname#org.org",
"room": "IT",
"building": "**",
"devicesystem": "Device",
"problem": "problem"
};
var ticket = new MyFreshdesk.Ticket({description: "We have provided you with a loaner",
subject: "** - Device - Problem- " + "FullName", email: "firstnamelastname#org.org",
type: "Support Request", custom_fields: customFields});
}
What i'm looking for is a way to grab the values in the last row of data since that data is what will need to be inputted into the ticket.
function LastRow() {
var ss= SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1");
var lr = getLastRow();
Logger.log(lr);
}

Extract Keenio Data into google spreadsheet

I am currently using ImportJSON to import Sendgrid Email with data Keenio Extraction Query API URL by calling the ImportJSON function in a Google Spreadsheet cell of Sheet DATA.
=ImportJSON("https://api.keen.io/3.0/projects/"& PROJECT_KEY & "/queries/extraction?api_key=" & API_KEY & "&event_collection=" & EVT_COL & "&timezone=" & TIMEZONE & "&latest=" & LATEST & "&property_names..........", PTDATA!$AB$1)
In Sheet PTDATA, in the last column cell i am setting a random number for ImportJSON to recalculate. The function runs on Spreadsheet open event. I have also added a custom menu to call the ReCalcCell custom function.
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('IMPORT DATA')
.addItem('KEENIO DATA', 'ReCalcCell')
.addToUi();
}
function ReCalcCell(){
var min = Math.ceil(0);
var max = Math.floor(9999);
var randomNum = Math.floor(Math.random() * (max - min + 1)) + min
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("PTDATA");
sh.getRange("$AB$1").setValue(randomNum);
}
PTDATA sheet has specific column header names for which i want to pull the data from DATA sheet. Towards the right of these columns, i have other calculation columns which work on these specific columns.
Since the columns in DATA sheet always appear in a random / shuffled order, i had to write a small custom function GCL which takes in a header name and returns its datarange address from DATA sheet as a string.
function GCL(header,dummy) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
var headings = sheet.getRange(1, 1, 1, sheet.getLastColumn()); // get the range representing the whole sheet
var width = headings.getWidth();
var lrow = sheet.getLastRow();
// search every cell in row 1 from A1 till the last column
for (var i = 1; i <= width; i++) {
var data = headings.getCell(1,i).getValue();
if (data == header) {
return ((sheet.getSheetName() + "!" + columnToLetter(i)+"2:" + columnToLetter(i) + lrow).toString()); // return the column range if we find it
break; // exit when found
}
}
return(-1); // return -1 if it doesn't exist
}
function columnToLetter(column)
{
var temp, letter = '';
while (column > 0)
{
temp = (column - 1) % 26;
letter = String.fromCharCode(temp + 65) + letter;
column = (column - temp - 1) / 26;
}
return letter;
}
Then i use the custom function GCL in each specific column to get it's datarange. Once data is populated, the PDATA sheet is used to create different Pivots for reporting purposes.
=ARRAYFORMULA(INDIRECT(GCL(A1,$AB$1)))
The problems i am facing is that though the ImportJSON data populates the DATA sheet:
DATA Sheet:
The columns appear shuffled everytime, so my calculation columns cannot calculate as the references go away. This renders the pivots useless! To counter this issue, i had to create the PDATA sheet to pull in specific columns using the custom function GCL.
The custom function GCL does not always refresh and most of the time shows #Ref error.
PDATA Sheet:
BTW, my JSON output from Keenio looks like this:
{
"result":
[
{
"sg_event_id": "92-OndRfTs6fZjNdHWzLBw",
"timestamp": 1529618395,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:59:55.000Z",
"created_at": "2018-06-21T22:00:28.532Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 38,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}, {
"sg_event_id": "bjMlfsSfRyuXEVy8LndsYA",
"timestamp": 1529618349,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:59:09.000Z",
"created_at": "2018-06-21T21:59:39.491Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 36,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}, {
"sg_event_id": "fru_s2s1RtueuqBMNoIoTg",
"timestamp": 1529618255,
"url": "https://noname.com?utm_campaign=website&utm_source=sendgrid.com&utm_medium=email",
"ip": "192.168.1.1",
"event": "click",
"keen": {
"timestamp": "2018-06-21T21:57:35.000Z",
"created_at": "2018-06-21T21:58:20.374Z",
"id": "555c1f7c5asdf7000167d87b"
},
"url_offset": {
"index": 29,
"type": "text"
},
"sg_message_id": "F5mwV1rESdyKFA_2bn1IEQ.filter0042p3las1-15933-5B2A68E8-36.0",
"useragent": "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)",
"email": "no.name#noname.com"
}
]
}
My questions are:
Is there a way to parse the JSON result without use of ImportJSON, which has to be entered as a custom function in a cell that also depends on recalculation? ImportJSON sometimes doesn't work properly.
How can this code be refactored or optimized so that it can always return data to PDATA sheet columns?
Is there a better way of accomplishing what i want without resorting to custom functions like GCL in the PDATA Sheet or ImportJSON in DATA sheet?
How about this sample script? This script parses the values retrieved from API using UrlFetchApp and put them to the sheet "DATA". You can run this at the menu of spreadsheet. Before you run this, please put the endpoint.
Sample script :
function onOpen() {
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('IMPORT DATA')
.addItem('KEENIO DATA', 'ReCalcCell')
.addItem('main', 'main')
.addToUi();
}
function main() {
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText(); // Modified
var values = JSON.parse(res);
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, e.url_offset.index, e.url_offset.type, e.sg_message_id, e.email]});
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
sheet.getRange(sheet.getLastRow() + 1, 1, putData.length, putData[0].length).setValues(putData);
}
Note :
When you use this, please put the endpoint including your token to url.
I confirmed this script using the JSON object in your question. So if the structure of the object is changed, it is required to also modify the script. Please be careful this.
Reference :
UrlFetchApp.fetch()
If I misunderstand about your issue, please tell me. I would like to modify it.
Edit 1 :
Pattern 1 :
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, JSON.parse(e["url_offset"]).index, JSON.parse(e["url_offset"]).type, e.sg_message_id, e.email]});
Pattern 2 :
var putData = values.result.map(function(e) {return [e.useragent, e.sg_event_id, e.timestamp, e.ip, e.url, e.event, e.keen.timestamp, e.keen.created_at, e.keen.id, e["url_offset"].index, e["url_offset"].type, e.sg_message_id, e.email]});
Edit 2 :
Could you please run this script and provide the values of the created file? Of course, please remove the personal information. But please don't modify the structure of the object. If you cannot do it, I would like to think of other ways.
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText();
DriveApp.createFile("sample.txt", res, MimeType.PLAIN_TEXT)
Edit 3 :
Please copy and paste this script in your script editor, run myFunction(). Then, please show the values of file. When you run this function, please confirm whether there are NOT the same function name in your project.
function myFunction() {
var url = "###"; // Please put the endpoint with your token.
var res = UrlFetchApp.fetch(url).getContentText();
DriveApp.createFile("sample.txt", res, MimeType.PLAIN_TEXT)
}
Edit 4 :
Please copy and paste this script in your script editor, run myFunction2(). Then, please show the results. When you run this function, please confirm whether there are NOT the same function name in your project.
Please confirm whether the keys and values of keen and url_offset are retrieved.
function myFunction2() {
var url = "###";
var res = UrlFetchApp.fetch(url).getContentText();
var values = JSON.parse(res);
for (var key in values.result[0]) {
Logger.log("key: %s, value: %s", key, values.result[0][key])
if (typeof values.result[0][key] == "object") {
for (var dkey in values.result[0][key]) {
Logger.log("key: %s, dkey: %s, value: %s", key, dkey, values.result[0][key][dkey])
}
}
}
}
Edit 5 :
Please copy and paste this script in your script editor, run myFunction3(). Then, please show the results. When you run this function, please confirm whether there are NOT the same function name in your project.
function myFunction3() {
var url = "###"; // Please set this.
var res = UrlFetchApp.fetch(url).getContentText();
var values = JSON.parse(res);
var obj = [];
for (var i = 0; i < values.result.length; i++) {
var temp = {};
var v = values.result[i];
for (var key in v) {
temp[key.replace(/_/g, "")] = v[key];
if (typeof v[key] == "object") {
for (var dkey in v[key]) {
temp[key.replace(/_/g, "") + dkey.replace(/_/g, "")] = v[key][dkey];
}
}
}
obj.push(temp);
}
var putData = obj.map(function(e) {return [e.useragent, e.sgeventid, e.timestamp, e.ip, e.url, e.event, e.keentimestamp, e.keencreatedat, e.keenid, e.urloffsetindex, e.urloffsettype, e.sgmessageid, e.email]});
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("DATA");
sheet.getRange(sheet.getLastRow() + 1, 1, putData.length, putData[0].length).setValues(putData);
}
Looking at what you are doing here, it might be much easier to design your spreadsheet in an "append-only" format with a Zapier integration.
Zapier is able to handle SendGrid events directly, and append those events to your spreadsheet, if that is what you want.
And then you can have your "calculation columns" on a separate Sheet in the spreadsheet.
Just an idea.