如果特定单元格已更改,则通过电子邮件通知,并在不更改跟踪的情

时间:2014-09-19 10:36:12

标签: google-apps-script google-sheets email-notifications

我做了什么:

如果M列中的任何单元格已更改,则已发送电子邮件通知以指定同一行中N列的地址。但我还需要在同一行的其他列中指定一些正文文本。我做了一些有效的工作,但它也导致如果其他声明的列(例如项目,客户,任务,执行程序)已被更改,则已将emil发送给。

我需要什么:

只需跟踪一个“M”列中的更改,并将来自其他列但来自同一行的其他数据放入电子邮件正文中。并且(这就是重点)没有跟踪其他列的更改,只有在更改M列时才会发送电子邮件。

可能这很容易,但我扭曲了......

我根据以下内容制作了这个脚本:

我的剧本:

function sendNotification() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = ss.getActiveSheet();
  var cell = ss.getActiveCell().getA1Notation();
  var row = sheet.getActiveRange().getRow();
  var cellvalue = ss.getActiveCell().getValue().toString();
  var sendto = '';
      if(cell.indexOf('M')!=-1){ 
      sendto = sheet.getRange('N'+ sheet.getActiveCell().getRowIndex()).getValue()
      }   
  var project = ''; 
      project = sheet.getRange('C'+ sheet.getActiveCell().getRowIndex()).getValue()   
  var customer = '';
      customer = sheet.getRange('D'+ sheet.getActiveCell().getRowIndex()).getValue()      
  var task = '';
      task = sheet.getRange('E'+ sheet.getActiveCell().getRowIndex()).getValue()      
  var executor = '';
      executor = sheet.getRange('F'+ sheet.getActiveCell().getRowIndex()).getValue() 
  var deadline = '';
      deadline = LanguageApp.translate(Utilities.formatDate(sheet.getRange('I'+ sheet.getActiveCell().getRowIndex()).getValue() , "GMT" , "EEEE, dd MMMM YYYY" ),'en','pl')
  var status = '';
      status = sheet.getRange('M'+ sheet.getActiveCell().getRowIndex()).getValue()         
  var mysubject = status + ' | ' + project + ': ' + task + ' - ' + ss.getName() + ' update';
  var mybody = '\nStatus: ' + status + '\n\nproject: ' + project + '\ncustomer: ' + customer + '\ntask: ' + task + '\nexecutor: ' + executor + '\nDeadline: ' + deadline + '\n\n' + ss.getName() + ': \n' + ss.getUrl();

  MailApp.sendEmail({
    to:sendto, 
    subject:mysubject,
    body:mybody});
};

1 个答案:

答案 0 :(得分:0)

我制作了一个示例脚本,您应该可以在https://docs.google.com/spreadsheets/d/11u0xkdtPlQsnVppCnPYM0CHuCTPLdmN8PcFlaW08lNw/edit#gid=0

看到

你必须制作副本才能真正用它做点什么。

但是 ...复制不会给你我设置的触发器。如果您编辑脚本,请转到菜单资源 - > 当前项目的触发器,并使自己成为 checkForChanges()函数的基于时间的触发器。我将其设置为“每分钟”用于测试目的。

在我的专栏中,我添加了四个新的:

  • concat - 只是需要监控其更改的所有行值的串联
  • 当前哈希 - 是从我添加到您脚本中的简单函数生成的
  • 上次修改 - 发送电子邮件后,脚本会为此单元格提供当前哈希
  • 的值
  • 已更改 - 比较上次修改当前哈希,如果不同,则说明为** true。

所以......周期性地,函数 checkForChanges() ChangeDetector 范围内运行,寻找 true 。如果它什么也没找到,它会立即退出。

每次发现更改时,它都会收集该行的数据并通过电子邮件发送。 (实际上,为了简单起见,我只是记录它。)

关键技巧是这对线:

  lastEdits[row_][0] = currentHashCodes[row_][0];
  ss_.getRangeByName("LastEdit").setValues(lastEdits);

请注意,如果您尽可能使用命名范围,您的代码会有多清晰。

以下是代码在将来某天丢失的代码:

/* Called from cells in column "Current Hash" */
function strHash(valCell) {
  var hash = 0;
  if (valCell.length == 0) return hash;
  for (i = 0; i < valCell.length; i++) {
    char = valCell.charCodeAt(i);
    hash = ((hash<<5)-hash)+char;
    hash = hash & hash; // Convert to 32bit integer
  }

  return hash;
}


/* Called periodically from a timed trigger. */
function checkForChanges() {

  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var changeDetector = ss.getRangeByName("ChangeDetector").getValues();
  for (row in changeDetector) {
    if (row > 0) {
      changed = changeDetector[row][0];
      if (changed) notify(ss, row);
    }
  }

}


/* Called by checkForChanges(). */
function notify(ss_, row_) {

  initializeRangeArrays(ss_);

  status = statii[row_][0];
  project = projects[row_][0];
  task = tasks[row_][0];
  customer = customers[row_][0];
  executor = executors[row_][0];
  deadline = deadlines[row_][0];
  sendto = recipients[row_][0];

  var mysubject = status + ' | ' + project + ': ' + task + ' - ' + ss_.getName() + ' update';
  var mybody = '\nStatus: ' + status 
             + '\n\nproject: ' + project 
             + '\ncustomer: ' + customer 
             + '\ntask: ' + task
             + '\nexecutor: ' + executor
             + '\nDeadline: ' + deadline
             + '\n\n' + ss_.getName()
             + ': \n' + ss_.getUrl();

  Logger.log("to: " + sendto);
  Logger.log("subject: " + mysubject);
  Logger.log("body: " + mybody);
  Logger.log("");

  lastEdits[row_][0] = currentHashCodes[row_][0];
  ss_.getRangeByName("LastEdit").setValues(lastEdits);

}; 


var recipients = null;
var projects = null;
var customers = null;
var tasks = null;
var deadlines = null;
var executors = null;
var statii = null;
var lastEdits = null;
var currentHashCodes = null;

var rangeArraysInitialized = false;
/* Called by notify(). */
function initializeRangeArrays(ss_) {

  if (  !  rangeArraysInitialized  ) {
    recipients = ss_.getRangeByName("Recipient").getValues();
    projects = ss_.getRangeByName("Project").getValues();
    customers = ss_.getRangeByName("Customer").getValues();
    tasks = ss_.getRangeByName("Task").getValues();
    deadlines = ss_.getRangeByName("Date").getValues();
    statii = ss_.getRangeByName("Status").getValues();
    executors = ss_.getRangeByName("Executor").getValues();
    lastEdits = ss_.getRangeByName("LastEdit").getValues();
    currentHashCodes = ss_.getRangeByName("CurrentHash").getValues();

    rangeArraysInitialized = false;
  }
}

更新2014/09/22:

我在演示电子表格中做了一些更改。请看一看。

我添加的脚本。 。 。

function strArrayHash(range) {
  var ret = new Array();
  var str = "";
  for (item in range) {
    str = range[item].toString();
    if (str.length > 0) {
      ret[item] = strHash(str);
      Utilities.sleep(50);      // play with this to reduce "internal execution error"s
    } else {
      ret[item] = "";
    }
  };
  return ret;
}

在“P”栏中,我换了。 。 。

=if( M2 = "", "", strHash(R2)))
=if( M3 = "", "", strHash(R3)))
      :
      :
=if( M22 = "", "", strHash(R22)))

。 。 。与。 。 。

=ARRAYFORMULA(if( M2:M514 = "", "", strArrayHash(R2:R514)))

。 。 。在单元格“P2”中。我删除了“P3:P22”

范围内所有单元格的内容

我将总行数增加到1026

在P列中出现值后,我使用[选择性粘贴]»[仅粘贴值]将单元格P2:P1026复制到M2:M1026中。

在我的i7笔记本电脑中,它需要30秒才能重新计算并检测第500行的变化。

如果我尝试执行所有1024行,则会出现内部执行错误。

可能你需要使“if”子句复杂化,以致它只对真正需要它的行进行哈希计算。

更新:: 2017/02/24没有人对此感兴趣所以我停止了运行脚本的触发器。