Auto timestamp and date in multiple columns - date

I have a Google sheet link where upon cell entry in column B a timestamp is placed in a cell in column C and a date in a cell in column G.
Two scripts with New Date does not work. Date cell is not allowed to have Time so that script is:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "Guests" ) { //checks that we're on Guests or not
var r = s.getActiveCell();
if( r.getColumn() == 2 ) { //checks that the cell being edited is in column B
var nextCell = r.offset(0,5);
if( nextCell.getValue() === '' ) //checks if the adjacent cell is empty or not?
nextCell.setValue(new Date(new Date().setHours(0,0,0,0))).setNumberFormat('dd/MM/yyyy');
}
}
}
This script runs fine but the timestamp script will not.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "Guests" ) { //checks that we're on Guests or not
var r = s.getActiveCell();
if( r.getColumn() == 2 ) { //checks that the cell being edited is in column B
var nextCell = r.offset(0,1);
if( nextCell.getValue() === '' ) //checks if the adjacent cell is empty or not?
nextCell.setValue(new Date());
}
}
}
I'm a total loser with scripts.
Thanks for your help.

Already solved it.
I used the Timestamp script since that had the Date. Formatted the cell to show only Time and for cell G - Date
I extracted this from the Time cell by using:
=TO_DATE(INT(C9))

Related

Send an email when one cell in a column contains data, but not when it's deleted

I would like an email to be sent when something is added to a cell anywhere in column K, but not when that cell is cleared. Is this possible? Here is what I have so far. The email sends okay when I edit cell K1 (it was limited to this single cell for testing) but it also sends an email when I clear this cell. Thank you for any advice, as you can see I'm mostly just guessing here.
function sendEmail() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet4 = ss.getSheetByName('Copy');
var emailAddress = sheet4.getRange(2,12).getValue();
var subject = sheet4.getRange(2,13).getValue();
var message = sheet4.getRangeList(['G1', 'G2', 'G3', 'G4', 'G5', 'G6', 'G7', 'G8'])
.getRanges()
.map(range => range.getDisplayValue())
.join('\n');
MailApp.sendEmail(emailAddress, subject, message);
}
function onEdit(e) {
if(e.range.rowStart === 1 && e.range.columnStart === 11) {
var sh = e.source.getActiveSheet();
if(sh.getName() === 'Copy') {
sendEmail()
}
}
}
Piggybacking off Karla's answer, generally when you delete single values, the event object (e) is absent of a value key. Therefore,
Try:
function onEdit(e) {
if (e.source.getActiveSheet().getName() === `Copy`) {
if (e.range.rowStart === 1 && e.range.columnStart === 11) {
if (`value` in e) sendEmail()
}
}
}
Maybe add to the "if" that if the value is different to empty it will not execute the code
Not sure which row or range belongs to K, so I name it "Range_in_K"
if(e.range.rowStart === 1 && e.range.columnStart === 11 && Range_in_K != "")

Choosing a specific column or cell for data to be entered into?

The script below places a static time stamp into column A whenever column B is edited.
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") {
var r = s.getActiveCell();
if (r.getColumn() == 2) {
var nextCell = r.offset(0, -1);
if (nextCell.getValue() === '')
nextCell.setValue(new Date());
}
}
}
However, I would like the timestamp to occur when columns B,C,D,E, etc. are edited.
How do I specify that Column A (or 1) is where I want the date to be placed?
The documentation knows a Method on a Sheet getRange(row, column)
So I guess, this should work:
var firstCell = s.getRange(r.getRow(), 1);
You can use Event object to get information about the cell that was edited. No need to use getActiveSheet, getActiveCell and offset:
function onEdit(e) {
var range = e.range; // Range that was edited
var sheet = range.getSheet(); // Sheet that was edited
var editedRow = range.getRow(); // Row that was edited
var editedCol = range.getColumn(); // Column that was edited
if (sheet.getName() == "Sheet1" && editedCol > 1) { // Check that edited sheet is "Sheet1" and edited column is not A
var colA = sheet.getRange(editedRow, 1); // Get corresponding cell in column A
if (colA.getValue() === "") colA.setValue(new Date()); // Set date if cell is empty
}
}
Reference:
Event objects: onEdit
Sheet.getRange(row, column)

Multiple OR conditions in time difference

I want to make a script that send me mail whith the content of what I've enterd in a form some days after the form is submitted. As a help for students to study. In the form enter what to study, then 1 day, 7 days and 28 days later get that in a mail.
I've made a form that collect time, recipient adress, subject and body for the mail. These are saved in a Google spreadsheet.
The code kind of work. But it send all of the mail from my test input in the sheet. I've added one 6 days ago, one yesterday and one today.
Today I should only get one mail and tomorrow two. But I get all of them today.
I think it's this line:
if (diffDays == 1 || diffDays == 7 || diffDays == 28) continue;
I've trided to change it, searched other ways of writing it, as array for example.
Here's the full code:
function createTimeDrivenTriggers() {
ScriptApp.newTrigger('reminder')
.timeBased()
.everyDays(1)
.atHour(16) // Change time of day here
.inTimezone("Europe/Stockholm")
.create();
}
function reminder() {
var today = new Date(); // Today
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var headerRows = 1;
for (i=0; i<data.length; i++){
if (i < headerRows) continue;
var row = data[i];
var time = row[0];
// Get time difference
var timeDiff = Math.abs(today.getTime() - time.getTime());
var diffDays = Math.ceil((timeDiff) / (1000 * 3600 * 24)-1);
if (diffDays == 1 || diffDays == 7 || diffDays == 28) continue;
var recipient = row[1];
var subject = row[2];
var body = row[3];
// Send mail
GmailApp.sendEmail(recipient, subject, body)
}
}
Thanks
Make sure to execute the code only when your if statement is true.
Include all code after if statement in the statement itself, only executing it when diffDays = 1, 2 or 28.
function reminder() {
var today = new Date(); // Today
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getDataRange();
var data = range.getValues();
var headerRows = 1;
for (i=0; i<data.length; i++){
if (i < headerRows) continue;
var row = data[i];
var time = row[0];
// Get time difference
var timeDiff = Math.abs(today.getTime() - time.getTime());
var diffDays = Math.ceil((timeDiff) / (1000 * 3600 * 24)-1);
if (diffDays == 1 || diffDays == 7 || diffDays == 28) {
var recipient = row[1];
var subject = row[2];
var body = row[3];
// Send mail
GmailApp.sendEmail(recipient, subject, body)
}
}
}
Note that continue means "stop processing this iteration of whatever loop is getting executed, and begin the next iteration." So your script sends reminders on every day except the days you want to remind them.
Changing your test to an "AND NOT", moving the code for reminding inside the existing check, or using a more expressive syntax like switch will satisfy your intent.
switch (diffDays) {
case 1:
case 7:
case 28:
sendReminder(your, Args, Here);
break;
case 0:
sendWelcome(some, other, Args);
break;
...
default:
break;
}
...
function sendReminder(your, Args, Here) {
/* Code that uses function arguments to build and send an email */
}
"And not" meaning if (diffDay != 1 && diffDay != ...), i.e. "if it's not this, and not that, and not this other thing, and not..."

Preventing repetitive data in date fields ActionsScript2

I have four date fields on a form and I need to alert the user when they have entered repetitive dates. I have already created an array that generates and checks the dates, but throwing the alert is not working for me. This is what I have so far.
else if ((dateRow1.text== dateRow2.text) || (dateRow1.text == dateRow3.text) || (dateRow1.text == dateRow4.text)) {
alert.show("You must have unique dates for each field");
Thanks.
You can check if your text fields are all distinct from each other:
var t:Array = [dateRow1, dateRow2, dateRow3, dateRow4];
var l:Number = t.length;
var i:Number;
var j:Number;
for (i = 0; i < l; i++) {
var ti = t[i]; // dateRow1... dateRow4
for (j in t) {
if (ti != t[j] && ti.text == t[j].text) trace(ti + "==" + t[j]);
}
}

Check date range vs. some other date ranges for missing days

Here is what I want to do:
I got a date range from e.g. 03.04.2013 to 23.04.2013 - that's my main range.
Now I have the possibility to create some own time ranges (e.g. 04.04.2013 to 09.04.2013 and 11.04.2013 to 23.04.2013). Those have to cover the whole main range, so every day of the main range (excluding weekens) needs an corresponding day in my own time ranges.
My plan would be to create an Array for the main range. Then I check each day of my own time ranges against the main range. If there is an accordance, I would remove the day from the main range.
So in the end, if everything is ok, there would be an emtpy array, because all days are covered by my own time ranges. If not, then the days not covered would still be in the main range and I could work with them (in this example: 03.04.2013, 10.04.2013)
Does anybody have an better idea to solve this problem? NotesDateTimeRanges?
I would add the dates into a sorted collection and then a "pirate algorithm". Look left, look right and if any of the looks fails you can stop (unless you want to find all missing dates).
Off my head (you might need to massage the final list to store the value back):
var AbsenctSince:NotesDateTime; //Start Date - stored in the NotesItem
var endDate:NotesDateTime; // Return, could be in Notes or Today
var wfDoc:NotesDocument = docApplication.getDocument();
var responseCDs:NotesDocumentCollection = wfDoc.getResponses();
var docResponse:NotesDocument;
var nextResponse:NotesDocument;
//Get the date, which limits the function - if there is a return information, then this is the limit, else today
AbsenctSince = wfDoc.getDateTimeValue("AbsentSince") ;
if (wfDoc.hasItem("ReturnInformationDat")) {
endDate = wfDoc.getDateTimeValue("ReturnInformationDat");
} else {
endDate = session.createDateTime("Today");
}
//Get all days between two dates - as pure Java!
var dateList:java.util.List = getWorkDayList(AbsenctSince.toJavaDate(), endDate.toJavaDate());
// Looping once through the reponse documents
var docResponse = responseCDs.getFirstDocument();
while (docResponse != null) {
nextResponse = responseCDs.getNextDocument(docResponse);
var CDValidSince:NotesDateTime = docResponse.getDateTimeValue("CDValidSince");
var CDValidTill:NotesDateTime = docResponse.getDateTimeValue("CDValidTill");
// Now we need get all days in this range
var removeDates:java.util.List = getWorkDayList(CDValidSince.toJavaDate(),CDValidTill.toJavaDate());
dateList.removeAll(removeDates);
docResponse.recycle();
docResponse = nextResponse;
}
// Both docs are null - nothing to recycle left
// Now we only have uncovered dates left in dateList
docApplication.replaceItemValue("openDates", dateList);
// Cleanup
try {
AbsenctSince.recycle();
endDate.recyle();
wfDoc.recycle();
responseCDs.recycle();
} catch (e) {
dBar.error(e);
}
function getWorkDayList(startDate, endDate) {
var dates:java.util.List = new java.util.ArrayList();
var calendar:java.util.Calendar = new java.util.GregorianCalendar();
calendar.setTime(startDate);
while (calendar.getTime().before(endDate)) {
var workDay = calendar.get(calendar.DAY_OF_WEEK);
if (workDay != calendar.SATURDAY && workDay != calendar.SUNDAY) {
var result = calendar.getTime();
dates.add(result);
}
calendar.add(java.util.Calendar.DATE, 1);
}
return dates;
}
I've done it this way now (seems to work so far):
var dateArray = new Array();
var responseCDs:NotesDocumentCollection = docApplication.getDocument().getResponses();
var dt:NotesDateTime = session.createDateTime("Today");
var wfDoc = docApplication.getDocument();
dt.setNow();
//Get the date, which limits the function - if there is a return information, then this is the limit, else today
var AbsenctSince:NotesDateTime = session.createDateTime(wfDoc.getItemValue("AbsentSince").toString().substr(0,19));
if (wfDoc.hasItem("ReturnInformationDat")) {
var endDate:NotesDateTime = session.createDateTime(wfDoc.getItemValue("ReturnInformationDat").toString().substr(0,19));
} else {
var endDate:NotesDateTime = session.createDateTime("Today");
}
//Get all days between two dates
dateArray = getDates(AbsenctSince, endDate);
for (var i=dateArray.length-1; i >= 0 ; i--) {
var checkDate:NotesDateTime = session.createDateTime(dateArray[i].toString().substr(0,19));
var day = checkDate.toJavaDate().getDay();
//Remove weekends first
if ((day == 6) || (day == 0)) { //6 = Saturday, 0 = Sunday
dBar.info("splice: " + dateArray[i]);
dateArray = dateArray.splice(i,1);
} else {
var docResponse = responseCDs.getFirstDocument();
//Work through all response docs to check if any date is covered
while (docResponse != null) {
var CDValidSince:NotesDateTime = session.createDateTime(docResponse.getItemValue("CDValidSince").toString().substr(0,19));
var CDValidTill:NotesDateTime = session.createDateTime(docResponse.getItemValue("CDValidTill").toString().substr(0,19));
//checkDate covered? If yes, it will be removed
if (checkDate.timeDifference(CDValidSince)/86400 >= 0 && checkDate.timeDifference(CDValidTill)/86400 <= 0 ) {
dBar.info("splice: " + dateArray[i]);
dateArray = dateArray.splice(i,1);
}
docResponse = responseCDs.getNextDocument();
}
}
}
docApplication.replaceItemValue("openDates", dateArray);
And I'm using this function (adopted from this question here):
function getDates(startDate:NotesDateTime, endDate:NotesDateTime) {
var dateArray = new Array();
var currentDate:NotesDateTime = startDate;
while (endDate.timeDifference(currentDate) > 0) {
dateArray.push( currentDate.getDateOnly() );
currentDate.adjustDay(1);
}
return dateArray;
}