Google form url point to last updated answer - google-apps-script

I have created a Google form with a Google spreadsheet associated to it that can be accessed (using the form URL) by each recipient I am sending it to. Right now the URL isn't unique to each recipient. The recipient can access the form then submit it. Upon submit, I am generating an edit URL via the GetEditResponse() function available in the Google FormResponse Class. The recipient can then edit their response using the edit url I provide them once they submit the form.
Here is my Google App Script code:
function myFunction() {
assignEditUrls();
}
function assignEditUrls() {
var form = FormApp.openById('formId');
//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++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(shortenUrl(responses[i].getEditResponseUrl()));
}
for (var j = 1; j < data.length; j++) {
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 = "apiKey";
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;
}
You can also view the Spreadsheet associated with the form.
The problem with this model is that the recipients need to get back an edited URL in order to edit their response at a later time. Instead I want them to keep the same URL I originally provided them with and each time they come back to that URL I should identify which recipient it is and redirect them with the last updated URL.
For that scenario to happen I would need to:
1. Create a unique URL (identifier) for each recipient.
2. The same URL should point to the last updated URL (Form) so that they can always use the same URL originally provided (backend process).
Is this possible to achieve using Google's available tools? If it is, how can I create a unique url to identify which recipient responded to the form?

Staying within the confines of Google Apps Script, you cannot provide a redirection to a Google Form.
Here are two ideas off the top of my head...
If you're willing to use another service to handle the redirection, you could have it do the redirection for you. (I'm not recommending any, just saying it's an option.)
You could write a Google Apps Script web service that would present a mock-up of your real form. Users would have a unique URL to pass their unique identifier as a HTTP query parameter; heck, you could use goo.gl to produce a short URL for each of them as you do now. Based on the identifier, pre-fill the fake form with their last results. Upon commit, your web service can submit the form programmatically.

Create a unique URL (identifier) for each recipient.
Creating a separate Google Form for each person will give you this.
The same URL should point to the last updated URL
Assuming your go with creating a separate Form for each user, try placing the edit URL back into each Form's description or 1st page some where after submission of the 1st response. You'd have to make it clear that they have to click on that link going forward. There isn't a setting to automatically re-direct original Google Form URLs to their corresponding Edit Response ones AFAIK, if that's what you are asking for.

Related

Set pre-filled value of Google Form item from google apps script

What I want to achieve?
I want to track the review status of a google document with a google form with dropdown options as "To do, In progress, Done". I have google form items as "URL of document, Status". I have created a google form template that I'll be using to create forms for various users. I want to be able to create a copy of the template form, and set predefined value of "URL" from google apps script, so that the users just have to select the status of the document.
What I tried?
I came across createResponse() method from this answer, but this requires .submit() to be used to save the response and the answer is recorded in sheets. I don't want to submit the form from the script itself. Here is the code:
function form()
{
var form = FormApp.create("Test");
form.addTextItem().setTitle("URL");
form.addTextItem().setTitle("Status");
var items = form.getItems();
var url = items[0].asTextItem();
var fr = url.createResponse('my predefined url');
var FormResponse = form.createResponse();
FormResponse.withItemResponse(fr);
FormResponse.submit();
Logger.log(form.getPublishedUrl());
}
Final query:
How do I get the published URL of the form with the pre-filled answer to the URL item from the apps script? Is it possible?
Turns out there is a method .toPrefilledUrl() which resturns the published url of form with prefilled values. Here is an example:
var form = FormApp.openByUrl("url");
var items = form.getItems();
var url = items[0].asTextItem();
var d= "some.url"
var fr = url.createResponse(d);
var FormResponse = form.createResponse();
var urlPub = FormResponse.withItemResponse(fr).toPrefilledUrl();
Logger.log(urlPub);

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).

Using data from var in subjectLine

I'm modifying Amit's code ( found here: http://labnol.org/?p=20884)
to try to send email with the data from a Google Form.
But what I'm trying to grab is from his keys and columns.
I want to specifically take the first 1 and 2 column's data from the row in question and use it as a var in the subject field.
But the output (in email and when sent to asana) is listed as undefined. Where did I go wrong?
/*
Send Google Form Data by Email v4.2
Written by Amit Agarwal amit#labnol.org
Source: http://labnol.org/?p=20884
*/
/**
* #OnlyCurrentDoc
*/
function Initialize() {
try {
var triggers = ScriptApp.getProjectTriggers();
for (var i in triggers)
ScriptApp.deleteTrigger(triggers[i]);
ScriptApp.newTrigger("EmailGoogleFormData")
.forSpreadsheet(SpreadsheetApp.getActiveSpreadsheet())
.onFormSubmit().create();
} catch (error) {
throw new Error("Please add this code in the Google Spreadsheet");
}
}
function EmailGoogleFormData(e) {
if (!e) {
throw new Error("Please go the Run menu and choose Initialize");
}
try {
if (MailApp.getRemainingDailyQuota() > 0) {
// You may replace this with another email address
var email = "x+00000000#mail.asana.com";
// Enter your subject for Google Form email notifications
var key, entry,
message = "",
ss = SpreadsheetApp.getActiveSheet(),
cols = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
// Iterate through the Form Fields
for (var keys in cols) {
key = cols[keys];
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
// Only include form fields that are not blank
if ((entry !== "") && (entry.replace(/,/g, "") !== ""))
message += key + ' :: ' + entry + "\n\n";
var first = entry[1];
var last = entry[2];
var subject = first+" "+last+": Interested Candidate";
}
MailApp.sendEmail(email, subject, message);
}
} catch (error) {
Logger.log(error.toString());
}
}
/* For support, contact developer at www.ctrlq.org */
entry is a string, defined here:
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
...which you later treat as an array:
var first = entry[1];
var last = entry[2];
At this point, first and last will both be undefined, because entry isn't an array. Further, this is inside a for loop that's traversing all the columns in the row - you can't see any bad side-effect from that, but these assignments and generation of a subject are happening multiple times.
That last clue suggests a better way to achieve your goal. Define the first and last variables before the loop, with default values. Then when looping over columns, watch for the columns containing the candidates' name, and update the default contents. Finally, after the loop, generate the subject line.
function EmailGoogleFormData(e) {
if (!e) {
throw new Error("Please go the Run menu and choose Initialize");
}
try {
if (MailApp.getRemainingDailyQuota() > 0) {
// You may replace this with another email address
var email = "x+00000000#mail.asana.com";
// Enter your subject for Google Form email notifications
var key, entry,
first = "unknown", last = "unknown",
message = "",
ss = SpreadsheetApp.getActiveSheet(),
cols = ss.getRange(1, 1, 1, ss.getLastColumn()).getValues()[0];
// Iterate through the Form Fields
for (var keys in cols) {
key = cols[keys];
entry = e.namedValues[key] ? e.namedValues[key].toString() : "";
// Only include form fields that are not blank
if ((entry !== "") && (entry.replace(/,/g, "") !== ""))
message += key + ' :: ' + entry + "\n\n";
if (key == "first") { // Assumes "first" is column header
first = entry;
}
if (key == "last") { // Assumes "last" is column header
last= entry;
}
}
var subject = first+" "+last+": Interested Candidate";
MailApp.sendEmail(email, subject, message);
}
} catch (error) {
Logger.log(error.toString());
}
}
Sandy Good has created a similar app Data Director. I don't know why he did not mention it here? May be it's not what you're looking for.
I haven't used it yet, but thought his works might help someone who needs it.
----------------------------------------
OVERVIEW:
Send form data to different sheet. Integrate with Calendar. Sends emails. Makes an Edit URL and/or a PreFilled URL.
The Data Director for Forms Add-on has multiple features. It can send the form response to an alternate spreadsheet. It can send an email or multiple emails when the Form is submitted. It can add a guest to your calendar event.
When your Google Form is submitted, the Data Director for Forms Add-on can get the last form submission, and save it to a second spreadsheet destination of your choice. The destination spreadsheet can be any Google spreadsheet that your Google account has permission to write to. For example: Your Google Form currently writes data to a spreadsheet, but you want the form response to also go into a second sheet in the same spreadsheet. This Add-on can do that. Or the Add-on can write a copy of the form response to a completely different spreadsheet.
You should install this add-on if you want to save a copy the form response to to a destination other than what is set in the Form's design.
But that's not all Data Director can do! Data Director will also create an Edit URL and/or a PreFilled URL, and save those links to the spreadsheet.
There's even more! It will also send an email to the email address of your choice with a custom message. This is an extra option that you may want or need to use.
Here's a list of What Data Director can do!
Send a copy of the form response to a Google spreadsheet.
The same Google spreadsheet that is already receiving the Form response, or
A different spreadsheet than is currently receiving the Form response.
Exclude the timestamp from the copied response if you choose. The default is to include the timestamp.
Create an Edit URL and save a link to the destination spreadsheet.
Create a PreFilled URL and save the link to the destination spreadsheet.
Send multiple emails to the email addresses of your choice.
Send an email to the email address collected from a Form field.
Include the Edit Url and/or the PreFilled Url in the email.
CC the email to the address of your choice, or not.
Includes the option to specify the subject line.
The Body of the email can be written in the settings for the email. No need to create a template email.

getRespondentEmail() returning my email not the Form responder

I am on a Google domain and am trying to extract the form respondents email (which is automatically collected as they have to be signed in to fill out the form)
but the result is that I keep getting my email returned instead of the form respondent?
This is code that is contained within the given form, hence using
var form = FormApp.getActiveForm()
but I cant work out why I am getting my own email every time?
function GetRespondersEmail(){
var form = FormApp.getActiveForm();
var responses=form.getResponses();
/// this will give you all responses of your form as an array////
///iterate the array to get respondent email id///
for(var i = 0; i < responses.length; i++){
var emailTo = responses[i].getRespondentEmail();
Logger.log('emailTo = '+emailTo);
return emailTo;
}
};
Ok so lesson learnt for me, what I am trying to do is access the single email address of the user filling in the form . So it turns out I can fully replace the above function with the function onFormSubmit()
ie
function onFormSubmit(e) {
var emailTo = e.response.getRespondentEmail();
}
Unless I am overlooking something this is far simpler than I was trying above.
getRespondentEmail() :
Gets the email address of the person who submitted this response, if the Form.setCollectEmail(collect) setting is enabled.
Did you set Form.setCollectEmail(collect) to true ?
NB: This feature is available only for forms created by users of Google Apps for Business, Google Apps for Education, or Google Apps for Your Domain. Email addresses for other types of Google accounts cannot be collected. For forms created with other Google accounts, this method throws a scripting exception.
Here is the edit:
I changed a bit your code. The problem is you return a value on your loop, so you never finish your loop, and return only the e-mail of the first response.
function GetRespondersEmail(){
var form = FormApp.getActiveForm();
var emailTo = []
var responses=form.getResponses();
/// this will give you all responses of your form as an array////
///iterate the array to get respondent email id///
for(var i = 0; i < responses.length; i++){
emailTo[i] = responses[i].getRespondentEmail();
}
Logger.log('emailTo = '+emailTo);
return emailTo;
};
I tried this and seems to answer your needs.

How to generate a Google Doc from a Form response, containing all Form items and responses?

I am currently using the following to send an email to a distribution list containing the headers/questions and corresponding answers when a Google Form is submitted:
function sendFormByEmail(e) {
// Subject field of email
var emailSubject = "New Student Case Filed";
// Comma-separated list of email addresses for distribution.
var yourEmail = "my email address";
// Spreadsheet key, found in the URL when viewing your spreadsheet.
var docKey = "my spreadsheet key";
// If you want the script to auto send to all of the spreadsheet's editors, set this value as 1.
// Otherwise set to 0 and it will send to the yourEmail values.
var useEditors = 0;
// Have you added columns that are not being used in your form? If so, set this value to
// the NUMBER of the last column that is used in your form.
// for example, Column C is the number 3
var extraColumns = 40;
if (useEditors) {
var editors = DocsList.getFileById(docKey).getEditors();
if (editors) {
var notify = editors.join(',');
} else var notify = yourEmail;
} else {
var notify = yourEmail;
}
// The variable e holds all the submission values in an array.
// Loop through the array and append values to the body.
// Need to omit headers with no response*
var s = SpreadsheetApp.getActive().getSheetByName("StudentCases");
if (extraColumns){
var headers = s.getRange(1,1,1,extraColumns).getValues()[0];
} else var headers = s.getRange(1,1,1,40).getValues()[0];
var message = "";
for(var i in headers) {
message += headers[i] + ' : '+ e.values[i].toString() + '\n\n';
}
Now I also want a Google Doc created containing the headers and responses. So far, I've been able to create the Doc, add the title, and add a paragraph, but now I need to replicate the array of headers/responses in the Google Doc as well.
// Create a new Google Doc named 'Case File' * need to figure out how to pull last name response from form.
var doc = DocumentApp.create('Case File: Last Name*');
// Access the body of the Doc, add a paragraph, *need to append array of headers/answers
var body = doc.getBody().body.appendParagraph();
// Get the URL of the Google Doc to include in Email
var url = doc.getUrl();
// Get ID of Doc to attach to email
var id = doc.getId()
One other issue I'd like to solve; I only need the headers/questions that contain a response, as many of them will not necessarily warrant an answer. So in other words, IF there is no answer, THEN do not append to email.
It seems you provided a list of general requirements without much in the way of what you tried with the results you got. StackOverflow will be helpful to you if you provide more pointed questions about what exactly you have tried..
Can you share the exact code you have tried please? What were your results?
From a high level, I would proceed with this general workflow.
Draft a gDoc template using placeholders in a variables nomenclature of your choice ~FName ~LName etc..
Use an onFormSubmit trigger to make a copy of the gDoc template when a new form is submitted.
Replace the ~FName ~LName placeholders in the copied gDoc with content captured in the form
Save the copied gDoc as a PDF
email the PDF to the email address provided in the form submission