Generate unique Form URL for each respondent - google-apps-script

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

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

Google script to login, retrieve cookie and get csv to sheets

I can get the cookie with curl -d "_u=user_name" -d "_p=password" --cookie-jar ./cookie https://url.tologin.com/admin/login successfully but can't do the same with google script
function myFunction() {
var payload = {"_u" : "user_name","_p" : "password"};
var opt ={"payload":payload,"method":"post"};
var response = UrlFetchApp.fetch("https://url.tologin.com/admin/login",opt);
var headers = response.getAllHeaders();
Logger.log(headers);
var cookie = headers['Set-Cookie'];
Logger.log(cookie);
Logger.log(response);
response.getContentText();
var header = {'Cookie':cookie};
var opt2 = {"headers":header};
var pagedata = UrlFetchApp.fetch("https://url.tologin.com/admin/sales/order/export/csv",opt2);
Logger.log(pagedata);
}
I get answer from webserver with failed login page and I can't figure out what is wrong with google script
As written in the MDN documentation, The request cookie header should be of the format:
Cookie: name=value; name2=value2; name3=value3
A list of name-value pairs in the form of <cookie-name>=<cookie-value>. Pairs in the list are separated by a semicolon and a space (;).
As written in the UrlFetchApp#HttpResponse documentation, getAllHeaders() returns
Returns an attribute/value map of headers for the HTTP response, with headers that have multiple values returned as arrays.
When a array is implicitly converted to a string, it is joint using a comma , ,which is not a valid cookie string. You could get a formatted cookie header from a Set-cookie header using a strip function like this:
const getCookie = setCookie =>
Array.isArray(setCookie)
? setCookie.map(getCookie).join("; ") //get each cookie and join them by ;
: setCookie.split("; ")[0]//get only the first part of cookie; remove irrelevant info like `Max-Age`, `expires` etc.
I got everything working
GAS script to
Login and fetch cookie with POST
build a newCookie and use it in GET requests
Get CSV and parse it to replace separators when necessary
Upload CSV content to an active sheet
function parseCsvResponse(csvString) {
var retArray = [];
var strLines = csvString.split(/\n/g);
var strLineLen = strLines.length;
for (var i = 0; i < strLineLen; i++) {
var line = strLines[i];
if (line != '') {
retArray.push(line.replace(/"/g, "").split(/;/));
//replace ; with separator from your CSV file
}
}
return retArray;
}
function myFunction(sheet) {
var payload = {'user_name' : 'username','password' : 'password'};
//replace with values on your login page "name=user_name" and "name=password"
//if your username contains # send it directly or use %40 instead
var opt ={
'payload': payload,
'method':'post',
"followRedirects": false,
"testcookie": 1
};
var response = UrlFetchApp.fetch("https://url.tologin.com/admin/login",opt);
//inspect the right link via Chrome inspect of your login page
Logger.log(response);
Logger.log(response.getResponseCode());
if ( response.getResponseCode() == 200 ) {
// Incorrect user/pass combo
} else if ( response.getResponseCode() == 302 ) {
// Logged-in
Logger.log("Logged in");
var headers = response.getAllHeaders();
Logger.log(headers);
var cookies = headers['Set-Cookie'];
// Extract the cookies from the login response
var cookieParts = [];
for (var i = 0; i < cookies.length; i++) {
var arr = cookies[i].split('; ');
cookieParts.push(arr[0]);
}
// Create a new cookie to send with subsequent requests
var newCookie = cookieParts.join('; ');
Logger.log(newCookie);
};
opt2 = {
"method" : "get",
"headers": {
"Cookie": newCookie
}
};
var url = "https://url.tologin.com/admin/sales/order/export/csv";
response2 = UrlFetchApp.fetch(url, opt2);
var resp1=response2.getContentText();
var csvContent = parseCsvResponse(response2.getContentText());
Logger.log(resp1);
Logger.log(csvContent);
// clear everything in the sheet
var sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents().clearFormats();
// set the values in the sheet (as efficiently as we know how)
sheet.getRange(1, 1, csvContent.length /* rows */, csvContent[0].length /* columns */).setValues(csvContent);
}
then
Import myFunction to sheet "Tools > Macros > Import"
First run asks for Google authentication of your App where myFunction is
Run myFunction "Tools > Macros > myFunction"
And you get correctly formatted CSV data to your sheet

Submitting a Google Form via Google Apps Script fails if Form is set to collect email

When submitting a form via Apps script, if the form is set to collect the user e-mail for responses, then the code fails with error:
"Sorry, the form response could not be submitted. Please wait a few
minutes and try again."
I think Google may have changed something as the code that worked last year is not working this year.
The error points at the "submit" line in the code. OR sometimes, the code runs, but still nothing appears in the sheet, or in the "responses" in the form itself.
If I turn off the option to collect e-mail, it runs fine and I see the submission in the Google sheet, just without an e-mail address.
I setup a test form, attached it to a new google sheet for responses, and pasted in my code:
function codeVoteDoVoteByForm()
{
// Get the Yes/No Google form
var myForm = FormApp.openById('File ID Here')
// Get a list of questions on it
var questions = myForm.getItems()
// Get question 0 (only one on this form) and mark it as multiple choice
var qt = questions[0].asMultipleChoiceItem()
// Set the users vote
var qr = qt.createResponse("I am here")
//Create the response
var FormResponse = myForm.createResponse()
var testemail = FormResponse.getRespondentEmail()
// Submit the response
FormResponse.withItemResponse( qr );
var myResponse = FormResponse.submit()
var responseString = myResponse.getTimestamp()
return "Vote recorded at " + responseString
}
My thought is that Google changed something, so now when running a script it's not able to get the users e-mail address for the formresponse, but I can't find any documentation to confirm this.
Any thoughts?
I think that the problem is when you are sending a reponse to a form that have collect email or authenticated user restrictions, without adding an email or authenticating the user. Unfortunely, I think that is not possible to send this sort of information with the API, so in my case:
I have disabled the restrictions, next
I have sent the response to the form, and finally
I enable the restrictions again
//disable restrictions temporaly
form.setLimitOneResponsePerUser(false);
form.setRequireLogin(false);
form.setCollectEmail(false);
var questions = form.getItems();
var formResponse = form.createResponse();
for (var i=0; i<questions.length; i++){
var question = questions[i].asMultipleChoiceItem();
var response = question.createResponse(correctAnswers[i]);
formResponse.withItemResponse(response);
}
formResponse.submit();
form.setLimitOneResponsePerUser(true);
form.setRequireLogin(true);
form.setCollectEmail(true);
This function will first check the question type:
function autoFormResponse_(formId) {
var form = FormApp.openById(formId);
//disable restrictions temporaly
form.setCollectEmail(false);
// answer questions
var questions = form.getItems();
var formResponse = form.createResponse();
var required_list = [];
for (var i=0; i<questions.length; i++){
// release required questions
var question = getQuestionItemAs_(questions[i]);
if (question.isRequired())
{
question.setRequired(false);
required_list.push(question);
}
}
// submit
formResponse.submit();
// restore required questions
form.setCollectEmail(true);
for (var i = 0; i < required_list.length; i++)
{
required_list[i].setRequired(true);
}
// return last response
var responses = form.getResponses();
return responses[responses.length - 1];
}
Helper function:
function getQuestionItemAs_(item)
{
var type = '' + item.getType();
switch (type) {
// https://developers.google.com/apps-script/reference/forms/item-type
case 'CHECKBOX': return item.asCheckboxItem();
case 'CHECKBOX_GRID': return item.asCheckboxGridItem();
case 'DATE': return item.asDateItem();
case 'DATETIME': return item.asDateTimeItem();
case 'DURATION': return item.asDurationItem();
case 'GRID': return item.asGridItem();
case 'IMAGE': return item.asImageItem();
case 'LIST': return item.asListItem();
case 'MULTIPLE_CHOICE': return item.asMultipleChoiceItem();
case 'PAGE_BREAK': return item.asPageBreakItem();
case 'PARAGRAPH_TEXT': return item.asParagraphTextItem();
case 'SCALE': return item.asScaleItem();
case 'SECTION_HEADER': return item.asSectionHeaderItem();
case 'TEXT': return item.asTextItem();
case 'TIME': return item.asTimeItem();
case 'VIDEO': return item.asVideoItem();
default: return false;
}
}

Error in Google Sheets Script when parsing XML

I have this function running in a Google Sheets script that pulls HTML from subreddits and returns them to a spreadsheet. It works for me some/most of the time, but other times I get an error "Could not parse text. (line 13)" which is the line with var doc = Xml.parse(page, true);. Any idea why this is happening or is this just a bug with Google Scripts? Here's the code that works...sometimes.
function getRedditHTML() {
var entries_array = [];
var subreddit_array = ['https://www.reddit.com/r/news/','https://www.reddit.com/r/funny/','https://www.reddit.com/r/science/'];
for (var s = 0; s < subreddit_array.length; s++) {
var page = UrlFetchApp.fetch(subreddit_array[s]);
//this is Line 13 that is breaking
var doc = Xml.parse(page, true);
var bodyHtml = doc.html.body.toXmlString();
doc = XmlService.parse(bodyHtml);
var root = doc.getRootElement();
var entries = getElementsByClassName(root,'thing');
for (var i = 0; i < entries.length; i++) {
var title = getElementsByClassName(entries[i],'title');
title = XmlService.getRawFormat().format(title[1]).replace(/<[^>]*>/g, "");
var link = getElementsByClassName(entries[i],'comments');
link = link[0].getAttribute('href').getValue();
var rank = getElementsByClassName(entries[i],'rank');
rank = rank[0].getValue();
var likes = getElementsByClassName(entries[i],'likes');
likes = likes[0].getValue();
entries_array.push([rank, likes, title, link]);
}
}
return entries_array.sort(function (a, b) {
return b[1] - a[1];
});
}
Here is what I found upon playing with importXML (my usual way of doing this) - for some reason I cannot narrow down - it DOES appear to randomly stall out and return null for a few minutes - so I'm guessing the issue with your thing is not the code but that the site or google temporarily blocks/won't return the data -
however I found the JSON endpoint to the piece you want - and I noticed that when XML went down - the JSON didnt.
You can take that and fix it to push your own array of topics/urls - I just left it for one link for now to show you how the URL breaks down and where it should be modified:
The URL is 'https://www.reddit.com/r/news/hot.json?raw_json=1&subredditName=news&sort=top&t=day&feature=link_preview&sr_detail=true&app=mweb-client
News is mentioned in 2 places so just modify all your URLs to follow that method - you can easily load that javascript in a browser to see all the fields available
Also the portion hot.json is where you can change whether you want the ranked list (called hot), or new,top,promoted, etc. you just change that keyword.
Score is the same as the upvotes/likes
function getSubReddit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet(); //get Active sheet
var subject = 'news';
var url = 'https://www.reddit.com/r/' + subject + '/hot.json?raw_json=1&subredditName=' + subject + '&sort=top&t=day&feature=link_preview&sr_detail=true&app=mweb-client'; //json endpoint for data
var response = UrlFetchApp.fetch(url); // get api endpoint
var json = response.getContentText(); // get the response content as text
var redditData = JSON.parse(json); //parse text into json
Logger.log(redditData); //log data to logger to check
//create empty array to hold data points
var statsRows = [];
var date = new Date(); //create new date for timestamp
//The following lines push the parsed json into empty stats array
for (var j=0;j<25;j++){
for (var i =0;i<25;i++){
var stats=[];
stats.push(date);//timestamp
stats.push(i+1);
stats.push(redditData.data.children[i].data.score); //score
stats.push(redditData.data.children[i].data.title); //title
stats.push(redditData.data.children[i].data.url); //article url
// stats.push('http://www.reddit.com' + redditData.data.children[i].data.permalink); //reddit permalink
statsRows.push(stats)
}
//append the stats array to the active sheet
sheet.appendRow(statsRows[j])
}
}

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.