How to create Google Forms using a Google Sheets - google-apps-script

I have created a google spreadsheet to use as questions in my google form, it's the first time i'm using the apps script, and with the help of some videos I made a code, but the values of the spreadsheet are not going to the form, so the questions remain without a title and I have no idea how to fix it.
function myForm() {
var app=SpreadsheetApp;
var spreadsheet=app.openById("1iYXYufNQ1NMUibEFfSWVvfD0vVi14cqMQNBbIok_JzM");
var sheet=spreadsheet.getSheetByName("Atividades");
var form=FormApp;
var formFinal=form.openById("1_XYQcnlpg3EssBHI7X5T2QKyhplRj3FKufyBxPrJxQs");
formFinal
.setTitle("Pesquisa de Tempo Estimado - Finalístico - Rodada 3")
.setDescription("Área temática: Finalístico \nRodada: 3")
.setConfirmationMessage("Obrigado pela participação!");
var item=formFinal.addSectionHeaderItem();
item.setTitle("Atividades");
sheet.getRange("A3:A14").getValues().map(function(elem,ind,obj){
var item2=formFinal.addTextItem();
item2.setTitle(elem[0]);
});
formFinal.setProgressBar(true);
}

I've just run your script ( of course, I've changed the file ids ), and it seems to work fine.
One thing you shouldn't forget is reloading the form page after running the script.
Additional information: I've added the below code to clear all of the items of the form so that I can run the script repeatedly.
// clear all items
var items = formFinal.getItems();
while(items.length > 0){
formFinal.deleteItem(items.pop());
}

Related

How to make Google Slide update value WITHOUT having to manually open it? [duplicate]

This question already has answers here:
pdf is created from google slide, but with the markers populated (via GAS)
(1 answer)
Created PDF does not reflect changes made to the original document
(2 answers)
Converting a google doc to a pdf results in blank pdf, google scripts
(1 answer)
Closed 3 months ago.
I have a Google Sheet with a Google Apps Script that basically intends to do the following when triggered:
Open a specific Google Slides file (it's a template)
Duplicate it into a specific subfolder
Change some placeholder values of this copy
Send this new Google Slide via Email as a PDF.
Everything works great EXCEPT for step 3, which for some reason ONLY updates when I manually open this new file. So the PDF that I receive via email actually still contains the placeholder values, as does the "preview" I see on my Drive before opening the Slides presentation. However, if I open it, it automatically shows the new updated values. I can't figure out why this might be happening.
Things I tried:
Add Utilities.sleep() to allow for the values to update, but it doesn't seem to work. Only opening it does it.
Add a line to .openById() again in case this "forces it to open", but doesn't work.
Adding a loop to refresh each slide.
This is a minimal reproducible code that I have, but would require a Google Slide with two placeholders as "[var_0]" and "[var_1]":
const idTemplate = "1BugZRdW8aOzgPOZun-j_U60lC-yXQFXug3R2-MR7AFs";
function generatePresentationData() {
var s = SpreadsheetApp.getActive();
//Data is in A2:B2 of "Records" sheet.
var ss = s.getSheetByName("Records");
var data = ss.getRange("A2:B2").getValues();
//Get the Google Slides template
var file = DriveApp.getFileById(idTemplate);
//Create a copy of the template into a specific folder, and get the ID of this copy
var newParte = file.makeCopy("New Parte", DriveApp.getFolderById("1rCEYVilGiOlxh03aFANzD4qgKXKBmODh"));
var newParteId = newParte.getId();
//Get the slides of this file.
var slides = getSlides_(newParteId);
//Replace the placeholders with the value in A2:B2. This is the part that doesn't actually "shows up" on this new file until I manually open it from my Drive. It does work correctly, so I don't think the issue lies here
writeDataToPlayersSlide_(slides, data);
//I have tried adding a wait period but didn't work
Utilities.sleep(60000);
//I also tried defining the file again before proceeding to send it via email, but again didn't work
var newParte = DriveApp.getFileById(newParteId);
//I also tried refreshing each slide
refreshSlides(SlidesApp.openById(newParteId));
//Get this new file as a Blob
var theBlob = newParte.getBlob().getAs('application/pdf');
var nameFile = 'Test.pdf';
var email = "mail#gmail.com";
var subject = 'Test Subject';
var body = 'Hi!, <br/>Here is your file.';
GmailApp.sendEmail(email, subject, body, {
htmlBody: body,
attachments: [{
fileName: nameFile,
content: theBlob.getBytes(),
mimeType: "application/pdf"
}]
});
}
function getSlides_(newParteId){
let presentation = SlidesApp.openById(newParteId);
let slides = presentation.getSlides();
return slides;
}
function writeDataToPlayersSlide_(slides, data){
let parte = slides[0];
for (let index=0;index<data[0].length;index++){
parte.replaceAllText(`[var_${index}]`,data[0][index]);
}
}
function refreshSlides(presentation){
// loop through all slides and refresh them
numSlides = presentation.getSlides().length;
for (i = 0; i < numSlides; i++) {
presentation.getSlides()[i].refreshSlide();
}
}
Instead of
//I have tried adding a wait period but didn't work
Utilities.sleep(60000);
//I also tried defining the file again before proceeding to send it via email, but again didn't work
var newParte = DriveApp.getFileById(newParteId);
//I also tried refreshing each slide
refreshSlides(SlidesApp.openById(newParteId));
use
newParte.saveAndClose();
newParte = DriveApp.getFileById(newParteId);
Apparently the problem is that you have missed that Google Apps Script applies changes to Docs Editors files after the script ends.
If order to force Google Apps Script applies the changes at some point, i.e before sendding an email, when making changes to
spreadsheets use SpreadsheetApp.flush()
documents use DocumentsApp.Document.saveAndClose()
presentations use SlidesApp.Presentation.saveAndClose()

Google Apps script to navigate to given row of google sheet

I'm developing a Chrome extension that has access to Google Sheets using a standalone Google Apps Script.
I need help to be able to navigate and highlight given variable row from my extension. either using Apps Script or some other way.
I've tried this code in google apps script.
function navigateToRow(parameters) {
var ss = SpreadsheetApp.openByUrl(parameters.url)
var sheet = ss.getSheets()[0]
var range = sheet.getRange('A1:D10'); // will get range from parameters in future
range.activate();
}
There is another solution that could solve the problem, but it refreshes the page: using url parameters, e.g.
https://docs.google.com/spreadsheets/d/<spreadsheet id>/edit#gid=<sheet id>&range=<a1 notation>.
Try something like :
var range = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1, 1);
SpreadsheetApp.setActiveRange(range);
Reference : https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#setactiverangerange
Solved using these lines of code inside the content-script.
hash = window.location.hash.split('&range=')[0]
hash += `&range=${currentRowInput.value}:${currentRowInput.value}#`;
window.location.hash = hash

build(),copy() & setHelpText are being left off of the TextValidationBuilder auto complete drop down

I wanted to learn a little more about Google Forms so I did this little form and as I was adding text validation to the the textItems which are meant to contain a URL and an Email I noticed that some of the things I expected to see in the code completion drop downs were not available. So I tried running without them and kept on getting errors like "cannot find setValidation(TextValidationBuilder)".
function createSimpleForm()
{
var linkValidation=FormApp.createTextValidation().requireTextIsUrl();
var emailValidation=FormApp.createTextValidation().requireTextIsEmail();
var ss=SpreadsheetApp.getActiveSpreadsheet();
var form=FormApp.create('Google Apps Script Question');
form.setDescription('A Simple Form to display my script editing problem.')
.setConfirmationMessage('Thanks. I\'ll be getting back to you at your email.')
.setAllowResponseEdits(true)
.setAcceptingResponses(false)
.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
var containerLink=form.addTextItem();
containerLink.setTitle('Enter a URL')
.setValidation(linkValidation);
var clientEmail=form.addTextItem();
clientEmail.setTitle('Enter an email address')
.setValidation(emailValidation)
.isRequired();
}
Then I noticed that only the build() command returns a TextValidation object and that's what the parameter for setValidation needs
So at that point I decided to stick the commands that I thought belong there and finished with a build() and code runs with no errors.
function createSimpleForm()
{
var linkValidation=FormApp.createTextValidation().setHelpText('This must be a URL.').requireTextIsUrl().build();
var emailValidation=FormApp.createTextValidation().setHelpText('This must be a EMail.').requireTextIsEmail().build();
var ss=SpreadsheetApp.getActiveSpreadsheet();
var form=FormApp.create('Google Apps Script Question');
form.setDescription('A Simple Form to display my script editing problem.')
.setConfirmationMessage('Thanks. I\'ll be getting back to you at your email.')
.setAllowResponseEdits(true)
.setAcceptingResponses(false)
.setDestination(FormApp.DestinationType.SPREADSHEET, ss.getId());
var containerLink=form.addTextItem();
containerLink.setTitle('Enter a URL')
.setValidation(linkValidation);
var clientEmail=form.addTextItem();
clientEmail.setTitle('Enter an email address')
.setValidation(emailValidation)
.isRequired();
}
I tried shutting down my browser and returning to the script editor but it doesn't seem to make any difference the same methods still missing from content assist. I'm wondering if any one else has had the same problem?
Yes, the methods build, copy, and setHelpText are missing from the autocomplete on TextValidationBuilder objects. You may want to report this on the Apps Script issue tracker.
Documentation is more reliable than the editor, so when in doubt, go with what documentation says. The autocomplete is flawed in other ways; for example, on the array objects it misses such basic methods as indexOf, map, filter, and reduce.

Run server-side code with google chart select

I have a google visualization table that I'm publishing in a web app.
Background:
I run a script that lists all the documents in a google folder in a spreadsheet. I then push that list into the google table I have published in the web app.
The need:
I want to manage those that same list of documents directly from the web app. I want to be able to to move the document from one folder to another when I select the applicable row on the table.
Two things I have accomplished:
I have a script that will move the document to a specific folder on
the google drive using it's doc id.
I have an event listener on the table so that when you click a row and
then click the delete icon, you get a prompt that asks, "are sure
you want to archive [enter document name]?" When I click ok, I get my
test prompt that says "document archived". When I click no, I get my
test prompt that says "request cancelled". So from this, I know I
have the appropriate code (at least that's how it seems).
What I'm struggling with:
I can't seem to get the codes above to work together. The event listener is providing me the url of the document which I have parsed to give me only the id. This is what I was hoping to use to get the rest of the code to run, but I think because I'm trying to interact with the server-side from the client-side, it's not working. Can anyone help me figure it out? I know that I need to use google.script.run.withSuccessHandler when running a server side script from the client side, but I don't know how it applies to this case the docid I need is being collected on table select. Any help is appreciated and I hope the above makes sense!
// Draw Dashboard
h2dashboard.bind([h2stringFilter, h2typeFilter], [h2chart]);
h2dashboard.draw(h2dataView);
google.visualization.events.addOneTimeListener(h2chart, 'ready', function() {
google.visualization.events.addListener(h2chart.getChart(), 'select', function() {
var selection = h2chart.getChart().getSelection();
var dt = h2chart.getDataTable();
// Get Value of clicked row
if (selection.length) {
var item = selection[0];
var docurl = dt.getValue(item.row, 1);
var docname = dt.getValue(item.row, 0);
var source = dt.getValue(item.row, 3);
// When button is clicked, show confirm box with value
$(document).ready(function() {
$("#hu2archive").on("click", function() {
var answer = confirm("Are you sure you want to archive " + docname + "?");
if (answer === true) {
var archive = DriveApp.getFolderById("FOLDER ID");
var docid = docurl.match(/[-\w]{25,}/); // This is where I'm grabbing the value from the row.
var doc = DriveApp.getFileById(docid);
doc.makeCopy(archive).setName(doc.getName());
source.removeFile(doc);
alert(docname + " has been archived!");
} else {
alert("Request cancelled");
}
});
});
}
});
});
I just got it! What I was having a hard time understanding was how to pass a variable from the client side to code.gs. I have only run a script in code.gs from the client side on button submit but never passed anything back.
So I ended up changing my code to the below which passes the variable I need into a successhandler where archiveDoc is the function in my code.gs and docurl is the name of the variable I need to pass from the eventlistener.
if (answer === true) { google.script.run.withSuccessHandler(onSuccess).withFailureHandler(err).archiveDoc(docurl);
I'm still new to coding so I just learned something new! So thanks Spencer Easton. I did in fact answer my own question.

How to (from a Google Spreadsheet) get the ID of a linked form

I have a Google Spreadsheet that a form is linked to and all form responses are stored in. What I am trying to find is the ID of the FORM itself. I tried this but this does not work..
(I'm running the following code FROM The script editor IN the spreadsheet that the form is linked to.)
function getID()
{
var form = FormApp.getActiveForm();
var formID = form.getId();
Logger.log(formID);
}
That returns NULL since the script is container-bound to the spreadsheet itself. Is there any other way to get the ID of the linked form or even the URL of the linked form?
I can MANUALLY get it by doing the following from the Spreadsheet.
Form > Edit form
This will show me the URL.
IF I knew the NAME of the form I could get it by name using the DriveApp.getFilesByName(), iterate through it and then use the File.getId() but I don't necessarily know the name.
Any ideas?
To avoid parsing the url, the safest way would be:
Logger.log( (FormApp.openByUrl(SpreadsheetApp.getActiveSpreadsheet().getFormUrl())).getId() );
or long hand version:
function logFormId_LongHand(){
var formURL = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
var form = FormApp.openByUrl(formURL);
var formId = form.getId();
Logger.log( formId );
}
To get the form object in a spreadsheet app script I use the following function:
function getLinkedForm(){
return FormApp.openByUrl( SpreadsheetApp.getActiveSpreadsheet().getFormUrl() );
}
To anyone else who stumbles upon this: The new spreadsheets allow this. I've tried it already and it works for me. Here is how I get the ID:
var formUrl = SpreadsheetApp.getActiveSpreadsheet().getFormUrl();
var formID = formUrl.match(/[-\w]{25,}/);
I got the regex from this question: Easiest way to get file ID from URL on Google Apps Script
Hope this helps.
If you only want the Form ID for the attached Form, and don't need to keep the Form URL for any reason, this one-liner should suffice:
var formID = SpreadsheetApp.getActiveSpreadsheet().getFormUrl().match(/\/d\/(.{25,})\//)[1];
Yes.
It would be a script like this that would be ran from within the spreadsheet:
function getFormUrl()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var formUrl = ss.getFormUrl();
Logger.log(formUrl);
}
However, if you try and run this in the new spreadsheets you receive this error message:
The api method 'getFormUrl' is not available yet in the new version of Google Sheets.
So, until they add support it will only work in older versions.
You can also find out / connect to the original form from the "Help" menu. Type form and then click "Edit Form." It will locate the original form. This was helpful when a search in drive was unsuccessful through the name of the form (???). So if you need the original editable form for whatever reason, can't find it, and have the linked Sheets doc, then you can now backtrack and go from there. :)