I'm modifying Amit's code ( found here: http://labnol.org/?p=20884)
to try to send email with the data from a Google Form.
But what I'm trying to grab is from his keys and columns.
I want to specifically take the first 1 and 2 column's data from the row in question and use it as a var in the subject field.
But the output (in email and when sent to asana) is listed as undefined. Where did I go wrong?
/*
Send Google Form Data by Email v4.2
Written by Amit Agarwal amit#labnol.org
Source: http://labnol.org/?p=20884
*/
/**
* #OnlyCurrentDoc
*/
function Initialize() {
try {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers)
ScriptApp.deleteTrigger(triggers[i]);
ScriptApp.newTrigger("EmailGoogleFormData")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit().create();
} catch (error) {
throw new Error("Please add this code in the Google Spreadsheet");
}
}
function EmailGoogleFormData(e) {
if (!e) {
throw new Error("Please go the Run menu and choose Initialize");
}
try {
if (MailApp.getRemainingDailyQuota() > 0) {
// You may replace this with another email address
var email = "x+00000000#mail.asana.com";
// Enter your subject for Google Form email notifications
var key, entry,
message = "",
ss = SpreadsheetApp.getActiveSheet(),
cols = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
// Iterate through the Form Fields
for (var keys in cols) {
key = cols[keys];
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
// Only include form fields that are not blank
if ((entry !== "") && (entry.replace(/,/g, "") !== ""))
message += key + ' :: ' + entry + "\n\n";
var first = entry[1];
var last = entry[2];
var subject = first+" "+last+": Interested Candidate";
}
MailApp.sendEmail(email, subject, message);
}
} catch (error) {
Logger.log(error.toString());
}
}
/* For support, contact developer at www.ctrlq.org */
entry is a string, defined here:
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
...which you later treat as an array:
var first = entry[1];
var last = entry[2];
At this point, first and last will both be undefined, because entry isn't an array. Further, this is inside a for loop that's traversing all the columns in the row - you can't see any bad side-effect from that, but these assignments and generation of a subject are happening multiple times.
That last clue suggests a better way to achieve your goal. Define the first and last variables before the loop, with default values. Then when looping over columns, watch for the columns containing the candidates' name, and update the default contents. Finally, after the loop, generate the subject line.
function EmailGoogleFormData(e) {
if (!e) {
throw new Error("Please go the Run menu and choose Initialize");
}
try {
if (MailApp.getRemainingDailyQuota() > 0) {
// You may replace this with another email address
var email = "x+00000000#mail.asana.com";
// Enter your subject for Google Form email notifications
var key, entry,
first = "unknown", last = "unknown",
message = "",
ss = SpreadsheetApp.getActiveSheet(),
cols = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
// Iterate through the Form Fields
for (var keys in cols) {
key = cols[keys];
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
// Only include form fields that are not blank
if ((entry !== "") && (entry.replace(/,/g, "") !== ""))
message += key + ' :: ' + entry + "\n\n";
if (key == "first") { // Assumes "first" is column header
first = entry;
}
if (key == "last") { // Assumes "last" is column header
last= entry;
}
}
var subject = first+" "+last+": Interested Candidate";
MailApp.sendEmail(email, subject, message);
}
} catch (error) {
Logger.log(error.toString());
}
}
Sandy Good has created a similar app Data Director. I don't know why he did not mention it here? May be it's not what you're looking for.
I haven't used it yet, but thought his works might help someone who needs it.
----------------------------------------
OVERVIEW:
Send form data to different sheet. Integrate with Calendar. Sends emails. Makes an Edit URL and/or a PreFilled URL.
The Data Director for Forms Add-on has multiple features. It can send the form response to an alternate spreadsheet. It can send an email or multiple emails when the Form is submitted. It can add a guest to your calendar event.
When your Google Form is submitted, the Data Director for Forms Add-on can get the last form submission, and save it to a second spreadsheet destination of your choice. The destination spreadsheet can be any Google spreadsheet that your Google account has permission to write to. For example: Your Google Form currently writes data to a spreadsheet, but you want the form response to also go into a second sheet in the same spreadsheet. This Add-on can do that. Or the Add-on can write a copy of the form response to a completely different spreadsheet.
You should install this add-on if you want to save a copy the form response to to a destination other than what is set in the Form's design.
But that's not all Data Director can do! Data Director will also create an Edit URL and/or a PreFilled URL, and save those links to the spreadsheet.
There's even more! It will also send an email to the email address of your choice with a custom message. This is an extra option that you may want or need to use.
Here's a list of What Data Director can do!
Send a copy of the form response to a Google spreadsheet.
The same Google spreadsheet that is already receiving the Form response, or
A different spreadsheet than is currently receiving the Form response.
Exclude the timestamp from the copied response if you choose. The default is to include the timestamp.
Create an Edit URL and save a link to the destination spreadsheet.
Create a PreFilled URL and save the link to the destination spreadsheet.
Send multiple emails to the email addresses of your choice.
Send an email to the email address collected from a Form field.
Include the Edit Url and/or the PreFilled Url in the email.
CC the email to the address of your choice, or not.
Includes the option to specify the subject line.
The Body of the email can be written in the settings for the email. No need to create a template email.
Related
So I have only recently learnt what Apps Script is.
I was attempting to make a google form that takes the data from that form and puts it into a google sheet.
Then there is a Apps script that runs inside the spreadsheet that when people submit a form it sends a nicely formatted email to be printed off, but after finding some code online I was able to change to my liking I realized that the images were being inserted into the email as attachments which won't print off in one nice clean document.
So I was wondering if anyone knew how I could take the image URLs from their cells and insert them into the email as inline images.
Here is my code so far:
function Initialize() {
try {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers)
ScriptApp.deleteTrigger(triggers[i]);
ScriptApp.newTrigger("EmailGoogleFormData")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit().create();
} catch (error) {
throw new Error("Please add this code in the Google Spreadsheet");
}
}
function EmailGoogleFormData(e) {
if (!e) {
throw new Error("Please go the Run menu and choose Initialize");
}
try {
if (MailApp.getRemainingDailyQuota() > 0) {
// You may replace this with another email address
var email = "email address";
// Enter your subject for Google Form email notifications
var subject = "Form";
var key, entry,
message = "",
ss = SpreadsheetApp.getActiveSheet(),
cols = ss.getRange(1, 2, 1, ss.getLastColumn()).getValues()[0];
// Iterate through the Form Fields
for (var keys in cols) {
key = cols[keys];
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
// Only include form fields that are not blank
if ((entry !== "") && (entry.replace(/,/g, "") !== ""))
message += key + ' : ' + entry + "\n\n";
}
MailApp.sendEmail(email, subject, message);
}
} catch (error) {
Logger.log(error.toString());
}
}
Thank you for your help in advance!
I have been searching the API and google for a way to change the images into inline images, but everything I've messed with hasn't worked.
Get the image as a blob and name it, use Context-ID.
https://developers.google.com/apps-script/reference/mail/mail-app#sendemailmessage
I have just set up a google form that automatically populates a Google Sheet. I'm trying to set up an automatic email to notify a specific contact everytime someone submits a form and marks their test as positive. If they submit a negative test, I don't want it to send anything.
I've written the following with the following trigger:
Select event source - From Spreadsheet
Select event type - On from Submission
I've linked the formula to another Sheet for the script contacts. I managed to get it to send an email, however, it was previously sending an email even if a negative test was submitted. Because of this, I added another 'If' for a negative display but now it wont send anything at all.
Can anyone help? This is my first time coding any feel like I have hit a roadblock.
Formula:
function positiveemailsubmission()
{
var positiverange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("LFT Submissions").getRange("G2:G50000")
var positivevalue = positiverange.getDisplayValue("Positive");
{
if (positivevalue.getDisplayValue() === "Positive")
{
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Script info").getRange("B2");
var emailAddress = emailRange.getDisplayValue();
var message = 'A positive LFT test has been submitted. Please see the form for more information (WWW.DOCUMENTLINK.CO.UK)';
var subject = 'Positive LFT Submission ';
GmailApp.sendEmail(emailAddress, subject, message);
}
if (positivevalue.getDisplayValue() === "Negative")
{
}
}
}
In order to provide a proper response to the question, I'm writing this answer as a community wiki, since the solution is in the comments section.
Like #Yuri Khristich said, positiverange was a range of a lot of rows "G2:G50000", in this case what you need is the value of the last row (last submit from Form) to determine if that specific answer was "Positive" or "Negative".
I tested your script with the changes mentioned in the comments and it worked.
function positiveemailsubmission()
{
var positiverange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("LFT Submissions");
var positivevalue = positiverange.getRange("G" + positiverange.getLastRow());
{
if (positivevalue.getDisplayValue() === "Positive")
{
var emailRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Script info").getRange("B2");
var emailAddress = emailRange.getDisplayValue();
var message = 'A positive LFT test has been submitted. Please see the form for more information (WWW.DOCUMENTLINK.CO.UK)';
var subject = 'Positive LFT Submission ';
GmailApp.sendEmail(emailAddress, subject, message);
}
if (positivevalue.getDisplayValue() === "Negative")
{
}
}
}
I have created a Google form with a Google spreadsheet associated to it that can be accessed (using the form URL) by each recipient I am sending it to. Right now the URL isn't unique to each recipient. The recipient can access the form then submit it. Upon submit, I am generating an edit URL via the GetEditResponse() function available in the Google FormResponse Class. The recipient can then edit their response using the edit url I provide them once they submit the form.
Here is my Google App Script code:
function myFunction() {
assignEditUrls();
}
function assignEditUrls() {
var form = FormApp.openById('formId');
//enter form ID here
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Form Responses');
//Change the sheet name as appropriate
var data = sheet.getDataRange().getValues();
var urlCol = 5; // column number where URL's should be populated; A = 1, B = 2 etc
var responses = form.getResponses();
var timestamps = [], urls = [], resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(shortenUrl(responses[i].getEditResponseUrl()));
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0]?urls[timestamps.indexOf(data[j][0].setMilliseconds(0))]:'']);
}
sheet.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
}
function shortenUrl(longUrl) {
// google url shortener api key
var key = "apiKey";
var serviceUrl="https://www.googleapis.com/urlshortener/v1/url?key="+key;
var options={
muteHttpExceptions:true,
method:"post",
contentType: "application/json",
payload : JSON.stringify({'longUrl': longUrl })
};
var response=UrlFetchApp.fetch(serviceUrl, options);
if(response.getResponseCode() == 200) {
var content = JSON.parse(response.getContentText());
if ( (content != null) && (content["id"] != null) )
return content["id"];
}
return longUrl;
}
You can also view the Spreadsheet associated with the form.
The problem with this model is that the recipients need to get back an edited URL in order to edit their response at a later time. Instead I want them to keep the same URL I originally provided them with and each time they come back to that URL I should identify which recipient it is and redirect them with the last updated URL.
For that scenario to happen I would need to:
1. Create a unique URL (identifier) for each recipient.
2. The same URL should point to the last updated URL (Form) so that they can always use the same URL originally provided (backend process).
Is this possible to achieve using Google's available tools? If it is, how can I create a unique url to identify which recipient responded to the form?
Staying within the confines of Google Apps Script, you cannot provide a redirection to a Google Form.
Here are two ideas off the top of my head...
If you're willing to use another service to handle the redirection, you could have it do the redirection for you. (I'm not recommending any, just saying it's an option.)
You could write a Google Apps Script web service that would present a mock-up of your real form. Users would have a unique URL to pass their unique identifier as a HTTP query parameter; heck, you could use goo.gl to produce a short URL for each of them as you do now. Based on the identifier, pre-fill the fake form with their last results. Upon commit, your web service can submit the form programmatically.
Create a unique URL (identifier) for each recipient.
Creating a separate Google Form for each person will give you this.
The same URL should point to the last updated URL
Assuming your go with creating a separate Form for each user, try placing the edit URL back into each Form's description or 1st page some where after submission of the 1st response. You'd have to make it clear that they have to click on that link going forward. There isn't a setting to automatically re-direct original Google Form URLs to their corresponding Edit Response ones AFAIK, if that's what you are asking for.
I am currently using the following to send an email to a distribution list containing the headers/questions and corresponding answers when a Google Form is submitted:
function sendFormByEmail(e) {
// Subject field of email
var emailSubject = "New Student Case Filed";
// Comma-separated list of email addresses for distribution.
var yourEmail = "my email address";
// Spreadsheet key, found in the URL when viewing your spreadsheet.
var docKey = "my spreadsheet key";
// If you want the script to auto send to all of the spreadsheet's editors, set this value as 1.
// Otherwise set to 0 and it will send to the yourEmail values.
var useEditors = 0;
// Have you added columns that are not being used in your form? If so, set this value to
// the NUMBER of the last column that is used in your form.
// for example, Column C is the number 3
var extraColumns = 40;
if (useEditors) {
var editors = DocsList.getFileById(docKey).getEditors();
if (editors) {
var notify = editors.join(',');
} else var notify = yourEmail;
} else {
var notify = yourEmail;
}
// The variable e holds all the submission values in an array.
// Loop through the array and append values to the body.
// Need to omit headers with no response*
var s = SpreadsheetApp.getActive().getSheetByName("StudentCases");
if (extraColumns){
var headers = s.getRange(1,1,1,extraColumns).getValues()[0];
} else var headers = s.getRange(1,1,1,40).getValues()[0];
var message = "";
for(var i in headers) {
message += headers[i] + ' : '+ e.values[i].toString() + '\n\n';
}
Now I also want a Google Doc created containing the headers and responses. So far, I've been able to create the Doc, add the title, and add a paragraph, but now I need to replicate the array of headers/responses in the Google Doc as well.
// Create a new Google Doc named 'Case File' * need to figure out how to pull last name response from form.
var doc = DocumentApp.create('Case File: Last Name*');
// Access the body of the Doc, add a paragraph, *need to append array of headers/answers
var body = doc.getBody().body.appendParagraph();
// Get the URL of the Google Doc to include in Email
var url = doc.getUrl();
// Get ID of Doc to attach to email
var id = doc.getId()
One other issue I'd like to solve; I only need the headers/questions that contain a response, as many of them will not necessarily warrant an answer. So in other words, IF there is no answer, THEN do not append to email.
It seems you provided a list of general requirements without much in the way of what you tried with the results you got. StackOverflow will be helpful to you if you provide more pointed questions about what exactly you have tried..
Can you share the exact code you have tried please? What were your results?
From a high level, I would proceed with this general workflow.
Draft a gDoc template using placeholders in a variables nomenclature of your choice ~FName ~LName etc..
Use an onFormSubmit trigger to make a copy of the gDoc template when a new form is submitted.
Replace the ~FName ~LName placeholders in the copied gDoc with content captured in the form
Save the copied gDoc as a PDF
email the PDF to the email address provided in the form submission
I've been recently having trouble with what I believe to be a concurrency issue when people are submitting the form near the same times, which is resulting in lost data for a google form. I'm already using the Lock service to prevent this issue, but I still seem to have problems. http://googleappsdeveloper.blogspot.com/2011/10/concurrency-and-google-apps-script.html
The form currently has onFormSubmit triggers: formSubmitReply and logMessage. formSubmitReply sends a confirmation to people that submitted the form and logMessage is supposed to back up the information in a separate spreadsheet in the case that rows in the regular spreadsheet get clobbered. It should be extracting the values from the formSubmit event and then appending it to the "log" sheet.
I've included all the current code for the script and replaced emails with place holders. Can I get some help identify anything buggy in the code that could be preventing the form from recording rows in the form?
function getColIndexbyName(colName){
var sheet=SpreadsheetApp.getActiveSheet();
var rowWidth=sheet.getLastColumn();
var row=sheet.getRange(1,1,1,rowWidth).getValues();//this is the first row
for ( i in row[0]){
var name=row[0][i];
if(name == colName || new RegExp(colName,'i').test(name)){
return parseInt(i)+1;
}
}
return -1
}
function makeReceipt(e){
/*This is for Student Volunteer auto-confirmation*/
var ss,sheet, rowWidth, headers, rowWidth,curRow, values, greeting, robot, msg, space, newline;
curRow=e.range.getRow();
ss=SpreadsheetApp.getActiveSpreadsheet();
sheet=ss.getSheetByName("RAW");
rowWidth=sheet.getLastColumn();
headers=sheet.getRange(1,1,1,rowWidth).getValues();
values=sheet.getRange(curRow,1,1,rowWidth).getValues();
greeting='Hi '+sheet.getRange(curRow,getColIndexbyName('First Name'),1,1).getValue()+"! <br><br>"+ ' ';
robot="<i>Below are the responses you submitted. Please let us know if any changes arise!</i> <br><br>";
msg=greeting+robot;
space=' ';
newline='<br>';
for( i in headers[0]){
//only write non "Reminders" column values
if(headers[0][i]!="Reminders"){
msg+="<b>";
msg+=headers[0][i];
msg+="</b>";
msg+=":";
msg+=space;
msg+=values[0][i];
msg+=newline;
}
}
return msg;
}
/**
* Triggered on form submit
**/
function formSubmitReply(e) {
var ss, row, mailIndex, userEmail, message, appreciation;
var lock = LockService.getPublicLock();
if(lock.tryLock(60000)){
try{
ss=SpreadsheetApp.getActiveSheet();
row=e.range.getRow();
mailIndex=getColIndexbyName('Email Address');
userEmail=e.values[mailIndex-1];
message=makeReceipt(e);
MailApp.sendEmail(userEmail, 'BP Day 2012 Confirmation for'+' '+userEmail,message,{name:"Name", htmlBody:message, replyTo:"example#example.com"});
messageAlert100(e);
} catch(err){
e.values.push("did not send email");
MailApp.sendEmail(""example#example.com","error in formSubmitReply"+err.message, err.message);
}
logToSpreadsheet(e);
} else {
//timeOut
try{
if(e && e.values){
logToSpreadsheet(e);
e.values.push("did not send email");
}
}catch(err){
MailApp.sendEmail("example#example.com", "error in logging script block "+err.message, err.message)
}
}
}
/**
* Triggered on form submit
**/
function messageAlert100(e){
var cheer_list, curRow, cheer_list, cheer_index, cheer, ss=SpreadsheetApp.getActiveSpreadsheet();
if(e && e.range.activate){
curRow=e.range.getRow();
}
cheer_list=["Congratulations!", "Give yourself a pat on the back!", "Yes!", "Cheers!","It's time to Celebrate!"];
cheer_index=Math.floor(Math.random()*cheer_list.length);
cheer=cheer_list[cheer_index];
if(typeof(curRow) != "undefined" && curRow % 100 ==0){
MailApp.sendEmail("example#example.com", ss.getName()+": "+cheer+" We now have "+ curRow + " Volunteers!", cheer+" We now have "+ curRow + " Volunteers!");
}
}
/**
*
**/
function logToSpreadsheet(e){
var ss=SpreadsheetApp.getActiveSpreadsheet(), sh;
if(!ss.getSheetByName("log")){
sh=ss.insertSheet("log");
}
sh=ss.getSheetByName("log");
if(e && e.values !==null){
sh.appendRow(e.values)
} else {
sh.appendRow(e);
}
Logger.log(e);
}
There is a very simple approach that I use to avoid concurrency issues with forms, I had to imagine that before GAS offered the lock method.
Instead of using the on form submit trigger I use a timer trigger (every few minutes or so) on a function that checks a column for a flag (MAIL SENT) ... if the flag is not present I send the email with processed data, copy to the backup sheet and set the flag. I do this on every row starting from the last one and stop when I find the flag. This way I'm sure all datarows are processed and no duplicate mail is sent.
It is actually very simple to implement, your script will need only a few modifications.
Viewed from the users side the result is nearly the same as they receive a mail just a few minutes after their submission.
EDIT : of course in this setup you cannot use e parameter to get the form data but you'll have to read data on the sheet instead... but that's not really a big difference ;-)