Reputation: 23200
Google Form's data can be pulled by API. Using Python I pulled it like this:
from __future__ import print_function
from apiclient import discovery
from httplib2 import Http
from oauth2client.service_account import ServiceAccountCredentials
import json
SCOPES = "https://www.googleapis.com/auth/forms.body.readonly"
DISCOVERY_DOC = "https://forms.googleapis.com/$discovery/rest?version=v1"
def get_google_form_data(form_id):
creds = ServiceAccountCredentials.from_json_keyfile_name('service.json', SCOPES)
http_auth = creds.authorize(Http())
service = discovery.build('forms', 'v1', http=http_auth, discoveryServiceUrl=DISCOVERY_DOC, static_discovery=False)
result = service.forms().get(formId=form_id).execute()
return result
if __name__ == "__main__":
form_id = 'xxx'
json_data = get_google_form_data(form_id)
with open("form_data.json", "w") as f:
json.dump(json_data, f, indent=4)
Respondents to the quiz may be shown different questions, depending on their answer. In the Form owner's web view of the Form, these the different sections are labeled consecutively ("Section 1", "Section 2", etc.) and they can associate values (a.k.a. answers) with "skip logic" using those section labels. For example the first answer may link to Section 10 and the second answer choice may link to Section 20.
In the JSON data, instead of those consecutive user-friendly labels, it calls it goToSectionId
which is "Item ID of section header to go to". That would be simple and straightforward, but there's nothing in the API (that I can find) which can be used to find identify the corresponding section header or questions within the header. It has "questionID", which is also an Item ID, but those are not the same as the section ID.
I would assume I must've made a mistake, but if I search the raw JSON with any value from goToSectionId
, in every case they only appear once, thus they provide no mapping. How can I extract the skip logic?
These are the API docs: https://developers.google.com/forms/api/reference/rest/v1/forms#QuestionItem
Upvotes: 1
Views: 138
Reputation: 465
I came across a very similar problem, i meaning the attribution of conditional logic to ChoiceQuestion type. However, having developed my project in Java, the solution I found is different, so please extract the "concept" despite the code which will probably not be useful to you.
Let's start with theory: we need to create a properly formatted JSON request to create a form. In this JSON request we can assign itemId properties chosen by us to the Items, rather than being satisfied with their automatic attribution.
i.e:
{
"formId": null,
"info":{
"documentTitle":"Test1",
"title":"Test 1"
},
"items":[{
"itemId":"00000001",
"questionItem": {
"question":{
"choiceQuestion":{
"options":[{
"value":"Option 1",
"goToSectionId": "00000002"
},
{
"value":"Option 2",
"goToSectionId": "00000004"
}],
"type":"DROP_DOWN"
},
"questionId":"619ff865"}
},
"title":"Question 1"
},
{
"description":"New Page descriptor",
"itemId":"00000002",
"pageBreakItem":{},
"title":"ciao"
},
{
"itemId":"00000003",
"questionItem":{
"question":{
"choiceQuestion":{
"options":[{
"value":"Yes"
},
{
"value":"No"
}],
"type":"RADIO"
},
"questionId":"73bcb98f"
}},
"title":"Question 2"
},
{
"itemId":"00000004",
"pageBreakItem":{
},
"title":"New Page descriptor"
},
{
"itemId":"00000005",
"questionItem":{
"question":{
"choiceQuestion":{
"options":[{
"value":"Yes"
},
{
"value":"No"
}],
"type":"RADIO"
},
"questionId":"73bcb98f"
}},
"title":"Question 3"
},],
"responderUri": null,
"revisionId": null
}
Please note that the JSON above consists of three questions and two page breaks. The answer to question 1 will activate the skip logic to question 2 (item 00000003) or question 3 (item 00000005).
Please note that pageBreakItems correspond to the options in question 1.
In my case this was the difficult part. Having noted that the Google Form API and the related Java libraries use BatchRequest where each element is inserted sequentially, the insertion of question return an error because there was no correspondence between the ids of the options and subsequent pageBreakitems (which had yet to be inserted).
The solution in my case was to make 2 different requests:
The first to insert all elements and the PageBreakItems except the questions with a conditional logic (Question1)
The second to insert the question with a conditional logic, like the Question 1
As an example I attach the Java method I wrote:
private void publishForm(Form form, CustomForm customForm) throws IOException {
BatchUpdateFormRequest batchRequest = new BatchUpdateFormRequest();
List<Request> requests = new ArrayList<>();
List<Request> goToSectionIdTypeRequest = new ArrayList<>();
Integer counter = 0;
for(CustomItem item : customForm.getItems()){
Request request = new Request();
request.setCreateItem(new CreateItemRequest());
request.getCreateItem().setLocation(new Location());
request.getCreateItem().setItem(item.getItem());
request.getCreateItem().getLocation().setIndex(counter);
/*
* If the element is a dropdown it can have also an goToSectionId parameter:
* in this case, it must be pushed inside the form in a second time
*/
if (item.getQuestionItem() != null) {
if (item.getQuestionItem().getQuestion().getChoiceQuestion() != null) {
if (item.getQuestionItem().getQuestion().getChoiceQuestion().getType() != null) {
String choiceType = item.getQuestionItem().getQuestion().getChoiceQuestion().getType();
if (choiceType.equals(ChoiceType.DROPDOWN.label)) {
if (item.getQuestionItem().getQuestion().getChoiceQuestion().getOptions().get(1).getGoToSectionId() != null) {
goToSectionIdTypeRequest.add(request);
continue;
}
}
}
}
}
requests.add(request);
counter++;
}
batchRequest.setRequests(requests);
this.formservice.forms().batchUpdate(form.getFormId(), batchRequest).setAccessToken(token).execute();
batchRequest.setRequests(goToSectionIdTypeRequest);
this.formservice.forms().batchUpdate(form.getFormId(), batchRequest).setAccessToken(token).execute();
It's not elegant but it works.
I remain at your disposal and I hope you can resolve your problem.
Thank you.
Upvotes: 1