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:')
这个错误让我完全困惑。我正在自动创建一个 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:')