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.
Related
So I managed to combine Google form, google calendar, as well as google sheets. When people submit the form (with a start date and end date), it will automatically appear in the google sheets as well as google calendar.
I modified the script to find conflict (to prevent double-booking), however, I just realized that even when the same person is trying to edit starting and ending date (via edit response), it will still show CONFLICT.
For example, someone books from date April 15th to April 17th, and he decided to change to April 16th to April 18th, because he previously booked 15-17, his new submission is having conflict with his own previous submission.
How can I add a function that will detect the same email to edit and submit data? (within empty day slot. Thanks in advance!
This is the function to create an object from sheet data
//Calendars to Output appointments
var cal = CalendarApp.getCalendarById('ID');
// Create an object from user submission
function Submission(){
var row = lastRow;
this.timestamp = sheet.getRange(row, 1).getValue();
this.email = sheet.getRange(row, 2).getValue();
this.name = sheet.getRange(row, 3).getValue();
this.date = sheet.getRange(row, 4).getValue();
this.dateout = sheet.getRange(row, 5).getValue();
this.room = sheet.getRange(row, 6).getValue();
this.day = sheet.getRange(row,8).setFormula("=(R[0]C[-3]-R[0]C[-4])+1").getValue();
var fillDownRange = sheet.getRange(2, 8, lastRow-1);
sheet.getRange(row,8).copyTo(fillDownRange);
// Info not from spreadsheet
this.status;
this.dateString = (this.date.getMonth() + 1) + '/' + this.date.getDate() + '/' + this.date.getYear();
this.dateOutString = (this.dateout.getMonth() + 1) + '/' + this.dateout.getDate() + '/' + this.dateout.getYear();
this.calendar = eval('cal' + String(this.room));
return this;
}
This is the function to detect conflict
function getEndTime(request){
request.endTime = new Date(request.dateout.getTime() + 24 * 60 * 60 * 1000);
}
// Check for appointment conflicts
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.date, request.endTime);
if (conflicts.length < 1) {
request.status = "Approve";
} else {
request.status = "Conflict";
}
}
This is the function to edit calendar
//update calendar (add)
function updateCalendar(request){
var event = request.calendar.createEvent(
"Booked",
request.date,
request.endTime
)
}
Instead of always retrieving the last row, you should retrieve the actual row of the latest submission
Mind that if people update their Google Form response, the submission row in the spreadsheet will not change - only the content.
You can retrieve the latest submitted / modified form response row with the event object event.range (provided your function is bound to a Google Sheets form submit trigger)
You can compare the modified row to the last row in the sheet
If the form response row is equal to the last row - a new response has been submitted
Sample:
function bindMeOnFormSubmitTrigger(event){
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var lastRow = sheet.getLastRow();
var row = event.range.getRow();
if (row == lastRow){
// insert a new event as done before
...
}
else {
// the submitted response was an edit of a previously submitted one
// identify the already existing event (e.g. if you specified the respondent email address in the event description) and modify the event data to the newly submitted values
var newFormResponseValues = event.values;
this.timestamp = newFormResponseValues[0];
...
}
}
If the form response row is differernt (smaller) then the last row in the sheet - it means that an existing form response has been edited (and consequently the event data might need to be modified in the calendar).
If you want to acccess the email of the respondent, you have to previously activate this option in the Google Forms UI settings:
I found a much better solution by using e.range. The better way to use this method is to create two separate sheets inside same spreadsheet. The main sheet will first retrieve response from the form, and then automatically create a copy to the second sheet. And if there is edited submission, it will go through the second sheet to find the edited row, then modify the row (as well as the calendar event). Credit to Tedinoz
Feel free to add comment/copy, cheers
function updateCalendarTwo(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var responsename = "Main sheet"
var copyname = "Copy Sheet";
var responsesheet = ss.getSheetByName(responsename);
var copysheet = ss.getSheetByName(copyname);
var calendar = CalendarApp.getCalendarById('Your Calendar ID');
// columns on copysheet
var checkInCol = 4;
var checkOutCol = 5;
var roomNumCol = 6;
var appCol = 11
var eventIDCol = 12;
var revCol = 14;
var response = e.range;
var rRow = response.getRow()
var rLC = responsesheet.getLastColumn();
var cLC = copysheet.getLastColumn();
var rLR = responsesheet.getLastRow();
var cLR = copysheet.getLastRow();
if (rLR > cLR){
var resprange = responsesheet.getRange(rLR,1,1,rLC);
var respdata = resprange.getValues();
copysheet.appendRow(respdata[0]);
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var event = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var eventID = event.getId().split('#')[0];
copysheet.getRange(rRow,appCol).setValue("approve");
copysheet.getRange(rRow,eventIDCol).setValue(eventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
}
} else {
var resprange = responsesheet.getRange(rRow,1,1,9);
var respdata = resprange.getValues();
var copyrespRange = copysheet.getRange(rRow,1,1,9);
copyrespRange.setValues(respdata);
var respAppRange = copysheet.getRange(rRow,appCol);
var respApp = respAppRange.getValue();
if (respApp == 'conflict') {
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
} else {
var eventEditId = copysheet.getRange(rRow,eventIDCol).getDisplayValue();
var editedEvent = calendar.getEventSeriesById(eventEditId);
editedEvent.deleteEventSeries();
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
};
var revRange = copysheet.getRange(rRow,revCol);
var revOldValue = revRange.getValue();
if (revOldValue == null || revOldValue == ""){
revOldValue = 0;
}
var revNewValue = revOldValue+1;
revRange.setValue(revNewValue);
}
}
I'm working on a script that generates a calendar event from a sheet in a google spreadsheet. after debugging the code I come up with no errors but when I run the code I get this "Cannot call method 'setTitle' of null". I have been unsuccessful in my attempts in troubleshooting and do not fully understand the substance of this error. Can someone help me understand what I'm doing wrong here?
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Export Events",
functionName : "exportEvents"
}];
ss.addMenu("Calendar Actions", entries);
};
/**
* Export events from spreadsheet to calendar
*/
function exportEvents() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet2");
var headerRows = 389; // Number of rows of header info (to skip)
var range = sheet.getDataRange();
var data = range.getValues();
var calId = "somewhere#farfaraway.com";
var cal = CalendarApp.getCalendarById(calId);
for (i=0; i<data.length; i++) {
if (i < headerRows) continue; // Skip header row(s)
var row = data[i];
var date = new Date(row[11]); // column L
var title = row[9]; // Column J
var estimator = row[6];
var tstart = new Date(row[11]);
tstart.setDate(date.getDate());
tstart.setMonth(date.getMonth());
tstart.setYear(date.getYear());
var tstop = new Date(row[11]);
tstop.setDate(date.getDate());
tstop.setMonth(date.getMonth());
tstop.setYear(date.getYear());
var loc = row[10]; // Column K
var desc = row[13]; // Column N
var id = row[0]; // Column A == eventId
// Check if event already exists, update it if it does
try {
var calev = cal.getEventSeriesById(id);
}
catch (e) {
// do nothing - we just want to avoid the exception when event doesn't exist
}
if (estimator == "a person" && !calev) {
//cal.createEvent(title, new Date("March 3, 2010 08:00:00"), new Date("March 3, 2010 09:00:00"), {description:desc,location:loc});
var newEvent = cal.createEvent(title, tstart, tstop, {description:desc,location:loc}).getId();
row[0] = newEvent; // Update the data array with event ID
}
else {
calev.setTitle(title);
calev.setDescription(desc);
calev.setLocation(loc);
// event.setTime(tstart, tstop); // cannot setTime on eventSeries.
// ... but we CAN set recurrence!
var recurrence = CalendarApp.newRecurrence().addDailyRule().times(1);
event.setRecurrence(recurrence, tstart, tstop);
}
debugger;
}
// Record all event IDs to spreadsheet
range.setValues(data);
}
I am trying to populate a google form with questions scraped from a google sheet. Currently when I run my code I am getting the questions created, but only 25% or so actually have the string, the rest are simply blank. The questions that appear correctly change every time I run the script. It is seemingly random.
function formPopulation() {
var ss = SpreadsheetApp.openById("--");
var sheet = ss.getSheetByName('Tracker');
var auditTool = ss.getSheetByName('Audit Tool');
var validatorInfo = ss.getSheetByName('Validator Info');
//Sheet Info
var rows = auditTool.getLastRow(); //Number of Rows
var columns = auditTool.getLastColumn(); //Number of Columns
var startRow = 1;
var startColumn = 1;
var dataRange = auditTool.getRange(startRow, startColumn, rows, columns);
//getRange(first row of data, first column of data, last row of data, last column of data)
var data = dataRange.getValues();
//Sets working range of script
var form = FormApp.openById("--");
var item = form.addListItem();
var entityName = "";
var arrayOfEntities = [];
var newEntity = '';
for (var i = 4; i < columns; i++) {
//4 because that is where entity names begin
entityName = data[i][2];
Logger.log('entityName: ' + entityName);
newItem = item.createChoice(entityName);
arrayOfEntities.push(newItem);
};
item.setTitle("Select Entity").setChoices(arrayOfEntities);
var requirement = "";
var arrayOfRequirements = [];
var newRequirement = '';
for (var j = 5; j < rows; j++) {
//5 because that is where Requirements begin
if (data[0][j] != null) {
requirement = data[0][j];
if (requirement != "" || requirment != null){
requirement = "question #" + j;
Logger.log('requirement: ' + requirement);
form.addMultipleChoiceItem().setTitle(requirement).setChoiceValues(['Complete', 'Incomplete']);
};
};
};
};
The first question is supposed to be a multiple choice item where each 'entity' is an option. The remainder of the questions are supposed to be whether each 'requirement' is marked complete or incomplete.
Here is the spreadsheet I am working from
you have a typo:
if (requirement != "" || requirment != null){
should be 'requirement'
Here in last forloop
requirement = "question #" + j;
Please verify, is it ok ? or you should use
requirement = "question #" + j + ' ' +data[0][j];
Here is my case.
A user fills a form for event booking, the submitted form is stored in a google spreadsheet which I have synced to a google calender so that it automatically sends the data to it.
Everything is working fine apart from the fact that event times could clash.
When customers book an event centre for let's say on 13/3/2015 T 10:00AM, if another user enters the same date and time, the entry should not be accepted.
To summarise it, I want to avoid a clash of events booking. Thank you all.
here is my script.
var calendarId = "mycalenderid";
//below are the column ids of that represents the values used in the spreadsheet (these are non zero indexed)
var startDtId = 9;
var endDtId = 10;
var titleId = 6;
var descId = 11;
var formTimeStampId = 1;
function getLatestAndSubmitToCalendar() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
var lr = rows.getLastRow();
var startDt = sheet.getRange(lr,startDtId,1,1).getValue();
//set to first hour and minute of the day.
//startDt.setHours(0);
//startDt.setMinutes(00);
var endDt = sheet.getRange(lr,endDtId,1,1).getValue();
//set endDt to last hour and minute of the day
//endDt.setHours(23);
//endDt.setMinutes(59);
var subOn = "Submitted on:"+sheet.getRange(lr,formTimeStampId,1,1).getValue();
var desc = "Added by :"+sheet.getRange(lr,descId,1,1).getValue()+"\n"+subOn;
var title = sheet.getRange(lr,titleId,1,1).getValue()+"DIA";
createEvent(calendarId,title,startDt,endDt,desc);
}
function createEvent(calendarId,title,startDt,endDt,desc) {
var cal = CalendarApp.getCalendarById(calendarId);
var start = new Date(startDt);
var end = new Date(endDt);
var loc = 'Script Center';
var event = cal.createEvent(title, start, end, {
description : desc,
location : loc
});
};
Here's a pseudocode of what you're trying to do:
function findEvent(desiredDateTime)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var data = range.getValues();
var lRow = range.getLastRow();
var flag = true;
var count = 0;
while (flag == true || count < lRow)
{
if (desiredDateTime >= data[count][startDtId] && desiredDateTime <= data[count][endDtId])
{
flag = false;
}
else
{
count++;
}
}
if (flag == true)
{
//Call function to add event
}else{
//Tell user desired date-time is not available.
//If you're asking for user's email address,
//simplest approach would be to send an email.
}
}
You might have to modify other bits and pieces of your code as well to accommodate this but it shouldn't be too hard. Hope this provides you with a certain direction to follow through.
Hello all I'm having trouble implementing array loops in my project... Here is what I want to do.
I have a spreadsheet called "Red Book" this sheet gets updated regularly once the staff have updated it I have a column where they can select to submit the data they've just entered on that specific row (editing this column calls an onEdit function).
The data will then be written to another spreadsheet (different file) called "Raw Data"
For each submit I have a unique identifier. I need the onEdit code to do the following...
Iterate through the column A to find the unique identifier
Once found update the data in columns 1 through 5
Below is the script I have so far:
function TransferToAppData(e) {
var destFile = SpreadsheetApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
var destSheet = destFile.getSheetByName("Raw App Data");
var ss = e.source;
var s = ss.getActiveSheet();
var uniConstRng = s.getRange("A1");
var uniqueConstVal = uniConstRng.getValue();
var NextOpenRow = destSheet.getLastRow() + 1;
var ActiveRow = e.range.getRow();
Logger.log(ActiveRow);
var uniqueVal = s.getRange(ActiveRow,1).getValue();
var add = s.getRange(ActiveRow,2).getValue();
var name = s.getRange(ActiveRow,3).getValue();
var dt = s.getRange(ActiveRow,5).getValue()
if (uniqueVal == "") {
s.getRange(ActiveRow,1).setValue(uniqueVal + 1);
uniConstRng.setValue(uniqueVal + 1);
var transferVals = s.getRange(ActiveRow,1,1,5).getValues();
Logger.log(transferVals);
destSheet.getRange(NextOpenRow,1,1,5).setValues(transferVals);
destSheet.getRange(NextOpenRow, 6).setValue("Applicant");
}
else {
var destLastRow = destSheet.getLastRow();
var destDataRng = destSheet.getRange(2,1,destLastRow,5)
var destValues = destDataRng.getValues();
var sourceValues = s.getRange(ActiveRow,1,1,5).getValues();
for( var i = 0; i < destValues.length; ++i){
if (destValues([i][0])==uniqueVal) {
for(n=0;n<destValues[0].length;++n){
///I"m stuck!!!
}
}
}
}
}
As you can see I have the first array loop going, but I'm having trouble figuring out how to do a second loop that iterates only on the row where the unique value is found and write the source data to ONLY to row where the unique value was found not the whole sheet.
I figured it out...
Below is the code and here is how it works...
When values in certain columns are edited this code is fired.
1--It finds the unique identifier located in the row which was edited.
2--Compares that identifier with a column of unique identifiers in another spreadsheet.
3--When a match is found it writes the change to the new spreadsheet and exits the loop
function TransferToAppData(e) {
var destFile = SpreadsheetApp.openById('1V3R2RnpA8yXmz_JDZSkBsK9tGR2LjHZp52p5I1CuQvw');
var destSheet = destFile.getSheetByName("Raw App Data");
var ss = e.source;
var s = ss.getActiveSheet();
var uniqueConstRng = s.getRange("A1");
var uniqueConstVal = uniqueConstRng.getValue();
var NextOpenRow = destSheet.getLastRow() + 1;
var ActiveRow = e.range.getRow();
var uniqueVal = s.getRange(ActiveRow,1).getValue();
if (s.getRange(ActiveRow,2).getValue() == "" || s.getRange(ActiveRow,3).getValue()=="" || s.getRange(ActiveRow,4).getValue()=="" || s.getRange(ActiveRow,5).getValue()=="") {
s.getRange(ActiveRow,13).clearContent();
Browser.msgBox("Address, Name, Date Entered & Rent are required fields!");
} else{
if (uniqueVal == "") {
s.getRange(ActiveRow,1).setValue(uniqueConstVal + 1);
uniqueConstRng.setValue(uniqueConstVal + 1);
var transferVals = s.getSheetValues(ActiveRow,1,1,5);
destSheet.getRange(NextOpenRow,1,1,5).setValues(transferVals);
destSheet.getRange(NextOpenRow, 6).setValue("Applicant");
}
else {
var destLastRow = destSheet.getLastRow();
var destValues = destSheet.getSheetValues(2,1,destLastRow,5);
var sourceValues = s.getSheetValues(ActiveRow,1,1,5);
for(var i = 0; i < destValues.length; ++i){
if (destValues[i][0]===uniqueVal) {
destSheet.getRange(i+2,1,1,5).setValues(sourceValues);
break;
}
}
}
s.sort(1,false);
destSheet.sort(1,false);
}
}