Script on Google Sheet to Run Function - javascript

Sample Data Sheet
Hi,
I currently have a runOnEdit script (below) that is set to trigger on changes in Column A, then pull the value from Column D and run it through a function and return the value in Column E. It works great. However, because I am using Zapier to push changes into the trigger column, the runOnEdit function is not working. Based on research, I know that onEdit wont detect changes from API services. So I would like to create a similar function that runs hourly on Google Sheets that basically checks a timestamp column (B) and compares it to another timestamp column (C). If it meets the if condition, then I want it to return the function value in the return column (E) for each cell. I'm using the timestamps to determine whether the trigger column was updated recently in which case I want the sub function to run and if it has not been run recently, then I want it to stop and leave the current value that exists in the result column E.
This is my current runOnEdit script that works great.
function runonEdit(e) {
var sh = e.source.getActiveSheet();
if(sh.getName() !== "Copy of DataFinal" || e.range.columnStart !== 1) return;// 1 is A, 2 is B, 3 is C and so on
var v = e.range.offset(0,5)
var value =v.getValue()
var o = e.range.offset(0, 4)
if (value == "") {
o.setValue("")
return null;
}
var timedis = gettime(value)
o.setValue(timedis)
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}
Here is the attempt at my new script to run hourly. But i can't seem to figure it out.
'''
function runtimedisfunc() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Copy of DataFinal'); //source sheet
var testrange = sheet.getRange('A:Z'); //range to check
var o = testrange.range.offset(0, 5)
var tsza = testrange.range.offset(0,2)
var tstdfunc = testrange.range.offset(0,3)
var v = e.range.offset(0,4)
var combaddvalue =v.getValue()
var timestampza= tsza.getValue()
var timestamptdfunc= tstdfunc.getValue()
if (timestampza < timestamptdfunc){
return null;
}
if (combaddvalue == "") {
o.setValue("")
return null;
}
if ((timestampza != "") || (timestampza > timestamptdfunc)){
var timedis = gettime(combaddvalue)
o.setValue(timedis)
tstdfunc.setValue(new Date())
}
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}
'''

Summary
You want to regularly populate column E based on data from column D
You want to do so only if the data in column D is not ""
Issue
As you noticed onEdit does not trigger for edits performed by an API
You want to run your script hourly rather than onEdit
Solution
Use an installable timed-driven trigger instead of the onEdit trigger
This will allow to run your script hourly without any implementation of timestamps or similarfeatures in you code
Just keep in mind that time-driven triggers don't support event objects like e.g. e.range - since there might not be any range edited at the moment when the trigger fires
You need to rewrite your function in such a way that it is not reliant on event objects
Sample:
function bindATimedrivenTriggerToMe() {
var sh = SpreadsheetApp.getActive().getSheetByName("Copy of DataFinal");
var lastRow = sh.getLastRow();
var v = sh.getRange(1,4,lastRow,1);
var values =v.getValues();
var o = sh.getRange(1,5,lastRow,1);
var valueArray = [];
for (var i =0; i < values.length; i++){
var value = values[i][0];
valueArray[i] = [];
if (value == "") {
valueArray[i].push("");
} else {
var timedis = gettime(value);
valueArray[i].push(timedis);
}
}
o.setValues(valueArray);
}
function gettime(f) {
var res= UrlFetchApp.fetch("https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&origins="+f+"&key=MYKEY");
var content = res.getContentText();
var json = JSON.parse(content);
Logger.log(content);
var time=json.rows[0].elements[0].duration.text;
var dis=json.rows[0].elements[0].distance.text;
var timedis=time+"|"+dis
return timedis
}

Related

Search and return data using getRange() and getValue

I am trying to create a sheet (using Google Sheets) for our volunteers to search for, update, and/or add mentoring information (javascript).
I started with the option to search (function onSearch) and it appears to work but the information does not appear in the sheet (attached FYI). I'd appreciate help in making this run.
date entry sheet
REVISED:
function myFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form1");
var str = formSS.getRange("D3").getValues()[3]; //Search for info entered in Form1$D3
var datasheet = ss.getSheetByName("TRACKING");
var values = datasheet.getRange(2,1,2); //Datasheet where info will be retrieved
if (values == str) {
var values1 = values.getValues(); //// get the tracking data if it matchs search request
var i = 1;
myFunction().onSearch = i < values.length; i++;
{
var output = datasheet.getRange(); ///retrieve information from the Tracking spreadsheet and
//populate the information in the appropiate cells.
formSS.get("E8").datasheet.getValue(1),
formSS.getRange("E10").getdatasheet.getValue(2),
formSS.getRange("E12").datasheet.getValue(3),
formSS.getRange("E14").datasheet.getValue(4),
formSS.getRange("J8").datasheet.getValue(5),
formSS.getRange("J10").datasheet.getValue(6),
formSS.getRange("J12").datasheet.getValue(7),
formSS.getRange("J14").datasheet.getValue(8);
return }}}
function onSearch() {
var SEARCH_COL_IDX=0;
var RETURN_COL_IDX=0;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formSS = ss.getSheetByName("Form1");
`` var datasheet = ss.getSheetByName("TRACKING");
var str = formSS.getRange("D3").getValues()[3]; //column Index
var values = ss.getSheetByName("Form1").getDataRange().getValues();
for (var i = 0; i < values.length; i++) {
var row = values[i];
if (row[SEARCH_COL_IDX] == str) {
RETURN_COL_IDX = i+1;
var values = [[formSS.getRange("E8").datasheet.getValue(1),
formSS.getRange("E10").getdatasheet.getValue(2),
formSS.getRange("E12").datasheet.setValue(3),
formSS.getRange("E14").datasheet.getValue(4),
formSS.getRange("J8").datasheet.getValue(5),
formSS.getRange("J10").datasheet.getValue(6),
formSS.getRange("J12").datasheet.getValue(7),
formSS.getRange("J14").datasheet.getValue(8)]];
}
}
}
Thanks for responding. No one had the answer, and I even read that what I was asking is not available in Google Sheets. I decided to use the filter function for each cell instead.
B3 is the search field TRACKING!C:C is the sheet to retrieve the
information Tracking!E:E is the matched column to return information.
I am new here and at programming but I hope this helps someone.
=IFERROR(FILTER(TRACKING!C:C,TRACKING!E:E=B3),TRUE)

Trying to paste Values from formula Google App Script

This is just a snippet of my code from Google App Script which iterates through each row in columns 1, 2, 3. If an edit is made in column 3, an incremental ID will be generated and a concatenation of the same row and different columns will also be generated - in this case Column D, E, and F. I am struggling with figuring out a way to change the formulas into values. What am I missing here?
// Location format = [sheet, ID Column, ID Column Row Start, Edit Column]
var locations = [
["Consolidated Media Plan",1,9,3]
];
function onEdit(e){
// Set a comment on the edited cell to indicate when it was changed.
//Entry data
var range = e.range;
var col = range.getColumn();
var row = range.getRow();
// Location Data
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
function getNewID(){
function IDrange(){
var dataRange = sheet.getDataRange();
var lastRow = dataRange.getLastRow();
return sheet.getRange(IDrowStart,IDcol,lastRow-IDrowStart).getValues();
};
//Get largest Value in range
function getLastID(range){
var sorted = range.sort();
var lastIDval = sorted[sorted.length-1][0];
return lastIDval;
};
//Stores leading letters and zeroes and trailing letters
function getLettersNzeroes(id){
//Get any letters or zeroes.
var re = new RegExp("^([a-zA-Z0])$");
var letterZero = [];
for(char = 0; char < id.length; char++){
if(re.test(id[char])){
letterZero.push([char,id[char]]);// [[position, letter or zero]]
};
};
// Categorize letters and zeroes into start and end blocks
var startLetterZero = "",
endLetter = "",
len = letterZero.length - 1;
for(j = 0; j < letterZero.length; j++){
if(letterZero[j][0] === j){
startLetterZero += letterZero[j][1];
}else if(letterZero[j][1] !== "0" && letterZero[len][0] - (len - j) == letterZero[j][0]){
endLetter += letterZero[j][1];
};
};
var startNend = {"start":startLetterZero,"end":endLetter};
return startNend;
};
//Gets last id number. Adds 1 an checks to set if its new length is greater than the lastNumber.
function getNewNumber(id){
var removeZero = false;
var lastNum = parseInt(id.replace(/\D/g,''),10);//Remove letters
var newNum = (lastNum+1).toString();
if(lastNum.toString().length !== newNum.length){
var removeZero = true;
};
var newNumSet = {"num":newNum, "removeZero": removeZero};
return newNumSet
};
var lastID = getLastID(IDrange());
var lettersNzeroes = getLettersNzeroes(lastID);
var newNumber = getNewNumber(lastID);
//If the number is 9,99,999,9999 etc we need to remove a zero if it exists.
if(newNumber.removeZero === true && lettersNzeroes.start.indexOf("0") !== -1.0){
lettersNzeroes.start = lettersNzeroes.start.slice(0,-1);
};
//Rejoin everything together
var newID = lettersNzeroes.start +
newNumber.num +
lettersNzeroes.end;
return newID;
};
for(i = 0; i < locations.length; i++){
var sheetID = locations[i][0],
IDcol = locations[i][1],
IDrowStart = locations[i][2],
EditCol = locations[i][3];
var offset = IDcol - EditCol;
var cell = sheet.getActiveCell();
if(sheetID === sheet.getName()){
if(EditCol === col){
//ID Already Exists the editing cell isn't blank.
if(cell.offset(0,offset).isBlank() && cell.isBlank() === false){
var newID = getNewID();
cell.offset(0,offset).setValue(newID);
cell.offset(0,-1).setFormulaR1C1('=concatenate(R[0]C[-1],"_",INDEX(Glossary!K:K,MATCH(R[0]C[2],Glossary!J:J,0)))');
};
};
};
};
};
EDIT:
This is my full code, I have been unsuccessful with trying to retrieve just the values of the formula within the same (i.e, If C9 gets edited, a formula with the values specific to the 9th row should be populated)
Also, I've tried to add an index/match formula to the concatenation formula at the bottom of the code - it works as expected on the google sheets, but when I run it with the script it pastes the correct formula but it returns a #NAME? error message. However, when I copy and paste the exact same formula in the cell, it works perfectly, any idea what could be causing this error?
This works for me. I know it's not exactly the same thing but I didn't have access to getNewId()
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!='Sheet1')return;
//e.source.toast('flag1');
if(e.range.columnStart==3 && e.range.offset(0,1).isBlank() && e.value) {
//e.source.toast('flag2');
e.range.offset(0,1).setValue(e.value);
e.range.offset(0,2).setFormulaR1C1('=concatenate(R[0]C[-1],"_",R[0]C[-2],"_",R[0]C[-3],"_",R[0]C[-4])');
}
}

How to retrieve data from columns dependent upon date

I have a rota (a fixed order of rotation (as of persons or duties)) that I've already had help with this week. It's up & running as is, but for simpler reading I'd like to transpose it.
You can see the transposed sheet as I'd like it here
The current script is for the pre-transposed table.
It would search Column 0 for the date. If it was was 7 days away it would retrieve the name from Column 1 & match it with e-mail address in separate sheet etc.
What I'd like to do is instead have the Date in Row 0 & then subsequent names in Row 1 etc etc
I've tried various things. I've stepped through the code & can see what it's doing, & I've done some reading through about 2 dimensional arrays, but I can't seem to find a way of getting the code to work down through columns, instead of across the rows.
Here's the code:
function sendEmails() {
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
// Fetch the range
var dataRange = sheet.getRange("B3:G50")
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {//only e-mail people with one week to go. To change that alter the "7" to the number of days you want
var subject = "Rota reminder!";
var emailAddress = [];
var message;
message = "Hello \n\n"+
"You are down to help at Youth Café this week. \n\n" +
"Please see the below rota for your role \n\n" +
"If you have any questions or problems let us know at thameyouthcafe#gmail.com \n\n" +
"Remember, you can check the rota anytime by clicking on the link below: \n\n"+
rotalink
for (var x = 1; x < 5; x++) { // 5 because emails are till col4
// var emailAddress = []; // Start by collecting the non-blank emails in an array
if (getEmailFromName(row[x]) != "") {
emailAddress.push(getEmailFromName(row[x]))
}
}
emailAddress = emailAddress.join(); // Join the array to get a comma separated string
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
and here's the getEmailFromName function that matches with SKey (which I presume comes from the "i" variable in the first function?
function getEmailFromName(sKey) {
// to use this function, don’t put anything in the first column (A) or row (1).
// Put the name (i.e. the key, or what we’re looking for) in column B.
// Put what we want to return in column C.
var columnToSearch = 1; //column B
// Set the active sheet to our email lookup
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss1.getSheetByName("EmailContactList")
ss1.setActiveSheet(sh1);
var data = SpreadsheetApp.getActiveSheet().getDataRange().getValues();
var line = -1;
for( var i = 0; i < data.length; i++ ) {
if( data[i][columnToSearch] == sKey ) {
line = i;
break;
}
}
if( line != -1 ) {
//do what you want with the data on "line"
return data[line][2]; //value on column C of the matched line
}
else {
return "";
// if criteria is not found
}
}
Try it this way:
function sendEmails() {
var ss1 = SpreadsheetApp.getActive();
var sh1 = ss1.getSheetByName("Rota")
ss1.setActiveSheet(sh1);
var rotalink = "https://docs.google.com/spreadsheets/d/1LgzUWSAGA2kbpar8r5nosU1bSHF7nrtvtUiHS3nB_e8";
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange("B3:G50")
var data = dataRange.getValues();
for (var i=0;i<dataRange.length;i++) {
var row = data[i];
var today=new Date();
var timecell = new Date(row[0]);
var timediff = new Date();
var one_day=1000*60*60*24;
var daystogo = Math.ceil((timecell.getTime()-today.getTime())/(one_day));
if (daystogo==7) {
var subject = "Rota reminder!";
var emailAddress = [];
var message = Utilities.formatString('Hello\n\nYou are down to help at Youth Café this week.\n\n Please see the below rota for your role \n\nIf you have any questions or problems let us know at thameyouthcafe#gmail.com \n\nRemember, you can check the rota anytime by clicking on the link below: \n\n%s',rotalink);
for (var x=1;x<5;x++) {
if(data[i][x]) {
emailAddress.push(data[i][x]);
}
}
MailApp.sendEmail(emailAddress.join(), subject, message);
}
}
}
Managed to solve it - thank you for your contributions. Turned out it was incredibly simple.
Just had to change this line:
var timecell = new Date(data[0])
to this:
var timecell = new Date(data[0][i])
so it iterates through the first row of each column.

How to control for loop to wait till complete a task in each loop

I am setting validation in each cell in a column base on the value in the cell in column before. My script works as I want if it runs for each cell one by one (no loop). Setting validation in the cell by script and to see the effect take a second or so (I mean obvious slowness). When I put the code into the for loop it did not complete the task on some cells, I think due to the time required to finish applying the validation setting on a cell. How can I control the for loop to loop next only after the validation setting take effect in the current cell?
I am trying to add dependent dropdown in each cell.
I adopted the code from /http://www.chicagocomputerclasses.com/google-sheets-apps-script-dynamic-dependent-dropdown-data-validation-lists/
//google scripts for google spreadsheet
function addDropDown(){
var tabLists = "Lists";
var tabValidation = "DATA";
var ss = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var datass = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabLists);
var datass0 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabValidation);
function flatten(arrayOfArrays){
return [].concat.apply([], arrayOfArrays);
}
var dists_in_data = datass0.getRange(5, 2,datass0.getLastRow()).getValues(); //Admin1 in data
var dists_in_list = datass.getRange(2, 9,datass.getLastRow()).getValues(); //Admin1 in Lists
dists_in_list = flatten(dists_in_list);
dists_in_data = flatten(dists_in_data);
for (var i in dists_in_data){
//i = 0
var search_key = dists_in_data[i];
var distStart = dists_in_list.indexOf(search_key) + 1;
dists_in_list.reverse();
var len = dists_in_list.length;
var distEnd = len - (dists_in_list.indexOf(search_key) + distStart - 1);
dists_in_list.reverse();
//preparing validation list for admin2
if(distStart != 0){
var validationRange = datass.getRange(distStart + 1, 10, distEnd );
var validationRule = SpreadsheetApp.newDataValidation().requireValueInRange(validationRange).build();
datass0.getRange(i + 5, 3).setDataValidation(validationRule);
}
}
}
I expect all cells should have been set with new validation rule.

Declaring Global Array in Google Spreadsheet

I am trying speedup the processing/calculation in Google spreadsheet by putting my bonus setting into global array using PropertiesServices instead of loading the bonus % from the Setting sheet whenever user update their Daily sheet.
The method I used seem not working as I expected, the FOR loop is unable to look for "ALL". Hope someone able to give some advice.
I suspected that JSON structure is different from my 2D array, but I don't know how to solve it, I am new to javascripts.
Setting sheet only containing 4 columns: Game, StartDate, EndDate, Bonus %
p/s: This is a simplified scripts.
function onOpen(event) {
var bonusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setting");
var bonusCompany = bonusSheet.getRange("A46:D503").getValues();
PropertiesService.getDocumentProperties().setProperty("bonusCompany", JSON.stringify(bonusCompany));
}
function onEdit(event) {
var colGame = "H";
var colBonus = "K";
var numColBonus = 11;
var nPoint;
var sheet = event.source.getActiveSheet();
var row = sheet.getActiveCell().getRow();
var col = sheet.getActiveCell().getColumn();
var cGame = sheet.getRange(colGame+row).getValue();
var dDate = new Date();
for(var k=0, kLen=bonusCompany.length; k<kLen; k++)
{
if((bonusCompany[k][0] == cGame || bonusCompany[k][0] == "ALL") && dDate.valueOf() >= bonusCompany[k][1] && dDate.valueOf() <= bonusCompany[k][2] ){
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(nPoint*bonusCompany[i][4]/100);
return;}
}
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(0);
}
If you want to use PropertiesService.getDocumentProperties(), how about following sample script?
Your script is almost correct. When the data is set by PropertiesService.getDocumentProperties().setProperty(), the data can be retrieved by PropertiesService.getDocumentProperties().getProperty(). The detail information is https://developers.google.com/apps-script/reference/properties/properties-service.
And when the array data is set by setProperty(), the array data is stored as strings by JSON.stringify(). So it is necessary to convert from the string data to the array data. Fortunately, it has already known that the values retrieved by getValues() from spreadsheet is 2 dimensional array. So the string data can be converted to an array data using the regular expression. The sample script is as follows. I added 2 lines to your script. Please check them.
Script :
function onOpen(event) {
var bonusSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Setting");
var bonusCompany = bonusSheet.getRange("A46:D503").getValues();
PropertiesService.getDocumentProperties().setProperty("bonusCompany", JSON.stringify(bonusCompany));
}
function onEdit(event) {
var colGame = "H";
var colBonus = "K";
var numColBonus = 11;
var nPoint;
var sheet = event.source.getActiveSheet();
var row = sheet.getActiveCell().getRow();
var col = sheet.getActiveCell().getColumn();
var cGame = sheet.getRange(colGame+row).getValue();
var dDate = new Date();
var d = PropertiesService.getDocumentProperties().getProperty("bonusCompany").split(/^\[|\]$/)[1].split(/\[(.*?)\]/); // I added this line.
var bonusCompany = [[(e=="\"\"" ? "" : e) for each (e in f)] for each (f in [d[j].split(",") for (j in d) if(j%2==1)])]; // I added this line.
for(var k=0, kLen=bonusCompany.length; k<kLen; k++) {
if((bonusCompany[k][0] == cGame || bonusCompany[k][0] == "ALL") && dDate.valueOf() >= bonusCompany[k][1] && dDate.valueOf() <= bonusCompany[k][2] ){
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(nPoint*bonusCompany[i][4]/100);
return;
}
}
sheet.getRange(event.range.rowStart,numColBonus,event.range.rowEnd-event.range.rowStart+1,1).setValue(0);
}
I don't know data of your spreadsheet. So if this modification doesn't work, can I ask you about the data of your spreadsheet?

Categories

Resources