Google表单中的voTer脚本无法正常运行

时间:2014-09-04 22:40:45

标签: google-apps-script google-sheets google-form

我是一名高中教师,正试图让他们的学校使用"realsmpart" voTer script在Google Apps中以电子方式投票,并且它没有按预期工作。

以下是设置它的YouTube video about the script

这是脚本:

function voTer(){
var sss = SpreadsheetApp.getActiveSpreadsheet();
var form = sss.getSheetByName("Sheet1");
var range = form.getRange(1,2,form.getLastRow(),1);
var lastEntry = range.getCell(form.getLastRow(),1).getValue();
var data = form.getRange(1,2,form.getLastRow()-1,1).getValues();
for (var i = 0; i < data.length; i++) {
  var row = data[i];
  var entry = row[0]; 
  if (entry == lastEntry){
var subject = "You Have Submitted!"; //Email subject
var recipient = lastEntry;
var body = "Hi there,<br><br>You've already submitted this form before, so this      
submission will not count.<br>Please contact ***, if you do want to change your answer.    
<br><br>Best,<br>***"; //Email body

if (recipient!=''){
      MailApp.sendEmail(recipient, subject, body,{htmlBody:body});
    }
    form.getRange(form.getLastRow(),2,1,form.getLastColumn()).clear();
    break;        
      }
    }
}

我们不需要指定范围,所以相反(为了不再获取此错误消息)我们将写入脚本以简单地忽略它。添加下面带有错误的区域旁边的脚本。

//ignore this warning

以下是关于如何使用脚本的the instructions,我已遵循该脚本但仍无法使脚本生效。

1 个答案:

答案 0 :(得分:1)

原始剧本是为“老人”编写的。表格提供,并没有更新。因此,对Sheet1的硬编码引用是错误的,因为&#39; new&#39;表格将创建一个&#34;表格回复&#34;片。对列数的依赖是“脆弱的”,因为该脚本假设受访者&#39;电子邮件地址将位于时间戳之后的第2列(B)中。实际上,电子表格中的问题顺序取决于它们添加到表单的顺序 - 并且由于新表单是由虚拟问题预先填充的,并且电子邮件地址列在表单中被视为隐藏问题,即使您删除了虚拟问题,电子邮件地址也绝不会在第2列。这样脆弱的代码现在就被打破了。

那整个//ignore this warning的事情?它只是一个评论,它不会阻止检测到错误。

好消息是有更好的方法。

我们可以利用提供给触发器功能的Event对象,而不是硬编码细节。阅读Sheets Form Submit事件对象。

为了索引特定的列,我已经包含了一个辅助函数columnNum()。它是作为电子表格自定义函数编写的,因此它产生了基于1的索引,以便与内置函数兼容。

Code.gs

此脚本比原始&#34; voTer&#34;更容易自定义,因为您需要摆弄的所有变量都位于顶部。如有必要,可以修改电子邮件的正文 - 每一行在数组中显示为单独的字符串。

/**
 * To customize the email sent to re-voters, change the values
 * of the following variables.
 */
var subject = "Invalid Vote Received";                //Email subject
var electoralOfficer = "your electoral officer";      // Person to contact for re-vote
var emailSignature = "Election committee";            // Signature on email
var timezone = Session.getScriptTimeZone();           // Timezone - use caution

function oneVote(vote) {
  var colUserName = columnNum("Username");

  // use the provided event object to get references to required objects
  var sheet = vote.range.getSheet();
  var ballot = vote.range.getRowIndex();
  var voter = vote.namedValues["Username"][0];

  // Get list of all previous votes
  var prevVoters = sheet.getRange(1, colUserName, ballot-1)
                        .getValues()  // Get email addresses as 2-dim array
                        .join(',')    // Join into a comma-deliminated string
                        .split(',')   // Then split into one-dimensional array

  // Check whether this voter previously voted
  var previousVote = prevVoters.indexOf(voter);

  // if they did, remind them then delete the new vote
  if (previousVote > -1) {
  var previousVoteTime = sheet.getRange(previousVote+1,columnNum("Timestamp")).getValue();
    var body =                                   //Email body
        ["Hi there,",
         "",
         "You previously voted on #TIMESTAMP#, so this submission will not count.",
         "Please contact #ELECTORALOFFICER# if you do want to change your vote.",
         "",
         "Thanks,",
         "#SIGNATURE#"
        ].join('<br>')
         .replace('#TIMESTAMP#',Utilities.formatDate(previousVoteTime, timezone, "MMM d 'at' h:mm a"))
         .replace('#ELECTORALOFFICER#', electoralOfficer)
         .replace('#SIGNATURE#', emailSignature)
    MailApp.sendEmail(voter, subject, body,{htmlBody:body});

    sheet.deleteRow(ballot);  // Could move to a "spoiled ballots" list instead.
  }
}

/**
 * Get the index of columnName.
 * @customfunction
 *
 * @param {String}    columnName  Column header to find
 * @param {Number}    headerRow   Column headers row index (Optional, default 1)
 * @returns {Number}  Column index, 0 if not found
 */
function columnNum(columnName,headerRow) {
  headerRow = headerRow ? headerRow - 1 : 0;  // Default: assume column headers in first row
  var rows = SpreadsheetApp.getActiveSheet().getDataRange().getValues();

  // Validate input
  if (headerRow < 0 || headerRow >= rows.length)
    throw new Error( "headerRow out of range 1..maxRows" );

  var headers = rows[headerRow];
  return headers.indexOf(columnName)+1;
}

可安装触发器

请务必设置触发器,以便在提交表单时运行。

Trigger

测试功能

为了测试触发功能,最好模拟在正常操作下接收的事件。此函数读取现有表单响应并将其作为新提交提供给oneVote()。见How can I test a trigger function in GAS?

/**
 * Test function simulates multiple form submission events for oneVote()
 * trigger function, by reading "active" spreadsheet. Make sure your
 * spreadsheet session is open to the Form Responses first.
 *
 * From https://stackoverflow.com/a/16089067
 */
function test_oneVote() {
  var dataRange = SpreadsheetApp.getActiveSheet().getDataRange();
  var data = dataRange.getValues();
  var headers = data[0];
  // Start at last row, work backwards to allow for deletions, skip headers in row 0
  for (var row=data.length-1; row > 0; row--) {
    var e = {};
    e.values = data[row];
    e.range = dataRange.offset(row,0,1,data[0].length);
    e.namedValues = {};
    // Loop through headers to create values & namedValues properties
    for (var col=0; col<headers.length; col++) {
      e.namedValues[headers[col]] = [e.values[col]];
    }
    // Skip invalid rows
    if (e.namedValues["Username"] === '') continue;
    // Pass the simulated event to oneVote
    oneVote(e);
  }
}