Google Form App Script; Trouble Obtaining Form Response Data - google-apps-script

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

Related

from Form Responses to another Sheet's last row and capable to overwrite in the future

Can somebody help me with my script. Its been quite sometime now and I still can't able to pull off what I want.
Script's Purpose: To transfer values from Form Responses (sheet) to Tracker (sheet) OnFormSubmit.
Then the entries from Form should be transferred on the last row/blank row every time. Then when the value is already transferred to Tracker it can be overwrite/updated if needed any time.
So whenever we have new entry from Form, the other value that's been altered should not be fixed with the transferred entries only.
Please see my initial script:
var lastrow = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1").getRange("A1").getDataRegion().getLastRow() //Gets last row from form responses
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses 1").getRange("C" + lastrow).getValue() // gets the value from column C and last row
var lastrowts = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tracker").getRange("A1").getDataRegion().getLastRow()
var ts = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tracker").getRange("C" + lastrowts).setValue(ss) // sets the value from ss
}
//function onFormSubmit(e) {
//var form = FormApp.openById("1FAIpQLScnJT1KEXiMvinZZ5LyIDG-l8FJcdeaGzU3MmTkshYNA4lcww")
//var formResponses = form.getResponses();
//for (var i = 0; i < formResponses.length; i++) {
//var formResponse = formResponses[i];
//var itemResponses = formResponse.getItemResponses();
//for (var j = 0; j < itemResponses.length; j++) {
//var itemResponse = itemResponses[j].getResponse();
//var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Tracker")
//Logger.log(itemResponse)
//ss.appendRow([itemResponse])
//}
//}
//}```
If you interested in the data from the linked spreadsheet then this should do the trick:
function onMyFormSubmit(e) {
const sh=e.source.getSheetByName('Tracker');
sh.appendRow(e.values);
}
There's often more information in an event object than is described in the documentation. And I usually check it with something like this:
e.source.getSheetByName('Sheet1').getRange('A1').setValue(JSON.Stringify(e));
Just stick that somewhere in your function in the beginning to remind yourself what parameters you have to play with. I usually usually just copy the header line in the response sheet and copy to the sheet I'm going to append to. You can do it in code if you prefer but I usually take the path of least resistance.

Collect Responses From Google Form?

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

Is there a way to submit google forms responses to different spreadshseets, based on user dropdown choice?

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

How to put the optional values of Checkbox and Multiple Choice in the prefilled form in Google Apps Script?

I'm using Google Forms as a tool to automate my workflow and generate documents from spreadsheet data.
To avoid typing all data twice, I'm trying to generate a form link with prefilled responses.
I can do it with this code (thanks to #Mogsdad previous response) and it works well, except when I have an optional "other" field in Checkbox and Multiple Choice optional "other" field. In this case, I'm not getting this optional value, which is just what I want, so the user won't have to retype it.
The code I'm using is this:
var ss = SpreadsheetApp.getActive();
var sheet = SpreadsheetApp.getActiveSheet();
var formUrl = ss.getFormUrl(); // Use a form attached to sheet
var form = FormApp.openByUrl(formUrl);
var items = form.getItems();
var cell_content = "Some string value in the spreadhseet";
for (var i = 0; i < items.length; i++ ) {
var id_item = items[i].getId();
var type_item = items[i].getType();
var item_of_form = form.getItemById(id_item);
if (type_item == "MULTIPLE_CHOICE"){
formItem = item_of_form.asMultipleChoiceItem();
if (formItem.hasOtherOption()){
formItem.showOtherOption(true);
}
var response = formItem.createResponse([cell_content]);
formResponse.withItemResponse(response);
}
var url = formResponse.toPrefilledUrl();
Logger.log(url);
All works fine (with Text and Paragraph responses and even with Checkbox and Multiple Choice (except with its optional value).
How can I put this optional values of checkbox and Multiple Choice in the prefilled form?
There does not seem to be a way to directly state that you want to set the response to "other". However, you can do it manually, and it shows how the pre-filled URL is crafted. I wrote some code (below) that will grab a list of items in a sheet (range B2:B4) which are supposed to be the responses to be pre-filled in "other.
The URLs will be logged, but you can use them how you please. Also, please keep in mind that this is only for the "other" question, the list where you get your data will pre-fill "other" regardless of what it says.
Try the following code:
function myFunction() {
var ss = SpreadsheetApp.getActive();
var formUrl = ss.getFormUrl(); // Use a form attached to sheet
var form = FormApp.openByUrl(formUrl);
var items = form.getItems();
var formItem = items[0].asMultipleChoiceItem(); // The question
var id = formItem.getId().toString()
var otherResponse = ss.getRange("B2:B4").getValues();
for (var i = 0; i < otherResponse.length; i++){
var string = "__other_option__&entry." + id + ".other_option_response=" + otherResponse[i];
var other = formItem.createResponse(string);
var respWithOther = decodeURIComponent(form.createResponse().withItemResponse(other).toPrefilledUrl());
var firstId = respWithOther.substring(117, 127);
var finalUrl = respWithOther.substring(0, 151) + firstId + respWithOther.substring(161, respWithOther.length);
Logger.log(finalUrl);
}
}

Get the last updated CELL in Google sheets and then grab the row. GAS

So I have a google form feeding into a sheet, and from the spreadsheet I'm making google docs from a template with the answers in the spreadsheet.
The form has the ability to save mid way and come back later via a custom question asking if they want to save and comeback (this submits the form). The script on my spreadsheet activates onFormSubmit so when they quit, the template gets created with half their answers. When they eventually come back and finish it off, I want the script to know where to create the template from.
For instance, 5 more rows were added since they quit and the script creates the template from a manual change of the line 'var tactics' by changing the numbers to the row. e.g. if I was about to test another entry, I'd change the numbers to the next empty row first, then when the form is submitted, it would use that row. Not practical. It wouldn't work.
I've looked around a bit and found onEdit(e) but that doesn't work unless it's a manual entry.
My question is, is there another way other than onEdit to find the last cell UPDATED, not ADDED else it'll grab the last row in the sheet, which I don't want. If it grabs the last cell updated then it will grab the correct row to run the script for. I'll add my script to the bottom if it'll help. ID's etc. have obviously been removed.
Any ideas?
function onFormSubmit(e) {
var Sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet()
var headers = Sheets.Spreadsheets.Values.get('myID', 'A1:U1');
var tactics = Sheets.Spreadsheets.Values.get('myID', 'A6:U6');
var templateID = "myID"
for(var i = 0; i < tactics.values.length; i++){
var Timestamp = tactics.values[i][0];
var IDCFREF = tactics.values[i][2];
var raisedby = tactics.values[i][4];
var AccMan = tactics.values[i][6];
var Contrib = tactics.values[i][7];
var Contract = tactics.values[i][8];
var CompName = tactics.values[i][9];
var ValidFrom = tactics.values[i][10];
var ValidTo = tactics.values[i][11];
var Freq = tactics.values[i][12];
var PDetailFreq = tactics.values[i][13];
var BillType = tactics.values[i][14];
var TypeOfRebate = tactics.values[i][15];
var RebateDetails = tactics.values[i][16];
var RTarget = tactics.values[i][17];
var GiveDeets = tactics.values[i][19];
var WhyGiveRebate = tactics.values[i][20];
var documentID = DriveApp.getFileById(templateID).makeCopy().getId();
DriveApp.getFileById(documentID).setName('Rebate ' + IDCFREF + ' Request');
var body = DocumentApp.openById(documentID).getBody();
var header = DocumentApp.openById(documentID).getHeader();
header.replaceText('##IDCF##', IDCFREF)
body.replaceText('##REF##', IDCFREF)
body.replaceText('##RAISED##', raisedby)
body.replaceText('##ACCMAN##', AccMan)
body.replaceText('##CONTRIB##', Contrib)
body.replaceText('##SIGNED##', Contract)
body.replaceText('##NAME##', CompName)
body.replaceText('##FROM##', ValidFrom)
body.replaceText('##TO##', ValidTo)
body.replaceText('##FREQ##', Freq)
body.replaceText('##BESPOKE##', PDetailFreq)
body.replaceText('##BILL##', BillType)
body.replaceText('##TYPE##', TypeOfRebate)
body.replaceText('##DEETS##', RebateDetails)
body.replaceText('##TARGET##', RTarget)
body.replaceText('##FULL##', GiveDeets)
body.replaceText('##ELAB##', WhyGiveRebate)
}
}
So for anyone who has the same issue, here was my solution:
function onFormSubmit(e) {
var range = e.range;
var ss = range.getSheet();
var row = range.getRowIndex();
var tactics = Sheets.Spreadsheets.Values.get('ID', "A:AQ"+row);
The way it works is just delete every row in the form responses sheet up until the last entry, and then voila. ("A:AQ"+row) gets the row index from the variable 'row' created from 'e.range' upon form submission.
It works great for me, if 3 forms were filled in and 3 lines were added to the sheet before another came back to edit his responses, the script knows what row to find the data on in the sheet, thus creating the correct template.