Google Forms Apps 脚本 - 随机缺少选项

Google Forms Apps script - randomly missing choices

这个错误让我完全困惑。我正在自动创建一个 google 表单(测验)。周期性地(但经常)多项选择项 缺少选项 ,这种情况经常发生 一旦页面刷新 。也就是说,答案选项看起来像是在屏幕上正确创建的,但是,一旦您刷新它们就会消失。奇怪的是(有趣的是)Stackdriver 中没有错误。

视觉上这就是正在发生的事情。这两个图像都是在 运行使用相同的输入为两种不同的形式使用相同的脚本时创建的:

如您所见,第一个已创建并有 4 个选项,第二个已创建且只有 1 个选项。

它经常发生,但并非总是如此。看起来它几乎 是一个 Google 错误,但是,我搜索了他们的跟踪器,没有人报告任何东西。

我会展示我们的“选择创建代码”,以防它指向任何东西,但是,我们使用 完全相同的输入 得到不同结果的事实表明还有其他东西进行中。

我只是展示了循环的一部分,但正如我所说,代码根本没有错误。有没有一种方法可以创建比 运行 应用程序脚本插件更好的大型(30 个问题)Google 表单?我希望有一个 API :(

无论如何,如果这是一个已知问题,或者有解决方法,我很想听听。

      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);

编辑 - 最小可重现错误

如果将以下脚本复制到 google-apps-script 环境和 运行 脚本中,它应该会导入一个 30 题的多项选择文档。看起来好像一切都已正确导入,然后刷新表单,您会发现缺少选项。代码在下面,我也有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"
  }
}

实际上我也遇到了同样的问题,我找到了一个 hacky 的解决方法。 它的复杂度很高 O(n^2),但它确实有效。

基本上,我会仔细检查每个问题的选择是否正确。

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);
    }
}

我的附加组件遇到了同样的问题,我想我设法修复了我的附加组件,我所做的是 - 我将 setChoices 移到了 setTitle 之后的任何其他属性之前。以前,我的代码与您的代码相似,我最后添加了 setChoices(在 setRequired 等之后)。

Google Form AppScript - Item choices disappear when viewing, however it's showing in editing view?

我不知道怎么解释,但经过几次测试后它似乎对我有用。

我遇到了同样的问题,但我解决了。服务器需要一些时间来创建项目。因此,让您的脚本等待项目完成。将此行放在项目创建任务之后。

SpreadsheetApp.flush();

有同样的问题。 简单的修复。而不是这个:

  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)
    

只需调换操作顺序,将标题设置在最后即可,如下所示:

    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:')