Google Form Limit to Number of Dropdown Options - google-apps-script

Previously, I was using Google Apps Script to populate the choices for a dropdown list question in Google Forms on a weekly basis. One question had as many as 3600 choices at one time. This week, when running the same script with a reduced number of choices (approx. 2000), I receive the following error via script.google.com: "Failed to set choices. You exceeded the maximum number of choices." Is there a new limit to number of choices that can be added? I cannot find one listed anywhere on the web.
Code snippet to reproduce the issue:
var sheetUrl = 'https://docs.google.com/spreadsheets/d/xxxxxxxxxxxxxxxxxxxxx/edit';
var sheet = SpreadsheetApp.openByUrl(sheetUrl).getSheetByName("Form Responses 1");
var data = sheet.getDataRange().getValues();
var formExample = FormApp.openById('xxxxxxxxxxxxxxxxxxxxxxxxxx');
var listOfCustomers= formExample.getItemById(1234567890);
var customerChoices = [];
for (var i = 0; i < data.length; i+=1){
customerChoices.push(listOfCustomers.asListItem().createChoice(data[i][0]));
}
listOfCustomers.asListItem().setChoices(customerChoices);

https://issuetracker.google.com/issues/63395462
mc...#google.com #4 Jul 18, 2017 01:55PM
Status: Won't Fix (Intended Behavior)
Since June, we've imposed a limit on the number of options that can be added to a Forms dropdown in Apps Script. The limit is currently 1,000 but is subject to change. If you need to provide more options, consider a free-form text field, multiple dropdowns, or a custom web form.

While this solution doesn't address the limit imposed on scripts (as of 2022/9/8 it's still 1000) it does provide a manual work-around for someone who doesn't need to automate the process.
Using manual copy-and-paste I have successfully created dropdowns with more than 1000 entries.
Steps:
Copy (ctrl + c) a list of more than 1000 items (from Google Sheets, for example)
Create an empty dropdown in a Google Form
Paste (ctrl + v) the list of items
I created a dropdown with 1690 entries that way.
Credit for this solution goes to TC.

Related

Dynamically Edit Multiple Choice Questions in Live Google Form with App Script

I am a teacher trying to create a registration system with Google Forms, Sheets, and App Script. I have tried using THIS THREAD to figure out the App Script I need to automatically update choices based on form submissions.
The Google Form will include class choices for Block 1, Block 2, Block 3, etc. Students will choose a class for each block and then submit the form. The Apps Script will look at the response spreadsheet, see that Class 1 for Block 1 is full, then removes it from the list of possible choices. The next student goes to register, and their only option is Class 2.
Based on the linked thread (see above), I tried this code. It seemed to work for one question... or so I thought. I linked my Spreadsheet here and include the Code Snippet.
Link to Spreadsheet -- Available Classes in "formMaker" Sheet. (if there's a better way to do this, please let me know)
Code:
function updateForm(){
var form = FormApp.openById('1Mrh79XlB_EX6jkK_dOX9sVIwAxTImqS8X2dIfEE0Juw'); // Base form
// need to read what dates are available and which are taken
var doc = SpreadsheetApp.getActiveSpreadsheet();
var dates = doc.getRange("FormMaker!A2:A").getValues(); //available options
var taken_dates = doc.getRange("Form Responses 6!C2:C").getValues();
// joining the taken dates into one string instead of an array to compare easier
var taken_dates_string = taken_dates.join("|");
var choice = [];
// loop through our available dates
for (d in dates){
}
var formItems = form.getItems(FormApp.ItemType.LIST); // our form list items
// assumption that first select list is the one you want to change
// and we just rewrite all the options to ones that are free
formItems[0].asListItem().setChoiceValues(choice);
}

Google Apps Script to count number of emails received yesterday, that has certain label, then save # daily to spreadsheet

Basically what the title says, but I want to:
1) Automatically count the number of emails received to my gmail account, that has a certain label on it
2) Count once everyday, even if zero
3) And report daily to a Google Spreasheet
4) So I can make a monthly report like such:
Date / #
Date / #
Date / #
.
.
.
Total for October / #
Average per day / #
I'm sure this is piece of cake using Google Script for script gurus, but I have no clue. Please teach me!
Open a new Untitled spreadsheet and go to Tools -> open Script editor and paste the code given below.
function CountEmail()
{
var label = GmailApp.getUserLabelByName("LabelName");
var labelname = label.getName();
var mails = label.getThreads();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var date = new Date();
sheet.appendRow([labelname,date,mails.length]);
}
To run the script daily you will need to set a project trigger in the app script.For that follow the steps given below:
Resources -> Current Project's Trigger -> Add trigger -> Time driven -> Hour timer -> select the time when you want the script to run.
The total number of emails and the average emails/day can be calculated in the spreadsheet itself by just using the Sum() function.
Referencing the code Suyash Gandhi has posted (citing it here in case it gets removed so there is no confusion).
NOTE: not my code!!! Credit to Suyash Gandhi
function CountEmail()
{
var label = GmailApp.getUserLabelByName("LabelName");
var labelname = label.getName();
var mails = label.getThreads();
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var date = new Date();
sheet.appendRow([labelname,date,mails.length]);
}
NOTE: not my code!!! Credit to Suyash Gandhi
See the picture below
What you see here are 2 threads, 1 email in the bottom one, 3 emails in the top one and 1 draft in the top one. That given code will return a 2 here. If you wish to count all 4 (or 5 if you want the draft) you will need to use Gmail API (see reference here).
I have a script that gets all emails (every message) for a specific time period and outputs the receive date, sender, recipient and title to a spreadsheet. This is the code that actually fetches the emails. The rest of the code is mostly creating files, generating the query string and reset the script if it runs too close to 6 minutes.
queriedMessages =
Gmail.Users.Messages.list(userInfo.mail,
{
'q': queryString,
'pageToken': execProperties.nextPageId
});
userInfo.mail is the email address you are fetching the emails from. This is simply written like this because the script can be run with any account
queryString is a string that is used to search for emails and is exactly the same as you use in the gmail search box. So you would have label:labelname
pageToken is a code of the page of the search (basically what is needed when you click the next page button in gmail). It is returned as part of this function so you would be able to access it from queriedMessages.nextPageToken. So if you get more than 1 page, then you will need it to access the rest of the messages.
Also, keep in mind that you are getting all the messages fitting the query, so if you do this daily, you may want to include a trigger. Also, keep in mind that functions firing from triggers ignore your timezone (known bug), but you can figure out how to create a query that works for only 1 day fairly easily. Personally I just grab +1 day on the beginning and the end and just filter those messages out.

Google Sheets: Hiding Columns based on date in row 1

I have no experience with scripting in Excel or Google Sheets, so I'm trying to branch out a bit and see if there's a solution to my problem. We use Google Sheets for a weekly calendar at our kitchen remodeling business. We organize the weeks from left to right and list the jobs we're currently working on in those columns. I would like to automatically hide all columns that have a date older than 4 weeks, so when the sheet opens, we're not starting with a date from a year ago. I can hide these columns manually each week, but when I do need to go back and look at previous weeks, I'm forced to unhide all thosecolumns and then highlight all of the columns I want to rehide. Having a script seems like the better solution.
Is there a way to have a script run every time the file is open so that we're always only displaying the previous 4 weeks and everything in the future? If so, would you be willing to help me understand how I might write that and get it working? Again, I'm a novice when it comes to anything beyond formulas, but very interested in learning more about the scripting capabilities.
Thank you!
With Apps Script via Tools->Script Editor, you could create a menu with an onOpen() function. The function in the menu (e.g. hidePast), would then need to check a given value in each column (to see what date that column refers to), and then flag it to be hidden or not. The onOpen function, because it is a "simple trigger", cannot do anything that requires "authorization" (such as interacting with non-local Spreadsheet data), hence the intermediate method. By creating a menu, you can make it easy for anyone using the spreadsheet to authorize and activate the function.
Example:
/* #OnlyCurrentDoc */
function onOpen() {
SpreadsheetApp.getActive().addMenu("Date Tools",
[{name:"Hide Past", functionName:"hidePast"},
{name:"Show All", functionName:"showAll"}]);
}
function showAll() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
sheet.unhideColumn(sheet.getDataRange());
ss.toast("All columns unhidden.");
}
function hidePast() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getActiveSheet();
// Acquire the 1st row of all used columns as an array of arrays.
var datelist = sheet.getSheetValues(1, 1, 1, sheet.getLastColumn());
// Drop the hours, minutes, seconds, etc. from today.
var now = new Date();
var today = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()));
// Inspect the datelist and compare to today. Start from the rightmost
// column (assuming the dates are chronologically increasing).
var col = datelist[0].length;
while(--col >= 0) {
var then = new Date(datelist[0][col]);
if(then < today) {
break;
}
}
// Bounds check, and convert col into a 1-base index (instead of 0-base).
if(++col < 1) return;
// col now is the first index where the date is before today.
// Increment again, as these are 2-column merged regions (and
// the value is stored in the leftmost range). If not incremented,
// (i.e. hiding only part of a merged range), spreadsheet errors will occur.
sheet.hideColumn(sheet.getRange(1, 1, 1, ++col));
ss.toast("Hid all the columns before today.");
}
Because you don't have a "database like" source it will be very difficult, but you can try to create a very complicated
QUERY()
you should filter the dates in another sheet (and you may face a dead end).
So I will suggest using this kind of structure and it will also allow you to make other kinds of filters (or Pivot Tables) in the future (maintainable and scalable).

How to make google spreadsheet refresh itself every 1 minute?

My google spreadsheet is using GOOGLEFINANCE('symbol','price) function to retrieve stock prices of my portfolio. Unfortunately, I have to refresh manually now. How can I make the spreadsheet refresh itself every 1 minute?
Thank you for your help.
If you're on the New Google Sheets, this is all you need to do, according to the docs:
change your recalculation setting to "On change and every minute" in your spreadsheet at File > Spreadsheet settings.
This will make the entire sheet update itself every minute, on the server side, regardless of whether you have the spreadsheet up in your browser or not.
If you're on the old Google Sheets, you'll want to add a cell with this formula to achieve the same functionality:
=GoogleClock()
EDIT to include old and new Google Sheets and change to =GoogleClock().
If you are only looking for a refresh rate for the GOOGLEFINANCE function, keep in mind that data delays can be up to 20 minutes (per Google Finance Disclaimer).
Single-symbol refresh rate (using GoogleClock)
Here is a modified version of the refresh action, taking the data delay into consideration, to save on unproductive refresh cycles.
=GoogleClock(GOOGLEFINANCE(symbol,"datadelay"))
For example, with:
SYMBOL: GOOG
DATA DELAY: 15 (minutes)
then
=GoogleClock(GOOGLEFINANCE("GOOG","datadelay"))
Results in a dynamic data-based refresh rate of:
=GoogleClock(15)
Multi-symbol refresh rate (using GoogleClock)
If your sheet contains a number of rows of symbols, you could add a datadelay column for each symbol and use the lowest value, for example:
=GoogleClock(MIN(dataDelayValuesNamedRange))
Where dataDelayValuesNamedRange is the absolute reference or named reference of the range of cells that contain the data delay values for each symbol (assuming these values are different).
Without GoogleClock()
The GoogleClock() function was removed in 2014 and replaced with settings setup for refreshing sheets. At present, I have confirmed that replacement settings is only on available in Sheets from when accessed from a desktop browser, not the mobile app (I'm using Google's mobile Sheets app updated 2016-03-14).
(This part of the answer is based on, and portions copied from, Google Docs Help)
To change how often "some" Google Sheets functions update:
Open a spreadsheet. Click File > Spreadsheet settings.
In the RECALCULATION section, choose a setting from the drop-down menu.
Setting options are:
On change
On change and every minute
On change and every hour
Click SAVE SETTINGS.
NOTE External data functions recalculate at the following intervals:
ImportRange: 30 minutes
ImportHtml, ImportFeed, ImportData, ImportXml: 1 hour
GoogleFinance: 2 minutes
The references in earlier sections to the display and use of the datadelay attribute still apply, as well as the concepts for more efficient coding of sheets.
On a positive note, the new refresh option continues to be refreshed by Google servers regardless of whether you have the sheet loaded or not. That's a positive for shared sheets for sure; even more so for Google Apps Scripts (GAS), where GAS is used in workflow code or referenced data is used as a trigger for an event.
[*] in my understanding so far (I am currently testing this)
GOOGLEFINANCE can have a 20 minutes delay, so refreshing every minute would not really help.
Instead of GOOGLEFINANCE you can use different source. I'm using RealTime stock prices(I tried a couple but this is the easiest by-far to implement. They have API that return JSON { Name: CurrentPrice }
Here's a little script you can use in Google Sheets(Tools->Script Editor)
function GetStocksPrice() {
var url = 'https://financialmodelingprep.com/api/v3/stock/real-time-
price/AVP,BAC,CHK,CY,GE,GPRO,HIMX,IMGN,MFG,NIO,NMR,SSSS,UCTT,UMC,ZNGA';
var response = UrlFetchApp.fetch(url);
// convert json string to json object
var jsonSignal = JSON.parse(response);
// define an array of all the object keys
var headerRow = Object.keys(jsonSignal);
// define an array of all the object values
var values = headerRow.map(function(key){ return jsonSignal[key]});
var data = values[0];
// get sheet by ID -
// you can get the sheet unqiue ID from the your current sheet url
var jsonSheet = SpreadsheetApp.openById("Your Sheet UniqueID");
//var name = jsonSheet.getName();
var sheet = jsonSheet.getSheetByName('Sheet1');
// the column to put the data in -> Y
var letter = "F";
// start from line
var index = 4;
data.forEach(function( row, index2 ) {
var keys = Object.keys(row);
var value2 = row[keys[1]];
// set value loction
var cellXY = letter + index;
sheet.getRange(cellXY).setValue(value2);
index = index + 1;
});
}
Now you need to add a trigger that will execute every minute.
Go to Project Triggers -> click on the Watch icon next to the Save icon
Add Trigger
In -> Choose which function to run -> GetStocksPrice
In -> Select event source -> Time-driven
In -> Select type of time based trigger -> Minutes timer
In -> Select minute interval -> Every minute
And your set :)
I had a similar problem with crypto updates. A kludgy hack that gets around this is to include a '+ now() - now()' stunt at the end of the cell formula, with the setting as above to recalculate every minute. This worked for my price updates, but, definitely an ugly hack.
use now() in any cell. then use that cell as a "dummy" parameter in a function.
when now() changes every minute the formula recalculates.
example:
someFunction(a1,b1,c1) * (cell with now() / cell with now())

Dynamically edit multiple choice options in live Google Form using Apps Script

I'm a high school teacher in L.A. trying to create a course registration system using Apps Script. I need the Google Form I'm using for this registration to:
Question 1) Update the choices available in subsequent multiple choice questions on new pages based on a student's current response choices.
Question 2) Eliminate choices from the form when a multiple choice option has reached it's "cap".
Question 1 Example)
A student registers for “tie-tying” in workshop 1, and gets taken to a new page. The Script edits the available choices on that new page based on the student’s first choice, and removes “tie-tying” from the list of possible choices on that new page, so “etiquette” is their only remaining option.
Question 2 Example)
Students can either register for “tie-tying” or “etiquette”, both responses are initially available in the Google Form. 30 students take the survey, all 30 register for the “tie-tying” workshop. The Apps Script references the response spreadsheet, realizes the “tie-tying” workshop is full, then removes it from the Google Form's list of possible choices. Student 31 goes to register, and their only option is “etiquette”.
If my question has already been asked and answered (believe me, I did search!) I'd appreciate the redirection.
I believe we can achieve your second objective without too much difficulty and modify the form, based on the current state of response.
The approach is to
Create the form and associate it with a response spreadsheet
In that response spreadsheet, create a script with a function (updateForm for instance)
Bind that function with the onFormSubmit event, see Using Container-Specific Installable Triggers.
Analyse the response in the updateForm function and modify your form using the Form Service
For instance
function updateForm(e) {
if (e.values[1] == 'Yes') {
Logger.log('Yes');
var existingForm = FormApp.openById('1jYHXD0TBYoKoRUI1mhY4j....yLWGE2vAm_Ux7Twk61c');
Logger.log(existingForm);
var item = existingForm.addMultipleChoiceItem();
item.setTitle('Do you prefer cats or dogs?')
.setChoices([
item.createChoice('Cats'),
item.createChoice('Dogs')
])
.showOtherOption(true);
}
}
When it comes to achieving the goal in your first question, its more delicate, as the form will not submit mid way. What is possible is to go to different pages based on different responses to a Multiple Choice question, your use case may fit this method, although its not very dynamic.
Further its possible to use html Service to create completely dynamic experience.
Let me know if you need further information.
You are not able to create this type of dynamic form using the Google Forms Service, because there is no interaction between the service and scripts during form entry, except upon Form Submission. In the case of a multi-page form, a script has no way to know that a student has completed one page and gone on to another.
You could achieve this using the HtmlService or UiService, though. In either case, you'd rely on the client-side form interacting through server-side scripts to get updated lists of course options, then modifying the next 'page'. It will be complex.
The other answer to this question will keep adding a multichoice select each time for the form is submitted. Using similar approach of:
Create the form and associate it with a response spreadsheet
In that response spreadsheet, create a script with a function (updateForm for instance)
Bind that function with the onFormSubmit event, see Using Container-Specific Installable Triggers.
Analyse the response in the updateForm function and modify your form using the Form Service
I've used the following code to modify a list select which could be easiliy modified for a multiple choice.
function updateForm(){
var form = FormApp.openById('YOUR_FORM_ID'); // Base form
// need to read what dates are available and which are taken
var doc = SpreadsheetApp.getActiveSpreadsheet();
var dates = doc.getRange("dates!A1:A10").getValues(); //available options
var taken_dates = doc.getRange("responses!F2:F51").getValues(); //just getting first 50 responses
// joining the taken dates into one string instead of an array to compare easier
var taken_dates_string = taken_dates.join("|");
var choice = [];
// loop through our available dates
for (d in dates){
// test if date still available
if (dates[d][0] != "" && taken_dates_string.indexOf(dates[d][0]) === -1){
choice.push(dates[d][0]); // if so we add to temp array
}
}
var formItems = form.getItems(FormApp.ItemType.LIST); // our form list items
// assumption that first select list is the one you want to change
// and we just rewrite all the options to ones that are free
formItems[0].asListItem().setChoiceValues(choice);
}