我正在制作一个与Google表格的回复表互动的脚本。
FormApp.getActiveForm().getDestinationId()
给我电子表格ID,但我找不到获取表格的方法。用户可以更改其名称和位置,因此我需要获取其ID,例如
Sheet.getSheetId()
我还必须确定响应使用的列数。它不等于表格中的问题数量。我可以计算表格中的项目数量:
Form.getItems().length
然后搜索gridItems,在每个中添加行数并将它们减去1:
+ gridItem.getRows().length - 1
最后,我认为没有办法将每个问题与表格中的每一列相关联,而是通过比较某些列名称与项目标题。
谢谢
答案 0 :(得分:3)
现在,有一种方法可以通过使用Sheet#getFormUrl()
来验证Google表格文件中具有多个链接表单的哪个工作表与当前表单相对应,该方法已添加到{中的Sheet
类中{3}}。
function getFormResponseSheet_(wkbkId, formUrl) {
const matches = SpreadsheetApp.openById(wkbkId).getSheets().filter(
function (sheet) {
return sheet.getFormUrl() === formUrl;
});
return matches[0]; // a `Sheet` or `undefined`
}
function foo() {
const form = FormApp.getActiveForm();
const destSheet = getFormResponseSheet_(form.getDestinationId(), form.getPublishedUrl());
if (!destSheet)
throw new Error("No sheets in destination with form url '" + form.getPublishedUrl() + "'");
// do stuff with the linked response destination sheet.
}
如果您取消了表单和目标电子表格的链接,那么显然您将无法使用getDestinationId
或getFormUrl
。
答案 1 :(得分:1)
我也需要这个,并且显然仍然没有便于它的应用程序脚本方法。最后,我着手寻找一种可靠的方法来确定工作表ID,这就是我通过编程方法解决的问题:
我确定有些人不喜欢这种方式,因为它会修改表单和电子表格,但它确实运行良好。
如果包含必要的等待时间,则执行所有查找/清理操作大约需要12秒。
这是我方法的代码,以防其他人想要使用它。
// Takes Apps Script 'Form' object as single paramater
// The second parameter 'obj', is for recursion (do not pass a second parameter)
// Return value is either:
// - null (if the form is not linked to any spreadsheet)
// - sheetId [int]
// An error is thrown if the operations are taking too long
function getFormDestinationSheetId(form, obj) {
var obj = obj || {}; // Initialise object to be passed between recursions of this function
obj.attempts = (obj.attempts || 1);
Logger.log('Attempt #' + obj.attempts);
if (obj.attempts > 14) {
throw 'Unable to determine destination sheet id, too many failed attempts, taking too long. Sorry!';
}
obj.spreadsheetId = obj.spreadsheetId || form.getDestinationId();
if (!obj.spreadsheetId) {
return null; // This means there actually is no spreadsheet destination set at all.
} else {
var tempFormItemTitle = '### IF YOU SEE THIS, PLEASE IGNORE! ###';
if (!obj.tempFormItemId && !obj.sheetId) { // If the sheet id exists from a previous recusion, we're just in a clean up phase
// Check that temp item does not already exist in form
form.getItems(FormApp.ItemType.TEXT).map(function(textItem) {
var textItemTitle = textItem.getTitle();
Logger.log('Checking against form text item: ' + textItemTitle);
if (textItemTitle === tempFormItemTitle) {
obj.tempFormItemId = textItem.getId();
Logger.log('Found matching form text item reusing item id: ' + obj.tempFormItemId);
}
return 0;
}); // Note: Just using map as handy iterator, don't need to assign the output to anything
if (!obj.tempFormItemId) {
Logger.log('Adding temporary item to form');
obj.tempFormItemId = form.addTextItem().setTitle(tempFormItemTitle).getId();
}
}
obj.spreadsheet = obj.spreadsheet || SpreadsheetApp.openById(obj.spreadsheetId);
obj.sheets = obj.sheets || obj.spreadsheet.getSheets();
obj.sheetId = obj.sheetId || null;
var sheetHeaderRow = null;
for (var i = 0, x = obj.sheets.length; i < x; i++) {
sheetHeaderRow = obj.sheets[i].getSheetValues(1, 1, 1, -1)[0];
for (var j = 0, y = sheetHeaderRow.length; j < y; j++) {
if (sheetHeaderRow[j] === tempFormItemTitle) {
obj.sheetId = obj.sheets[i].getSheetId();
Logger.log('Temporary item title found in header row of sheet id: ' + obj.sheetId);
break;
}
}
if (obj.sheetId) break;
}
// Time to start cleaning things up a bit!
if (obj.sheetId) {
if (obj.tempFormItemId) {
try {
form.deleteItem(form.getItemById(obj.tempFormItemId));
obj.tempFormItemId = null;
Logger.log('Successfully deleted temporary form item');
} catch (e) {
Logger.log('Tried to delete temporary form item, but it seems it was already deleted');
}
}
if (obj.sheetId && !obj.tempFormItemId && !obj.tempColumnDeleted) {
try {
obj.sheets[i].deleteColumn(j + 1);
obj.tempColumnDeleted = true;
Logger.log('Successfully deleted temporary column');
} catch (e) {
Logger.log('Could not delete temporary column as it was still attached to the form');
}
}
if (!obj.tempFormItemId && obj.tempColumnDeleted) {
Logger.log('Completed!');
return obj.sheetId;
}
}
SpreadsheetApp.flush(); // Just in case this helps!
// Normally this process takes three passes, and a delay of 4.5 secs seems to make it work in only 3 passes most of the time
// Perhaps if many people are submitting forms/editing the spreadsheet, this delay would not be long enough, I don't know.
obj.delay = ((obj.delay || 4500));
// If this point is reached then we're not quite finished, so try again after a little delay
Logger.log('Delay before trying again: ' + obj.delay / 1000 + ' secs');
Utilities.sleep(obj.delay);
obj.attempts++;
return getFormDestinationSheetId(form, obj);
}
}
&#13;
答案 2 :(得分:1)
@tehhowch非常接近正确答案,但是代码存在问题:无法保证form.getPublishedUrl()
和sheet.getFormUrl()
将返回完全相同的字符串。在我的情况下,form.getPublishedUrl()
返回了一个以https://docs.google.com/forms/d/e/{id}/viewform
形式形成的URL,而sheet.getFormUrl()
返回了https://docs.google.com/forms/d/{id}/viewform
。由于表单ID是URL的一部分,因此更可靠的实现是:
function get_form_destination_sheet(form) {
const form_id = form.getId();
const destination_id = form.getDestinationId();
if (destination_id) {
const spreadsheet = SpreadsheetApp.openById(destination_id);
const matches = spreadsheet.getSheets().filter(function (sheet) {
const url = sheet.getFormUrl();
return url && url.indexOf(form_id) > -1;
});
return matches.length > 0 ? matches[0] : null;
}
return null;
}
答案 3 :(得分:0)
要获取电子表格,一旦您拥有DestinationID,请使用SpreadsheetApp.openById()。完成后,您可以检索工作表数组,并按索引获取响应表,无论其名称如何。
var destId = FormApp.getActiveForm().getDestinationId();
var ss = SpreadsheetApp.openById(destId);
var respSheet = ss.getSheets()[0]; // Forms typically go into sheet 0.
...
从这一点来说,您可以使用其他电子表格服务方法操纵电子表格中的数据。
我还必须确定响应使用的列数。它不等于表格中的问题数量。我可以计算表单中的项目数...(但这与电子表格不匹配)
你是对的 - 当前项目的数量不等于电子表格中的列数。每个响应在目标表中占用的列数包括已从表单中删除的任何问题,并排除非问题的项目。此外,电子表格中列的顺序是创建问题的顺序 - 当您重新排列表单或插入新问题时,电子表格列顺序不会反映新订单。
假设电子表格中的唯一列来自表单,以下是您可以使用它们的方法:
...
var data = respSheet.getDataRange().getValues(); // 2d array of form responses
var headers = data[0]; // timestamp and all questions
var numColumns = headers.length; // count headers
var numResponses = data.length - 1; // count responses
你的最后一点是正确的,你需要关联名称。
最后,我认为没有办法将每个问题与表格中的每一列相关联,而是通过比较某些列名称与项目标题。