Google Script Not Iterating Through Rows - google-apps-script

I have a google script that changes the event dates to the day of the year, so 8/26/15 is 237, and also changes today's date to it's day of the year. It then compares the event date day of the year against today's date day of the year. If the event date integer is smaller than today's date integer than it changes the event description in the google calendar to Event Closed. The problem is it only does this for the first event in the spreadsheet. It does not iterate through all of the events and change all of the event descriptions that are before today's date.
function compareDates(){
var sheet = SpreadsheetApp.getActive().getSheetByName("Events");
var data = sheet.getDataRange().getValues();
var headerRow = 1;
var i = 1;
for (i = 1; i < data.length; i += 1) {
var row = data[i];
var room = row[5];
var description = row[6];
var agegroup = row[7];
var registration = row[8];
var calname = row[14];
var registrants = row[17];
var id = sheet.getRange(headerRow + 1, 16).getValues();
var event = CalendarApp.getEventSeriesById(id);
var description1 = (description + '\n' + '\n<b>Room:</b> ' + room + '\n' + '\n<b>Event Type:</b> ' + calname + '\n' + '\n<b>Age Group:</b> ' + agegroup);
var today = parseInt(Utilities.formatDate(new Date(sheet.getRange("U2").getValues()), "EST", "D"));
var eventDate = parseInt(Utilities.formatDate(new Date(row[0]), "EST", "D"));
var testED = sheet.getRange(headerRow + i, 22).setValue(eventDate);//To see if var Today works.
var testT = sheet.getRange(headerRow + i, 23).setValue(today);//To see if var eventDate works.
if (today > eventDate && registration === 'Y') {
var descriptionHTMLEC = '\n <div id="registration_button" ><a style="text-align:right;color:white!important;text-decoration:bold;background-color:rgb(121,183,138);background-image:-webkit-linear-gradient(top,rgb(221,75,57),rgb(121,183,138nnn ));color:#ffffff;border:1px solid rgba(0,0,0,0);border-radius:2px;display:inline-block;font-size:12px;font-weight:bold;height:27px;line-height:27px;padding:0px 20px 0px 20px;text-align:center;text-transform:uppercase;white-space:nowrap;text-decoration:none;color:white" target="_blank">Event Closed</a>';
var descriptionRegistrationEC = (description1 + '\n' + '\n' + descriptionHTMLEC);
event.setDescription(descriptionRegistrationEC);
}
}
}

It seems you are modifying the same event on each iteration. The statement:
var id = sheet.getRange(headerRow + 1, 16).getValues();
Will assign the same value to id each time. Shouldn't it be:
var id = sheet.getRange(headerRow + i, 16).getValues();
For more readable code you can just the getTime() function on the date object.
var today = new Date(sheet.getRange("C2").getValues()).getTime();
var eventDate = new Date(row[0]).getTime();

Related

Replacing Google Sheet row values when message is edited in Telegram Bot using Apps Script

I want to replace the values in the row when I edit a message on my telegram bot chat
How can I make use of the message_id to locate the row of values and directly replace it when the message has been edited?
function doPost(e) {
var contents = JSON.parse(e.postData.contents);
var id = contents.message.from.id;
var name = contents.message.chat.first_name;
var text = encodeURIComponent(contents.message.text);
var textstring = text.toString().replaceAll("%20", " ").replaceAll("%0A", "\n");
var messageid = contents.message.message_id
var dateNow = new Date;
var dd = dateNow.getDate();
var mm = dateNow.getMonth() + 1;
var yyyy = dateNow.getFullYear()
var hhmmss = dateNow.getHours() + ":" + dateNow.getMinutes() + ":" + dateNow.getSeconds();
var formatDate = dd + "/" + mm + "/" + yyyy + " " + hhmmss;
if (textstring.startsWith('/')) {
console.log("Command Line");
var answer = "[Command in development]";
sendMessage(id, answer);
}
else {
var answer = "Added: " + text;
sendMessage(id, answer);
sheet.appendRow([messageid, formatDate, id, name, textstring]);
}
}
First row is the initial message and second row is the intended outcome of the edited message
Outcome.jpg
You get all the ids and find the array index. Then if it is found you insert "over" the row. Else append:
function doPost(e) {
var contents = JSON.parse(e.postData.contents);
var id = contents.message.from.id;
var name = contents.message.chat.first_name;
var text = encodeURIComponent(contents.message.text);
var textstring = text.toString().replaceAll("%20", " ").replaceAll("%0A", "\n");
var messageid = contents.message.message_id
var dateNow = new Date;
var dd = dateNow.getDate();
var mm = dateNow.getMonth() + 1;
var yyyy = dateNow.getFullYear()
var hhmmss = dateNow.getHours() + ":" + dateNow.getMinutes() + ":" + dateNow.getSeconds();
var formatDate = dd + "/" + mm + "/" + yyyy + " " + hhmmss;
if (textstring.startsWith('/')) {
console.log("Command Line");
var answer = "[Command in development]";
sendMessage(id, answer);
}
else {
var answer = "Added: " + text;
sendMessage(id, answer);
const ids = sheet.getRange(1, 1, sheet.getLastRow()).getValues().flat()
const index = ids.indexOf(messageid)
const dataArray = [messageid, formatDate, id, name, textstring]
//If the id is not found then it returns -1. So this updates. Arrays are 0 index to we need to + 1 to get the right row.
if (index >= 0) {
sheet.getRange(index + 1, 1, 1, dataArray.length).setValues([dataArray])
//Else insert.
} else {
sheet.appendRow(dataArray)
}
}
}

It is about sending an email based on the changes in the sheet

I want my function to activate on specific sheet only. But I really do not know how to write. I have like 14 sheets. And anytime one of the sheets' values are changed. It automatically sends out an email which I do not want. I want my function to send email only based on the sheet I want. (I have add the trigger manually on my current project trigger).
function CheckbudgetAUD() {
for(var i=2;i<1000; i++){
var num1 = i;
var r = 4;
var department = "AUD";
var transactionIDRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,14);
var transactionID = transactionIDRange.getValue();
var monthRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,12);
var month = monthRange.getValue() ;
var costdescriptionRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,3);
var costdescription = costdescriptionRange.getValue();
var costRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,4);
var cost = costRange.getValue();
var actualbudgetRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,4);
var actualbudget = actualbudgetRange.getValue();
var ApprovalRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,18);
var Approval = ApprovalRange.getValue();
var purposeRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,7);
var purpose = purposeRange.getValue();
var timeRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,1);
var time = timeRange.getValue();
var CEOsapprovalRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,19);
var CEOsapproval = CEOsapprovalRange.getValue();
var addinformationRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,5);
var addi = addinformationRange.getValue();
var thisisdoneRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,30);
var thisisdone = thisisdoneRange.getValue();
var BlankRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,2);
var Blank = BlankRange.getValue();
//Emails Range
var email1Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Forecasted budget").getRange("F18");
var email1address = email1Range.getValue();
var email2Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Forecasted budget").getRange("F21");
var email2address = email2Range.getValue();
var email3Range = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(department).getRange(num1,6);
var email3address = email3Range.getValue();
// Messages Range
var message1 = "Your request has been " + Approval + "d." + "\n" + "\n" + "Transaction ID: " + transactionID;
var message = "For the " + month + ", " + department + "'s " + costdescription + ' budget exceeded the limit'+ '\n'+ '\n'+ "CURRENT BUDGET APPLICATION INFORMATION" + "\n" + "COST: "+ cost + '\n' + "Transaction ID: "+ transactionID +'\n' + "Cost item: " + costdescription + "\n" + "Purpose: "+ purpose + "\n" + "Department Name: " + department + "\n" + "Timestamp: " + time + "\n" + "additional information: " + addi+ "\n" + 'Click the link below to approve'+ '\n' + 'https://docs.google.com/forms/d/e/1FAIpQLSduX3ol31Ddy3klEpynlO33wprEivAr-e9BL7fZ6Th-JQgjZA/viewform';
var subject = 'Exceeded the budget limit for current month';
var subject1 = 'Form Approval/Decline';
if ( Blank !== "" && thisisdone == "Apple"){
MailApp.sendEmail(email3address, subject1, message1);
}
//Over budget email
if ( Approval == "Decline" && thisisdone == "Apple"){
if ( cost < 200000 ) {
MailApp.sendEmail(email1address, subject, message);}
else if ( cost >= 200000 ) {
MailApp.sendEmail(email2address, subject, message);
}
}
}
}
I am also basically sending out email to 3 different parties at the same time in one function. Just FYI.
You might like to consider this sort of approach. You will find it to run much faster. Although there may be some corrections you will want to make. I noticed some duplication in the range assignments which are reflected in my assignments as well. Keep in mind that column number start at one and array indices start at zero.
function CheckbudgetAUD() {
var department = "AUD";
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName(department)
var fsh=ss.getSheetByName("Forcasted budget");
var vA=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn()).getValues();
for(var i=0;i<vA.length; i++){
var transactionID = vA[i][13];
var month = vA[i][11];
var costdescription = vA[i][2];
var cost = vA[i][3];
var actualbudget=vA[i][3];
var Approval=vA[i][17];
var purpose=vA[i][6];
var time=vA[i][0];
var CEOsapproval=vA[i][18];
var addi=vA[i][4];
var thisisdone=vA[i][29];
var Blank=vA[i][1];
var email1address = fsh.getRange("F18").getValue();
var email2address = fsh.getRange("F21").getValue();
var email3address = vA[i][5];
var message1 = "Your request has been " + Approval + "d." + "\n" + "\n" + "Transaction ID: " + transactionID;
var message = "For the " + month + ", " + department + "'s " + costdescription + ' budget exceeded the limit'+ '\n'+ '\n'+ "CURRENT BUDGET APPLICATION INFORMATION" + "\n" + "COST: "+ cost + '\n' + "Transaction ID: "+ transactionID +'\n' + "Cost item: " + costdescription + "\n" + "Purpose: "+ purpose + "\n" + "Department Name: " + department + "\n" + "Timestamp: " + time + "\n" + "additional information: " + addi+ "\n" + 'Click the link below to approve'+ '\n' + 'https://docs.google.com/forms/d/e/1FAIpQLSduX3ol31Ddy3klEpynlO33wprEivAr-e9BL7fZ6Th-JQgjZA/viewform';
var subject = 'Exceeded the budget limit for current month';
var subject1 = 'Form Approval/Decline';
if (Blank!=="" && thisisdone=="Apple"){
MailApp.sendEmail(email3address, subject1, message1);
}
if (Approval=="Decline" && thisisdone=="Apple"){
if (cost<200000) {
MailApp.sendEmail(email1address, subject, message);
}else if( cost >= 200000 ) {
MailApp.sendEmail(email2address, subject, message);
}
}
}
}
If you're attempting to run something like this from onEdit() then you will want to limit which sheets that you want it to run on. For example consider the event object to be represented by an e then
function onEdit(e) {
var sh=e.range.getSheet();
if(sh.getName()!='AUD')return;
This will limit the script to running only for sheet AUD.

Script to search a column for a value and return the row number to use in a variable

I have a code I use to send email reminders based on dates in a spreadsheet. Most of the sheets I use it for are simple and under 100 lines of data. In the past I have used individual variables for each cell but I am looking for a way to find all instances of a value in a column (D), in this case, it is a city code ("SEA"), and return the row number, which I then want to take and use in a var to make it act like a vlookup.
ex: var empname = ss.getRange("A"+ rownumber).getValue();
Which should return column A of whichever row has "SEA" in it.
The challenge then is to be able to go down the column and do the same for each row that has "SEA" in it.
This is a one-line version of what I have done in the past.
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var ss = spreadsheet.getSheetByName("Active");
var empnum = ss.getRange("A8").getValue();
var empfirstname = ss.getRange("C8").getValue();
var emplastname = ss.getRange("B8").getValue();
var emplocation = ss.getRange("D8").getValue();
var test = ss.getRange("H8").getValue();
if (test === -1 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.' + '\n'
} else if (test === 0 && emplocation === "SEA"){
var message = empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.' + '\n'
} else if (test === 1) {
var message = "";
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
The H column will present a -1, 0 or 1 depending on the date in the G column to determine if someone is due to take a test or not. I then want to send an email with the Employee Number, First Name and Last Name for each employee at the SEA branch. This has worked for me in the past but I have 230+ lines of employees and don't want to create that many variables. My solution is to get the row number of each row with a "SEA" location and use that as a variable in a getRange call. Not sure exactly how that would work, or if it even would. If there is a better solution, I am amenable to that.
Requirement:
Send an email when a row contains "SEA" and column H matches -1 or 0.
Solution:
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//find all occurrences of "SEA" and push range to array
var search = "SEA";
var ranges = ss.createTextFinder(search).findAll();
var message = ''; //pre-define email body
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) { //make sure message is not blank
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}
Explanation:
Okay, so I've made quite a few changes to your original script, if there's anything I've missed please let me know, I'm happy to explain it.
First of all, I've changed the script so that we're using textFinder, this is so that we can search through your sheet to find your desired pattern "SEA". This then pushes all of the ranges to an array that we are looping through in the for loop.
When looping through the ranges, we can grab all of the data in the row (e.g. values[0][0] is the data in column A). This is then pushed to var message to build the email body in the same format you specified in your question.
I've tried to keep the variable names alike to the ones in your original script to make it easier to understand which parts of the script are essentially the same as your original. Also there are comments throughout the script that should help you understand what it's doing at each point.
References:
textFinder Documentation
Update:
To scan only a certain column, try the code below instead. I've defined var range separately so that we can use textFinder on that range rather than the whole sheet.
function findAndSendMail() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Active');
//define range to search
var lastRow = ss.getLastRow();
var range = ss.getRange(1, 4, lastRow); //range for column D
//find all occurrences of "SEA" in column D and push range to array
var search = "SEA"
var ranges = range.createTextFinder(search).findAll();
var message = '';
//loop through each range
for (i = 0; i < ranges.length; i++) {
var row = ranges[i].getRow();
var lastCol = ss.getLastColumn();
var values = ss.getRange(row, 1, 1, lastCol).getValues(); //get all values for the row
var empnum = values[0][0]; //column A
var empfirstname = values[0][2]; //column C
var emplastname = values[0][1]; //column B
var emplocation = values[0][3]; //column D
var test = values[0][7]; //column H
if (test === -1) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' is past due for testing.\n');
} else if (test === 0) {
message+=Utilities.formatString(empnum + " " + empfirstname + ' ' + emplastname + ' will be due for testing soon.\n');
}
}
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Email Group").getRange("A1");
var emailAddress = emailRange.getValues();
var subject = 'Test reminder';
if (message) {
MailApp.sendEmail(emailAddress, subject, '**This is an automated message**\n\n' + 'Test reminder:\n\n' + message + '\n\n**This is an automated message**\n');
}
}

Transportation request form date/time issue

We have a transportation request form that is used to assign vehicles. It also populates a calendar to keep up with which vehicles are being used on which day. Issue, if the time and date of the "return details" aren't after the "departure details" it throws an error and the calendar item creation function won't run.
ex. 12am is an issue for some. They may put...depart 1/20 4pm, return 1/20 12am. Which is actually before departure. Other times they simply put the wrong dates from being in a hurry.
I would like to check that part of the submission, if there is an error send and edit url link in an email back to the requestor with instructions to fix.
Any help on this would be appreciated.
function createCalEvent(){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var columnIndex = headers[0].indexOf(calendarColumnName);
var data = sheet.getDataRange().getValues();
var form = FormApp.openById(formURL);
var emailAddress = "transportation#.net";
var myCal = CalendarApp.getCalendarById('transportation#.net');
for(var i = startRow-1; i < data.length; i++) {
if(data[i][0] != '' && data[i][columnIndex] == '') {
var timestamp = Utilities.formatDate(data[i][0], 'CST', 'MMMM dd, yyyy h:mm a');
var staffName = data[i][1];
var departureDate = Utilities.formatDate(data[i][2],'CST', "EEEE, MMMM dd, yyyy");
var departureTime = Utilities.formatDate(data[i][3], 'CST', "h:mm a");
var returnDate = Utilities.formatDate(data[i][4],'CST', "EEEE, MMMM dd, yyyy");
var returnTime = Utilities.formatDate(data[i][5],'CST', "h:mm a");
var destination = data[i][6];
var groupAttending = data[i][7];
var reasonForTrip = data[i][8];
var teacherInCharge = data[i][9];
var specialRequest = data[i][10];
var vehicleOneAssigned = data[i][11];
var vehicleTwoAssigned = data[i][12];
var originalEmail = data[i][13];
var numberOfPassengers = data[i][14];
var requestorEmailAddress = data[i][15];
var pdfUrlLink = data[i][40];
var editURL = data[i][41];
if(pdfUrlLink != ''){
var title = staffName + '-' + destination + '-' + vehicleOneAssigned;
var descriptionList = 'Group Attending: ' + groupAttending + '<br> Number of Passengers: ' + numberOfPassengers + '<br> Reason For Trip: ' + reasonForTrip + '<br> Teacher In Charge: ' + teacherInCharge + '<br> Special Request: ' + specialRequest + '<br> Request Link: ' + pdfUrlLink;
var event = myCal.createEvent(title,
new Date(departureDate + ' ' + departureTime),
new Date(returnDate + ' ' + returnTime),
{description: descriptionList});
var eventID = event.getId();
Logger.log('Calendar Calendar Event ID: ' + eventID);
sheet.getRange(i+1, columnIndex+1).setValue(eventID);
}
}
}
}

Looping through a google sheet to remove rows - Compare dates

The code needs to delete rows have a date before the current date. I'm not getting any syntax errors, but I think there is a semantic problem as the code will not change the spreadsheet in any way.
function checkDateValue(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
var todaysDate = new Date().getDate();
var columnGCells = sheet.getRange("G2:G");
var columnG = columnGCells.getValues();
for (i = 1; i < sheet.height; i++) {
var endDate = new Date()
endDate = columnG[i];
if (todaysDate.getYear() >= endDate.getYear()){
if (todaysDate.getMonth() >= endDate.getMonth()){
if (todaysDate.getDay >= endDate.getDay()){
sheet.deleteRow(i);
}
}
}
}
};
checkDateValue();
The code would probably look something like this:
function checkDateValue(e) {
var colG_values,columnGCells,columnNumber,endDate,i,lastRow,
sheet,startRow,todaysDate;
startRow = 2;//Get data starting in this row
columnNumber = 7://The column number to get the data from
sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
lastRow = sheet.getLastRow();
todaysDate = new Date().getDate();
//Logger.log('todaysDate: ' + todaysDate);
columnGCells = sheet.getRange(startRow,columnNumber,lastRow-1,1);
colG_values = columnGCells.getValues();
for (i = lastRow; i > 1; i--) {//Loop through values from last to first-
//Need to stop at row one-
endDate = colG_values[i];
endDate = new Date(endDate);
if (todaysDate.getYear() >= endDate.getYear()){
if (todaysDate.getMonth() >= endDate.getMonth()){
if (todaysDate.getDay >= endDate.getDay()){
sheet.deleteRow(i);//Delete this row
}
}
}
};
};
function runTest() {
checkDateValue();
};