Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Forms Apps script - randomly missing choices

This bug has me completely confused. I am auto-creating a google form (quiz). Periodically (but quite often) the multiple choice items are missing choices, this often happens once the page is refreshed. That is, the answer options look like they are created correctly on screen, but, once you refresh they disappear. Oddly (and interestingly) there are no errors in Stackdriver.

Visually this is what is happening. Both of these images were created when running the same script with the same input for two different forms:

correct choicesmissing choices

As you can see, the first one is created and has 4 options, the second is created and has just 1 option.

It happens frequently, but not all the time. Seems like it almost has to be a Google bug, but, I've searched their tracker and no one has reported anything.

I'll show our "choice creation code" in case it points to anything, but, the fact that we get different results with exactly the same input suggests there's something else going on.

I'm just showing one part of a loop, but like I said, the code isn't erroring at all. Is there an approach to creating large (30 question) Google forms which is better than running an apps-script plugin? I wish there was an API :(

Anyway, if this is a know issue, or there's a workaround, I'd love to hear it.

      var item = form.addMultipleChoiceItem();
      item.setPoints(points);

      if (data.answer_sheet) { item.setTitle("Question " + position) }

      var choices = [];

      for (var i=0; i < question.count; i++) {
        choices.push(item.createChoice(String.fromCharCode(65 + i), i+1 == question.correct));
      }

      item.setRequired(required);
      item.setChoices(choices);

Edit - minimal reproducible error

If you copy the following script into a google-apps-script environment and run the script, it should import a 30 problem multiple choice document. It will look as if everything has been imported correctly, then refresh the form and you will find that choices are missing. The code is below, I have also made a gist of it on github

function onOpen(e) {
  FormApp.getUi()
    .createAddonMenu()
    .addItem("Add Questions to Blank Quiz", "processUpload")
    .addToUi();
}

function onInstall(e){
  onOpen(e);
};

function processUpload() {
  var form = FormApp.getActiveForm();
  var ui = FormApp.getUi();

  // Delete all items in the form
  var items = form.getItems();

  while(items.length > 0){
    form.deleteItem(items.pop());
  }

  createTest("ignored", true);
}

function questionUrl(url) {
  if (url.lastIndexOf("https:", 0) !== 0) {
    return "https:" + url;
  } else {
    return url;
  }
}

function fetchImages(questions) {
  let urls = questions
              .map(q => questionUrl(q.url))

  let imageResponses = UrlFetchApp.fetchAll(urls);

  return imageResponses.map(e => e.getBlob());
}

function createTest (url, required) {
  var data = sampleData();

  var form = FormApp.getActiveForm();

  form.setIsQuiz(true);

  form.setTitle(data.title)

  // don't want shuffling since image items are separate from answers
  form.setShuffleQuestions(false);

  let questionImages = fetchImages(data.quiz.questions);

  data.quiz.questions.forEach(function(question, index) {
    var position = question.position || index + 1;
    var points = question.points || 1;
    var title = "Question " + position;

    if (!data.answer_sheet) {

      form.addImageItem()
        .setImage(questionImages[index])
        .setTitle("Question " + position)
    }

    if (question.type === "mc") {
      var item = form.addMultipleChoiceItem();
      item.setPoints(points);

      if (data.answer_sheet) { item.setTitle("Question " + position) }

      var choices = [];

      for (var i=0; i < question.count; i++) {
        choices.push(item.createChoice(String.fromCharCode(65 + i), i+1 == question.correct));
      }

      item.setRequired(required);
      item.setChoices(choices);
    } else {
      var item = form.addTextItem();
      if (data.answer_sheet) { item.setTitle("Question " + position) }
      item.setPoints(points);
      item.setRequired(required);
    }
  });
}

function sampleData() {
  return {
    "answer_sheet": false,
    "quiz": {
      "questions": [
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 1,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 2,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 3,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 4,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 5,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 6,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 1,
          "count": 4,
          "points": 1,
          "position": 7,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 8,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 9,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 10,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 11,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 1,
          "count": 4,
          "points": 1,
          "position": 12,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 13,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 14,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 15,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 1,
          "count": 4,
          "points": 1,
          "position": 16,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 17,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 18,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 19,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 20,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 21,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 22,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 23,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 24,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 25,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 26,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 27,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 3,
          "count": 4,
          "points": 1,
          "position": 28,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 4,
          "count": 4,
          "points": 1,
          "position": 29,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        },
        {
          "correct": 2,
          "count": 4,
          "points": 1,
          "position": 30,
          "type": "mc",
          "url": "//via.placeholder.com/468x100"
        }
      ]
    },
    "title": "30 problem doc"
  }
}
like image 418
patrickdavey Avatar asked Sep 06 '25 03:09

patrickdavey


2 Answers

Had the same issue. Simple fix. Instead of this:

  var form = FormApp.getActiveForm()
    const students = ['Han S.','Luke S.','Yoda']

    var pickYourName = form.addMultipleChoiceItem()
        // RISK OF EMPTY CHOICES: DON'T SET CHOICEVALUES LAST
        pickYourName.setTitle('Choose your name:')
        pickYourName.setChoiceValues(students)
    

Simply swap the order of operations and set the title last., like this:

    var form = FormApp.getActiveForm()
    const students = ['Han S.','Luke S.','Yoda']

    var pickYourName = form.addMultipleChoiceItem()
        // HAD NO ISSUE DOING IT THIS WAY: CHOICES FIRST, THEN TITLE
        pickYourName.setChoiceValues(students)
        pickYourName.setTitle('Choose your name:')
like image 159
Jasper Cuvelier Avatar answered Sep 08 '25 18:09

Jasper Cuvelier


Actually I have the same problem, I found a hacky way around it. Its complexity is high O(n^2), but it works.

Basically, I'm double checking every question got it's choices correctly.

const items = [];
for(var i=0;i<questions.length;i++) {
    items.push(form.addMultipleChoiceItem());
}

for(var j=0;j<questions.length;j++) {
    for(var i=0;i<questions.length;i++) {
        var item = items[i];
        if(item.getChoices()[0].getValue() === "A") continue; // already set question with choices, ignore it 
        item.setPoints(points);

        if (data.answer_sheet) { item.setTitle("Question " + i) }

        var choices = [];

        for (var i=0; i < question.count; i++) {
            choices.push(item.createChoice(String.fromCharCode(65 + i), i+1 == question.correct));
        }

        item.setRequired(required);
        item.setChoices(choices);
    }
}
like image 33
mina sameh Avatar answered Sep 08 '25 20:09

mina sameh