We have a series of Google Forms that contain multiple choice questions, each with 4 possible answers.
I would like to be able to export the question and all possible answers to a Google Sheet for all of the questions and answers in that Google Form.
For example:
Q1: What is the capital of England?
A: London
B: Paris
C: Madrid
D: Helsinki
I've tried a variety of add-ons. There are loads that allow Google Sheets > Google Form, but nothing in reverse (that I can find), so I assume it will be a script of some kind.
Any help would be really appreciated.
Thanks. Liam.
In the following code, which I made using Apps Script, you can find a way to extract questions and answers from a google form and then put the values in a certain sheet of your choice
// Open a form by ID.
var form = FormApp.openById('YOUR-FORM-ID');
// Open a sheet by ID.
var sheet = SpreadsheetApp.openById('YOUR-SHEET-ID').getSheets()[0];
// variables for putting the questions and answers in the right position
var question_position = 0;
var answers_position = 0;
// main function to run
function getFormValues() {
form.getItems().forEach(callback);
}
// Iterate over all questions
function callback(el){
// check if the question is multiple choice
if (el.getType() == FormApp.ItemType.MULTIPLE_CHOICE) {
// change the type from Item to MultipleChoiceItem
var question = el.asMultipleChoiceItem();
var choices = question.getChoices();
// set the title of the question in the cell
sheet.getRange(question_position +1, 1).setValue(question.getTitle());
var i = 0;
// set the answers in the right cells
for (i; i < choices.length; i++){
sheet.getRange(answers_position + 1, 2).setValue(choices[i].getValue());
answers_position++;
}
question_position += i;
answers_position++;
}
question_position++;
}
Docs:
If you're wondering where I got all this info you can check these two links:
Spreadsheet
Google Forms
I got almost the same problem that you were dealing with, I created a little script with the documentation for my own purposes but I think it may help you to understand how to retrieve the information.
You need to be aware of these two API:
https://developers.google.com/apps-script/reference/forms (forms) and https://developers.google.com/apps-script/reference/spreadsheet (sheets)
Google Form mapping
Then, I would check how to post it into a Google Sheet through the API.
Check you have all the permissions set.
This seems like you'd need an Apps Script add-on or a manually developed Apps-Script script. Try to find a freelancer or a coworker to build it for you.
Sheets is the easiest to work with: https://developers.google.com/apps-script/reference/spreadsheet/
I needed a script to convert some Google Forms to the GIFT Moodle format. I modified #alberto-vielma script to obtain a SpreadSheet with the questions and choices in Moodle GIFT format.
Just copy and paste the values in the the SpreadSheet in a text file to import into Moodle.
// Open a form by ID.
var form = FormApp.openById('YOUR-FORM-ID'); // YOU GET IT FROM THE URL
// Open a sheet by ID.
var sheet = SpreadsheetApp.openById('YOUR-SPREADSHEET-ID').getSheets()[0];
// variables for putting the questions and answers in the right position
// Change this number to the line you want the question starts
var question_position = 1;
// main function to run
function getFormValues() {
form.getItems().forEach(callback);
}
// Iterate over all questions
function callback(el){
// check if the question is multiple choice
if (el.getType() == FormApp.ItemType.MULTIPLE_CHOICE) {
// change the type from Item to MultipleChoiceItem
var question = el.asMultipleChoiceItem();
var choices = question.getChoices();
// set the title of the question in the cell
var qRange = sheet.getRange(question_position++, 1);
qRange.setValue(question.getTitle() + " {");
var i = 0;
// set the answers in the right cells
for (i; i < choices.length; i++){
var choiceRange = sheet.getRange(question_position++, 2);
var current = choices[i];
var prefix = current.isCorrectAnswer() ? "'=" : "~";
choiceRange.setValue(prefix + current.getValue());
}
var qRangeEnd = sheet.getRange(question_position++, 1);
qRangeEnd.setValue("}");
question_position ++;
}
}
I have set up a script to pull in data from a JSON API into a Google Sheet. I have set it to refresh by adding a third parameter which isn't used in the API call but is linked to a cell which another script adds the current time to. This ensures that the API is called regularly.
We are then using this Google Sheet to input data into Google Ads.
It all seems to function correctly, however, when the sheet has been closed for a while (e.g. overnight) and Google Ads tries to update from the sheet, it imports #NAME? instead of the correct API value.
I have set up another script which records the API values at regular intervals. This seems to record the values correctly, suggesting that the API calls are working whilst the sheet is closed.
// Make a POST request with a JSON payload.
// Datetime parameter isn't use in API call but is used to refresh data
function TheLottAPI(game,attribute,datetime) {
var data = {
'CompanyId': 'GoldenCasket',
'MaxDrawCount': 1,
'OptionalProductFilter': [game]};
Logger.log(data);
var options = {
'method' : 'post',
'contentType': 'application/json',
// Convert the JavaScript object to a JSON string.
'payload' : JSON.stringify(data)};
var response = UrlFetchApp.fetch('https://data.api.thelott.com/sales/vmax/web/data/lotto/opendraws', options);
Logger.log('output: '+ response);
// Convert JSON response into list
var json = JSON.parse(response)
var drawList=json ["Draws"];
// Extract attribute from list
for(var i=0;i<drawList.length;i++)
{var value=drawList[i][attribute];}
Logger.log(value)
return value;
SpreadsheetApp.flush();
};
// Set date & time to refresh API call
function RefreshTime() {
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attributes").getRange("K4").setValue(new Date().toTimeString());
}
The correct numeric values from the API should be shown, rather than the #NAME? error.
I have checked that the API call is functioning correctly by using another script to copy the current values. The API was updating at the appropriate times overnight.
function RecordDraws() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Draw Amounts");
var source = sheet.getRange("A3:D3");
var values = source.getValues();
values[0];
sheet.appendRow(values[0]);
};
This is my guess
Google Sheets custom functions definitions are loaded when the spreadsheet is opened by using the Google Sheets UI, then formulas are calculated and as custom functions are already defined they are calculated correctly. If the spreadsheet isn't opened this way the custom functions definitions aren't loaded thus the spreadsheet doesn't know what to do with that function and returns #NAME?
If you are already running a script that updates some values, enhance that script to do the calculations that does your custom function.
Try converting this
SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Attributes").getRange("K4").setValue(new Date().toTimeString());
Into this:
SpreadsheetApp.openById("id").getSheetByName("Attributes").getRange("K4").setValue(new Date().toTimeString());
Because I don't think there is an "active sheet" when the Spreadsheet it's closed or the method is called from the API.
I am creating an addon for Google Sheets that my local High School's volunteer clubs can use to keep track of their member's volunteer hours. Most of the code is done and works very nicely, and I am currently working on a system that will send a member a spreadsheet listing all of the volunteer events that they have logged. I have GAS create a separate spreadsheet, and then send an email with that separate spreadsheet attached in PDF. When the email is received, the PDF is empty except for a singular empty cell at the top left of the page.
I am pretty new to GAS but have been able to grasp the content pretty easily. I have only tried one method of sending the Spreadsheet and that is by using the .getAs(MimeType.PDF). When I changed the "PDF" to "GOOGLE_SHEETS," GAS returned the error: "Blob object must have non-null data for this operation." I am not entirely sure what a Blob object is, and have not found any website or video that has fully explained it, so I am not sure how to go about troubleshooting that error.
I think I'm having a problem grabbing the file because it either sends an empty PDF or it returns an error claiming it needs "non-null data."
function TigerMail()
{
var Drive = DriveApp;
var app = SpreadsheetApp;
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var Name = LOOKUP.getRange("E1").getValue();
Name = Name + "'s Hours";
//app.openById(Name+"'s Hours");
var HOURS = app.create(Name);
var ESheet = HOURS.getSheets()[0];
var ROW = LOOKUP.getLastRow();
var arr = LOOKUP.getRange("D1:J"+ROW).getValues();
var cell = ESheet.getRange("A1:G"+ROW);
cell.setValues(arr);
////////////////////////////////////////////////////
var LOOKUP = app.getActiveSpreadsheet().getSheetByName("Student
Lookup");
var cell = LOOKUP.getRange("D1");
var Addr = cell.getValue();
var ROW = LOOKUP.getLastRow();
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
TigerMail.sendEmail(Addr, "Hours", "Attached is a list of all of the
events you have volunteered at:", {attachments: [FORMAT]} );
}
the final four lines are where the errors are occurring at. I believe I am misunderstanding how the .next() and .getFilesByName() work.
(above the comment line: creating a spreadsheet of hours)
(below the comment line: grabbing the spreadsheet and attaching it to an email)
Here is the link to the Google Sheet:
https://docs.google.com/spreadsheets/d/1qlUfTWaj-VyBD2M45F63BtHaqF0UOVkwi04XwZFJ4vg/edit?usp=sharing
In your script, new Spreadsheet is created and put values.
You want to sent an email by attaching the file which was converted from the created Spreadsheet to PDF format.
If my understanding is correct, how about this modification? Please think of this as just one of several answers.
Modification points:
About Drive.getFilesByName(Name), unfortunately, there is no method of getFilesByName() in Drive.
I think that when you want to use the created Spreadsheet, HOURS of var HOURS = app.create(Name) can be used.
About var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS), in the case of Google Docs, when the blob is retrieved, the blob is automatically converted to PDF format. This can be also used for your situation.
In order to save the values put to the created Spreadsheet, it uses SpreadsheetApp.flush().
When above points are reflected to your script, it becomes as follows.
Modified script:
Please modify as follows.
From:
var file = Drive.getFilesByName(Name);
var file = file.next();
var FORMAT = file.getAs(MimeType.GOOGLE_SHEETS);
To:
SpreadsheetApp.flush();
var FORMAT = HOURS.getBlob();
Note:
In your script, it seems that var ROW = LOOKUP.getLastRow() is not used.
References:
flush()
getBlob()
If I misunderstood your question and this was not the result you want, I apologize.
I have a Google Sheet with 5 columns (First Name, Address, SKU, Quote, Status).
I have an apps script function (createQuote) which looks at the above variable's values from google sheet row and create a google document quote replacing the variables to values.
I use Zapier to insert row into my above google sheet.
What am struggling with-:
I need a way to trigger my createQuote function right when a new row is inserted via zapier (Google Sheet API call).
I tried playing with triggers but couldn't make it, any help is appreciated.
thank you
here is the code for my function-
function quoteCreator(){
docTemplate = "googledocidgoeshere"
docName = "Proposal"
var sheet = SpreadsheetApp.getActive().getSheetByName("Main")
var values = sheet.getDataRange().getValues()
var full_name = values[1][0]
var copyId = DriveApp.getFileById(docTemplate).makeCopy(docName+" for "+full_name).getId()
// Open the temporary document
var copyDoc = DocumentApp.openById(copyId);
// Get the document’s body section
var copyBody = copyDoc.getActiveSection();
// Replace place holder keys/tags,
copyBody.replaceText("keyFullName", full_name);
copyDoc.saveAndClose();
// Convert temporary document to PDF by using the getAs blob conversion
var pdf = DriveApp.getFileById(copyId).getAs("application/pdf");
// put the link of created quote in the quote column
var url = DocumentApp.openById(copyId).getUrl()
var last = sheet.getRange(2, 7, 1, 1).setValue(url)
}
Note-: I haven't put the loop yet in above, i'll do that once it starts working as per my requirements.
Changes made via Sheets API or Apps Script do not fire onEdit triggers. I give two workarounds for this.
Web app
Have whatever process updates the sheet also send a GET or POST request to your script, deployed as a web application. As an example, a GET version might access https://script.google.com/.../exec?run=quoteCreator
function doGet(e) {
if (e.parameter.run == "quoteCreator") {
quoteCreator();
return ContentService.createTextOutput("Quote updated");
}
else {
return ContentService.createTextOutput("Unrecognized command");
}
}
The web application should be published in a way that makes it possible for your other process to do the above; usually this means "everyone, even anonymous". If security is an issue, adding a token parameter may help, e.g., the URL would have &token=myToken where myToken is a string that the webapp will check using e.parameter.token.
GET method is used for illustration here, you may find that POST makes more sense for this operation.
Important: when execution is triggered by a GET or POST request, the methods getActive... are not available. You'll need to open any spreadsheets you need using their Id or URL (see openById, openByUrl).
Timed trigger
Have a function running on time intervals (say, every 5 minutes) that checks the number of rows in the sheet and fires quoteCreator if needed. The function checkNewRows stores the number of nonempty rows in Script Properties, so changes can be detected.
function checkNewRows() {
var sp = PropertiesService.getScriptProperties();
var oldRows = sp.getProperty("rows") || 0;
var newRows = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Main").getLastRow();
if (newRows > oldRows) {
sp.setProperty("rows", newRows);
quoteCreator();
}
}
I'm doing multiple queries (find amount of open/closed issues, use the open/closed issues (per person) to create a leaderboard of developer issue closes, etc - find how many issues were closed this week and how many opened last week - what's the newest issue, what's the oldest issue) to GitHub's API and trying to get the resultant data written onto a Google Sheet. I'm using Google Apps Script Editor to do this.
I can get the JSON data from GitHub API to the console no problem, but I'm not sure how to then parse and write it to Google Sheets. How could I do this, please?
An example of a JSON data (this time it was "open issues" grabbed from http://github.com/vizorvr/patches/ )
[17-08-08 13:47:46:353 EEST] {"total_count":271,"incomplete_results":false,"items":[{"url":"https://api.github.com/repos/vizorvr/patches/issues/2035","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2035/events","html_url":"https://github.com/vizorvr/patches/issues/2035","id":245953104,"number":2035,"title":"Figure out which Three.JS plugins are missing from Patches and add them","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-07-27T07:41:52Z","updated_at":"2017-07-27T07:41:52Z","closed_at":null,"body":"Go to http://threejs.org and compare the list of ThreeJS plugins in Patches and what is available on the ThreeJS site. Then implement them.","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2034","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2034/events","html_url":"https://github.com/vizorvr/patches/issues/2034","id":245952930,"number":2034,"title":"Inspect the Radial Point Geometry -plugin and figure out what it does or should do","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":0,"created_at":"2017-07-27T07:41:07Z","updated_at":"2017-07-27T07:41:07Z","closed_at":null,"body":"Go into Patches and add the Radial Point Geometry -patch.\r\n\r\nStudy what it does. Figure if it does anything. If it does something, come up with an example explaining how it should be used - then help with the popup documentation.\r\n\r\n![vizor](https://user-images.githubusercontent.com/4966687/28659400-fb6f7a5c-72b7-11e7-83c9-6da26fe5ce13.png)\r\n","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2023","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2023/events","html_url":"https://github.com/vizorvr/patches/issues/2023","id":241289959,"number":2023,"title":"Shortcut: Shift-Tab: improve by opening \"Inspector->Properties\"","user":{"login":"esaruoho","id":4966687,"avatar_url":"https://avatars1.githubusercontent.com/u/4966687?v=4","gravatar_id":"","url":"https://api.github.com/users/esaruoho","html_url":"https://github.com/esaruoho","followers_url":"https://api.github.com/users/esaruoho/followers","following_url":"https://api.github.com/users/esaruoho/following{/other_user}","gists_url":"https://api.github.com/users/esaruoho/gists{/gist_id}","starred_url":"https://api.github.com/users/esaruoho/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/esaruoho/subscriptions","organizations_url":"https://api.github.com/users/esaruoho/orgs","repos_url":"https://api.github.com/users/esaruoho/repos","events_url":"https://api.github.com/users/esaruoho/events{/privacy}","received_events_url":"https://api.github.com/users/esaruoho/received_events","type":"User","site_admin":false},"labels":[{"id":165125694,"url":"https://api.github.com/repos/vizorvr/patches/labels/enhancement","name":"enhancement","color":"84b6eb","default":true},{"id":346495796,"url":"https://api.github.com/repos/vizorvr/patches/labels/user","name":"user","color":"fbca04","default":false},{"id":270777736,"url":"https://api.github.com/repos/vizorvr/patches/labels/UX","name":"UX","color":"5319e7","default":false}],"state":"open","locked":false,"assignee":{"login":"gmarinov","id":14101296,"avatar_url":"https://avatars0.githubusercontent.com/u/14101296?v=4","gravatar_id":"","url":"https://api.github.com/users/gmarinov","html_url":"https://github.com/gmarinov","followers_url":"https://api.github.com/users/gmarinov/followers","following_url":"https://api.github.com/users/gmarinov/following{/other_user}","gists_url":"https://api.github.com/users/gmarinov/gists{/gist_id}","starred_url":"https://api.github.com/users/gmarinov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gmarinov/subscriptions","organizations_url":"https://api.github.com/users/gmarinov/orgs","repos_url":"https://api.github.com/users/gmarinov/repos","events_url":"https://api.github.com/users/gmarinov/events{/privacy}","received_events_url":"https://api.github.com/users/gmarinov/received_events","type":"User","site_admin":false},"assignees":[{"login":"gmarinov","id":14101296,"avatar_url":"https://avatars0.githubusercontent.com/u/14101296?v=4","gravatar_id":"","url":"https://api.github.com/users/gmarinov","html_url":"https://github.com/gmarinov","followers_url":"https://api.github.com/users/gmarinov/followers","following_url":"https://api.github.com/users/gmarinov/following{/other_user}","gists_url":"https://api.github.com/users/gmarinov/gists{/gist_id}","starred_url":"https://api.github.com/users/gmarinov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gmarinov/subscriptions","organizations_url":"https://api.github.com/users/gmarinov/orgs","repos_url":"https://api.github.com/users/gmarinov/repos","events_url":"https://api.github.com/users/gmarinov/events{/privacy}","received_events_url":"https://api.github.com/users/gmarinov/received_events","type":"User","site_admin":false}],"milestone":null,"comments":0,"created_at":"2017-07-07T14:57:29Z","updated_at":"2017-07-07T15:44:19Z","closed_at":null,"body":"Improve Shift-Tab so that it opens the Inspector ( press I ) AND selects Properties-tab instead of Node.\r\n\r\n","score":1.0},{"url":"https://api.github.com/repos/vizorvr/patches/issues/2022","repository_url":"https://api.github.com/repos/vizorvr/patches","labels_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/labels{/name}","comments_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/comments","events_url":"https://api.github.com/repos/vizorvr/patches/issues/2022/events","html_url":"https://github.com/vizorvr/patches/issues/2022","id":24086639
I have, thus far, grabbed something called pullJSON to try and pull the data, which looks like this - however, unfortunately it seems destructive - it does not write to a specific sheet, just any old active sheet. I'm not sure how to modify it to write it to a specific sheet so that it'll work, so that's why I'm asking about how to parse stuff.
function pullJSON() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var sheet = ss.getActiveSheet();
var url="https://api.github.com/search/issues?q=repo:vizorvr/patches+state:closed"; // Paste your JSON URL here
var response = UrlFetchApp.fetch(url); // get feed
var dataAll = JSON.parse(response.getContentText()); //
var dataSet = dataAll;
var rows = [],
data;
for (i = 0; i < dataSet.length; i++) {
data = dataSet[i];
rows.push([data.id, data.name,data.email]); //your JSON entities here
}
dataRange = sheet.getRange(1, 1, rows.length, 3); // 3 Denotes total number of entites
dataRange.setValues(rows);
}
Edit 2
Was able to get the number of open issues in the repo, and the number of closed issues in the repo, but to dump them to a specific cell is still very vague for me.
var openPatches = "https://api.github.com/search/issues?q=repo:vizorvr/patches+state:open"
var closedPatches = "https://api.github.com/search/issues?q=repo:vizorvr/patches+state:closed"
var openResponse = UrlFetchApp.fetch(openPatches, {'muteHttpExceptions': true});
var Data = JSON.parse(openResponse.getContentText());
Logger.log("Open issues on Patches GitHub:")
Logger.log(Data.total_count)
var closedresponse = UrlFetchApp.fetch(closedPatches, {'muteHttpExceptions': true});
var data = JSON.parse(closedresponse.getContentText());
Logger.log("Closed issues on Patches GitHub:")
Logger.log(data.total_count)
Logger.log(Object.keys(data))
Could I in theory just write Data.total_count and data.total_count to different cells on a sheet, without having to have a google sheet open in another tab on the browser?
Based on your update, to write the data in to a specific sheet you need to use getSheetByName to write in to a specific sheet
var url ="<<SPREADSHEET URL>>";
var ss= SpreadsheetApp.openByUrl(url)
var sheet = ss.getSheetByName("<<SHEET NAME>>");
To store the values in different cell, you can define the range and set the values accordingly.
sheet.getRange("A1").setValue(Data.total_count); //Open issues
sheet.getRange("B1").setValue(data.total_count); //Closed issues