Email notification based on date and name - google-apps-script

hoping someone can steer me in the right direction.
I have a schedule sheet and a contact sheet.
The schedule sheet looks something like this
4/11/16 John Front Desk
4/11/16 Chris Back Room
4/11/16 John Stage
4/11/16 Sarah Front Desk
4/12/16 Joe Back Room
4/12/16 Alex
The contact sheet looks something like this
John john#gmail.com
Chris chris#gmail.com
Alex
Sarah sarah#gmail.com
Joe
So basically the script, will go through the sheet, and if date in the date column is 3 days away from today, it will email a reminder to the person who is assigned a task that day. It will compare the name on the schedule sheet to the name in the contact sheet to find the email address and add it to the emailadd variable.
It works, but due to my limited knowledge in scripting, I feel I am doing this extremely ineffectively. Additionally, when for example John has three duties on the same day, he'll get three separate emails instead of one.
Also, some folks don't have email address, so the script will still attempt to email the message, fail and move on to the next one. Though that's totally fine for me since it's no more than 10-12 emails per night, I feel that there is a more effective way to do this. Any assistance would be really appreciated!
Here is the code
function checkReminder() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(spreadsheet.getSheetByName('Notification'));
var schedulesheet = spreadsheet.getActiveSheet();
var lastRow = schedulesheet.getLastRow();
var contactspreadsheet = SpreadsheetApp.getActiveSpreadsheet();
SpreadsheetApp.setActiveSheet(contactspreadsheet.getSheetByName('Contacts'));
var contactsheet = contactspreadsheet.getActiveSheet();
var conlastRow = contactsheet.getLastRow();
var startRow = 2;
//Grab column of scheduled dates and number of rows in the schedule sheet
var schedulerange = schedulesheet.getRange(2,2,lastRow-startRow+1,1 );
var numRows = schedulerange.getNumRows();
var scheduledassigneddate = schedulerange.getValues();
var scheduledformatteddate = Utilities.formatDate(new Date(scheduledassigneddate), "GMT-4:00", "MM/dd/YYYY");
//Grab column of scheduled names in the schedule sheet
var schedulenamerange = schedulesheet.getRange(2, 3, lastRow-startRow+1, 1);
var schedulednames = schedulenamerange.getValues();
//Grab column of assignments
var scheduleassignmentrange = schedulesheet.getRange(2, 4, lastRow-startRow+1, 1);
var assignmentrange = scheduleassignmentrange.getValues();
//Grab column of names and number of rows in the contact sheet
var contactrange = contactsheet.getRange(2,2,conlastRow-startRow+1,1 );
var contactname = contactrange.getValues();
var numRows2 = contactrange.getNumRows();
//Grab column of email addresses in the contact sheet
var contactemailrange = contactsheet.getRange(2, 3, conlastRow-startRow+1, 1);
var assignedemail = contactemailrange.getValues();
// Format today's date to match lists of dates in schedule sheet
var today = new Date();
today = today.setDate(today.getDate()+3);
var threedaysaway = Utilities.formatDate(new Date(today), "GMT-4:00", "MM/dd/YYYY");
var emailadd ="";
// Loop over the column of date values in the schedule sheet
for (var i = 0; i <= numRows - 1; i++) {
var scheduleddate = scheduledassigneddate[i][0];
var scheduledformatteddate = Utilities.formatDate(new Date(scheduleddate), "GMT-4:00", "MM/dd/YYYY");
//if the scheduled date is three days away, start associating names to email.
if(scheduledformatteddate == threedaysaway)
{
var notify_name = schedulednames[i][0];
for (var j = 0; j <= numRows2 - 1; j++) {
// Set email variables to null
var email_value = 0;
var msg = "";
//compare names falling under date that is 3 days away from today
var comparenames = contactname[j][0];
var assignment = assignmentrange[i];
if(notify_name == comparenames)
//if the name in the schedule sheet matches the name in the contact sheet,
//grab the email address in the next colum "assignedemail" and add it to var emailadd.
emailadd = emailadd + assignedemail[j] +",";
assignment = assignmentrange[i];
}
msg = msg + "Hello "+notify_name+", \n\nYou are scheduled to cover " + assignment + " on " +threedaysaway;
email_value++;
Logger.log(emailadd);
Logger.log(msg);
//if email_value is not null, send email. Loop back to stop and start over until loop is finished.
if(email_value) {
MailApp.sendEmail(emailadd,
"Reminder Email", msg);
}
emailadd = "";
assignment = "";
}
}
};

You can modify your approach to this:
Filter data with dates falling under the condition for notification.
Filter Names to remove duplicate.
Prepare sending notification function.
Search for the tasks under one person and concatenate it to the email body. To search for a specific value of a row here is an SO ticket that will guide you.
Here is a sample code:
function findCell() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var dataRange = sheet.getDataRange();
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
var row = "";
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == "User") {
row = values[i][j+1];
Logger.log(row);
Logger.log(i); // This is your row number
}
}
}
}
Send Notification with the prepared email body.
NOTE:
DATE NAME TASK
4/11/16 John Front Desk
That is the sample format for the task sheet.

Related

App Script - Send Email based on days left to date - Can't make script send it X days left to date

I am using this spreadsheet Here
I am trying to send an email to a specific recipient "email#myemail.com" when there are 3 days left for a specific date.
Each row represents a person and I want the email sent to send me an email for each person whose date of offboarding - date of today is exactly 3 days.
This is my output: Email received
This is the script i am using:
function Offboarding_Reminder() {
// get the spreadsheet object
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set the first sheet as active
SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[0]);
// fetch this sheet
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
// the rows are indexed starting at 1, and the first row
// is the headers, so start with row
var startRow = 2;
// grab column 11 (the 'days left' column)
var range = sheet.getRange(2,12,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var days_left_values = range.getValues();
// Now, grab the user name column
range = sheet.getRange(2, 1, lastRow-startRow+1, 1);
var reminder_name = range.getValues();
var warning_count = 0;
var msg = "";
// Loop over the days left values
for (var i = 0; i < numRows - 1; i++) {
var days_left = days_left_values[i][0];
if(days_left == 3) {
// if it's exactly 3, do something with the data.
var user_name = reminder_name[0][i];
msg ="Reminder:"+reminder_name+" offboarding is due in "+days_left+" days.\n";
warning_count++;
}
}
if(warning_count) {
MailApp.sendEmail("email#myemail.com","Reminder Offboarding",msg)
}
};
Two things I don't understand:
Why is my sent email giving me all the user emails instead of just the ones that have days_left == 3?
To automate this script, once it works, I should just add a trigger to the appscript?
Thanks for the help!
I actually revised your code since it wasn't optimized. It had a lot of redundant and unnecessary lines.
Here are the changes:
fetched the range from user column to days_left column once, not separately
created array to store users matching the condition
formatted the email to look better.
Code:
function offboardingReminder() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// set active sheet to first sheet
spreadsheet.setActiveSheet(spreadsheet.getSheets()[0]);
var sheet = spreadsheet.getActiveSheet();
// figure out what the last row is
var lastRow = sheet.getLastRow();
var startRow = 2;
// grab all data from user to days left
var range = sheet.getRange(startRow, 1, lastRow - startRow + 1, 12);
var values = range.getValues();
var users = [];
// loop all data per row
values.forEach(function(row) {
// if days left is 3
if(row[11] == 3) {
// add user if 3 days left
users.push(row[0]);
}
});
// if users has elements
if(users) {
// Formatted the message as html to look nicer
var message = "<html><body><h1>Reminder!!!</h1><p>The following user/s offboarding is due in 3 days:</p>";
// created bulleted list for list of users
var emails = "<ul>";
users.forEach(function(user){
emails = emails + "<li>" + user + "</li>";
});
emails += "</ul>";
message = message + emails + "</body></html>";
MailApp.sendEmail("email#myemail.com", "Reminder Offboarding", "", {htmlBody: message, noReply: true});
}
}
Sample data:
Sample output:
Note:
You can calculate the days left in script instead, but if it is already needed in sheets, then reusing it would be fine.
I populated the days left column via formula =DAYS(C2,TODAY()) for row 2 (dragged for other rows)

Sending out emails based on date in Google Sheets Scripts

I'm trying to create a Script that will send an email for every cell that contains today's date. Here's what I have so far:
function email() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var rowCounter = 1;
var limit = sheet.getLastRow();
// Fetch the estimated dates & Today's date
var estimatedReturnDateRange = sheet.getRange("F:F");
var estimatedReturnDate = estimatedReturnDateRange.getCell(rowCounter, 1);
var todayDateRange = sheet.getRange("S1");
var todayDate = todayDateRange.getValue();
// Check totals sales
for(i=1; i<=limit; i++){
if (estimatedReturnDate = todayDate){
// Fetch the email address
var emailAddress = "maxbkimmel#gmail.com";
// Send Alert Email.
var message = estimatedReturnDate; // Second column
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
rowCounter++;
estimatedReturnDate = estimatedReturnDateRange.getCell(rowCounter, 1);
}
rowCounter =1;
}
This is how I envision the logic of the script working:
estimatedReturnDate initially grabs the first cell in column F, which is a list of dates.
todayDate grabs cell S1, which contains today's date.
a for loop then loops through all rows of the sheet, and checks if estimatedReturnDate = todayDate.
If it does, an email is sent that contains the Row Number that matched today's date.
Then, rowCounter is incremented, estimatedReturnDate is set to the next cell in the row, and the loop runs again.
The problem I'm having is that when I run this script, an email is sent out for each row in the sheet, regardless of whether estimatedReturnDate matches todayDate or not.
Does anyone know what would be causing this?
function email() {
var ss=SpreadsheetApp.getActive();
var sheet=ss.getSheets()[0];//This is always the left most sheet but not necessarily the same sheet depending how users move the sheets around.
var vA=sheet.getRange(1,5,sheet.getLastRow(),1).getValues()
var dt=new Date();
var toda=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()).valueOf();//midnight yesterday
var tmro=new Date(dt.getFullYear(),dt.getMonth(),dt.getDate()+1).valueOf();//midnight today
for(var i=0;i<vA.length;i++){
var dt=new Date(vA[i][0]).valueOf();//date from column5 of spreadsheet
//dt is between midnight yesterday and midnight today
if(dt>=toda && dt<=tmro){
var emailAddress = "maxbkimmel#gmail.com";
var message = Utilities.formatDate(dt, Session.getScriptTimeZone(), "E MMM dd, yyyy");
var subject = 'Your Google Spreadsheet Alert';
MailApp.sendEmail(emailAddress, subject, message);
}
}
}
Utilities.formatDate
Date Class

How do you set up automatic email alerts triggered by date

I am struggling to write an apps script to trigger automatic emails based on a date. I have reviewed other questions/answers but can't get it right. My Google Sheet is below:
Google Sheet
My alert data is in the sheet called H&S Reviews.
My email message is in cell A1 of the Email Alerts Sheet.
The trigger date to send an email is column J of the H&S Reviews sheet and I thought that the code could be written against this and cell J1 which is today's date.
Column J = date email to be sent
Column I = first name of email recipient
Column H = email address
Column B = task per message
Column C = description per message
My code is called functionsendEmails`. You will see that it needs your expertise.
Also, I am hoping that the alert could be written to the email recipients Google Calendar but not sure if this is possible.
Here is the code from your script modified to send the email when date on J column match the date in J1. I recommend you change your code to use getValues() [1] and loop the resulting array instead of using getValue() every time, is much more optimized and will prevent you from hitting the Google quotas[2].
function sendEmails() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var sheet = spreadsheet.getSheetByName("H&S Reviews").activate();
var lastRow = sheet.getLastRow();
var message = spreadsheet.getSheetByName("Email Alerts").getRange(1,1).getValue();
for (var i = 3;i<=lastRow;i++){
var emailAddress = sheet.getRange(i, 8).getValue();
var firstName = sheet.getRange(i, 9).getValue();
var todaysDate = sheet.getRange(1, 10).getValue();
var date = sheet.getRange(i, 6).getValue();
date = Utilities.formatDate(date,'GMT+0200','dd MMMM yyyy');
var task = sheet.getRange(i, 2).getValue();
var description = sheet.getRange(i, 3).getValue();
var messageBody = message.replace("{name}",firstName).replace("{Task}",task).replace("{Description}",description).replace("{Date}",date);
var subject = "Health & Safety Review Task";
var sendDate = sheet.getRange(i, 10).getValue();
var sheetDate = new Date(sendDate);
Sdate=Utilities.formatDate(todaysDate,'GMT+0200','yyyy:MM:dd')
SsheetDate=Utilities.formatDate(sheetDate,'GMT+0200', 'yyyy:MM:dd')
Logger.log(Sdate+' =? '+SsheetDate)
if (Sdate == SsheetDate){
var subject = "Health & Safety Review Task";
MailApp.sendEmail(emailAddress, subject, messageBody);
Logger.log('SENT :'+emailAddress+' '+subject+' '+messageBody)
}
}
}
[1] https://developers.google.com/apps-script/reference/spreadsheet/range#getValues()
[2] https://developers.google.com/apps-script/guides/services/quotas

Use array index to write value to different sheet row

I'm currently trying to get this piece of code to send events from a google sheet to a google calendar (Credit to Adam McFarland on this post).
My sheet is currently around 300 rows & growing so to speed things up I've set the range to start at row 248. But this then seems to throw off the part that notes the event as 'done'. It sets value of "In 2 calendar" to rows 2, 3, 4 & 5?!?
Easy solution would be just to set the range to the whole sheet again but I'm still learning. I'd like to learn what exactly here isn't working correctly, and also a bit more about how iteration works.
//mark as entered, enter ID
sheet.getRange(i+2, 32).setValue('In 2 calendar');
Complete code below:
function pushToCalendar() {
//spreadsheet variables
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(248,1,lastRow,40);
var values = range.getValues();
var updateRange = sheet.getRange('G1');
var numValues = 0;
for (var i = 0; i < values.length; i++) {
//check to see if name and type are filled out - date is left off because length is "undefined"
if ((values[i][0].length > 0) && (values[i][2].length > 0)) {
//check if it's been entered before
if (values[i][30] != 'In calendar') {
//Declare which calendar ID to use (IGNORE THIS FOR NOW)
var calendar = CalendarApp.getCalendarById('calendarID')
// if (values [i][3] != 'Tropical 2450 Pontoon'){
// var calendar = CalendarApp.getCalendarById('calendarID')
//create event https://developers.google.com/apps-script/class_calendarapp#createEvent
var newEventTitle = values[i][3]+'. '+values[i][2]+'. '+values[i][13]
+'. '+values[i][5]+'/'+values[i][6]+'/'+values[i][7]
+'. '+values[i][18]+' total, '+values[i][25]+' to pay. '+values[i][0];
// var newEvent = calendar.createEvent('hello', Date[i][1], Date[i][5]);
var newEvent = calendar.createEvent(newEventTitle,
//new Date(values[i][6]),
new Date(values[i][32]),
new Date(values[i][33]));
//{guests:'tures.com.au', sendInvites: true});
//mark as entered, enter ID
sheet.getRange(i+2, 32).setValue('In 2 calendar');
} //could edit here with an else statement
}
numValues++;
}
}

Google Scripts - Dates/Spreadsheets/Automatic Emails

Apologies in advance - I am not a programmer. Just trying to piece together something for my business. Currently, I have a script that references a spreadsheet and sends out an automated email to my receptionist. This script provides all of the information I'd like it to but problem is that it sends out a separate email for every entry. Ideally, I would like to send a single email to my receptionist that contains a list of all of the appointments for the upcoming day.
Issues I'm having include:
I need to consolidate multiple emails into a single email containing a list of the upcoming appointments for the next day.
I don't know how to select the rows I need because they are date values (DD/MM/YYYY). So I'm Looking for a way to read the date, recognize that it is one day before the appointment, and add these entries to a single list.
If anyone can help me out with this it would be greatly appreciated!
Here's what I have so far:
function receptionist() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 8)
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[2];
var timeslot = sheet.getRange(i+2,7,1,1).getValues();
var name = sheet.getRange(i+2,2,1,1).getValues();
var date = Utilities.formatDate(new Date(sheet.getRange(i+2,6,1,1).getValue()),"EST", "E MMM dd, yyyy");
var receptionist= name+" scheduled on "+date+" at "+timeslot;
var subject = "Appt. List";
var emailAddress = "receptionist#example.com";
MailApp.sendEmail({emailAddress, subject, receptionist});
Thanks in Advance!
I've tested this code, and it works:
function receptionist() {
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = 2; // Number of rows to process
var dataRange = sheet.getRange(startRow, 1, numRows, 8)
var data = dataRange.getValues();
var receptionist = "Today's Appointments \r\r";
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[2];
var timeslot = sheet.getRange(i+2,7,1,1).getValue();
var name = sheet.getRange(i+2,2,1,1).getValue();
var dateData = sheet.getRange(i+2,6,1,1).getValue();
var dayOfAppointment = dateData.getFullYear() + dateData.getMonth() + dateData.getDate();
var todaysDate = new Date();
var todaysDayNumber = todaysDate.getFullYear() + todaysDate.getMonth() + todaysDate.getDate();
if (todaysDayNumber === dayOfAppointment) {
var date = Utilities.formatDate(new Date(),"EST", "E MMM dd, yyyy");
var thisAppointment = name+" scheduled on "+date+" at "+timeslot + "\r";
receptionist+=thisAppointment;
};
};
var subject = "Appt. List";
var emailAddress = "receptionist#example.com";
MailApp.sendEmail(emailAddress, subject, receptionist);
};
You do need a for loop. I removed the curly braces from the .sendEmail() parameters. Note the "\r" values to wrap each line to a new line. This Line:
receptionist+=thisAppointment;
makes the receptionist variable longer by one appointment line on each iteration. I didn't go through every line of code to determine whether the code is optimal or not. The basics of what you need are there. It may work "as is".