EDIT: more information. Every special-ed binder requires a review to check for compliance. The review is a 60 plus questionnaire that we turned into a Google form for ease of communication and also to keep data in our district about numbers of binders that are or are not compliant. Well there are really three levels of errors that can occur which need different fixes, from paperwork, to re-training. The goal is that when the form is filled out if an email would be sent depending on what level(s) of error occurred. So if I answered "no" to any question between 6 and 9 then I would get an email stating that a level 1 error was made. Same for level 2 and for level 3 errors. The issue I'm having is that I can only get an email to work when only one error was made or all errors were made. I made a test form, with less questions to test out the coding: https://forms.gle/sM1qWRE7MykVT1Cq6
Also, here is a link to form response spreadsheet so you can get an idea of what I'm trying to accomplish. https://docs.google.com/spreadsheets/d/1YLmIrFO1AK0i0BujwIL1sFVLHuopV0y-cztcykFfpTg/edit?usp=sharing
Finally, here is the code I used for the sample:
function sendEmail() {
var ss = SpreadsheetApp.getActive();
var responses = ss.getSheetByName('Sheet1');
var data = responses.getRange(2,1,responses.getLastRow()
-1,7).getValues();
Logger.log(data);
data.forEach(function(row,i) {
var email = row[1];
var answer = row[2];
var replied = row[6];
var level1 = row[3];
var level2= row[4];
var level3 = row[5];
if (replied == '') {
//if (level1 == 'No' && level2 =='No' && level3 == 'No') {
//var body = "The Sped compliance form submitted shows levels 1,2,
and 3 erorrs. Please review check the district review for further insructions";
//}
if (level1 == 'No'){
Logger.log('yes response');
Logger.log(email);
var body = "The SPED compliance check has found a Level 1 error, please see documentation to fix errors.";
}
else if (level2 == 'No') {
Logger.log("no Response");
Logger.log(email);
var body = "The SPED compliance check has found a Level 2 error, please see documentation to fix errors";
}
else if (level3 == 'No'){
var body = "The SPED compliance check has found a Level 3 error, please see documentation to fix errors";
}
else {
}
var subject = "thank you for submitting your form";
GmailApp.sendEmail(email, subject, body);
var d = new Date();
responses.getRange(i + 2,7).setValue(d);
}
});
If a person answers 'No' for any questions in 1-3 then it is a level 1 error, if a person answers 'No' to any questions in 6-9 it is a level 2 error, and if a person answers 'No' to any of the questions in 11-15 it is a level 3 error. I can a response email if there is only 1 error (level 1,2, or 3), however if a person chooses no to more than one set of responses then the code will not work. I'm new to this so I know I'm missing some detail, just not too sure how to get the logic to work.
I have tried adding a clause for if all levels are selected. It didn't work
What I would like to see is that if a person commits a level 1 and other level error that it would show both levels of error.
How about doing it with a counter instead of all those conditions? For example, depending on the Level:
Level 1: if the answer is No, Add 1
Level 2: if the answer is No, Add 10
Level 3: if the answer is No, Add 100
This way you would only need 3 conditions: I edited my code according to yours:
data.forEach(function(row,i, responses) {
var counter = 0;
var email = row[1];
var answer = row[2];
var replied = row[6];
var level1 = row[3];
var level2= row[4];
var level3 = row[5];
if (level1 == 'No'){
counter += 1;
}
if (level2 == 'No'){
counter += 10;
}
if (level3 == 'No'){
counter += 100;
}
if (counter < 10){
var body = "The SPED compliance check has found a Level 1 error, please see documentation to fix errors.";
} else if (counter >= 10 && counter < 100){
var body = "The SPED compliance check has found a Level 2 error, please see documentation to fix errors";
} else {
var body = "The SPED compliance check has found a Level 3 error, please see documentation to fix errors";
}
var subject = "thank you for submitting your form";
GmailApp.sendEmail(email, subject, body);
var d = new Date();
responses.getRange(i + 2,7).setValue(d);
});
However, it would be nice to see the rest of your code to figure out how it is developed.
Related
If you are reading my question, Thank you for taking the time out of your day to help.
Background:
I have a form that my field techs use in order to request parts, I would like to keep them updated on the status of a part order with an automated sms text to their phone.
Specifics:
Link to test Sheet
https://docs.google.com/spreadsheets/d/1hEDEk-3-z3Wh6PNLoY6PgmbSJZ7OcdMR0kFCl0BRrYU/edit?usp=sharing
Parts Request (Picture)
Status Change
When a Status is changed in (column G), this will trigger an SMS to be sent.
Recipient
The SMS will be sent to the Team Lead ( Column B) in that row.
Example: Status is changed (G2), SMS is sent to Team Lead (B2).
Employee Info (Picture)
Employee Information:
The Script pulls the Employee Telephone number(Employee Info! B2) from the Employee Info Sheet
Text Body:
The Message that is sent would be the entire row in the text message
-Team Lead
-Type of Request
-Job Name
-Part Description
-QTY Missing
-Status
The Script i have tried so far has been a simple one, based on a trigger of anytime a change is made to the sheet. Here is what i have used so far, this has worked and has been sending generic texts. Any Help would be greatly appreciated.
function sendText() {
var EmailTo = "'Mobile Number'"#txt.att.net";
var subject = "Whatever";
var body = "Text";
MailApp.sendEmail(EmailTo, subject, body);
}
The processes needed to achieve your goal involves fetching the desired details on the row wherein the status of the request changes, incorporating the given phone number to the appropriate carrier domain, and adding an installable trigger so that the script will automatically work when there are changes to the Status Column.
Here is the script:
function sendUpdates(e) {
var ss = e.source;
var shEmployeeInfo = ss.getSheetByName("Parts Request");
var shPartsRequest = ss.getSheetByName("Employee Info");
var row = e.range.getRow();
var column = e.range.getColumn();
if(column == 7) { //To limit checking of changes to the Status column
var info = shEmployeeInfo.getRange(row, 2, 1, 6).getValues();
var header = shEmployeeInfo.getRange(1, 2, 1, 6).getValues();
var carrier = shPartsRequest.getRange(row,4).getValues();
const subject = "Insert Subject Here."; //Edit this to change the subject
const carrierMap = { "ATT": 'txt.att.net',
"T-Mobile": 'tmomail.net',
"Sprint": 'messaging.sprintpcs.com',
"Verizon": 'vtext.com',
"Cricket": 'mms.mycricket.com' }
var phoneNumber = shPartsRequest.getRange(row,2).getValues();
var emailTo = `${phoneNumber}#${carrierMap[carrier]}`;
var body = "";
for(let i = 0; i <= 5; i++) {
body += header[0][i] + ": " + info[0][i] + "\n";
}
MailApp.sendEmail(emailTo, subject, body);
}
}
The installable trigger should be set as:
Please refer to the Installable Triggers Guide for more information.
As for the result, I made a test case and got this:
I'm working on making a Google Sheet update relevant Google Form questions from a list of cells if the header matches a question. The code runs fine, but for some reason, I continue to run into an error when using a certain set of data that is known to not have duplicates, having run it through =Unique() and used the duplicate removal data tool in GSheets. Here is a link to the GSheet for the source data. The 'Room Number' dataset is the one that is throwing the error when attempting to upload it to the Google Form, here. I've removed the imports and changed data for privacy, but the error is still replicated in this data/structure. Thank you for any insight you may have, I'm hoping someone with a bit more experience can steer me in the right direction. I'm a total novice with coding, so any insight on what I did wrong would be great as well.
var ssID = "1EoTHkLlqXuZ8wf-L2rEm1PQ6NUFkO_KTMu7BrzvN-30";
var formID = "1r-mcbZCd4EDHRC-8_gpOJZw8Oaj6IzekzNIxSegEwuA";
var wsData = SpreadsheetApp.openById(ssID).getSheetByName("QTR Inspection Data");
var form = FormApp.openById(formID);
function main(){
var labels = wsData.getRange(1,1,1,wsData.getLastColumn()).getValues()[0];
labels.forEach(function(label,i){
//Logger.log(label);
//Logger.log(i)
var options = wsData.getRange(2, i + 1,wsData.getLastRow()-1,1).getValues().map(function(o){ return o[0] }).filter(function(o){ return o !== ""});
//Logger.log(options);
updateRoomNumberUsingTitle(label,options);
});
//Logger.log(labels);
}
function updateRoomNumberUsingTitle(title,values) {
var items = form.getItems();
var titles = items.map(function(item){
return item.getTitle();
});
var pos = titles.indexOf(title);
if(pos !== -1){
var item = items[pos];
var itemID = item.getId();
updateRoomNumber(itemID,values);
}
}
function updateRoomNumber(id,values) {
var item = form.getItemById(id);
item.asListItem().setChoiceValues(values);
}
Room #135 has 2 occurences! You have then to remove one of them.
To prevent further occurence, you can change this
updateRoomNumberUsingTitle(label, options.join().split(',').filter(onlyUnique));
and add
function onlyUnique(value, index, self) {
return self.indexOf(value) === index;
}
I need a trigger (or two) and I'm doing something wrong. Or maybe I need to tweak my code.
I want an email to send automatically when I update a line in my spreadsheet. The three columns that update are notes, status, and resolution. Right now, in my test version, it sends an email as soon as I change any one of those columns. Which is fine, if I'm only updating one thing. But if I want to add a note, change the status, and enter a resolution all at once, it sends three separate emails. My first trigger for sending an email upon form submission works great.
Here is my code. Any help would be appreciated
PS: I'm sure there are cleaner ways to do this, but I'm familiar with this from the past. It's just been long enough that I don't remember all of it.
function formSubmitReply(e) {
var userEmail = e.values[3];
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
// Set the status of the new ticket to 'New'.
// Column F is the Status column
sheet.getRange("F" + lastRow).setValue("New");
// Calculate how many other 'New' tickets are ahead of this one
var numNew = 0;
for (var i = 2; i < lastRow; i++) {
if (sheet.getRange("F" + i).getValue() == "New") {
numNew++;
}
}
MailApp.sendEmail(userEmail,
"Helpdesk Ticket #" + lastRow,
"Thanks for submitting your issue. \n\nWe'll start " +
"working on it as soon as possible. You are currently " +
"number " +
(numNew + 1) + " in the queue. \n\nHelp Desk.",
{name:"Help Desk"});
}
function emailStatusUpdates() {
var sheet = SpreadsheetApp.getActiveSheet();
var row = sheet.getActiveRange().getRowIndex();
var userEmail = sheet.getRange("D" + row).getValue();
var subject = "Helpdesk Ticket #" + row;
var body = "We've updated the status of your ticket.\n\nStatus: " + sheet.getRange("F" +
row).getValue();
body += "\n\nNotes: " + sheet.getRange("E" + row).getValue();
body += "\n\nResolution: " + sheet.getRange("G" + row).getValue();
MailApp.sendEmail(userEmail, subject, body, {name:"Help Desk"});
}
function onOpen() {
var subMenus = [{name:"Send Status Email", functionName: "emailStatusUpdates"},
{name:"Schedule Appointment", functionName: "scheduleAppointment"},
{name:"Push to KB", functionName: "pushToKb"}];
SpreadsheetApp.getActiveSpreadsheet().addMenu("Help Desk Menu", subMenus);
}
It looks that you are using an on edit trigger to send and email that works OK when you only need to edit one cell but when you need to edit two or more the email should be sent until you ended to edit all the related cells.
One way to achieve the desired behaviour is to add one or more conditions to send the email.
One way to implement the above is to add an auxiliary column to set if the email should be sent inmediately or not. What about calling this "Hold" and add a checkbox?
This is just is a brief example of how to implement the above as an installe on edit trigger
/**
* Columns C,D,E are notes, status and resolution respectively
* Column F is Hold
*/
function sendUpdate(e){
if(e.range.columnStart > 2 && e.range.columnStart < 6 || e.range.columnStart === 6){
var hold = e.range.getSheet().getRange(e.range.rowStart, 6).getValue();
if( hold !== 'TRUE'){
// Call here your send email function
} else {
// Ask if the email should be sent now
}
}
}
I have some code where there are some if/else if statements to determine the reason that a Google Form has closed. I want to pass this reason to another function which will send an email notifying me that the form has closed and the why. I don't know why the reason, which should be a string, is not getting passed to the function that sends the email.
I'm not dealing with the form at all in this code. For simplicity, I opened a new standalone script, not linked to any Google Form or Sheet, to test just this portion (determine reason and send email). Here is the code:
First function to determine reason the form closed and to call second function to send email:
function FindReason() {
var Day1Seats = 5;
var Day2Seats = 5;
var Day1Responses = 6;
var Day2Responses = 1;
var FormClose = false;
if(Day1Responses >= Day1Seats) {
var reason = "All seats taken for Day 1";
SendAnEmail(reason);
}
else if(Day2Responses >= Day2Seats) {
var reason = "All seats taken for Day 2";
SendAnEmail(reason);
}
else if(FormClose) {
var reason = "Form close date";
SendAnEmail(reason);
}
}
Second function, which sends email (email address obviously changed for privacy):
function SendAnEmail(closereason) {
var recipientTO = "name#example.com";
var subject = "This is the subject";
//var closereason = "All seats taken for Day 1";
MailApp.sendEmail({
to: recipientTO,
subject: subject,
htmlBody: (new Date()) + "<br>Your form has been closed because: <br>" + closereason + "<br>End of message."
});
}
When I run FindReason(), I do get an email but the body doesn't properly show the reason. This is an example of the emails I get:
Sun Feb 24 2019 12:26:01 GMT-0800 (PST)
Your form has been closed because:
undefined
End of message.
I don't know why the reason is shown as undefined. I tried setting the reason inside the SendAnEmail function (the line that has been commented out) and that worked properly, but I need to be able to change the reason.
Edit: It works now. It turns out the "run" button was set to run the "SendAnEmail" function rather than the "FindReason" function. Thanks to everyone who offered suggestions.
What's probably happening is that else if(FormClose = TRUE) is not going to work.
= is the assignment operator, you want equality, which is ===.
TRUE is lowercase in js, so you want true.
You also use False, which should be false.
If you look in the google scripts editor's console, you should see an error of some sort, because it won't know what False or TRUE are (it will think they are undeclared variables).
try else if(FormClose === true) or even more simply, else if (FormClose), because if it evaluates to true, that will do the job.
function FindReason() {
var Day1Seats = 5;
var Day2Seats = 5;
var Day1Responses = 6;
var Day2Responses = 1;
var FormClose = false;
if(Day1Responses >= Day1Seats) {
var reason = "All seats taken for Day 1";
SendAnEmail(reason);
} else if(Day2Responses >= Day2Seats) {
var reason = "All seats taken for Day 2";
SendAnEmail(reason);
} else if(FormClose) {
var reason = "Form close date";
SendAnEmail(reason);
}
}
I am using the following two scripts to either reply to or forward emails when certain labels are applied. I have two sheets (replySheet and forwardSheet) that hold label names in the first column. replySheet then has the email reply text in the next cell, while forwardSheet has the email address to forward the message to.
Two questions:
I have received the error message "Service invoked too many times for one day" for GmailApp.getUserLabelByName. I understand that the limit for Google Apps for Education is 10,000 per day, but this code should just run every five minutes, or 288 times every day for each label. What am I misunderstanding? Any thoughts for re-writing the code to avoid this?
.moveToArchive() doesn't seem to do anything in replyLabel(). I've tried moving it to different points in the code (before and after sending the reply), but it doesn't archive the thread.
Thank you for any suggestions to either issue. Please let me know if I can make my question any clearer.
var thisSS = SpreadsheetApp.getActiveSpreadsheet();
var forwardSheet = thisSS.getSheetByName('Forwards');
var emailSheet = thisSS.getSheetByName('Email');
var alias = emailSheet.getRange(3, 2).getValue();
var replyTo = emailSheet.getRange(2, 2).getValue();
var fromName = emailSheet.getRange(1, 2).getValue();
var replySheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Replies');
function forwardLabel() {
var data = forwardSheet.getRange(2, 1, forwardSheet.getLastRow(), 2).getValues();
for (i in data) {
var row = data[i];
var name = row[0].toString();
var email = row[1].toString();
if (name && (email != "")) {
var label = GmailApp.getUserLabelByName(name);
var threads = label.getThreads(0, 100);
for (i in threads) {
var messages = threads[i].getMessages();
for (j in messages) {
Logger.log(messages[j].getSubject());
messages[j].forward(email, {bcc:alias, from:alias, name:fromName}).markRead();
label.removeFromThread(threads[i]);
}
Utilities.sleep(1000);
}
}
}
}
function replyLabel() {
var data = replySheet.getRange(2, 1, replySheet.getLastRow(), 2).getValues();
var signature = emailSheet.getRange(4, 2).getValue().toString();
var alias = emailSheet.getRange(3, 2).getValue();
for (i in data) {
var labelName = data[i][0].toString();
var label = GmailApp.getUserLabelByName(labelName);
var replyText = data[i][1].toString();
replyText = replyText + signature;
if (label && (replyText !== "")) {
var labeledEmails = label.getThreads(0, 100);
for (j in labeledEmails) {
labeledEmails[j].moveToArchive();
label.removeFromThread(labeledEmails[j]);
var messages = labeledEmails[j].getMessages();
var message = messages[0];
message.reply(replyText,{htmlBody:replyText, bcc:alias, from:alias, name:fromName});
Utilities.sleep(2000);
}
}
}
}
How many labels do you have ? You have nested loops and the 288 gets multiplied by each loop and you could quickly be hitting the 10,000.
Also, note that you are not counting other GMail Read operations like getTHreads() and getMessages().
If you factor in all these, you could have a number exceeding 10,000.