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);
}
};
Related
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.
I have the 2 columns in my table schema:
Column D= Date, i.e. 20180611 [yyymmdd]
Column F= Continuous Value, i.e. 0.1, 0.6, -0.3 etc.
This is what I want to happen:
Check in column D for yesterday's date. Then, take in the corresponding row, and check if column F is greater than 0.5 (for yesterday's date). If TRUE, then send an email alert.
This is the script I have but it does not trigger for some reason. What is wrong with it?
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) {
try {
date = dates[dateRow].toDateString();
if (date === yesterdayString) {
++dateRow;
// To account for zero-based array
break;
}
} catch (error) {
Logger.log(error);
}
}
var value = sheet.getRange('F' + dateRow).getValue();
if (value >= 0.5) {
var result = ('Alert found on: ' + date);
MailApp.sendEmail('blabla#gmail.com', 'Alert', result);
}
};
Here is the data
The problem could be due to the use of an open reference D2:D to get values and then use dates.length to set the number of iterations on the for loop because it could be a number too large.
One "quick and dirty" way that could solve the above issue is to replace
var dateCount = dates.length;
by
var dateCount = sheet.getDataRange().getValues().length;
I am trying to make a for loop with dates but it isn't going as planned.
What I am trying to do is input startdate in cell A1 and enddate in cell B1.
Then a for loop which runs: if startdate is less then enddate, put date in cell A2 and proceed to next column. The only thing I am stuck with is how to add one day to a date.
See code below:
function test()
{
var app = SpreadsheetApp;
var ss = app.getActive();
var sheet = ss.getActiveSheet();
var start = sheet.getRange("A1").getValue();
var end = sheet.getRange("B1").getValue();
var day = 3600*24*1000;
var diff = Math.floor((end-start)/day);
var column = 1;
for(var i=0;i<=diff;i++)
{
sheet.getRange(2, column).setValue(start+i*day);
column++;
}
}
Can someone explain to me why it doesn't work.
Thanks a lot
Assuming start is a Date, then your issue is:
start + i * day
Where the + operator is used, the left operand (the Date) is an object so is reduced to a primitive with hint string, so the + is treated as string concatenation, not a number.
You can try:
+start + i * day
to coerce start to a number, but then the result will be a number, not a Date. Also it doesn't deal with the length of day issue. I suggest you first copy the start date so you don't modify it, then increment the copy per the duplicate:
function test() {
var app = SpreadsheetApp;
var ss = app.getActive();
var sheet = ss.getActiveSheet();
var start = sheet.getRange("A1").getValue();
start = new Date(+start);
var end = sheet.getRange("B1").getValue();
var column = 1;
for (var i=0; start <= end; i++) {
sheet.getRange(2, column++).setValue(new Date(start.setDate(start.getDate() + 1)));
}
}
I have a sheet with over 400 calendar events and I am trying to write a script that will take these events from my spreadsheet and create events for them in my Google Calendar. To prevent the script from duplicating events every time it runs, I am setting the eventID as a check in the last column of my spreadsheet. The logic is that if the startTime column and endTime columns are not blank and the EventID column IS blank, then the script should run and create a new event, then log the ID into the last column.
I am finding that the code logs an eventID in the last column all the time, which means it creates an event every time, even when the conditions are not met.
function createCalendarEvent() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('MasterList');
var data = sheet.getSheetValues(3, 1, sheet.getLastRow(), 12);
var calendar = CalendarApp.getCalendarById('*CalendarID*');
for (var i = 0; i <= sheet.getLastRow(); i++) {
var title = data[i][0]; //String
var date = new Date(data[i][1]); //Date
var startTime = new Date(data[i][9]); //DateTime
var endTime = new Date(data[i][10]); //DateTime
var location = data[i][5]; //String
var status = data[i][11]; //String
var allDay = data[i][3]; //String
if (allDay === 'ALL DAY' && status === ''){
var allDayEvent = calendar.createAllDayEvent(title, date, {location: location});
sheet.getRange(i + 3, 12).setValue(allDayEvent.getId());
}
else if (startTime !== '' && endTime !== '' && status === '' ) {
var event = calendar.createEvent(title, startTime, endTime, {location: location});
sheet.getRange(i + 3, 12).setValue(event.getId());
}
else {
Logger.log('\nRow %s is missing start and/or end time or the event has already been created.\n', i + 3);
}
}
}
Based on suggestions in the comments, I determined that the "empty" cells were being treated at the "zero" date of Google Sheets, which is 12/30/1899 0:00:00. So while I could not see a value in the cell, the code was still grabbing that value. Once I set the condition that the endTime had to be greater than the startTime, it worked out (and flushed out a few errors in the spreadsheet where one of the users indeed entered end times that were before the start time).
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.