How do I use a google apps script to auto fill a google form with info taken from the user? [duplicate] - google-apps-script

With the recent study-from-home dynamics hastly implemented by budget schools, I am now facing a mindless-robot-like-task of filling in attendance for my kids everyday multiple times per child. The school shared this form:
I, being a developer, want to create a UI and have my kids submit thier attendance to this form on thier own when the teacher asks for one over Zoom. Ids it possible? I'm a .Net developer and have not developed anything for Office 365 Online and have no idea where to start looking for Google.

It is possible by reconstructing the form URL with pre-filled values.
In the Google Form, each question is named internally with "entry.". For each of the question, you need to find the entry number and assign them the right values and add it to the URL parameters like in the below image:
(Edited Oct 2021: The ids are no longer found in the name attribute of each input field in the HTML as shown in the image. They are now located in a div within the form element. #hdrz's answer below to look in the Javascript present just at the end of the body tag is now also defunct.)
I've recreated your form here https://docs.google.com/forms/d/e/1FAIpQLSfrGn49hcbeioNNa25Osp4fwTG2xV3BmmN9-cMWWC2-xvcQyg/viewform
And here is the reconstructed URL with prefill values
https://docs.google.com/forms/d/e/1FAIpQLSfrGn49hcbeioNNa25Osp4fwTG2xV3BmmN9-cMWWC2-xvcQyg/viewform?entry.1475351979=Julia&entry.280503419=Andrews&entry.519373518=4&entry.301819105=E
Hope it helps

As #AHunt writes, the entry number used to be in a name attribute on the form fields. As of this answer time, I can no longer find the name attribute on any of the form fields. However, now there is a script tag at the end of the body tag, with a short javascript code, and in it you can find the entry numbers, see here:
Also, it is possible to submit the form directly with pre-filled values. Just replace viewform in the URL with formResponse.
Similar URL as in the accepted answer, but now it will be submitted at once. Note that all required fields have to be filled!
https://docs.google.com/forms/d/e/1FAIpQLSfrGn49hcbeioNNa25Osp4fwTG2xV3BmmN9-cMWWC2-xvcQyg/formResponse?entry.1475351979=Julia&entry.280503419=Andrews&entry.519373518=4&entry.301819105=E&entry.1124370742=Art

I think I may be late but still give you a solution. I had made similar script to send my school attendance.
Every field in Google Forms is associated with a entry.<id>. You have two ways to automate the form.
One way is to extract those IDs with and make a dictionary where entry.<id> is the key and your answer is the value.Then you have to send a POST request to the form URL with the dictionary as the data. You have automated the form.
To extract the IDs, inspect the html code and look at the <script> at the (very) end of the page. It lòoks something like this :
var FB_PUBLIC_LOAD_DATA_ = [null,[null,[[2030831236,"First Name (in English)",null,0,[[1475351979,null,1]
]
]
,[86681139,"Last Name (in English)",null,0,[[280503419,null,1]
]
]
,[836880978,"Grade",null,2,[[519373518,[["KG 1",null,null,null,0]
,["KG 2",null,null,null,0]
,["1",null,null,null,0]
,["2",null,null,null,0]
,["3",null,null,null,0]
,["4",null,null,null,0]
,["5",null,null,null,0]
,["6",null,null,null,0]
,["7",null,null,null,0]
,["8",null,null,null,0]
,["9",null,null,null,0]
,["10",null,null,null,0]
,["11",null,null,null,0]
,["12",null,null,null,0]
]
,1,null,null,null,null,null,0]
]
]
,[221348070,"Section",null,2,[[301819105,[["A",null,null,null,0]
,["B",null,null,null,0]
,["C",null,null,null,0]
,["D",null,null,null,0]
,["E",null,null,null,0]
,["G",null,null,null,0]
]
,1,null,null,null,null,null,0]
]
]
,[366027193,"Subject",null,2,[[1124370742,[["Math",null,null,null,0]
,["Science",null,null,null,0]
,["English",null,null,null,0]
,["Arabic",null,null,null,0]
,["Islamic",null,null,null,0]
,["Social",null,null,null,0]
,["Moral",null,null,null,0]
,["Art",null,null,null,0]
,["Computer",null,null,null,0]
,["French",null,null,null,0]
,["Physics",null,null,null,0]
,["Chemistry",null,null,null,0]
,["Biology",null,null,null,0]
,["Business",null,null,null,0]
]
,1,null,null,null,null,null,0]
]
]
]
,null,null,[null,null,null,null,null,[null,null,null,[3,169,244,null,1]
,[217,242,253,null,1]
]
]
,null,null,null,"Attendance Form",48,null,null,null,null,null,[2]
]
As you can see there are two numbers with each field. One of them is the ID and another one I don't know. The second number is the ID we need. Using RegEx we can extract all the numbers and collect every second number in a list. This list will contain all the IDs.
Another way, as stated by others, is to reconstruct the URL with prefilled values. But in this too you have to extract the IDs.
I have included both of them in one and made this script :
import requests
from bs4 import BeautifulSoup
import re
def get_questions(url):
page = requests.get(url)
soup = BeautifulSoup(page.content, 'html.parser')
content = soup.body.find_all(text = re.compile('var FB'))
match = re.findall('[,]["][\w\s]+["][,]', str(content))
#It will match all the questions in the form
question_strings = [x.strip('"') for x in match]
match_ids = re.findall('(?<=\[\[)(\d+)', str(content))
#It will find all the numbers in the content
question_ids = ['entry.' + x for x in match_ids[1:]]
#It will leave the first numbers (they are not the ids)
return question_ids
# Below are only for when you want to know the form fills with their corresponding entry ids
# questions = dict(zip(question_strings, question_ids))
# return questions
def send_answers(url, fname, lname, grade, section, subject): #arrange this as per your form requirements
ids = get_questions(url)
answers = [fname, lname, grade, section, subject]
response = dict(zip(ids, answers))
if 'viewform' in url:
s = url.index('viewform')
response_url = url.replace(url[s::], 'formResponse?')
try:
r = requests.post(response_url, response)
if r.status_code == 200:
return '[!] Attendence posted !'
#In case an error happens, it will raise an exception
else:
raise Exception
#After raising the exception it will retry to submit using url reconstruction with prefilled values
except:
try:
ans_list = [x + '=' + y for x, y in zip(ids, answers)]
for i in range(0, len(ans_list)):
response_url += ans_list[i]
response_url += '&'
response_url.strip("&")
r = requests.get(response_url)
status = r.status_code
if status == 200:
return '[!] Attendance sent !'
else:
raise Exception
#If still an error happens, it will print out a message.
except:
return '[!] Attendance not sent !'
url = 'Form URL here'
fname = 'Your first name here'
lname = 'Your last name here'
grade = 'Your grade here'
section = 'Section here'
subject = 'Enter subject'
print(send_answers(url, fname, lname, grade, section, subject))
Hope it helps. Sorry for my bad English.

As a Google Form editor you can get a pre-filled URL (complete with entry values) by choosing the 'Get pre-filled link' option at the top right of the page, after clicking the three dots.
This will open the form in a new tab. The Submit (or Next) button will be replaced with a 'Get link' button.
After filling in the desired form responses, clicking 'Get link' will open a preview of what users will see. If everything is correct, clicking the 'COPY LINK' button (at the bottom left of page) will provide the URL.
The URL will look like this:
https://docs.google.com/forms/d/e/yourFormidhere/viewform?usp=pp_url&entry.1890935147=exampleprefilledvalue1&entry.1928475566=exampleprefilledvalue2&entry.2145528193=exampleprefilledvalue3

You can find the IDs in the console development section of Chrome.
Fill the form open networks tabs.
You can find the ID of each query in the form.

Easiest way is to simply launch Developer Tools (F12 or Ctrl+Shift+I usually) and go to the Network tab. Enable "Preserve log". Then just submit your form as usual, with the values you want (or test values).
In the Network tab, type "formResponse" in the filter bar. Find the latest entry and select it. Then go to Payload tab and click "view source". You should see something like this.
The text you see in the form data is simply what your append to the link of your form plus a '?' (question mark). So in this case our link will be https://docs.google.com/forms/d/e/1FAIpQLSfrGn49hcbeioNNa25Osp4fwTG2xV3BmmN9-cMWWC2-xvcQyg/viewform?entry.1475351979=test&entry.280503419=test&entry.519373518=KG+1&entry.1124370742=French&entry.301819105=A&dlut=1662432187937&hud=true&entry.519373518_sentinel=&entry.301819105_sentinel=&entry.1124370742_sentinel=&fvv=1&partialResponse=%5Bnull%2Cnull%2C%22-6523749705829110087%22%5D&pageHistory=0&fbzx=-6523749705829110087.
Now let's just remove the extra parameters that aren't answers. Find the last answer you input and remove the rest. Our new link will be https://docs.google.com/forms/d/e/1FAIpQLSfrGn49hcbeioNNa25Osp4fwTG2xV3BmmN9-cMWWC2-xvcQyg/viewform?entry.1475351979=test&entry.280503419=test&entry.519373518=KG+1&entry.1124370742=French&entry.301819105=A
This will open the form page with pre-filled values. If you actually want to submit the response right away, replace "viewform" with "formResponse".
The google form I tried is from #AHunt's answer, so thank you for that sample google form.

Related

Google form Multiple Choice Other option not visible when submitted from app script

I have a google form where I have a multiple choice question "Territory field". This field is having two valid answer
United States and
Others (When you choose other option you can write down any Country name in text area)
Now I am trying to submit this form using google app script. While submitting from app script if I am sending answer to this multiple choice question as "United States" its workiing. I want to send Others as answer to this question while submitting the form. Can anyone help me out. I am including picture of field and script below for reference .
Picture of form field with multiple choice
I am trying to submit this form using google script (part of script mentioned below).
var formID = "xxxxxxxxxxxxxxxxxxxxxx";
var Formobj= FormApp.openById(formID);
var formResponse = Formobj.createResponse();
var items = Formobj.getItems();
var territory = items[1]
var territoryvalue = "United States"
if (territoryvalue.indexOf("United States") !== -1)
{
var territoryfield = territory.asMultipleChoiceItem()
var territoryresponse = territoryfield .createResponse([["United States"]])
formResponse.withItemResponse(territoryresponse);
}
Field value after form is submitted with United states as answer
Need help in submitting this form with "Others" as answer
The documentation for MultipleChoiceItem.createResponse() reads:
Creates a new ItemResponse for this multiple-choice item. Throws an exception if the response argument does not match a valid choice for this item, unless showOtherOption(enabled) is set to true.
So to submit an "other" response, just pass it the value.
var territoryresponse = territoryfield.createResponse([["Other Country"]])
If instead you want to add a new value as a selectable choice in the form, then you can create a new choice and then set the item choices.
var territory = items[0].asMultipleChoiceItem();
var newCountryChoice = territory.createChoice("Another Country");
var choices = territory.getChoices(); // Get the existing choices
choices.push(newCountryChoice); // Append the new choice to the list of existing
territory.setChoices(choices); // Set the updated choices
I saw you filed a bug in Issue Tracker, and based on your explanation there I could finally understand this issue.
This seems to be a bug:
In Forms, if a Multiple choice item gets submitted with an Other choice via Apps Script, this other choice is not populated when trying to edit the response. If the response is submitted via UI, the Other option is populated.
It's important to note that the response itself is getting submitted successfully, and can be seen in the Responses tab of the Form; it's just not populated when trying to edit the response.
Steps to reproduce:
Create a new Form.
Create a Multiple choice item for the Form, if there is not one already.
Click add "Other" in order to have an Other option.
Click Settings (gear icon) and check the option Respondents can: Edit after submit.
Open the script editor bound to the Form, and copy and run the following code:
function submitAndGetEditResponse() {
var form = FormApp.getActiveForm();
var item = form.getItems()[0].asMultipleChoiceItem();
var otherOption = "Option 2"; // Not one of the named options
var itemResponse = item.createResponse([[otherOption]]);
var formResponse = form.createResponse().withItemResponse(itemResponse);
var editUrl = formResponse.submit().getEditResponseUrl();
console.log(editUrl);
return editUrl;
}
Access the URL (editUrl) with the browser.
Option 2 should be populated when trying to edit the response, but it's not.
Issue Tracker:
The issue you filed in Issue Tracker just got forwarded internally by Google:
Google Form Submission Through Google App Script - Multiple Choice Question's Other Option Not Populating
Anyone affected by this, please consider clicking the star on the top-left in order to keep track of it and to help prioritizing it.
I tried replicating your code and got the solution for what you wanted.
If you want your "Other" answer to be ticked in the individual responses. Your response string should look like this: "other_option (Country)"
For example, if you want Mexico to appear in the "Other:" option, you should write "__other_option Mexico" as the value for your territoryvalue variable. It should look like this in that part of your code:
var territoryvalue = "__other_option__ Mexico"

"Create or update" form behavior when hitting back button

I have the following workflow on a website:
Some user John Doe declares a company through form 1
(fields: name, head office location)
After John Doe submits (HTTP POST) form 1, he is redirected (HTTP 302) to company form 2 with additional legal information about the company.
The problem is, if John Doe hits the back button of his browser during step 2, he will land on the form 1, with data filled by the browser (using values he already submitted — that's what Firefox and major browsers seem to do).
John Doe might then think he can use this form to update some information (e.g. fix a typo in the name of the company) whereas he will actually create a new company doing so, as we don't know on the server side whether he wants to declare a new company or update the one he just created.
Do you know any simple solution to handle that problem ?
Use javascript/jquery script after the page is loaded to empty all the inputs. This will prevent confusion of "updating the company".
jQuery would look something like this:
$('#elementID').val('');
You can also handle the situation by manipulating the browser history
on load of form 2, and pass the CompanyId generated on submit of form 1 using querystring. So that you can actually update the company as the user
Suppose John submits form1.html, a unique CompanyId "1001" is generated and redirected to form2.html. Now on load of form2 you can modify the browser history form1.html?companyid=1001 using
var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 1", "form1.html?companyid=1001");
Now, when the user click back button and submits the form1 again. you can check for companyid in querystring and update the company.
I think it is more user-friendly when user can return back to previous form and update it (instead preventing the described behavior).
I use in most cases similar way to handle described problem:
Let's assume that user is on the page /some-page, that contains "Create new company" button.
When the user opens this page, will be executed special method createOrFindCompanyDraft() on the server-side. This method creates new company "draft" record in DB (only for the current user). For example, draft record has primary key id=473. When you execute this method again it will return the same record with the id=473 (with "draft" status). "Draft" record should't display on any other interfaces.
And "Create new company" has link /company/common/473.
When user go to /company/common/473, you display form 1, that will be filled from "draft" record. At first time user will see empty form.
Technically user will update the existing record, but you can display "Create new company" title on the page.
Then user go to form 2, for example, /company/legal-info/473, you create similar draft record for the this form (similar to step 1).
When user submit the form 2, you will remove "draft" status from the record id=473 (and any related records).
Next time when user open page /some-page, will be created new draft record for the current user.
Browser history will contain:
/some-page
/company/common/473
/company/legal-info/473
/some-page2
I like this approach, because all form only update records. You can go to previous/next form many times (for example "Back"/"Forward" browser buttons). You can close browser, and open not completed forms tomorrow. This way doesn't require any additional manipulation with the browser history.
try this
<form autocomplete="off" ...></form>
And Another
Use temporary tables or session to store the Page 1 form data. If the page 2 form is submitted use the temporary data of page 1 which is stored in database or in session.
Use a Separate key (Hidden field ) in both page 1 and page 2.
Actually I thought of a trick to obtain that "create on first post, update after" behavior (just like the user thinks it should behave).
Let's say the step 1 form is at the URL /create_company/. Then I could have that page generate a random code XXX and redirect to /create_company/?token=XXX. When I create the company I save the information that it was created through page with token XXX (for instance, I save it in user's session as we don't need to keep that information forever) and when the form is submitted, if I know that a company was already generated using this token, I know the user used the same form instance and must have used the back button since the token would be different if he explicitly asked for another company.
What do you think ? (I initially thought there should be a simpler solution, as this seems a little bit over-engineered for such a simple issue)
This is more like a UX question.
I'd think that the solution lies within the information given to the user on that form, to help them understand what they're doing.
Set a title that says 'Create a company', for example, and set your submit button as 'Create Company' will help your user with that. Use a unique id when you create the company object, and pass the id back to the same URL in order to perform an update. You should then update your title and button that tells user that they are updating instead of creating.
In that sense I'd say it's better to use a more generic URL like /company and /company?id=12345.
You could also consider using Restful API protocol to help your server identifies the CRUD operation. http://www.restapitutorial.com/lessons/httpmethods.html
Without the "routing" part of django it is hard to help. I can just answer my experience from the express.js-router functionality:
you can specify a post on /company, which is for new users.
you can specify another route for post on /company/:companyid for a changing form
and as a response from the create-post you can redirect to the different location.

How can I prevent to set a required recipient and the Open Graph object to appear?

I am currently trying to embed a Yammer Open Graph feed into SharePoint pages. I am using the following configuration:
yam.connect.embedFeed({
container: "#embedded-feed"
, feedType: "open-graph"
, feedId: ""
, config: {
defaultGroupId: 27862 // recipient is required so I specified a dedicated group for article feeds
//, use _ sso: true
, header: false
, footer: false
, showOpenGraphPreview: false
, defaultToCanonical: false
, hideNetworkName: true
, promptText: "What do you think of this article?"
}
, objectProperties: {
url: "" // <empty> takes the url from the current page
, type: "page"
}
});
(feed configuration can be generated through https://www.yammer.com/widget/configure)
It seems that a recipient is required, so I had to create a dummy group and specify this dummy group-id where all the messages are landing. I don't want to polute some regular group with Open Graph object feeds. Can this be addressed in a better way?
Additionally, at least the first message contains the Open Graph object attached to it. This doesn't look nice, and is quite unnecessary as the feed is completely embedded onto the object already. Can this attachment be prevented? Moreover, as soon as you specify a calculated object url into the objectProperties, the object is attached to all messages. Which is completely overdone. Ideally the Open Graph object url is only used to identify the specific object feed.
Refer to the Facebook api where these two requirements are met: https://developers.facebook.com/docs/plugins/comments
I think I have captured your questions below. Added my answer
How can I prevent to set a required recipient?
You don't need to specify defaultGroupId. Adding this value simply changes the default Yammer group that the Embed Feed lists.
From the documentation:
'You can set a default group for comments in an OGO Object Feed that will appear in the destination box of the publisher by specifying a defaultGroupId. The user can change this and post to a group of their choice.
If you do not specify a defaultGroupId, the destination box will be left blank and the user will be prompted to add a group on their own when posting a comment.
Specifying defaultGroupId: 0 will set the default to All Company.'
https://developer.yammer.com/docs/commenting
How can I prevent the Open Graph object to appear in the posting message window?
I am pretty sure this is by design. I can see how this can be confusing. I don't think there is a way to disable that.

How can I move to subsequent web pages after POST (ing) a html form in R?

A happy and a prosperous New Year, 2016 to everyone.
Here, I am using NCDC website to download weather data from say 01-Jan-2015 to 31-Mar-2015 for a state in the US say Connecticut. The link is mentioned below in the script. From filling details on this webpage to page in which you click on submit after entering your email id, there are intermediate pages that require some inputs before continuing on to a next page. My goal is to write a R script that automatically triggers download (submit order) for different states for different time periods after providing required inputs.
For purpose of ease, I am putting steps that one need to follow to download data for Connecticut. These steps are (please go through 2 snapshots attached):
Step-1 : Here, you select a dataset, State name, and time period-
4 values that would go in the form on landing page are Daily Summaries, 2015-01-01 to 2015-03-31, States, and Connecticut.
library(httr)
url <- "http://www.ncdc.noaa.gov/cdo-web/search?datasetid=GHCND"
fd <- list(
submit = "Search",
searchStartDate = "2015-01-01",
searchEndDate = "2015-03-31",
resultType = "States",
searchString = "Connecticut"
)
resp<-POST(url, body=fd, encode="form")
Step-2 : Here, you would need to click first on ADD TO CART and then on View All Items under Cart. This would be possible, if we operate on resp from above step. I am not able to figure out a way to do this operation.
Step-3 : Here, you need to select 2nd option under "Select the Output Format" before clicking on CONTINUE.
Step-4 : Here, you have to select many options as highlighted in the image before clicking on CONTINUE.
Step-5: Finally, you enter the email id and click on "SUBMIT ORDER".
If you need any other input from my end, do let me know.
Thanks

How to get/read Email ID from a from response

I have created an application where I am collecting form responses from various users. I am getting responses with email id in responses spreadsheet. As I don't want to store data in spreadsheet so I am reading data trough responses. I am facing some challenges please guide.
Query 1
while using onFormSubmit(e) I am not able to read submitted form, given code is returning null:
var form = FormApp.getActiveForm();
Logger.log('usename:' + form.getId());
error " Cannot call method "getId" of null." although if I hard coded value of formid var form = FormApp.openById('<<form_id_xyz>>'); then it is working fine and I can read responses as well.
How can I get form responses for multiple users?
Query 2
getRespondentEmail(); is not working in my case. Even I use form id <<form_id_xyz>> and trying to get email id from responses which I have captured at the time of form submission form.setCollectEmail(true); I tried following code in onFormSubmit(e) function but dint get a result:
var formResponse=form.response;
Logger.log('email id of user: ' + formResponses.getRespondentEmail());
and another way:
Logger.log('email id of user: ' + form.getRespondentEmail());
and
Logger.log('email id of user: ' + e.values[1]);
nothing works for me. Kindly guide.
Query 1: Hope it's clear in my comment.
Query 2:
Sorry to say, I don't understand your second query problem completely.
However as per your requirement I am suggesting this code.
If you have created a form you should know the form id (I assume) so try this code.
var form=FormApp.openById('your form id here');
//this returns your form which you created//
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++){
Logger.log(responses[i].getRespondentEmail());
}
I think it's important to note that at the present time the answer to your question is: You can get what they enter, but you cannot get their true verified Email Address. This is explained better in this question and one of the answers details some workarounds such as publishing form as a web script.
The accepted answer displays what email address the user has typed into the form. There is no authentication to this beyond it having an # symbol thus a user could type foofoo#zoomZoom.com and it would be viewed in the forms results and scripts.
What's annoying is that google IS capturing the user's true email address because if settings are set to Allow One Response Per User, then the user is limited to one submission -- regardless of what they put as their email account. I'm not sure why Google won't provide a method to view the submitter's login email address since it has been disclosed to the user that this will be disclosed.
Microsoft Forms does capture this.