Google Apps Script: Email Reminder Have Time Pulled from Column Values - javascript

I'm trying to create a Google Sheets setup with a script that will create calendar events and set reminder times for those events.
I have everything working with one exception: I want to be able to have a column "Reminder Time" where each cell in that column can have a different reminder time to correspond to a given calendar entry (So for example, for Type A, a calendar event will be created with a reminder 3 days in advance, but for Type B, the calendar event will be created 6 days in advance, so in the "Reminder Time" column, the first cell would be 4320 and the second cell would be 8640 (times in minutes)).
My problem is that Type A and Type B are both being assigned the first value in the "Reminder Time" column (The reminder time column is column 8).
function pushToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
//Define reminder, THIS IS WHERE MY PROBLEM SEEMS TO LIE
var reminder = sheet.getRange(2, 8).getValue();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2, 1, lastRow, 5);
var values = range.getValues();
var calendar = CalendarApp.getCalendarById('joncodle9gk4#group.calendar.google.com')
var numValues = 0;
for (var i = 0; i < values.length; i++) {
if ((values[i][0].length > 0) && (values[i][3].length > 0)) {
if (values[i][4] != 'y') {
var newEventTitle = 'Note Due: ' + values[i][0] + ' - ' + values[i][3];
var newEvent = calendar.createAllDayEvent(newEventTitle, values[i][2]);
newEvent.addEmailReminder(reminder);
var newEventId = newEvent.getId();
sheet.getRange(i + 2, 5).setValue('y');
sheet.getRange(i + 2, 6).setValue(newEventId);
}
}
numValues++;
}
}

Your "reminder" is only ever read from H2, rather the row for that event. So you need to move your
var reminder = sheet.getRange(2,8).getValue();
into your row loop:
var reminder = sheet.getRange(i+1,8).getValue();

Related

Google sheet script auto add new rows with copied formulas

I need to create function which adds rows with copied formulas from above rows. After the script is launched it should result in accurate number (set 5 i this code) of blank rows at the end of the sheet.
The code I managed to create counts what number of rows should be added but adds only one row with copied formulas at the end.
Please help me edit this code to multiple the result of the function by "rowstoadd" parameter.
Sheet image
function autoaddRows() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Harmonogram');
var range = sheet.getRange("B2:B").getValues();
var lastRowB = range.filter(String).length + 2;
var lastRowA = sheet.getLastRow();
var blanknrows = sheet.getLastRow() - lastRowB;
if (blanknrows < 5) {
let rowstoadd = 5 - blanknrows;
Browser.msgBox(rowstoadd);
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let lastRowIndex = sheet.getLastRow();
let existingRange = getRowRange(sheet, lastRowIndex);
sheet.insertRowAfter(lastRowIndex);
let newRange = getRowRange(sheet, ++lastRowIndex);
existingRange.copyTo(newRange);
newRange.setFormulas(newRange.getFormulas());
newRange.clearNote();
}
function getRowRange(sheet, rowIndex) {
return sheet.getRange(rowIndex, 1, 1, sheet.getLastColumn());
}
}
I believe your goal is as follows.
You want to keep 5 new empty rows.
For example, when the last row of column "A" is 10 and the last row of column "B" is 9, you want to add 4 new rows.
Also, you want to put the formula to the column "B" of the inserted rows.
In this case, how about the following modified script?
Modified script:
function autoaddRows() {
var addRows = 5;
var sheet = SpreadsheetApp.getActive().getSheetByName('Harmonogram');
var range = sheet.getRange("B2:B").getValues();
var lastRowB = range.filter(String).length + 1;
var lastRow = sheet.getLastRow();
var blanknrows = lastRow - lastRowB;
var diff = addRows - blanknrows;
if (diff > 0) {
sheet.insertRowsAfter(lastRow, diff);
// var range = sheet.getRange("B" + lastRowB);
// range.copyTo(range.offset(1, 0, diff + 1), SpreadsheetApp.CopyPasteType.PASTE_FORMULA, false);
var numberOfCol = sheet.getLastColumn() - 1;
var range = sheet.getRange(lastRowB, 2, 1, numberOfCol);
range.copyTo(range.offset(1, 0, diff + 1, numberOfCol), SpreadsheetApp.CopyPasteType.PASTE_FORMULA, false);
range.clearNote();
}
}
In this modification, the difference between the last rows between column "A" and column "B" is retrieved. And the empty rows are inserted using the difference.
In your script, newRange.clearNote(); is used. So, I also add range.clearNote();. If you want to remove it, please remove it.
References:
insertRowsAfter(afterPosition, howMany)
copyTo(destination, copyPasteType, transposed)

Google Sheets - Moving row to another sheet based on specific column

Very new to the Google Sheets script editing, but I'm trying to move rows between sheets. I've done this successfully using getLastRow however now I'm attempting to do this based on a specific column. The reason for this is that the sheet the info is heading to has preset data validation fields and checkboxes, resulting in the row going to row 1000 every time.
I'm looking for when an option is selected on the dropdown menu on the current sheet, it will find the first free row based on a certain column, and replace that row (which is essentially empty except for the preset fields).
Currently I have as follows:
function onEdit(event) {
// Move rows to different sheets based on action selected
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = SpreadsheetApp.getActiveRange();
if(range.getColumn() == 5 && range.getValue() == "Active") {
var row = range.getRow();
var numColumns = sheet.getLastColumn();
var targetSheet = spreadsheet.getSheetByName("Active");
var target = targetSheet.getRange(targetSheet.getLastRowSpecial(8) + 1, 1);
sheet.getRange(row, 1, 1, numColumns).moveTo(target);
sheet.deleteRow(row);
and I found the below and other examples when googling but nothing seems to work:
function getLastRowSpecial(column){
// Get the last row with data for the whole sheet.
var numRows = targetSheet.getLastRow();
// Get all data for the given column
var data = targetSheet.getRange(1, column, numRows).getValues();
// Iterate backwards and find first non empty cell
for(var i = data.length - 1 ; i >= 0 ; i--){
if (data[i][0] != null && data[i][0] != ""){
return i + 1;
}
}
}
Any help is greatly appreciated!
function onEdit(e) {
//Logger.log(JSON.stringify(e));
//e.source.toast(e.range.columnStart, e.value)
var sh=e.range.getSheet();
if(e.range.columnStart==8 && e.value=="Active") {
var tsh=e.source.getSheetByName("Active");
var target=tsh.getRange(getColumnHeight(8,tsh,e.source) + 1, 1);
sh.getRange(e.range.rowStart,1,1,sh.getLastColumn()).moveTo(target);
sh.deleteRow(e.range.rowStart);
}
}
function getColumnHeight(col,sh,ss){
var ss=ss||SpreadsheetApp.getActive();
var sh=sh||ss.getActiveSheet();
var col=col||sh.getActiveCell().getColumn();
var s=0;
var h=0;
if(sh.getLastRow()) {
var v=sh.getRange(1,col,sh.getLastRow(),1).getValues().map(function(r){return r[0];});
v.forEach(function(e,i){if(e==''){s++;}else{s=0;}h++;});
}
return (h-s);
}

How to take user input to find a row number and apply all data in that row to a doc template?

I am working on a script that allows a user to automatically take data from a spreadsheet and apply it to a Google Docs template.
My createTemplate() function individually takes each row in a sheet and applies it to a doc template. The function will do this for each row in the active sheet.
I have another function, chooseRow(), that allows the user to search for a tagNo. through a prompt.
The tagNo. is a unique identifier for a machine that is stored in the spreadsheet.
Each machine has its own tagNo, and user will always know the tagNo.
This function is logging the row number for me, but I cannot apply the same code as the createTemplate() function to actually create the document.
I have tried applying the code from createTemplate() to chooseRow() to hopefully get the result I am looking for, but it just ended doing the exact same as createTemplate().
I applied the code to the inside of the for loop used in chooseRow().
function chooseRow(){
var ui = SpreadsheetApp.getUi(); // Same variations.
var result = ui.prompt('Please enter the Tag number of the row you wish to print.', ui.ButtonSet.OK_CANCEL);
// Process the user's response.
var button = result.getSelectedButton();
var response = result.getResponseText();
if (button == ui.Button.OK) {
// User clicked "OK".
ui.alert('Your name is ' + response + '.');
} else if (button == ui.Button.CLOSE) {
// User clicked X in the title bar.
ui.alert('You closed the dialog.');
}
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
}
}
}
}
function createTemplate(){
var sleepInt = 0;
var templateId = "1uSAcH8F21zEjuprIcE2_d84ojQT24ek85Y1W6L17Xno"; //Template ID (Taken from address bar)
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.toast("starting");
Utilities.sleep(sleepInt);
var sheet = ss.getActiveSheet();
//starting with row 2 and column 1 as our upper-left most column, get values from cells from 1 row down, and 15 columns along - hence (2,1,1,15)
var data = sheet.getRange(2, 2, 11, 18).getValues();
ss.toast("created document and adding data");
Utilities.sleep(sleepInt);
//Get the title and tag number columns ready. Also get todays date
//Keeping this block above the for loop allows the code to run faster as it doesn't have to do a .getRange or recalculate the date every time the loop iterates
var docTitle = sheet.getRange(2, 2, 11, 1).getValues();//this is grabbing the data in field B2
var docTitleTagNumber = sheet.getRange(2, 3, 11, 1).getValues();
var today = new Date();
var dd = today.getDate();
var mm = today.getMonth() + 1;
var yyyy = today.getFullYear();
today = dd + '/' + mm + '/' + yyyy;
//apply data to template
for(var i in data){
var row = data[i];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
body.replaceText("%TAG%", row[1]);
body.replaceText("%CATEGORY%", row[2]);
body.replaceText("%DESCRIPTION%", row[3]);
body.replaceText("%AREA%", row[4]);
body.replaceText("%SERIALNO%", row[5]);
body.replaceText("%MODEL%", row[6]);
body.replaceText("%MANUAL%", row[7]);
body.replaceText("%HOOKUP%", row[8]);
body.replaceText("%WEB%", row[9]);
body.replaceText("%CONNECTED%", row[11]);
body.replaceText("%CALIBRATED%", row[12]);
body.replaceText("%AUTOMATED%", row[13]);
body.replaceText("%SAT%", row[14]);
body.replaceText("%SIGNED%", row[16]);
doc.saveAndClose();
ss.toast("added data");
Utilities.sleep(sleepInt);
//Copy the modified template into the specific folder, then delete the first copy we made (to modify it)
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("16wRGBVdV0OZ5YfKhqEQSFMsux-ekGCCa");
newFolder.addFile(file);
ss.toast("finished");
Utilities.sleep(sleepInt);
//uses i to iterate through each row in the first column
var newDocTitle = docTitle[i][0];
var newDocTagNumber = docTitleTagNumber[i][0];
//apply document names, tag numbers, and dates
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
}
}
The expected output is one document containing all of the data from the row that contains the tagNo. that the user entered into the prompt, but the results I am getting are the same as createTemplate().
The problem is I was taking the entire for loop in createTemplate() and applying it inside the if statement in chooseRow(). This can be solved by taking the following code in createtemplate(),
var row = data[i];
var docId = DriveApp.getFileById(templateId).makeCopy().getId();
var doc = DocumentApp.openById(docId);
var body = doc.getActiveSection();
body.replaceText("%SITEID%", row[0]);
body.replaceText("%TAG%", row[1]);
body.replaceText("%CATEGORY%", row[2]);
body.replaceText("%DESCRIPTION%", row[3]);
...
body.replaceText("%AUTOMATED%", row[13]);
body.replaceText("%SAT%", row[14]);
body.replaceText("%SIGNED%", row[16]);
doc.saveAndClose();
ss.toast("added data");
Utilities.sleep(sleepInt);
//Copy the modified template into the specific folder, then delete the first copy we made (to modify it)
var file = DriveApp.getFileById(doc.getId());
var newFolder = DriveApp.getFolderById("16wRGBVdV0OZ5YfKhqEQSFMsux-ekGCCa");
newFolder.addFile(file);
ss.toast("finished");
Utilities.sleep(sleepInt);
//uses i to iterate through each row in the first column
var newDocTitle = docTitle[i][0];
var newDocTagNumber = docTitleTagNumber[i][0];
//apply document names, tag numbers, and dates
doc.setName(newDocTitle + " " + newDocTagNumber + " " + today);
along with the appropriate variables, and pasting it into the if statement here, beneath Logger.log(i);
for(var i = 0; i < values.length; i++){
for(var j = 0; j < values[i].length; j++){
if(values[i][j] == response){
Logger.log(i);
}
}
}

Trigger email alert based on last row's condition NOT WORKING

I attempted to build a script but there are some issues. The table format are 2 columns which are date and values. These are the needs:
IDEAL STATE
Grab the last filled row (today's date) in the Google Sheets called "test".
Check in that row if the value in column F is greater than 0.5.
If it greater than 0.5, then trigger an email.
In email body, it should state "Results found on [date]."
This was my starting point but it does not produce what I want. These are the issues:
CURRENT STATE
1.The script grabs every row in which column F was greater than 0.5 in the past. I only want to check for today (which would be the last row). It should not look through everything in the past.
2.The email body states: Result found on [row number]". This makes no sense. I want the date to show, not the row number.
This is the current code. Please help.
function readCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("test");
var values = sheet.getRange("F3:F").getValues(); //this has the values
var date = sheet.getRange("D3:D").getValues(); // this has the date
var results = [];
for(var i=0;i<values.length;i++){
if(values[i]>=0.5)
{
results.push("Result found on:" +(i+3));
}
}
MailApp.sendEmail('blabla#gmail.com', 'Alert', results.join("\n"));
};
Last Row in this context is Row 217, not 218, assuming sheet.getLastRow() would ignore #DIV/o! values. See screenshot for this.
LATEST UPDATE
The current Error is related "toDateString". I think it may be related that my Google Sheet is one day behind. So, it today is Jan 10, the last row in my Google Sheet is Jan 9th. I think that is why the error happens. Can you confirm? In that case, how do I change it to today-1 day?
See below.
Here's how you can check the last row:
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var lastRow = sheet.getLastRow();
var value = sheet.getRange('F' + lastRow).getValue();
var date = sheet.getRange('D' + lastRow).getValue();
if (value >= 0.5) {
var result = 'Result found on: ' + date;
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};
After seeing your data, I think the code below would suit you better.
function readCell() {
var sheet = SpreadsheetApp.getActive().getSheetByName('test');
var dates = sheet.getRange('D1:D').getValues();
var date = null;
var dateRow = 0;
var dateCount = dates.length;
var yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
var yesterdayString = yesterday.toDateString();
for (dateRow; dateRow < dateCount; ++dateCount) {
date = dates[dateRow];
if (date instanceof Date) {
date = date.toDateString();
if (date === yesterdayString) {
++dateRow;
// To account for zero-based array
break;
}
}
}
var value = sheet.getRange('F' + dateRow).getValue();
if (value >= 0.5) {
var result = 'Result found on: ' + date;
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};

Average of Numbers in Column If Condition In Different Column Satisfied

I have a data range of which the first column are dates and the second column are numerical values. There are some rows where the second column value is blank and I do not want them to be counted.
I am trying to find the average of the second column values if they satisfy the criteria of being within 3 months ago from today (blank values should not be counted).
But I am stuck as I cannot even get the total correct. And I do not know how to proceed further to get the average.
The code belows seem to give me appended strings instead of summing up the numbers mathematically.
Can anyone help please?
Thanks in advance.
function average() {
// open spreadsheet
var spreadsheet = SpreadsheetApp.openById("spreadsheetID");
// set the named sheet as active
var sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName("SheetName"));
// figure out what the last row is
var lastRow = spreadsheet.getSheetByName("Form responses 1").getLastRow();
// the rows are indexed starting at 1, and the first row is the headers, so start with row 2
var startRow = 2;
// get the data range
var responsesValues = spreadsheet.getSheetByName("Form responses 1").getRange("A1:Q" + lastRow).getValues();
// define the dates
var timeToday = new Date().getTime();
var dateToday = new Date().getDate();
var date = new Date();
var threeMonthsAgo = new Date(date.getFullYear(), date.getMonth() - 3, 0);
// grab column 1 (date of entry column) (second variable in getRange function below)
var dataRange = sheet.getRange(2,1,lastRow-startRow+1,1 );
var numRows = dataRange.getNumRows();
var dateOfEntryValues = dataRange.getValues();
// grab column 2 (values to be averaged)
range = sheet.getRange(2, 2, lastRow-startRow+1, 1);
var Values = range.getValues();
var warning_count = 0;
var sumValues = 0;
// Loop over the values
for (var i = 0; i <= numRows - 1; i++) {
var dateOfEntry = dateOfEntryValues[i][0];
if(dateOfEntry > threeMonthsAgo && dateOfEntry !== "") {
// if it's within 3 months ago, add the values.
sumValues += Values[i][0];
warning_count++;
}
}
There is a lot more simple version. Just put into the Destination Cell the formula
=ArrayFormula(AVERAGE(IF(DATEDIF(C1:C;TODAY();"D")<=90;E1:E)))
replacing C1:C with the column with dates and E1:E with the column with numbers.

Categories

Resources