Need help with my code. I need a timestamp for two status columns.
I need to stamp a date on column Labeling 1 when the status is In progress and another one on Labeling 2 when the status is Done. Same thing with QA 1 and QA 2.
Here's what I've done:
function onEdit(d){
var sheet = SpreadsheetApp.getActiveSheet();
var actRng = sheet.getActiveRange();
var rowIndex = actRng.getRowIndex();
var editColumn = actRng.getColumn();
if(sheet.getName() == "Dominios 2023" && rowIndex>1){ //Cambiar nombre de hoja
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues();
var valor = actRng.getValue();
var statusCol = headers[0].indexOf('Status')+1;
var tiempoActual = new Date();
var datestartCol = headers[0].indexOf('Labeling 1')+1;
var datendCol = headers[0].indexOf('Labeling 2')+1;
if(editColumn==statusCol){
temporizadorLbl(sheet,rowIndex,valor,tiempoActual,datestartCol,datendCol);
}
}
}
function temporizadorLbl(sheet,rowIndex,valor,tiempoActual,datestartCol,datendCol){
if (valor=='IN PROGRESS'){
sheet.getRange(rowIndex,datestartCol).setValue(Utilities.formatDate(tiempoActual,'GMT-3','dd/MM/yyyy'));
}
if(valor=='DONE'){
sheet.getRange(rowIndex,datendCol).setValue(Utilities.formatDate(tiempoActual,'GMT-3','dd/MM/yyyy'));
}
}
function onEdit(g){
var sheet = SpreadsheetApp.getActiveSheet();
var actRng = sheet.getActiveRange();
var rowIndex = actRng.getRowIndex();
var editColumn = actRng.getColumn();
if(sheet.getName() == "Dominios 2023" && rowIndex>1){ //Cambiar nombre de hoja
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues();
var valor = actRng.getValue();
var statusCol = headers[0].indexOf('Status QA')+1;
var tiempoActual = new Date();
var datestartCol = headers[0].indexOf('QA 1')+1;
var datendCol = headers[0].indexOf('QA 2')+1;
if(editColumn==statusCol){
temporizador(sheet,rowIndex,valor,tiempoActual,datestartCol,datendCol);
}
}
}
function temporizador(sheet,rowIndex,valor,tiempoActual,datestartCol,datendCol){
if (valor=='IN PROGRESS'){
sheet.getRange(rowIndex,datestartCol).setValue(Utilities.formatDate(tiempoActual,'GMT-3','dd/MM/yyyy'));
}
if(valor=='DONE'){
sheet.getRange(rowIndex,datendCol).setValue(Utilities.formatDate(tiempoActual,'GMT-3','dd/MM/yyyy'));
}
}
Here's an Example Sheet where I need timestamps.
Thanks!
first time poster so I apologise if if im not following the correct etiquette.
So I have code that, in essence, creates 70 forecast docs in specified folders. This works fine.
I have used the same code, repointed the all the folder references, but now times out.
Code is as follows, any suggestions on how I can make this run faster?
function createFiles(weeknr, masterIDs, masterfolders, mastercodes, archivefolders) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var date = new Date()
var textdate = Utilities.formatDate(date, "Europe/London", "dd/MM/YYYY HH:mm")
var fileslog = ss.getSheetByName("FileList")
var year = date.getYear()
var mainfolder = DriveApp.getFolderById("1HdG3eJZrIMF-CRpx17Reqdyd7JM5v5ZW")
var weekfolder = mainfolder.createFolder('Week ' + weeknr + ' ' + year)
var tempArray = []
for (var i = 0; i < masterIDs.length; i++) {
// for (var i=0;i<masterIDs.length;i++){
var folderID = masterfolders[i]
var masterID = masterIDs[i]
var code = mastercodes[i]
var archivefolder = archivefolders[i]
var newfile = DriveApp.getFileById(masterID).makeCopy(code + ' DCS WK' + weeknr,
DriveApp.getFolderById(folderID))
var newID = newfile.getId()
tempArray.push([newfile.getName(), newID, newfile.getUrl(),
newfile.getDateCreated(), "Created", folderID, archivefolder, weeknr])
SpreadsheetApp.openById(newID).getSheetByName("Main").getRange(3, 3).setValue(weeknr)
weekfolder.addFile(newfile)
//NEW UPDATE 14/02 to set the owner to network finance
newfile.setOwner('network.finance#dpdgroup.co.uk')
//End of UPDATE
fileslog.getRange(fileslog.getLastRow() + 1, 1, 1, 8).setValues([tempArray[i]])
}
// Logger.log(tempArray )
// if(tempArray.length>0){
// fileslog.getRange(fileslog.getLastRow()+1, 1, tempArray.length,
8).setValues(tempArray)
// }
}
function archiveFiles(weeknr, fileIDs, masterfolders, mastercodes, archivefolders) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var date = new Date()
var textdate = Utilities.formatDate(date, "Europe/London", "dd/MM/YYYY HH:mm")
var fileslog = ss.getSheetByName("FileList")
//var year = date.getYear()
//var mainfolder = DriveApp.getFolderById("1HdG3eJZrIMF-CRpx17Reqdyd7JM5v5ZW")
//var weekfolder = mainfolder.createFolder('Week '+weeknr+' '+year)
var tempArray = []
for (var i = 0; i < fileIDs.length; i++) {
// for (var i=0;i<masterIDs.length;i++){
var folderID = masterfolders[i]
var fileID = DriveApp.getFileById(fileIDs[i])
var code = mastercodes[i]
var archivefolder = archivefolders[i]
DriveApp.getFolderById(archivefolder).addFile(fileID)
DriveApp.getFolderById(folderID).removeFile(fileID)
//var newfile = DriveApp.getFileById(masterID).makeCopy(code + ' DCS WK'+weeknr,
DriveApp.getFolderById(folderID))
//var newID = newfile.getId()
tempArray.push([fileID.getName(), fileIDs[i], fileID.getUrl(),
fileID.getLastUpdated(), "Archived", masterfolders[i], archivefolders[i], weeknr])
//weekfolder.addFile(newfile)
}
Logger.log(tempArray)
if (tempArray.length > 0) {
fileslog.getRange(fileslog.getLastRow() + 1, 1, tempArray.length, 8).setValues(tempArray)
}
}
Posted the answer for documentation purpose.
As discussed in the comments the problem was the file size, splitting the copied files in batches did the trick:
So turns out, the file was simply too large to create 70 files in the allotted time. So I split it down to batches of 30 and its worked perfectly. thanks for your help
So I've been working on this code that will check a google sheet for a date two weeks from now and send a reminder. The first time I run the code, it works great. The problem is, the second time I run the code it ignores the duplicate checker and then sends an email using data from a row that has already been sent. I'm trying to figure out A) why is it doing that, and B) how do I fix it.
function sendEmails3() {
var today = new Date();
today.setDate(today.getDate() + 14); // Today's date, without time
today = today.toLocaleDateString();
var EMAIL_SENT = "EMAIL_SENT";
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 2; // First row of data to process
var numRows = sheet.getLastRow() - 1; // Number of rows to process
// Fetch the range of cells A2:B999
var dataRange = sheet.getRange(startRow, 1, numRows, 999)
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (var i = 0; i < data.length; ++i) {
var row = data[i];
var emailAddress = row[0]; // First column
var firstName = row[1]; // Second column
var lastName = row[2]; // Third column
var event1 = row[4];
var event2 = row[6];
var event3 = row[8];
var emailSent = row[9];
var sheetDate1 = row[13].toLocaleDateString();
var sheetDate2 = row[14].toLocaleDateString();
var sheetDate3 = row[15].toLocaleDateString();
Logger.log(sheetDate1)
if (sheetDate1 == today) {
var event = event1;
var sheetDate = sheetDate1;
} else if (sheetDate2 == today) {
var event = event2;
var sheetDate = sheetDate2;
} else if (sheetDate3 == today) {
var sheetDate = sheetDate3;
var event = event3;
}
if (sheetDate != today) {
continue;
}
Logger.log(sheetDate)
Logger.log(event)
Logger.log(emailAddress)
if (emailSent != "EMAIL_SENT") { // Prevents sending duplicates
var subject = "Reminding you that your " + event + " is coming up on " +
sheetDate + "!";
MailApp.sendEmail(emailAddress, subject, "", {
htmlBody: slsdf
});
sheet.getRange(startRow + i, 10).setValue(EMAIL_SENT);
// Make sure the cell is updated right away in case the script is interrupted
SpreadsheetApp.flush();
var sheetDate = "";
Logger.log(sheetDate)
var event = "";
}
}
}
please ignore the garbage in the part where its supposed to be emailing html, it's juts a huge pile of html and shouldn't effect anything.
I have been trying to figure out Google Apps Script validation in Google Sites and have yet to make the validation part work correctly.
I need to validate 2 things:
That there is at least 5 characters in the "location" textbox (up to 100)
That a date has been selected from the dropdown
If both conditions are not met, then it should make visible 2 things:
warnException
warnExceptionMes
That's it.
The rest of my logic is working great. I am just starting out.
The full logic is listed below. I have replaced our domain info with xxxxxxxxx.
So far, it either never shows the messages and does nothing or just getting one of the items right allows it to move forward. They should both meet the requirements or the warnExceptions should be thrown. This would also be the same if a user loaded the page and did not fill either/or area out and just pushed the button.
How can I validate the Location textbox and the dateBox?
var templateIDToCopy = 'xxxxxxxxxx';
var folderIDtoCopyTo = 'xxxxxxxxxx';
var councilMembers = ['Unknown','Name 1','Name 2'];
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(196, 25);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
// Setup error message
var warnException =app.createImage('https://sites.google.com/xxxxxxxxxx/minutes/create-new-minutes/Opps.png').setId('warnException').setVisible(false);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').setVisible(false);
// handlers
var handler1 = app.createClientHandler()
.validateLength(location, 0, 50).validateMatches(dateBox, '2', 'g')
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler2 = app.createServerHandler('handlerFunction')
.validateLength(location, 1, 100).validateNotMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler1).addClickHandler(handler2);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);//.add(warnException).add(warnExceptionMes);
hpanelException.setWidget(1,1,warnException).setStyleAttribute("text-align", "right")
.setWidget(1,2,warnExceptionMes)
// .setWidget(1,2,nextSteps)
app.add(hpanelException);
return app;
}
function handlerFunction(e) {
var app = UiApp.getActiveApplication();
app.getElementById('submit').setText('Building, please wait...').setEnabled(false);
var location = e.parameter.location;
var determineName = e.parameter.minuteTaker;
var date = e.parameter.dateBox;
var timeZone = date.toString().substr(25,6)+":00";
var dateMilli = date.getTime();
var hour = parseInt(e.parameter.hour);
var amPm = e.parameter.amPm;
if (amPm == 'PM' && hour != 12) hour = hour + 12;
if (hour == 12 && amPm == 'AM') hour = 0;
var hourMilli = hour * 3600000;
var minMilli = parseInt(e.parameter.minute) * 60000;
var milliTotal = dateMilli + hourMilli + minMilli;
// create custom format
var newDate = Utilities.formatDate(new Date(milliTotal), timeZone, 'MM/dd/yy hh:mm aaa');
app.getElementById('dateTimeLabel').setText(newDate);
// make a copy of the minutes template to use
var duplicateID = DriveApp.getFileById(templateIDToCopy)
.makeCopy('Simply Minutes v1.0 - Stage 1: Building new minutes...')
.getId();
// get the id of the annual folder where minutes will be stored
var getFolderID = DriveApp.getFolderById(folderIDtoCopyTo);
// copy new minutes sheet to the annual folder where minutes are stored
var moveIT = DriveApp.getFileById(duplicateID).makeCopy('Simply Minutes v1.0 - Stage 2: Building new minutes...', getFolderID).getId();
// get the new minutes doc that was created
var template = DocumentApp.openById(moveIT);
var templateHeader = template.getHeader();
var templateBody = template.getActiveSection();
// fill in the values
templateHeader.replaceText("<date>", newDate);
templateBody.replaceText("<date>", newDate);
templateHeader.replaceText("<location>", location);
templateBody.replaceText("<location>", 'N/A');
var email = Session.getEffectiveUser().getEmail();
var eUser = Session.getEffectiveUser().getUsername();
var createdBy = '';
if(ContactsApp.getContact(email)){
var fullName = ContactsApp.getContact(email).getFullName();
createdBy = fullName;
}
else {
createdBy = 'N/A';
};
var determineName = e.parameter.minuteTaker;
templateHeader.replaceText("<minutetaker>", determineName);
templateHeader.replaceText("<createdby>", createdBy)
templateBody.replaceText("<minutetaker>", determineName);
templateBody.replaceText("<createdby>", createdBy);
template.setName(newDate + ' TAC Minutes Recorded By ' + determineName);
// close out the doc
template.saveAndClose();
// remove the copy that was left in the root directory
// DriveApp.getFileById(duplicateID).isTrashed();
DriveApp.getFileById(duplicateID).setTrashed(true);
app = UiApp.getActiveApplication();
app.getElementById('submit').setText('Completed!').setEnabled(false);
app.getElementById('nextSteps').setVisible(true);
return app;
}
Try like this (see below) I changed a bit the validations and separated in 2 handlers + added a "clear" handler to be able to click-to-delete the warnings... test code here
You have also to add something to clear the warnings in the server handler and, why not combine the image and text warning in a single widget ? (easier to clean)
code below :
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var clearHandler = app.createClientHandler().forEventSource().setVisible(false)
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(196, 25);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
// Setup error message
var warnException =app.createImage('https://sites.google.com/xxxxxxxxxx/minutes/create-new-minutes/Opps.png').setId('warnException').setVisible(false).addClickHandler(clearHandler);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').setVisible(false).addClickHandler(clearHandler);
// handlers
var handler0 = app.createClientHandler()
.validateLength(location, 0, 5)
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler1 = app.createClientHandler()
.validateNotMatches(dateBox, '2', 'g')
.forTargets(warnException).setVisible(true)
.forTargets(warnExceptionMes).setVisible(true);
var handler2 = app.createServerHandler('handlerFunction')
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler0).addClickHandler(handler1).addClickHandler(handler2);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);//.add(warnException).add(warnExceptionMes);
hpanelException.setWidget(1,1,warnException).setStyleAttribute("text-align", "right")
.setWidget(1,2,warnExceptionMes)
// .setWidget(1,2,nextSteps)
app.add(hpanelException);
return app;
}
EDIT second version following your comment.
I simulated a server function that takes some time so you see all the steps. + I combined the warning as suggested. (demo code updated with version 2)
var templateIDToCopy = 'xxxxxxxxxx';
var folderIDtoCopyTo = 'xxxxxxxxxx';
var councilMembers = ['Unknown','Name 1','Name 2'];
function doGet(e) {
var text= new Array();
var app = UiApp.createApplication();
var hpanel = app.createGrid(4, 6).setId('pannel');
var hpanelException = app.createGrid(2,3).setId('hpanelException');
var location = app.createTextBox().setName('location').setId("location").setWidth('200');
var minuteTaker = app.createListBox().setName('minuteTaker').setId("minuteTaker").setWidth('200')
for (var i = 0 ; i < councilMembers.length; i++) {
minuteTaker.addItem(councilMembers.valueOf()[i]);
}
var dateBox = app.createDateBox().setId('dateBox').setName('dateBox').setFireEventsForInvalid(false);
var hour = app.createListBox(false).setId('hour').setName('hour')
// var hour = app.createListBox(false).setId('hour').setName('hour')
for(h=1;h<13;++h){hour.addItem(h)}
var min = app.createListBox(false).setId('minute').setName('minute')
.addItem('00').addItem('15').addItem('30').addItem('45');
var amPm = app.createListBox(false).setId('am').setName('amPm')
.addItem('AM').addItem('PM');
var dateTimeLabel = app.createLabel('',false).setId('dateTimeLabel');
var submit = app.createButton('Create Minutes').setId('submit').setPixelSize(195, 65);
var nextSteps = app.createAnchor('Please click here to see the minutes archive.', 'https://drive.google.com/xxxxxxxxxx/folderview?xxxxxxxxxx').setId('nextSteps').setVisible(false);
var clearHandler = app.createClientHandler();
// Setup error message
var warnException =app.createImage('https://dl.dropboxusercontent.com/u/211279/clock_e0.gif').addClickHandler(clearHandler);
var warnExceptionMes = app.createLabel('The date and Location are required. Please try again.').setStyleAttribute('font-weight', 'normal').setStyleAttribute('font-size','14px').addClickHandler(clearHandler);
var warnPanel = app.createHorizontalPanel().add(warnException).add(warnExceptionMes).setId('warning').setVisible(false);
clearHandler.forTargets(warnPanel).setVisible(false);
// handlers
var handler0 = app.createClientHandler()
.validateLength(location, 0, 5)
.forTargets(warnPanel).setVisible(true)
var handler1 = app.createClientHandler()
.validateNotMatches(dateBox, '2', 'g')
.forTargets(warnPanel).setVisible(true)
var handler2 = app.createClientHandler()
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.forEventSource().setText('Server Handler is running...').setEnabled(false)
.forTargets(warnPanel).setVisible(false);
var handlerS = app.createServerHandler('handlerFunction')
.validateLength(location, 6, 100).validateMatches(dateBox, '2', 'g')
.addCallbackElement(location).addCallbackElement(dateBox).addCallbackElement(hpanel);
submit.addClickHandler(handler0).addClickHandler(handler1).addClickHandler(handler2).addClickHandler(handlerS);
hpanel.setWidget(0,0,app.createLabel('Select Date'))
.setWidget(0,1,app.createLabel('Hour'))
.setWidget(0,2,app.createLabel('Minutes'))
.setWidget(0,3,app.createLabel('AM/PM'))
.setWidget(0,4,app.createLabel('Location'))
.setWidget(0,5,app.createLabel('Minute Taker'))
hpanel.setWidget(1,0,dateBox)
.setWidget(1,1,hour)
.setWidget(1,2,min)
.setWidget(1,3,amPm)
.setWidget(1,4,location)
.setWidget(1,5,minuteTaker)
hpanel.setWidget(2,5,submit)
app.add(hpanel);
hpanelException.setWidget(1,1,warnPanel).setStyleAttribute("text-align", "right")
app.add(hpanelException);
return app;
}
function handlerFunction(e) {
var app = UiApp.getActiveApplication();
Utilities.sleep(1000);
app.getElementById('submit').setText('SERVER HANDLER is DONE');
// app.getElementById('warning').setVisible(false);// not necassary anymore, see clientHandler2
return app;
}