Sort Google Forms responses on Submit - google-apps-script

I have three columns in my Sheet where Google Forms store response data: Gender, Name and Email.
If user sends form with Gender-field value equals "Male" I want to store this response to existing sheet named "Males".
Here is my solution(I've already added special trigger for it and here is event hendler implimentation), but it doesn't work:
function onItemAdd(e) {
var formData = e.values;
if (formData[0] == "Male") {
SpreadsheetApp.setActiveSheet(ss.getSheets()[1]);
var form = FormApp.getActiveForm().getResponses();
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Males").getDataRange().getValues()[0];
var formRow = e.range;
var filteredCell = formRow.getCell(1, formRow.getLastColumn()).offset(0, 1).setValue(formData);
}
}

There is no values property for the On Form Submit event object. There are only 3 properties of the On Form Submit event object.
authMode
response
source
source gets the form. response gets the form response.
var thisResponse = e.response.getItemResponses();
var gender = thisResponse[0].getResponse();
Logger.log('gender: ' + gender);

Related

Is there a way to pass information to a Google Form from a link sent in an email (parameters in the URL maybe)?

I want to send an email to folks using a script that will ask them to confirm an appointment. I'd like to make it easy for them to confirm. I was thinking I could have a link go to a Google Form, but I would like that form to contain information about the appointment; I thought about putting parameters in the form URL (e.g. https://docs.google.com/forms/d/e/[formID]/viewform?location=Office1&subject=management) but I don't see a way to grab that URL in the script attached to the form (only the normal URL of the form). Any way I can get the URL with the parameters? Or is there some other way to pass information to the form? (Or, failing that, to a Google Doc or something?)
I tried using getPublishedURL but that gets the standard URL, no parameters...
Question: Is a way to pass parameter information to a Google Form from a link sent in an email.
Answer: No.
But there is a way that you can use a Google Forms link, sent in an email, that would enable a person to confirm an appointment.
In brief:
create a Google Form with three questions
Question 1 = Title: "User Details", Type: "Paragraph Text"
Question 2 = Title: "My appoitment time is", Type: "Short-answer Text"
Question 3 = Title: "Acknowledgement", Type: "List Item", one options = "yes"
create a Google spreadsheet with two sheets
sheet 1 = user details = name, email, appointment time plus two checkboxes ("ResponseCreated" and "Email sent")
sheet 2 = Form Responses - linked from the Google Form
add one additional column: "EditResponse URL"
write/run a script to create form responses using the data on sheet1
this will populate questions 1 and 2
Sheet 2(Form Responses) is automatically updated.
write/run a script to create the EditResponseUrl for the data on sheet="Form Responses"
write/run a script to send emails to the user details on Sheet1
use the EditResponseUrl from sheet 2 to create an HTML link in the email
-when each user clicks the link in their email, they are directed to a form that contains their details, and the time of their appointment.
They select "Yes" (to acknowledge the appointyment) and then Submit.
Sheet 2 is automatically updated from the form - this is your evidence of their acknowledgement.
Create Form Responses
function createResponse() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sourceSheetName = "User details"
var source = ss.getSheetByName(sourceSheetName)
// get the number of entries
var aVals = source.getRange("A2:A").getValues()
var aLast = aVals.filter(String).length
// get the data; 3 columns plus a checkbox// one row = header
var sourceRange = source.getRange(2,1,aLast,4)
//Logger.log("DEBUG: source range = "+sourceRange.getA1Notation())
var sourceValues = sourceRange.getValues()
var formUrl = ss.getFormUrl();
var form = FormApp.openByUrl(formUrl); // grabs the connected form
var questions = form.getItems();
// Getting the fields of the form questions
var userInfo = questions[0].asParagraphTextItem();
var appntInfo = questions[1].asTextItem();
var updateArray = new Array
for(i = 0; i < sourceValues.length; i++) {
if (sourceValues[i][3] == false){
var formResponse = form.createResponse();
var d1 = "Name: "+sourceValues[i][0]+"\nEmail address: "+sourceValues[i][1]
var r1 = userInfo.createResponse(d1)
var d2 = sourceValues[i][2]
var r2 = appntInfo.createResponse(d2)
formResponse.withItemResponse(r1)
formResponse.withItemResponse(r2)
formResponse.submit()
updateArray.push([true])
}
else {
updateArray.push([true])
}
}
// Logger.log("DEBUG: checkbox range = "+source.getRange(2,4,sourceValues.length).getA1Notation())
// Logger.log(updateArray) // DEBUG
source.getRange(2,4,sourceValues.length).setValues(updateArray)
}
Get EditResponseUrl
function responseURL() {
var form = FormApp.openById('10cG91VSwmIvCS8PQbJwtrQk47uWVmcH6i5pX83KsuVE')
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Form Responses 1')
var formResponses = form.getResponses()
for (var i = 0; i < formResponses.length; i++) {
var formResponse = formResponses[i]
sheet.getRange(i+2, 5).setValue(formResponse.getEditResponseUrl());
}
}
Send email
function sendEmails(){
var ss = SpreadsheetApp.getActiveSpreadsheet()
var userSheetName = "User details"
var usersheet = ss.getSheetByName(userSheetName)
var formSheetName = "Form Responses 1"
var formsheet = ss.getSheetByName(formSheetName)
// get the number of entries
var aVals = usersheet.getRange("A2:A").getValues()
var aLast = aVals.filter(String).length
// get the data; 3 columns// one row = header
var userRange = usersheet.getRange(2,1,aLast,5)
// Logger.log("DEBUG: source range = "+userRange.getA1Notation())
var userValues = userRange.getValues()
var formRange = formsheet.getRange(2,1,aLast,5)
// Logger.log("DEBUG: form range = "+formRange.getA1Notation())
var formValues = formRange.getValues()
//Logger.log(formValues)
// return
var sentArray = new Array
var emailSubject = "Request for Confirmation of Appointment"
for (var i=0;i<userValues.length;i++){
if (userValues[i][4] == false){ // test if email has already been sent
var name = userValues[i][0]
var email = userValues[i][1]
var apptTime = userValues[i][2]
var respURL = formValues[i][4]
var html_link = "<a href='"+respURL+"'> our Appointment confirmation form</a>"
//Logger.log(html_link)
var html_body = "Hello, "+ name +",<br><br>"
+ "Your appointment is at "+apptTime+". Would you please confirm your appointment by going to " + html_link + ".<br><br>"
+ "Thank you, <br>"
+ "Signature"
MailApp.sendEmail({
to: email,
subject: emailSubject,
body: "Can add a Plain Text version of the email body here for email apps that dont do html",
htmlBody: html_body
})
sentArray.push([true])
Logger.log("mail sent to "+name)
}
else{
sentArray.push([true])
}
}
usersheet.getRange(2,5,userValues.length).setValues(sentArray)
}
User Details (sheet1)
Form Responses (sheet2)
Email
Form - Confirm appointment

pre-fill google form from google apps script

I have a student registration form, there's student id which is a required field. I have a google apps script function which tells if this student is registered for any class or not. Is there a way to auto-fill the field course registered via calling the google apps script function yes or no.
Yes you can create a pre filled response with the forms ID, not that the pre filled fields are showed in the URL
Function formPrefill(formId){
var form = FormApp.openById(formId);
try{
var items = form.getItems();
var formResponse = form.createResponse();
// Prefill SessionId
var formItem = "SOMETHING HERE"
var response = formItem.createResponse(sessionId);
formResponse.withItemResponse(response);
//--------ANOTHER FIELD-------------
formItem = items[4].asMultipleChoiceItem();
response = formItem.createResponse('YOUR FIELD NAME');
formResponse.withItemResponse(response);
}catch(e){catch an error here}
}
check https://developers.google.com/apps-script/reference/forms/form#createresponse

Apps Script - Programmatically submit answers from Google Sheet to Google Form - ERROR - "Sorry, this response has already been submitted."

I have a Google Form and a Google Spreadsheet.
timestamp | name | email | revenue | Edit Url
2015-2-2 02:22:22 | David | | |
2015-2-2 07:22:22 | Paul | | |
2015-2-2 09:22:22 | Olive | | |
What I am trying to accomplish:
Based on the information in the Spreadsheet (name, email, revenue) I'd like to programmatically iterate through each row, populate the Form based on the information in each row then submit the form and for each form submitted generate an edit URL which will be stored in the Edit Url column.
So far this is my Google app Script:
function myFunction() {
createSurveyResponses();
}
function createSurveyResponses() {
// Open a form by ID and add a new text item.
var form = FormApp.openById('form_id');
var response = form.createResponse();
var sheet = SpreadsheetApp.openById('spreadsheet_id');
var getAct = sheet.getActiveSheet();
var data = sheet.getDataRange().getValues();
// Access the text item as a generic item.
var items = form.getItems();
var item = items[0];
var urls = [];
var resultUrls = [];
for (var j = 1; j < data.length; j++) {
var dop = data[j][0]
if (item.getType() == 'TEXT') {
var textItem = item.asTextItem();
var itemResponse = textItem.createResponse(data[j][0]);
var another = response.withItemResponse(itemResponse);
response.submit();
}
}
// get the responses from the spreadsheet
var fresponses = form.getResponses();
for (var i = 0; i < fresponses.length; i++) {
var resp = [fresponses[i]];
urls.push([shortenUrl(fresponses[i].getEditResponseUrl())]);
}
var getdata = getAct.getRange(2,5,fresponses.length)
getdata.setValues(urls);
}
function shortenUrl(longUrl) {
// google url shortener api key
var key = "AIzaSyBVG4Q5i1mNI0YAO0XVGZ3suZU8etTvK34";
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;
}
However, when I run the code, after the first iteration (first row) I get an error Sorry, this response has already been submitted. (line 34, file "") which is when I'm submitting the response response.submit();.
What am I doing wrong?
My ultimate goal is to generate a unique URL for each row so that my recipients can use that URL to update their responses whenever they want (getEditResponseUrl()).
This answer explains how to submit answers from a Google Sheet to a Google Form.
The first thing that you need to know is the difference between a Form Response and an Item Response.
Form Response - All the answers to all the questions in the Form.
Item Response - One answer to one question.
To programmatically submit a response to a Google Form, the code must create a Form Response, and then add Item Responses to the Form Response, and then submit the Form Response. A common mistake is to try to submit the Item Response.
In order to add multiple Item Responses to the Form Response, you'll probably use a loop. And if the code is adding multiple Form Responses, then that will probably use a loop. So, you'll need a loop inside of another loop.
There are multiple things that can go wrong. But basically, the code needs to create both a Form Response, and then multiple Item Responses need to be added to the Form Response. If you confuse the Form and Item Responses, then something will go wrong.
In the code example provided, the outer for loop, loops through the number of spreadsheet rows. The inner for loop, loops through the items in a single form response.
The submit method can NOT be used in the inner loop. Each form item (question) must have an answer added to it with createResponse() and then the Item response must be added to Form response. The word response can be used for either the Form response as a whole, or a response (answer) to a single question.
The Item response is added to the Form response with:
newResponse.withItemResponse(itemResponse);
The method withItemResponse may be confusing. You do not need to chain another method to it to add the answer.
Here is code:
function createSurveyResponses(ss_ID) {
if (ss_ID === undefined) {
ss_ID = '';
};
var ss = SpreadsheetApp.openById(ss_ID);
var sheet = ss.getSheetByName('Sheet1');
//Get data starting in row 2, column 2
var data = sheet.getRange(2, 2, sheet.getLastRow()-1, sheet.getLastColumn()-1).getValues();
var i = 0,
j = 0,
form,
items,
thisRow,
Name = "",
Email = "",
Revenue,
FormURL = "",
formID,
thisItem,
itemTypeIs,
response,
arraySS_Values = [],
editURL;
var arrayItemNames = ['Name','Email','Revenue'];
for (i=0;i<data.length;i+=1) {
thisRow = data[i];
Name = thisRow[0];
Email = thisRow[1];
Revenue = thisRow[2];
FormURL = thisRow[3];
arraySS_Values = [];
arraySS_Values.push(Name);//Fill an array with the cell values of one row from the spreadsheet
arraySS_Values.push(Email);
arraySS_Values.push(Revenue);
Logger.log('Name: ' + Name);
if (FormURL === "" || FormURL === undefined) { //If there is no form, create one
form = FormApp.create(Name);
formID = form.getId();
items = addItemsToForm(form, arrayItemNames);
} else {
form = FormApp.openByUrl(FormURL);
items = form.getItems(FormApp.ItemType.TEXT);
if (items.length === 0) { //If there are no form items, you must add them
items = addItemsToForm(form, arrayItemNames);
};
};
var newResponse = form.createResponse();
for (j=0;j<items.length;j+=1) {
thisItem = items[j];
itemTypeIs = thisItem.getType();
if (itemTypeIs===FormApp.ItemType.IMAGE || itemTypeIs===FormApp.ItemType.PAGE_BREAK || itemTypeIs===FormApp.ItemType.SECTION_HEADER) {
continue; //quit this loop, and loop again if the form item is an image, page break or section header
};
if (itemTypeIs === FormApp.ItemType.TEXT) {
var textItem = thisItem.asTextItem();
var itemResponse = textItem.createResponse(arraySS_Values[j]);
newResponse.withItemResponse(itemResponse);
Logger.log('itemResponse: ' + itemResponse.getResponse());
};
};
newResponse.submit();
var preFill_url = newResponse.toPrefilledUrl();
Logger.log('preFill_url: ' + preFill_url);
sheet.getRange(i+2, 5).setValue(preFill_url);
};
};
function addItemsToForm(form, arrayItemNames) {
var i=0;
for (i=0;i<arrayItemNames.length;i+=1) {
form.addTextItem().setTitle(arrayItemNames[i]);
};
return form.getItems();
};
The code attempts to deal with the situation of whether a form already exists or not. If the spreadsheet does not have a form URL, then a new form is created. I don't know if you can use shortened URL's with this code, because it may need to open the form by using the URL.

Generate unique Form URL for each respondent

I have a Google Form With a Google spreadsheet to store responses. In my spread sheet I have 4 columns: name, email, revenue, and a fourth column, Id, which is used to identify the particular recipient.
What I am trying to accomplish is to generate a unique URL for each respondent so that they can respond to the form and use the same URL to edit the form at a later time.
I've looked at the getEditUrl() (Google Apps Script) method which creates a unique URL for the respondent after submitting the response-- code below:
function myFunction() {
assignEditUrls();
}
function assignEditUrls() {
var form = FormApp.openById('1vsqvwomoqSXwF6TlNkmmktAAk2av2r-2LRrBYJdv3VQ');
//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++) {
var resp = responses[i];
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(shortenUrl(responses[i].getEditResponseUrl()));
withItemResponse(responses[i])
}
for (var j = 1; j < data.length; j++) {
var dop = data[j][0]
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 = "AIzaSyBVG4Q5i1mNI0YAO0XVGZ3suZU8etTvK34";
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;
}
However I want to do it the other way which is to first generate the unique URL to be then sent to respondents so they can submit and edit their responses without the need of sending them another URL (e.g. the editurlresponse).
Is this possible to do?
Originally posted to https://webapps.stackexchange.com/a/86399/88163
Yes, it's possible, but with slight different approach:
Submit one answer for each respondent in order to get one edit URL by each of them, then send the corresponding URL to each respondent.
Below is a code snippet that programmatically submits a response and log some response attributes including the edit response url by through two code lines, the first one has an issue, the second one is a workaround. Please note that these lines use getEditResponseUrl() not the toPrefilledUrl().
It will work as a stand alone or as a bounded script.
/*
This code shows how to get the edit response url of a
programmatically submitted response to a Google Form
*/
// Replace the form ID by your own form
var formID = '1234567890abcdefghijklmnopqrstuvwxyz';
function myFunction() {
var form = FormApp.openById(formID);
var response = form.createResponse();
var items = form.getItems();
var item = items[0];
if (item.getType() == 'TEXT') {
var textItem = item.asTextItem();
var itemResponse = textItem.createResponse('my text');
response.withItemResponse(itemResponse);
}
// Submit response
var submittedResponse = response.submit();
// Get submitted response attributes
var values = {
ID : submittedResponse.getId(),
TS : submittedResponse.getTimestamp(),
/*
Issue 4476: FormApp: getEditResponseUrl() produces invalid URL
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4476
*/
ER1 : submittedResponse.getEditResponseUrl(),
/* Workaround from
https://code.google.com/p/google-apps-script-issues/issues/detail?id=4476#c2
*/
ER2 : submittedResponse.getEditResponseUrl().replace(
/\?edit2=.*/,"?edit2=" + submittedResponse.getId()
)
};
Logger.log(values);
}
References
Class FormResponse - Google Apps Script Reference
Issue 4476: FormApp: getEditResponseUrl() produces invalid URL

Pre-filling a google form

I want to pre-fill a Google form such that some of the fields which are pre-filled, are the responses from another form.
What i mean to say is that i send out a few columns of the responses of one of my forms and get the information validated by a third party. Is this kind of pre-filling possible?
function sendForm(e) {
//Code to open second form and get the items of the form
var form = FormApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
var items = form.getItems();
var formItem = items[0].asTextItem();
var formResponse = form.createResponse();
var count=0;
var response = formItem.createResponse(e.namedValues["Name"].toString());
formResponse.withItemResponse(response);
formItem = items[1].asTextItem();
response = formItem.createResponse(e.namedValues["Email"].toString());
formResponse.withItemResponse(response);
//Creates URL with pre-filled data
var url = formResponse.toPrefilledUrl();
//Mail the link to the people required.
}
This code goes in the response sheet of the first form and a trigger must be added to call this function every time a form submit action happens.