I'm trying to put together a script which cycles through a two dimensional range from a spreadsheet; when the criteria is met (in this case, when training is a month and a week respectively before needing renewed) an email should be sent.
I was able to achieve this when the script only had to cycle through one column with several rows; however now that I'm attempting to cycle through several columns and several rows, the script is running without error, but the data is not triggering an email to be sent.
A smaller version of the script I'm attempting to use is displayed below, though the original is much more extensive in an attempt to cover the large range of data. The script not present here is a repetition of the 'Column' sections for each individual column that needs to be taken into consideration.
function trainingReminder() {
//For testing purposes
//You can view the log by typing CMD+ENTER (CTRL + E) after you run the script.
Logger.log(msg);
// grab the Google Sheet object
var googleSheets = SpreadsheetApp.getActiveSpreadsheet();
// activates the first sheet
SpreadsheetApp.setActiveSheet(googleSheets.getSheets()[0]);
// grabs the sheet
var currentSheet = googleSheets.getActiveSheet();
// find the last row
var lastRow = currentSheet.getLastRow();
// The first row starts at row 2 to account for headers
var startRow = 2;
//**************************************************************//
// Column AU //
//**************************************************************//
// get AU column data (Training Name)
var range = currentSheet.getRange(2,48,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var remainingDays = range.getValues();
// get AR column data (Employee Names)
//range = currentSheet.getRange(2, 45, lastRow-startRow+1, 1);
var columnAR = range.getValues();
var warning_count = 0;
var msg = "";
// Loop column AU (Training Name) (1 Month)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 30) {
// Checks to see if a cell value in column AU equals 30
var employeeName = columnAR[i][0];
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due in
"+daysLeft+" days.\n";
warning_count++;
}
}
// Loop column AU (Training Name) (1 Week)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 7) {
// Checks to see if a cell value in column AU equals 7
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,48);
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due a week.\n";
warning_count++;
}
}
//**************************************************************//
// Column AV //
//**************************************************************//
// get AV column data (Training Name)
var range = currentSheet.getRange(2,49,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var remainingDays = range.getValues();
// get AR column data (Employee Names)
//range = currentSheet.getRange(2, 45, lastRow-startRow+1, 1);
var columnAR = range.getValues();
var warning_count = 0;
var msg = "";
// Loop column AV (Training Name) (1 Month)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 30) {
// Checks to see if a cell value in column AV equals 30
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,49)
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due in "+daysLeft+" days.\n";
warning_count++;
}
}
// Loop column AV (Training Name) (1 Week)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 7) {
// Checks to see if a cell value in column AV equals 7
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,49);
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due a week.\n";
warning_count++;
}
}
//**************************************************************//
// Column AW //
//**************************************************************//
// get AW column data (Training Name)
var range = currentSheet.getRange(2,50,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var remainingDays = range.getValues();
// get AR column data (Employee Names)
//range = currentSheet.getRange(2, 45, lastRow-startRow+1, 1);
var columnAR = range.getValues();
var warning_count = 0;
var msg = "";
// Loop column AW (Training Name) (1 Month)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 30) {
// Checks to see if a cell value in column AW equals 30
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,50)
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due in "+daysLeft+" days.\n";
warning_count++;
}
}
// Loop column AW (Training Name) (1 Week)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 7) {
// Checks to see if a cell value in column AW equals 7
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,50);
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due a week.\n";
warning_count++;
}
}
// Loop column CG (Training Name) (1 Week)
for (var i = 0; i <= numRows - 1; i++) {
var daysLeft = remainingDays[i][0];
if(daysLeft == 7) {
// Checks to see if a cell value in column CG equals 7
var employeeName = columnAR[i][0];
var employeeTraining = range.getValue(1,86);
msg = msg + "Don't Forget: "+employeeName+"'s "+employeeTraining+" is due a week.\n";
warning_count++;
}
}
if(warning_count) {
MailApp.sendEmail("example#email.com",
"Training Reminder", msg);
}
};
I realise there's probably something very obvious to anyone competent with writing scripts, but unfortunately I'm not there yet with it. Any help would be greatly appreciated!
Related
I have a script that gathers Full Names contained within an email group
The idea is to have an up to date list of members of a team to use within a drop down.
It is working correctly but I can't seem to get it to setValues() Everything I have tried, either only lists one name from the list, all values in one cell or doesn't work. I think it's to do with the fact it's a 1 dimensional array but I could be wrong.
Here's what I have so far:
function listGroupMembers() {
var GROUP_EMAIL = "email#email.com";
var group = GroupsApp.getGroupByEmail(GROUP_EMAIL);
var users = group.getUsers();
var str = "Group " + GROUP_EMAIL + " has " + users.length + " members: ";
var valueRange=[];
valueRange[0]=[];
for (var i = 0; i < users.length; i++) {
var user = users[i];
var email = user.getEmail();
var contact = ContactsApp.getContact(email);
if(contact!=null){
valueRange[i]=[];
valueRange[i].push (contact.getFullName());
}
}
var sheet = SpreadsheetApp.getActive().getSheetByName("sheet name");
var numberRows = valueRange.length;
var range = sheet.getRange(1, 1, numberRows, 1);
range.setValues(valueRange);
}
I am struggling with out to get these values, into Column1, Row1, on my sheet like follows in seperate cells:
Name 1
Name 2
Name 3
Name 4
The array is as follows: [Name 1, Name 2, Name 3, Name 5, etc]
Please help, hope this all makes sense!
To output your values into a value range and assign this value range to a row:
Based on the code you provided, perform the following modification:
function listGroupMembers() {
var GROUP_EMAIL = "emailgroup#email.com";
var group = GroupsApp.getGroupByEmail(GROUP_EMAIL);
var users = group.getUsers();
var str = "Group " + GROUP_EMAIL + " has " + users.length + " members: ";
var valueRange=[];
valueRange[0]=[];
for (var i = 0; i < users.length; i++) {
var user = users[i];
var email = user.getEmail();
var contact = ContactsApp.getContact(email);
valueRange[i]=[];
if(contact!=null){
valueRange[i].push (contact.getFullName());
}else{
valueRange[i].push ("");
}
}
var sheet = SpreadsheetApp.getActive().getSheetByName("INSERT HERE THE NAME OF YOUR SHEET");
var numberColumns = valueRange[0].length;
var range = sheet.getRange(1, 1, 1, numberColumns);
range.setValues(valueRange);
}
Explanation:
setValues() expect a 2-D value range, that is a nested array with the dimensions [numberRows][numberColumns]. For a row, it is a 2-D array with the height of 1 and the width corresponding to the number of your values. For a column, it would be exactly the opposite.
push() is a Javascript method useful for populating the array elements of the value range with your values
To insert values vertically (in one column) instead of horizontally - loop through rows instead of columns. Sample:
for (var i = 0; i < users.length; i++) {
var user = users[i];
var email = user.getEmail();
var contact = ContactsApp.getContact(email);
valueRange[i]=[];
if(contact!=null){
valueRange[i].push (contact.getFullName());
}else{
valueRange[i].push ("");
}
}
var sheet = SpreadsheetApp.getActive().getSheetByName("INSERT HERE THE NAME OF YOUR SHEET");
var numberRows = valueRange.length;
var range = sheet.getRange(1, 1, numberRows, 1);
range.setValues(valueRange);
In my sheet column A is date and column B is time duration values, I want to find the dates which are repeated and sum up the corresponding time values of the repeated dates and show the sum in the last relevant repeated date. And delete all the other repeated dates. ie if 18/07/2019 is repeated 4 times i have to sum up all the four duration values and display the sum value in the 4th repeated position and delete the first three date 18/07/2019. I have to do this all those dates that are repeated. I have wrote code to my best knowledge
function countDate() {
var data = SpreadsheetApp.getActive();
var sheet = data.getSheetByName("Sheet5");
var lastRow = sheet.getLastRow();
var sh = sheet.getRange('A1:A'+lastRow);
var cell = sh.getValues();
var data= sheet.getRange('B1:B'+lastRow).getValues();
for (var i =0; i < lastRow; ++i){
var count = 0;
var column2 = cell[i][0];
for (var j =0; j < i; j++)
{
var p=0;
var column4 = cell[j][0];
if (column4 - column2 === 0 )
{
var value1 = data[j][0];
var value2 = data[i][0];
var d = value2;
d.setHours(value1.getHours()+value2.getHours()+0);
d.setMinutes(value1.getMinutes()+value2.getMinutes());
sheet.getRange('C'+(i+1)).setValue(d).setNumberFormat("[hh]:mm:ss");
sheet.deleteRow(j+1-p);
p++;
}
}
}
}
The copy of the sheet is shown
column C is the values I obtain through the above code AND column D is the desired value
After computing the sum I need to delete the repeated rows till 15 here
Answer:
You can do this by converting your B-column to a Plain text format and doing some data handling with a JavaScript dictionary.
Code:
function sumThemAllUp() {
var dict = {};
var lastRow = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getLastRow();
var dates = SpreadsheetApp.getActiveSpreadsheet().getRange('A1:A' + lastRow).getValues();
var times = SpreadsheetApp.getActiveSpreadsheet().getRange('B1:B' + lastRow).getValues();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).setNumberFormat("#");
for (var i = 0; i < dates.length; i++) {
if (!dict[dates[i][0]]) {
dict[dates[i][0]] = times[i][0];
}
else {
var temp = dict[dates[i][0]];
var hours = parseInt(temp.split(':')[0]);
var minutes = parseInt(temp.split(':')[1]);
var additionalHours = parseInt(times[i][0].split(':')[0]);
var additionalMinutes = parseInt(times[i][0].split(':')[1]);
var newMinutes = minutes + additionalMinutes;
var newHours = hours + additionalHours;
if (newMinutes > 60) {
newHours = newHours + 1;
newMinutes = newMinutes - 60;
}
dict[dates[i][0]] = newHours + ':' + newMinutes;
}
}
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A1:B' + lastRow).clear();
var keys = Object.keys(dict);
for (var i = 0; i < keys.length; i++) {
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('A' + (i + 1)).setValue(keys[i]);
SpreadsheetApp.getActiveSpreadsheet().getSheets()[0].getRange('B' + (i + 1)).setValue(dict[keys[i]]);
}
}
Assumptions I made:
There are a few assumptions I made when writing this, you can edit as needed but I figured I should let you know:
There are only dates in Column A and only times in Column B.
The times in column B are either Hours:Minutes or Minutes:Seconds. Either way, if the value to the right of the : hits 60, it adds one to the left value and resets.
The Sheet within the Spreadsheet is the first sheet; that which is returned by Spreadsheet.getSheets()[0].
References:
w3schools - JavaScript Objects
Spreadsheet.getSheets()
w3schools - JavaScript String split() Method
MDN web docs - parseInt() method
Google Sheets > API v4 - Date and Number Formats
I am trying to set up a sheet with varying email address and
dates. when a date (due) counts down to 20 say, I'd like to send the
owner of the issue an automatic email. All info in in the same row
just varying columns. I post my semi-functional script here, I can get the correct line item to pull based on date, I just cant get the script to pull the associated email with the date. Or I get a ton of emails that i dont want. Sheet
Any help would be much appreciated!!
function checkReminder() {
// 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();
// Number of rows to process
var numRows = sheet.getLastRow()-1;
// 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 2
var startRow = 2;
// Fetch the range of cells A2:B3
var dataRange = sheet.getRange(startRow, 2 ,lastRow,
sheet.getLastColumn());
// Fetch values for each row in the Range.
var data = dataRange.getValues();
//Logger.log(data)
// grab column 20 (the 'days left' column) changed numrow to last r
row
get last row change
var range = sheet.getRange(startRow,20,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var days_left_values = range.getValues();
// Now, grab the reminder name column
range = sheet.getRange(2, 16, lastRow-startRow+1, 1);
var reminder_name_values = range.getValues();
var warning_count = 0;
var msg = "";
//msg = msg + "Trial Reminder Trial: "+reminder_name+" is due in
"+days_left+" days.\n"
//for (i in data) {
// var row = data[i];
// First column
// var emailAddress = row[13];
// Recipe column (Priority HIGH)
// var message = row[14];
// var subject = "Reminder CAPA ";
//subject = subject + reminder_name;
// Loop over the days left values
for (var k = 0; k <= numRows-1; k++) {
var days_left = days_left_values[k][0];
if(days_left == 20) {
// if it's exactly 20, do something with the data.
var reminder_name = reminder_name_values[k][0];
msg = msg + "Reminder CAPA: "+reminder_name+" is due in
"+days_left+" days.\n";
warning_count++;
}
if(warning_count) {
//MailApp.sendEmail(emailAddress, subject, message);
Logger.log(msg);
}
}}
This function sends email reminders based on a few conditions. One of the things I need to check for is that the Visit ID (which is in column 11 in the "email log" sheet) exists in a separate sheet ("DATA", stored in the enrollmentData variable). How do I search this array and return the ID to match in the IF statement below?
function sendReminders() {
var ss = SpreadsheetApp.getActiveSpreadsheet ();
var sheet = ss.getSheetByName("Email Log");
var rows = sheet.getLastRow()-1;
var startRow = 2; // First row of data to process
var numRows = rows; // Number of rows to process
var now = new Date();
// Fetch the range of cells
var dataRange = sheet.getRange(startRow, 1, numRows, 22)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
var today = Utilities.formatDate(new Date(), "GMT-5", "m/d/yyyy")
var reminderSent = "Reminder Sent";
//get email body and subject
var bodyTem = ss.getSheetByName("Email Templates").getRange('b8').getValues();
var subject = ss.getSheetByName("Email Templates").getRange('d13').getValues();
//get enrollments data to search for visit ID
var enrollmentData = ss.getSheetByName("DATA").getRange('H:H').getValues();
for (var i = 0; i < data.length; i++) {
var row = data[i];
//set conditions
var sendReminder = row[18];
var reminderDate = row[19];
var reminderStatus = row[20];
var visitID = row[11]
//need condition to look for visit ID to not include already cancelled. Search enrollmentData for visitID and return as foundID for conditional below
if (sendReminder == "Yes" && reminderStatus != reminderSent && reminderDate >= today && visitID == foundID) {
//assemble email
var studentEmail = row[13];
var firstName = row[12];
var instructor = row[0];
var body1 = bodyTem.replace(/*name*/gi,firstName);
var body2 = body1.replace(/*instructorFull*/gi,instructor);
MailApp.sendEmail(studentEmail, subject, body2);
//need to write in that the reminder email was sent.
sheet.getRange(startRow + i, 20).setValue(reminderSent);
sheet.getRange(startRow + i, 21).setValue(now);
};
};
};
You want to search the array
var enrollmentData = ss.getSheetByName("DATA").getRange('H:H').getValues();
The method getValues always returns a double array: in this case, it's of the form [[1], [2], [3],..] since each row has one element. I usually flatten this:
var enrollmentDataFlat = enrollmentData.map(function(row) {return row[0];});
Now enrollmentDataFlat is like [1, 2, 3, ..] so indexOf will work as usual:
if (enrollmentDataFlat.indexOf(visitID) != -1) {
// it's there
}
else {
// it's not there
}
I want to send email reminder if the date columns is 7 or 1 days away from today. I have already made the script and what i want to add is it should consider others columns too, not only 1 with one script, for sending reminder for respective columns.
For eg:
It should remind for Plan Date, Plan Date 1, Plan Date 2 and Plan Date 3.
Please see the Sample Attached.
Script:
function checkReminder() {
// get the spreadsheet object
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// fetch this sheet
var sheet = spreadsheet.getSheets()[0];
// figure out what the last row is
var lastRow = sheet.getLastRow();
// figure out what the last column is
var lastCol = sheet.getLastColumn();
// the rows are indexed starting at 1, and the first row
// is the headers, so start with row 2
var startRow = 2;
// the columns are indexed starting at 2, and the first column
// is the headers, so start with column 2
var startCol = 2;
// grab column 3 (the 'days left' column)
var range = sheet.getRange(2,3,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var days_left_values = range.getValues();
// Now, grab the reminder name column
range = sheet.getRange(2, 1, lastRow-startRow+1, 1);
var reminder_info_values = range.getValues();
// Now, grab the first row
range = sheet.getRange(1, 2, lastCol-startCol+1, 1);
var column_info_values = 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 == 1) {
// if it's exactly 1, do something with the data.
var reminder_name = reminder_info_values[i][0];
var column_name = column_info_values[0][0];
msg = msg + "Reminder: "+reminder_name+" - "+column_name+" is due in "+days_left+" day.\n";
warning_count++;
}
}
for (var i = 0; i <= numRows - 1; i++) {
var days_left = days_left_values[i][0];
if(days_left == 7) {
// if it's exactly 7, do something with the data.
var reminder_name = reminder_info_values[i][0];
var column_name = column_info_values[0][0];
msg = msg + "Reminder: "+reminder_name+" - "+column_name+" is due in "+days_left+" days.\n";
warning_count++;
}
}
if(warning_count) {
MailApp.sendEmail("myidsample#gmail.com",
"Reminder Spreadsheet Message", msg);
}
};
You're currently fetching only single column and processing that only.
In order to make this script run over other columns too, you'll need to loop through them, fetch data and then process them one by one.
Here you're fetching data from 3rd column :
// grab column 3 (the 'days left' column)
var range = sheet.getRange(2,3,lastRow-startRow+1,1 );
var numRows = range.getNumRows();
var days_left_values = range.getValues();
Like wise you can run a loop till last column and fetch data from 3rd, 5th, 7th .. columns and keep processing them.
So the code goes like this
function checkReminder() {
// get the spreadsheet object
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
// fetch this sheet
var sheet = spreadsheet.getSheets()[0];
// figure out what the last row is
var lastRow = sheet.getLastRow();
// figure out what the last column is
var lastCol = sheet.getLastColumn();
// the rows are indexed starting at 1, and the first row
// is the headers, so start with row 2
var startRow = 2;
// the columns are indexed starting at 2, and the first column
// is the headers, so start with column 2
var startCol = 2;
// Now, grab the reminder name column
range = sheet.getRange(2, 1, lastRow - startRow + 1, 1);
var reminder_info_values = range.getValues();
// Now, grab the first row
range = sheet.getRange(1, 2, lastCol - startCol + 1, 1);
var column_info_values = range.getValues();
var warning_count = 0;
var msg = "";
for (var u = 3; u <= lastCol; u += 2) {
// grab the day's left column
var range = sheet.getRange(2, u, lastRow - startRow + 1, 1);
var numRows = range.getNumRows();
var days_left_values = range.getValues();
// 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 == 1) {
// if it's exactly 1, do something with the data.
var reminder_name = reminder_info_values[i][0];
var column_name = column_info_values[0][0];
msg = msg + "Reminder: " + reminder_name + " - " + column_name + " is due in " + days_left + " day.\n";
warning_count++;
}
}
for (var i = 0; i <= numRows - 1; i++) {
var days_left = days_left_values[i][0];
if (days_left == 7) {
// if it's exactly 7, do something with the data.
var reminder_name = reminder_info_values[i][0];
var column_name = column_info_values[0][0];
msg = msg + "Reminder: " + reminder_name + " - " + column_name + " is due in " + days_left + " days.\n";
warning_count++;
}
}
if (warning_count) {
MailApp.sendEmail("myidsample#gmail.com",
"Reminder Spreadsheet Message", msg);
}
}
}
This is un-tested code, let me know if any issue arises. I'll be happy to help you.
Thank you!