This endeavor uses a Google Form for input, the Response sheet to gather user response data and trigger code, and a separate workbook that pushes the multiple choice questions' answer choices.

Users complete a Google Form to choose a date from a list for in person group sessions, or a date from a list of remote group sessions, each as a separate multiple choice question. Each type of appointment, in person or remote, has a list of dates and times on separate sheets tabs in a Google Sheets workbook and a number in an adjacent cell as to how many seats are available for that appointment time. A Google Apps Script triggers on form submit and subtracts a seat from the appropriate appointment type. When the seat number gets down to 0, the appointment no longer appears as a choice on the appointment form.

This works perfectly each time, except in the event that two of the same type and time appointments are selected in a row. In this case, although the seat says 0 on the Google Sheets, the Google Form still includes this appointment time as a choice until the NEXT time the form is submitted. So, the seat available says -1. It won't be listed as a choice again. If the appointment times are chosen such that they are not the same ones in a row, the number of seats will not get to -1 and the appointment are not overbooked.

The Google Apps Script on the form respons spreadsheet triggers when the response sheet is changed, opened, or form submitted.

I added a similar script to the workbook with the dates so that when it is edited, it will push the changes to the form and when the form is opened in the editor, it will also update from the workbook. These were added to try to fix the issue. They have helped isolate this instance of the two choices in a row scenario. It works well enough to let it be, but it is driving me crazy because I can't figure out why it does that.

It is tough for an outsider to reproduce because of the separate documents needed. So, I was hoping someone could tell by the code itself, especially since it works most of the time.

This is the code


function autodates(e) {
  
// Get a reference to the spreadsheet "Placement Test: User Editable Appointment Form (Responses)"
  const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();

//Get a reference to the Form 
  const form =FormApp.openById("1XfvnBAnd1-fqgd5kCycv8fUToCTnLPeysb9Niw_j7bk")
  
//For the purpose of writing this program, the form's question ID's are necessary. Use the following script to get the unique item ID for the questions of the form by logging them to this and selecting run while you reproduce this program for other forms. When you run this, you will be required to give permissions! Collect the Id's for the form questions that you will update with the dates listed on a separate form so that you can identify the question to edit for your form.
  var questions = form.getItems();
  questions.forEach(question => {
    Logger.log(question.getTitle())
    Logger.log(question.getType())
    Logger.log(question.getId().toString())
  })
  
//First date list question retrieved by the logger.logs above:
  const dateQuestion = form.getItemById("1598842866");
 
//Second date list question retrieved by the logger.logs above:
  const dateQuestion2 = form.getItemById("713650670");
  
//UPDATING DATES
    //Get a reference to the workbook you want to access that has the list of dates and how many seats available per date
    
const dateListWB = SpreadsheetApp.openById("1EnNmU4Ii5BVruFOnlMF8pa7a7EoASZ33CweeR8_vdmg");
    
//First Date Question
//Get a reference to the correct tab of the workbook associated with the corresponding dates for the question. In this case, its the first tab:
    
const iPsheet = dateListWB.getSheetByName("In Person Appt Dates");
    
//and specify the range you want to collect. Since the dates are in column A, and number of seats in B:
    const iPsheetArray = iPsheet.getRange("A3:B").getValues();
    
//but you don't want to get the rows with empty spaces:
    const filterediPsheetArray = iPsheetArray.filter(iProw=>iProw[0]);
    
//Limit the choices so that if the value of column B, the number of seats, falls below 1, it can no longer be used
    const limitchoice = filterediPsheetArray.filter(function (iProw) {
      return iProw[1] >= 1;
    });
    Logger.log('limitchoice');
    Logger.log(limitchoice);
    
//The second number in the splice below indicates THE NUMBER OF DATE CHOICES that will show on the form for this question.
    if (limitchoice.length > 0) {
      var datecut = limitchoice.splice(0,5).map(function(row) {
        return row [0]
      });
      Logger.log('datecut');
      Logger.log(datecut)
    } 
    if (datecut.length > 0) {
      dateQuestion.asMultipleChoiceItem().setChoiceValues(datecut);
    } else {
      dateQuestion.asMultipleChoiceItem().setChoiceValues([]);
    };

//Second Date Question
    
//Get a reference to the correct tab of the workbook associated with the corresponding dates for the question. In this case, its the second tab:
    const remSheet =dateListWB.getSheetByName("Remote Appointment Dates");
    
//Get a reference to the Range you want to access from that sheet
    const remSheetArray = remSheet.getRange("A1:B").getValues();
    
//filter out any empty rows from the sheet
    const filteredremSheetArray = remSheetArray.filter(remrow => remrow[0]);
    
//Limit the choices so that if the value of columb B (the number of seats) falls below 1, it can no longer be used as a choice
    const limitchoice2 = filteredremSheetArray.filter(function(rem3row){
      return rem3row[1] >= 1;
    });
    Logger.log(limitchoice2);
    
//The second number in the splice below indicates the number of date choices that will be on for this question.
    if (limitchoice2.length > 0) {
      var datecut2 = limitchoice2.splice(0, 5).map(function(rem2row) {
        return rem2row[0];
      });
    }
    if (datecut2.length > 0) {
      dateQuestion2.asMultipleChoiceItem().setChoiceValues(datecut2);
      Logger.log(datecut2);
    } else {
      dateQuestion2.asMultipleChoiceItem().setChoiceValues([]);
    }

//UPDATING THE COUNT (or the number of seates for the date/time)
  
//First Date
//Get the date the student selected
  const rDateChoice = e.values[5]; //e.values are the numbered cells 
  counting the first column as [0]. Column B is [1] etc.
  const dateChoice = rDateChoice;
  
//Get the range of the data to search through
  const range = iPsheet.getRange("A:B");
  const data = range.getValues();
  
//Set the starting row index.
  let rowIndex = 1;
  
//Loop through the date choices until a blank row is encountered.
  while (rowIndex <= data.length && data[rowIndex][0] != "") {
    
//decrease value in column B by 1
    if (data[rowIndex][0] === dateChoice) {
      iPsheet.getRange(rowIndex +1, 2).setValue(data[rowIndex][1] - 1);
      break;
    }
    //move to the next row.
    rowIndex++;
  }
  
//Second Date
  const rDateChoice2 = e.values[6];
  const dateChoice2 = rDateChoice2;
  
//Get the range of the data to search through
  const range2 = remSheet.getRange("A:B");
  const data2 = range2.getValues();
  
//Set the starting row index.
  let rowIndex2 = 1;
  
//Loop through the date choices until a blank row is encountered.
  while (rowIndex2 <= data2.length && data2[rowIndex2][0] != "") {
    
//If the search string is found, decrease the value in column B by 1.
    if (data2[rowIndex2][0] === dateChoice2) {
      remSheet.getRange(rowIndex2 + 1, 2).setValue(data2[rowIndex2][1] - 1);
      break;
    }
    
//Move to the next row
    rowIndex2++;
  }
}
1

There are 1 best solutions below

0
Daniel Collier On

You can't use an empty list as in dateQuestion2.asMultipleChoiceItem().setChoiceValues([]);

You should rather use an unavailable option just to bypass the question. And, close the form if both options are gone.