Validate form with a row value from spreadsheet before submit - google-apps-script

I have a Form linked to SpreadSheet and i want to validate the text field "email", and if field exists, go to specific section/page break programatically because Google Forms doesn't give me the option to validate a textfield.
This is a bit of the code i have done until now:
function selectedItems(){
//Select the form and get the items
var form = FormApp.getActiveForm();
var formResponses = form.getResponses();
var formResponse = formResponses[formResponses.length - 1];
var itemResponses = formResponse.getItemResponses();
var name = "";
var email = "";
var gender = "";
//get email field response of the form
for(var i = 0; i < itemResponses.length; i++){
switch(itemResponses[i].getItem().getTitle()){
case "Correo electrónico:": email = itemResponses[i].getResponse();
break;
case "Sexo:": gender = itemResponses[i].getResponse();
break;
case "Nombre completo:": name = itemResponses[i].getResponse();
break;
default:
break;
}
}
//get the spreadsheet where the data is stored
var spreadSheet = SpreadsheetApp.openById(MI FORM ID)
var spreadSheetData = spreadSheet.getDataRange()
var rowValue = spreadSheetData.getValues()
//get the sections title... i don't know what to do here
var sections = form.getItems(FormApp.ItemType.PAGE_BREAK)
var section = sections[0].getTitle()
}
Basically:
I need that if mail exists in spreadsheet, instead of continue to the next section in Google Forms, i need that the user go to last page. I don't know if it's possible with a Form made via GForms. Thanks in advance.

There is no way of achieving this as even though you end up validating a text field such that it contains an email address, you cannot redirect the user to another section as there is no method for it.
From the documentation:
createChoice(value, navigationItem) > Creates a new choice with a page-navigation option that jumps to a given page-break item. This is equivalent to createChoice(value, navigationType) with navigationType set to FormApp.PageNavigationType.GO_TO_PAGE. Choices that use page navigation cannot be combined in the same item with choices that do not use page navigation.
However, the above method is specific to the MultipleChoiceItem Class.
A potential workaround is to use a multiple choice item and redirect the user based on their choice - however, this implies that you will end up relying on the user's statement, without any control from your side.
function selectedItems() {
//other code
let form = FormApp.getActiveForm();
let emailQuestion = form.addMultipleChoiceItem()
.setTitle('Please input your email')
var validEmailSection = form.addPageBreakItem()
.setTitle('Yayy! You have a valid email!')
.setHelpText('Ah bee cee dee');
emailQuestion.setChoices([
emailQuestion.createChoice('Yes, I confirm I have a valid email', validEmailSection),
emailQuestion.createChoice('No, the email is incorrect', FormApp.PageNavigationType.CONTINUE),
]);
}
Another option...
File a feature request on Google's Issue Tracker here!
Reference
TextValidationBuilder Class;
MultipleChoiceItem Class.

Related

How do I use apps script to programmatically create/submit a google form response on a google form that collects emails? [duplicate]

I have the following issue. I am trying to create a script that will autofill a template google document using the submission of a google form. I am able to get the script to work for questions that are input with text but am struggling on getting the data from questions in the form that are checkboxes (or multiple choice) to work and fill the google document. Any assistance would be great. For example the variable identified as "offense" is from a question with checkboxes that has about 30 different options, I would like each option that is checked on the form to replace text within my google doc. Thanks.
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[4];
var studentName = e.values[3];
var oe = e.values[16];
var gradelevel = e.values[14];
var program = e.values[15];
var offense = e.values[6];
var action = e.values[18];
var serve = e.values[31];
var makeUp = e.values[32];
var comments = e.values[29];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('1nPWC0IKc1zUJXYxbGahJsSW4uNWwhxnLM8shcD8kEE4');
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById('1FlpHRKqYrEHttA-3ozU3oUVJlgiqqa-F')
var copy = file.makeCopy(studentName + ', ' + timestamp, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('<<Student Name>>', studentName);
body.replaceText('<<Incident Date>>', timestamp);
body.replaceText('<<Student Grade>>', gradelevel);
body.replaceText('<<Open enrolled?>>', oe);
body.replaceText('<<IEP/504?>>', program);
body.replaceText('<<Reason for Referral (Handbook)>>', offense);
body.replaceText('<<Administrative Action>>', action);
body.replaceText('<<Date(s) to be Served>>', serve);
body.replaceText('<<Make up Date(s)>>', makeUp);
body.replaceText('<<Comments>>', comments);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
You need to use the labels assigned to the checkboxes to determine if they have been checked. Same for multiple coice.
You can't use ListItems because you can't set the glyph to a check box so I simply insert text with a checkbox character.
I created a form
I then created an onFormSubmit(e) installed trigger in the spreadsheet to get the form response and put it in the Doc. Here I've simply used an active doc to perform my tests. You will need to adjust the script to handle your template doc.
function onFormSubmit() {
// test data
let e = {"authMode":"FULL","namedValues":{"Timestamp":["8/16/2022 14:40:26"],"Student Grade":["Junior"],"Reason for Referrel":["Bad grades, Disruptive in class, Other"],"Student Name":["Joe Smith"],"Open Enrollment":["Yes"]},"range":{"columnEnd":5,"columnStart":1,"rowEnd":2,"rowStart":2},"source":{},"triggerUid":"12151926","values":["8/16/2022 14:40:26","Joe Smith","Junior","Yes","Bad grades, Disruptive in class, Other"]};
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let referrels = ["Bad grades","Unexcused absence","Disruptive in class","Fighting","Other"];
body.replaceText("<<Student Name>>",e.namedValues["Student Name"]);
body.replaceText("<<Student Grade>>",e.namedValues["Student Grade"]);
body.replaceText("<<Open Enrollment>>",e.namedValues["Open Enrollment"]);
// Notice the regex expression below because findText doesn't seem to handle parenthesis well
let text = body.findText("<<Reason for Referral.*>>");
body.replaceText("<<Reason for Referral.*>>","");
if( text ) {
let index = body.getChildIndex(text.getElement().getParent())+1;
referrels.forEach( item => {
let checked = e.namedValues["Reason for Referrel"][0].indexOf(item);
if( checked >= 0 ) {
let listItem = body.insertListItem(index,item);
index = body.getChildIndex(listItem)+1;
}
}
);
}
}
catch(err) {
Logger.log(err);
}
}

Script to autofill google doc from google form using checkboxes

I have the following issue. I am trying to create a script that will autofill a template google document using the submission of a google form. I am able to get the script to work for questions that are input with text but am struggling on getting the data from questions in the form that are checkboxes (or multiple choice) to work and fill the google document. Any assistance would be great. For example the variable identified as "offense" is from a question with checkboxes that has about 30 different options, I would like each option that is checked on the form to replace text within my google doc. Thanks.
function autoFillGoogleDocFromForm(e) {
//e.values is an array of form values
var timestamp = e.values[4];
var studentName = e.values[3];
var oe = e.values[16];
var gradelevel = e.values[14];
var program = e.values[15];
var offense = e.values[6];
var action = e.values[18];
var serve = e.values[31];
var makeUp = e.values[32];
var comments = e.values[29];
//file is the template file, and you get it by ID
var file = DriveApp.getFileById('1nPWC0IKc1zUJXYxbGahJsSW4uNWwhxnLM8shcD8kEE4');
//We can make a copy of the template, name it, and optionally tell it what folder to live in
//file.makeCopy will return a Google Drive file object
var folder = DriveApp.getFolderById('1FlpHRKqYrEHttA-3ozU3oUVJlgiqqa-F')
var copy = file.makeCopy(studentName + ', ' + timestamp, folder);
//Once we've got the new file created, we need to open it as a document by using its ID
var doc = DocumentApp.openById(copy.getId());
//Since everything we need to change is in the body, we need to get that
var body = doc.getBody();
//Then we call all of our replaceText methods
body.replaceText('<<Student Name>>', studentName);
body.replaceText('<<Incident Date>>', timestamp);
body.replaceText('<<Student Grade>>', gradelevel);
body.replaceText('<<Open enrolled?>>', oe);
body.replaceText('<<IEP/504?>>', program);
body.replaceText('<<Reason for Referral (Handbook)>>', offense);
body.replaceText('<<Administrative Action>>', action);
body.replaceText('<<Date(s) to be Served>>', serve);
body.replaceText('<<Make up Date(s)>>', makeUp);
body.replaceText('<<Comments>>', comments);
//Lastly we save and close the document to persist our changes
doc.saveAndClose();
}
You need to use the labels assigned to the checkboxes to determine if they have been checked. Same for multiple coice.
You can't use ListItems because you can't set the glyph to a check box so I simply insert text with a checkbox character.
I created a form
I then created an onFormSubmit(e) installed trigger in the spreadsheet to get the form response and put it in the Doc. Here I've simply used an active doc to perform my tests. You will need to adjust the script to handle your template doc.
function onFormSubmit() {
// test data
let e = {"authMode":"FULL","namedValues":{"Timestamp":["8/16/2022 14:40:26"],"Student Grade":["Junior"],"Reason for Referrel":["Bad grades, Disruptive in class, Other"],"Student Name":["Joe Smith"],"Open Enrollment":["Yes"]},"range":{"columnEnd":5,"columnStart":1,"rowEnd":2,"rowStart":2},"source":{},"triggerUid":"12151926","values":["8/16/2022 14:40:26","Joe Smith","Junior","Yes","Bad grades, Disruptive in class, Other"]};
try {
let doc = DocumentApp.getActiveDocument();
let body = doc.getBody();
let referrels = ["Bad grades","Unexcused absence","Disruptive in class","Fighting","Other"];
body.replaceText("<<Student Name>>",e.namedValues["Student Name"]);
body.replaceText("<<Student Grade>>",e.namedValues["Student Grade"]);
body.replaceText("<<Open Enrollment>>",e.namedValues["Open Enrollment"]);
// Notice the regex expression below because findText doesn't seem to handle parenthesis well
let text = body.findText("<<Reason for Referral.*>>");
body.replaceText("<<Reason for Referral.*>>","");
if( text ) {
let index = body.getChildIndex(text.getElement().getParent())+1;
referrels.forEach( item => {
let checked = e.namedValues["Reason for Referrel"][0].indexOf(item);
if( checked >= 0 ) {
let listItem = body.insertListItem(index,item);
index = body.getChildIndex(listItem)+1;
}
}
);
}
}
catch(err) {
Logger.log(err);
}
}

Script onForm Submit trigger not working properly

I created a google form that will take the latest response and move the selected choice to the other section. So if someone checks out a laptop, once they submit the form the laptop choice will appear in the check in section. When I manually run the script it works perfectly fine but once I add the trigger it works for the first few times then it starts creating multiple triggers for one submission which then creates multiple new checkboxes on the form that all say the same thing. Like for example I'll have three different laptop choices when there should only be one. So I had to take the trigger off and I've looked at other similar questions about this problem but they all involve spreadsheets but mine is purely working with the google form so I'm not sure if those solutions will work for me.
I didn't add all my code since part of it is the same thing just with different variables for moving choices from check in to checkout.
var form = FormApp.openById('1I5uMesHbeVZ_RSP8wxmmpPA7-Sgcc4b6dzzH305c8K8');
/**
Responds to a form submission event when the on formSubmit trigger is
enabled
*
* #param {Event} e The event parameter created by a form submission
*/
//function that gets checkout responses
function myFunction(e) {
//getting form responses
var formResponses = form.getResponses();
//getting latest response
var latestFR = formResponses[form.getResponses().length-1];
//getting the item/question responses, checkout check in
var itemResponses = latestFR.getItemResponses();
//looping through item responses to see which item has a response
for (var i=0; i<itemResponses.length; i++) {
switch (itemResponses[i].getItem().getTitle()) {
//if only response to checkout
case "Checkout":
var outAnswer = itemResponses[i].getResponse();
outAnswer.forEach(addOut);
outAnswer.forEach(deleteOut);
break;
//if only response to check in
case "Check In":
var inAnswer = itemResponses[i].getResponse();
inAnswer.forEach(addToCheckOut);
inAnswer.forEach(deleteIn);
break;
//if response to both check out/in
case "Checkout" && "Check In":
var outAnswer = itemResponses[i].getResponse();
var inAnswer = itemResponses[i].getResponse();
outAnswer.forEach(addOut);
outAnswer.forEach(deleteOut);
inAnswer.forEach(addToCheckOut);
inAnswer.forEach(deleteIn);
break;
}}
//getting email response to send email
var email = itemResponses[0].getResponse();
//testing to see if it gets the latest submission
//delete my email later
var subject = 'Response';
var emailTo = [email];
var body = 'Response is' + outAnswer + inAnswer;
MailApp.sendEmail(emailTo, subject, body, {
htmlBody: body});
}
//function that adds the latest response from checkout to check in
section
function addOut(outAnswer) {
//getting check in section item with its choices
var a = form.getItems(FormApp.ItemType.CHECKBOX)[1].asCheckboxItem();
//getting choices from check in
var choices = a.getChoices();
//creating new choice for check in
var choice = a.createChoice(outAnswer);
//adding the choice to the choices
choices.push(choice);
//setting the choices with new choice for check in
a.setChoices(choices);
}
//function that deletes answer from checkout
//only works when its a string so convert outAnswer to string value with
toString but only works with a single choice
function deleteOut(outAnswer) {
var del = form.getItems(FormApp.ItemType.CHECKBOX)
[0].asCheckboxItem();
del.setChoices(del.getChoices().filter(function (choice) {
return choice.getValue() !== outAnswer.toString(); }));
}
You're going to need to do the same kind of thing as the spreadsheet answers suggested, create a script lock and use it to dump quick successive triggers.
Just add the following lines to the top of your script:
var lock = LockService.getScriptLock();
try {
lock.waitLock(3000);
} catch (e) {Logger.log('Could not obtain lock after 3 seconds.');}
Utilities.sleep(3000);
You can also add a "lock.releaseLock();" to the end of your script, but it isn't necessary, locks release on their own.
All this code does is reject any new submissions in the next three seconds after it is triggered. If that isn't enough, change the time in the waitlock AND the sleep to 5000 (forms generally take less than three seconds to run a script like this so you are forcing the script to take longer).

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;
}
}

How to assign Go_To_Page in Form Service on Multiple Choice?

Just starting to use the Forms Service in Google Apps Script. Need to direct the form to take the user to a specific page depending on the answer that is given. Here's my current code
form.addMultipleChoiceItem()
.setTitle('What would you like to do?')
.setRequired(true)
.setChoiceValues(['Request a new reservation.','Change the date or number of tickets for an existing reservation.'])
Now, I've found this section in the documentation: Enum PageNavicationType
But they don't example the use of Go_To_Page. Also the creation of the ChoiceValues is wonky to me.
Anyone out there worked this out?
Instead of .setChoiceValues you want to use .setChoices([arrayOfChoices]), and use the .createChoice(value, page) to create the choice.
Edit: Updated code to fix errors
function createAMCQuestion(){
var af = FormApp.getActiveForm();
var pRed = af.getItemById("1300443051").asPageBreakItem(); //use findPageIds to get the page id of a pre-created page
var pGreen = af.getItemById("902629766").asPageBreakItem();
var item = af.addMultipleChoiceItem().setTitle('Pic a color?'); // creates the Multiple Choice Question
af.moveItem(item.getIndex(), 1); // Makes the question show up as the second question in the form (starts at 0)
//create choices as an array
var choices = [];
choices.push(item.createChoice('Red', pRed));
choices.push(item.createChoice('Green', pGreen));
// assignes the choices for the question
item.setChoices(choices);
}
function findPageIds(){
var af = FormApp.getActiveForm();
var pages = af.getItems(FormApp.ItemType.PAGE_BREAK);
for (i in pages){
var pName = pages[i].getTitle();
var pId = pages[i].getId();
Logger.log(pName+":"+pId);
}
}