I am quite new to Google Scripts and attempting to learn, but documentation is either conflicting, non-existent, or not working for me.
The goal is to have a script that collects responses from a Google Form and sends emails based on one of the user input fields (a drop down box where the user can select their location). Problem being that I cannot retrieve the Form responses. I am receiving an error that the "getItemResponses();" is not a function. How do I get responses from a Form? All the other parts I have figured out.
Flow - I have setup a trigger by going to Edit -> Current projects' triggers -> created a new trigger that on submit it runs my function. What is supposed to happen is (later in the script) sendMail sends a mail message as me to the var "respondent". However, I receive the error "getItemResponses();" is not a function when just attempting to run from the script editor and filling out a test form submissions (via email on the latter).
Thank you for any help in advance. Please note, again, I'm new at the Google Scripts.
function sendEmails(e) {
// Get active form and responses
var formResponse = e.response;
var itemResponses = formResponse.getItemResponses();
// Assign responses to variables
var respondent = itemResponses[0].getResponse();
var district = itemResponses[1].getResponse();
var urgency = itemResponses[2].getResponse();
var type = itemResponses[3].getResponse();
var topic = itemResponses[4].getResponse();
// TAM email address. Used in sendTo field later
var sendTo = "person#email.com";
MailApp.sendEmail(sendTo,"subject","message body");
if(type == "Option 1") {
MailApp.sendEmail(respondent,"subject","message body");
}
}
Issue:
form.getResponses() returns an array of FormResponse, as you can see here. An array of responses does not have a method called getItemResponses() and this is the reason you code is failing. What you're doing right now is getting all the responses corresponding to a form, and not the one that was just submitted.
You should use the method getItemResponses() from a single FormResponse instead.
Solution:
Assuming that:
You want the script to run (and, if the conditions are met, send an email) every time the form is submitted.
You have installed an onFormSubmit trigger, connected to your form.
If that's the case, you should be using the trigger event object to get the FormResponse. You should replace this:
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var itemResponses = formResponses.getItemResponses();
With this:
var formResponse = e.response;
var itemResponses = formResponse.getItemResponses();
Update:
Your form does not have 5 items, but 4. First item is actually the respondent email, and as such, it has to be retrieved via FormResponse.getRespondentEmail(). Because of this, itemResponses only gets to index 3, not to 4, and itemResponses[4] is undefined. That's the reason behind your new error. You should do this instead:
var respondent = formResponse.getRespondentEmail();
var district = itemResponses[0].getResponse();
var urgency = itemResponses[1].getResponse();
var type = itemResponses[2].getResponse();
var topic = itemResponses[3].getResponse();
Reference:
Event Objects: Form submit
Form.getResponses()
FormResponse.getRespondentEmail()
While it is possible to do what you want to do from the form its self it will be difficult as a beginner since as you noticed there aren't many example scripts to work off.
It will be much easier to do this from the responses sheet and there are plenty of examples around for processing form data from a spreadsheet.
The example below is a simple (untested) script to send a reply on form submission.
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var respSheet = ss.getSheetByName('Form responses 1');
var rowIndx = e.range.getRow();
var formData = respSheet.getRange(rowIndx, 1, 1, respSheet.getLastColumn()).getValues();
var respondent = formData[0].getResponse();
var district = formData[1].getResponse();
var urgency = formData[2].getResponse();
var type = formData[3].getResponse();
var topic = formData[4].getResponse();
MailApp.sendEmail(respondent, district + ' ' + topic, "Your reply was recieved and is being processed");
respSheet.getRange(rowIndx, 5).setValue('Email send to ' + respondent);
}
Related
I have a sheet named "Credentials" populated with the following data:
And another sheet in the same spreadsheet named "Clients" which is linked to a google form:
My goal here is to show on the confirmation message the data from the "Credentials" sheet which is in the same row as the row that is filled when a form is submitted. E.G: the form data that was stored in row 2 should show "usr a" and "pass a" on the confirmation message
I have this apps script to do so but it always shows the last data from the "Credentials" sheet on the form confirmation message and i can't see why:
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet_clientes = ss.getSheetByName('Clients')
var sheet_demos = ss.getSheetByName('Credentials')
var form = FormApp.openByUrl(ss.getFormUrl())
var demosRange = sheet_demos.getRange('A2:B4')
var demosData = demosRange.getValues()
var lr = sheet_clientes.getLastRow()
var user = demosData[lr][0]
var password = demosData[lr][1]
form.setConfirmationMessage('Congratulations: Your user is:\n' + user + '\n Your password is:\n' + password)
}
You can use the Form Submit Trigger of Google Apps Script to run the script whenever a response was submitted.
But for setConfirmationMessage(message) Bryan P explained that
we can't conditionally set and immediately display a custom
message for the current user's response based on what their answers
were. The confirmation message is "front-loaded" in a sense.
Since we cannot use setConfirmationMessage() for this case, I created an alternative that will send the credentials to the email address the provided in the response.
Try this:
Code:
function onFormSubmit(e) {
var range = e.range;
var sheet = range.getSheet();
var row = range.getRow();
var spreadsheet = sheet.getParent();
var credSheet = spreadsheet.getSheetByName("Credentials");
var creds = credSheet.getRange(row, 1, 1, 2).getValues();
var values = e.namedValues;
var email = values['Email Address'];
var username = creds[0][0];
var password = creds[0][1];
var message = 'Your user is:' + username + '\nYour password is:' + password;
var subject = 'Credentials';
GmailApp.sendEmail(email, subject, message)
}
Trigger Setup:
In your Apps Script, go to the left side menu and click Triggers.
Click Add Trigger.
Copy the setup below.
Click Save
Note: Make sure to copy and save the code provided above before creating the trigger.
Example:
References:
Event Object
Class Range
Class Sheet
Class GmailApp
I'm new to google scripts so please bear with me. I'm working on automating communication for a 2 condition study signup using a google form and google sheets. Essentially, when a participant submits a form, I need to send a confirmation email (trigger is form submission) with the date they chose, and a new unique ID based on their condition (I have two separate lists of premade IDs I need to use). Right now, I have the main form response sheet and then I'm using = query to separate the survey responses into sheets for the two conditions based on a multiple choice question in the survey ('Morning' and 'Evening'). I have the ID lists filled into the corresponding sheets so that when the = query sorts the responses, they are automatically "assigned" an ID as the rows fill. I have the code working to send emails without the IDs, just using the name and date in the first sheet of the spreadsheet (the form response sheet), but am running into problems grabbing the ID from the other corresponding sheet to put into the email.
Here is what I have:
function IntialEmails(e){
var ss = SpreadsheetApp.openById('MyownSpreadsheetID'); //not including real ID for privacy reasons
var type = e.values[4];
if (type == 'Condition A') {
var sheet = ss.getSheets()[1];
ss.setActiveSheet(sheet);
}
if (type == 'Condition B') {
var sheet = ss.getSheets()[2];
ss.setActiveSheet(sheet);
}
var userName = sheet.values[0];
var userEmail = sheet.values[2];
var date = sheet.values[4]; //Using date from corresponding spreadsheet based on survey format
var ID = sheet.values[5]; // Should be from corresponding sheet
var subject = "Study Signup Confirmation and Participant ID";
var templ = HtmlService
.createTemplateFromFile('My html'); //not including real ID for privacy reasons
templ.date = date;
templ.ID = ID;
templ.userName = userName;
var message = templ.evaluate().getContent();
MailApp.sendEmail({
to: userEmail,
subject: subject,
htmlBody: message
});
}
I am super stuck, and continuously getting errors. This code throws back the error 'TypeError: Cannot read property '0' of undefined' If anyone can help me out it would be much appreciated!
If you want to get a value from a sheet you can do something like this:(Even I wouldn't normally do it this way I'm kind of following the same method that you used. I'm going to assume that you have your id's or what ever data in rows. But obviously I don't know since you didn't provide that information.
function initEmails(e) {
var ss=SpreadsheetApp.getActive();
var shts=ss.getSheets();
var sh=(e.values[4]=="Condition A")?shts[1]:shts[2];
var vs=sh.getRange(1,1,sh.getLastRow(),7).getValues();
for(var i=0;i<vs.length;i++) {
if(vs[i][7]!='USED') {
var userName=vs[i][0];
var userEmail=vs[i][2];
var date=vs[i][4];
var ID=vs[i][5];
var subject = "Study Signup Confirmation and Participant ID";
sh.getRange(i+1,8).setValue('USED');//this provides a way to keep track of which row was used last
}
/*.............I have no idea what your trying to do beyond this point.
}
But I don't think that you going to be able to insert these values into your html template this way:
templ.date = date;
templ.ID = ID;
templ.userName = userName;
Normally, templates are filled with desired data during the evaluate process by using scriptlets.
Templated HTML
I have many users who submit workload data via a google from. These include a field that designates the site they are reporting from.
I now have 1000s of responses that I query > filter from the main response submission sheet, into separate spreadsheets for each site. The data load time for the query is getting longer and longer with the growing responses.
[FORM] --> [MASTER SHEET WITH RESPONSES]
|
|
/ | \
QUERY FILTER INTO SEPARATE SHEETS
I'd like to, instead, force the form to submit the responses directly into the appropriate site spreadsheet based on which site is selected in the form. Is there a way to do this?
[FORM: USER SITE SELECTION] --> SCRIPT SENDS DATA DIRECTLY TO SHEET FOR THAT SITE
You asked whether there is a way to force a form to submit responses directly into a spreadsheet based on a form selection?
In comments TheMaster and DimuDesigns referred to i) using a script bound to the form, not the spreadsheet, and ii) using onFormSubmit as an installable trigger. The following answer is an example of those principles.
In testing, I found that it was essential that the form should be named onFormSubmit(e). If the code is named as a custom function, then I found it gave an error "You do not have permission to call SpreadsheetApp.openByUrl."
Possibly I might/could/should have used switch instead of if...else. But there are many ways of realising this outcome, and this code should be regarded as just one example.
function onFormSubmit(e) {
//so5745108802
var form = FormApp.getActiveForm();
//Logger.log(JSON.stringify(e));
var resp = e.response.getItemResponses();
//Logger.log(resp);
//Logger.log("DEBUG: resp = "+resp+", and length = "+resp.length);
var dateStamp = Utilities.formatDate(new Date(), "GMT+10:00", "dd-MM-yyyy");
var output = [];
var temp=[];
temp.push(dateStamp);
for (var j = 0; j < resp.length; j++) {
var itemResponse = resp[j];
// Logger.log('DEBUG: Response to the question "%s" was "%s"',itemResponse.getItem().getTitle(),itemResponse.getResponse());
// Logger.log("DEBUG: the response = "+ itemResponse.getResponse())
temp.push(itemResponse.getResponse());
}
output.push(temp);
// Logger.log(output);
var target = resp[2].getResponse();
// Logger.log("DEBUG: the target sheet is "+target)
// Note: be sure to edit the urls for your own spreadsheets
if (target === "A"){
// target = A
var url = "https://docs.google.com/spreadsheets/d/url1/edit" // Spreadsheet=so_57451088_A
}
else if (target === "B"){
// target = B
var url = "https://docs.google.com/spreadsheets/d/url2/edit" // Spreadsheet=so_57451088_B
}
else {
// target = C
var url = "https://docs.google.com/spreadsheets/d/url3/edit" // Spreadsheet=so_57451088_C
}
var ss = SpreadsheetApp.openByUrl(url);
Logger.log(ss.getName());
var sheetname = "Sheet1";
var sheet = ss.getSheetByName(sheetname);
var sheetLR = sheet.getLastRow();
var targetRange = sheet.getRange(sheetLR+1,1, 1, resp.length+1);
// Logger.log("DEBUG: the target range = "+targetRange.getA1Notation());
targetRange.setValues(output);
}
Form screenshot
Example Spreadsheet screenshot
I think I am encountering a bug? I had a script in google sheets were it would get the submitted Google Form values and put them into a google doc table.
I have migrated my code over to the Google Form and upon submitting it doesn't seem to grab the data I entered.
function doTheWork(e) {
var timeStamp = String(e.values[0]);
var entryEmail = String(e.values[1]);
var name = String(e.values[2]);
var number = String(e.values[3]);
var cells = [timeStamp, entryEmail, name, number];
};
As part of the debugging process I found this was the error, thanks to this code:
var name = 'Bob';
var number = '20';
var cells = [name, number];
Basically, manually adding the string works. But I need it to take the data from the form.
I'm answering the questions because while the links in the comments above are what put me on track it did take me a while to comprehend what the documentation meant.
Here was the solution:
// Get the for - You could alternatively open by id.
var form = FormApp.getActiveForm();
// Get responses
var formResponses = form.getResponses();
// Loop through the filled out form to gather all of the responses
for (var i = 0; i < formResponses.length; i++) {
// Prepare the array
var formResponse = formResponses[i];
// Just FYI I got the response URL because it was handy for my situation. It is not necessary code but I if anyone else gets stuck the way I did, hopefully this helps
var editUrl = String(formResponse.getEditResponseUrl());
var itemResponses = formResponse.getItemResponses();
// My form had two fields for this test. Name & Number. Here is how I was able to access the answers.
var name = itemResponses[0].getResponse();
var number = itemResponses[1].getResponse();
}
I am working with google forms via app script. I want to determine if the current form that is being submitted is in edit mode or a new response? How can I check this in the onSubmit event.
If yes that user is edit a previously submitted response than I want to change the value in my spread sheet to "yes".
below is a snippet of my code:
function testExcel2() {
var email = "email";
var s = SpreadsheetApp.openById("id");
var sheet = s.getSheets()[0];
var headers = sheet.getRange(1,1,1,sheet.getLastColumn() - 1).getValues()[0];
var datarow = sheet.getRange(sheet.getLastRow(),1,1,sheet.getLastColumn() - 1).getValues()[0];
var message = "";
for(var i in headers)
{
message += "" + headers[i] + " : " + datarow[i] + "\n\n";
}
MailApp.sendEmail(email, "Submitted Data Test", message);
var af = FormApp.getActiveForm();
//af.setCustomClosedFormMessage("The form is currently processing a submission, please refresh the page.");
af.setConfirmationMessage('Thanks for responding!')
//af.setAcceptingResponses(false);
var rowKey = "o" + sheet.getLastRow();
var editCell = sheet.getRange(rowKey).setValue('no');
}
The Google Form form submit event doesn't have a field that could help to know if the response is a new response or if it's a response edit. As Sandy already said through question comments, the Form Servicer classes and methods, nor the response values include something that could help on this.
By the other hand, the Google Sheets submit event has a range field that could help. The following script bounded to a Google spreadsheet logs the response row:
function onFormSubmit(e){
var response = e.range;
Logger.log(response.getRow());
}
The above could be used to keep updated a column to hold a revision counter. If the corresponding cell is blank, then the response is a new response, other way it's a response edit.
The following script it to be bounded to the spreadsheet that receives the Form responses. It requires a on form submit installable trigger. Other instructions to adapt it are included on the script comments.
/*
*
* Global Variables
*
*/
/*
* Sheet name used as destination of the form responses
*/
var sheetName = 'Form Responses';
/*
* Name of the column to be used to hold the response revision counter
* It should match exactly the header of the related column,
* otherwise it will do nothing.
*/
var revisionsColumn = 'Rev';
/*
* Responses starting row
*/
var startRow = 2;
function setRevisionCounts(e){
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var revisionsIndex = headers[0].indexOf(revisionsColumn);
var data = sheet.getDataRange().getValues();
var response = e.range;
var rowIndex = response.getRow()-1;
var rev = data[rowIndex][revisionsIndex]+1;
sheet.getRange(rowIndex+1, revisionsIndex+1).setValue(rev);
}
This is not really an answer but rather a 'heads up'. I have found that if you resubmit (edit) a form that data that is not changed is resent unchanged.
Why does this matter? Let's say you have a branch in your form eg Do you like apples or bananas? If you like apples then the next question might be what is the colour of your car? Let's say 'red' of if you like bananas the next question might be how old are you? Let's say 35.
So you choose apples and red and submit the form.
Then you edit your reply and instead choose bananas and 35.
When the form is resubmitted the new data still says car=red and (as expected) age=35.
I would expect/hope that now car=null and age=35 but this is not the case. Car=red still appears against the updated row in the destination sheet.
I hope this is useful to you and maybe someone will suggest a work around.
You can compare the response length vs row length(or the number of columns in the row). Unless the submissions edits/changes every field/response then you know it should be less than the total number of questions.
function onFormSubmit(e) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
const response = (e.range)
const responseValues = response.getValues()
const responseRow = Number(response.getRow())
const rowValues = sheet.getRange(responseRow, 1, 1, sheet.getLastColumn()).getValues()
if(responseValues.length < rowValues.length){
//edited submission
}else{
//likely new submission
}
Amateur here from Excel
ugly but works and this is a simple version - looks for notes - selects that row, updates values on second sheet and deletes the notes.
function mody() {
const sh = SpreadsheetApp.getActiveSpreadsheet();
const mn = sh.getSheetByName('Form Responses 1');
const puty = sh.getSheetByName('putter');
var lst = mn.getLastRow();
var dd = puty.getLastRow();
dd=dd+1
//replace row with edited form response if any
for (var z = 2;z< lst+1;z++){
var nts = mn.getRange(z,2,1,6).getNotes()
var ttt = ((nts.join()).length)
if (ttt>5){
mn.getRange(z,2,1,6).clearNote() //removes the notes on the modified
row
dd = z
lst = z
}
}
puty.getRange(dd,1,1,5).setValues(mn.getRange(lst,1,1,5).getValues());
}