【问题标题】:Google Calendar events to Google Sheers automatic refresh with onEdit trigger使用 onEdit 触发器将 Google 日历事件自动刷新到 Google 表格
【发布时间】:2019-09-14 14:43:47
【问题描述】:

我正在尝试将我的 Google 日历中的时间事件抓取到 Google 电子表格中。

当在我的 Google 日历中创建新的时间事件时,该事件应自动同步到我的 Google 电子表格中。这应该由onEdit 事件触发器自动完成。

目前它只能通过刷新 Google 电子表格来运行。

也许有人对我的挑战有更好的解决方案。这是我的代码:

function createSpreadsheetEditTrigger() {
  var ss = SpreadsheetApp.getActive();
  ScriptApp.newTrigger('myCalendar')
      .forSpreadsheet(ss)
      .onEdit()
      .create();
}

function myCalendar(){

  var now=new Date();

 // Startzeit
  var startpoint=new Date(now.getTime()-60*60*24*365*1000);
  // Endzeit
  var endpoint=new Date(now.getTime()+60*60*24*1000*1000);

  var events=CalendarApp.getCalendarById("your-calendar-ID").getEvents(startpoint, endpoint);

  var ss=SpreadsheetApp.getActiveSpreadsheet().getSheetByName("TEST"); 

  ss.clear(); 

  for (var i=0;i<events.length;i++) {

         ss.getRange(i+1,1 ).setValue(events[i].getTitle());
         ss.getRange(i+1,2).setValue(events[i].getDescription());
         ss.getRange(i+1,3).setValue(events[i].getStartTime());
         ss.getRange(i+1,4).setValue(events[i].getEndTime());
   }
 }

【问题讨论】:

  • 日历事件没有触发器。你看错了方向。 OnEdit 是电子表格事件的触发器。唯一可行的方法是定期检查日历的时间驱动触发器..
  • 谢谢 Serge insas ...你是对的。这是我对触发器的误解之一。它们仅在电子表格事件上运行...

标签: google-apps-script google-sheets google-calendar-api triggers


【解决方案1】:

问题

在 Google 日历中创建事件时执行更新电子表格的函数。

解决方案

使用 EventUpdated 可安装触发器,每次在日历中修改事件(例如创建、更新或删除 - 请参阅参考资料)时都会触发该触发器。从那里,您可以采用简单的方式(使用内置的 CalendarApp 类更新电子表格中的所有数据)或困难的方式(更新通过增量同步更改的数据 - 请参阅官方指南)。

第 0 部分 - 安装触发器

/**
 * Installs Calendar trigger;
 */
function calendarTrigger() {
  var trigger = ScriptApp.newTrigger('callback name here')
  .forUserCalendar('calendar owners email here')
  .onEventUpdated()
  .create();
}

第 1 部分 - 回调(日历 -> 电子表格)

/**
 * Updates spreadsheet;
 * @param {Object} e event object;
 */
function updateSpreadsheet(e) {  
  //access spreadsheet;
  var ss = SpreadsheetApp.openById('target spreadsheet id');
  var sh = ss.getSheetByName('target sheet name');
  var datarng = sh.getDataRange(); //assumed that data is only calendar data;

  //access calendar;
  var calendar = CalendarApp.getCalendarById(e.calendarId);

  //set timeframes;
  var start = new Date();
  var end =new Date();

  //get year before and three after;
  start.setFullYear(start.getFullYear()-1);
  end.setFullYear(end.getFullYear()+3);

  //get events;
  var events = calendar.getEvents(start, end);

  //map events Array to a two-dimensional array of values;
  events = events.map(function(event){
    return [event.getTitle(),event.getDescription(),event.getStartTime(),event.getEndTime()];
  });

  //clear values;
  datarng.clear();

  //setup range;
  var rng = sh.getRange(1,1, events.length, events[0].length);

  //apply changes;
  rng.setValues(events);
}

备注

  1. 根据 Tanaike 的评论 - 如果事件是通过脚本或请求触发的(请参阅限制参考),请务必考虑到 不触发 的触发器(简单和可安装)。要启用此类功能,您必须引入 polling 或与 WebApp 捆绑,该脚本将在创建事件后调用(请参阅下面的捆绑示例)。
  2. 您的解决方案更适合反向流程:在电子表格中编辑 -> 在日历中编辑(如果您修改它以在日历上执行操作而不是更新电子表格,ofc)。
  3. 利用Date 内置对象的方法,如getFullYear()(其他方法请参见参考资料),使您的代码更灵活、更易于理解。顺便说一句,我会将“一天中的毫秒数”数据存储为常量 (86400000)。
  4. 永远不要在循环中使用getRange()getValue()setValue() 和类似的方法(并且通常尽可能少地调用它们) - 它们是 I/O 方法,因此速度很慢(您可以自己查看)通过尝试在循环中写入 >200 行)。在开始时获取所需的范围/值,执行修改并批量写入(例如使用setValues() 方法)。

参考

  1. EventUpdated事件reference;
  2. 日历增量同步guide;
  3. Date 内置对象reference;
  4. setValues()方法reference;
  5. Using batch Google Apps 脚本中的操作;
  6. Installablesimple 触发限制;

WebApp 捆绑

第 0 部分 - 先决条件

如果您想通过脚本执行来创建/更新/删除日历事件,您可以将目标脚本与一个简单的 WebApp 捆绑在一起。您需要确保:

  1. WebApp 部署访问设置为anyone, even anonymous(强烈建议引入某种形式的请求认证);
  2. WebApp 代码有 一个 函数,名为 doPost 接受事件对象(通常命名为 e,但由您决定)作为单个参数。

第 1 部分 - 构建 WebApp

此构建假定所有修改都在 WebApp 中进行,但您可以,例如,返回回调名称以在成功请求时运行并在调用脚本中处理更新。由于在上面的回调中只使用了事件对象的calendarId 属性,我们可以向它传递一个只有这个属性集的自定义对象:

/**
 * Callback for POST requests (always called "doPost");
 * @param {Object} e event object;
 * @return {Object} TextOutput;
 */
function doPost(e) {
  //access request params;
  var body = JSON.parse(e.postData.contents);

  //access calendar id;
  var calendarId = body.calendar;

  if(calendarId) {
    updateSpreadsheet({calendarId:calendarId}); //callback;
    return ContentService.createTextOutput('Success');
  }else {
    return ContentService.createTextOutput('Invalid request');
  }
}

第 2 部分 - 示例调用脚本

此构建假定调用脚本和 WebApp 是同一个脚本项目(因此可以通过 ScriptApp.getService().getUrl() 访问其 Url,否则粘贴在 WebApp 部署期间提供给您的 URL)。构建需要熟悉UrlFetchApp(请参阅reference)。

/**
 * Creates event;
 */
function createEvent() {
  var calendar = CalendarApp.getCalendarById('your calendar id here');

  //modify whatever you need to (this build creates a simple event);
  calendar.createEvent('TEST AUTO', new Date(), new Date()); 

  //construct request parameters;
  var params = {
    method: 'post',
    contentType: 'application/json',
    muteHttpExceptions: true,
    payload: JSON.stringify({
      calendar: calendar.getId()
    })
  };

  //send request and handle result;
  var updated = UrlFetchApp.fetch(ScriptApp.getService().getUrl(),params);
  Logger.log(updated); //should log "Success";
}

【讨论】:

  • 我认为这是一个很好的答案。作为附加信息,如何添加手动创建、更新和删除事件时触发事件触发器的信息?我认为这些信息可能对 OP 和其他用户有用。
  • @Tanaike - 谢谢!同意,将相应更新,知道触发器只能手动触发确实很重要(如果我没记错的话,这是对触发器的一般限制,既简单又可安装)
【解决方案2】:

enter code here
// There are can be many calendar in one calendar of user like his main calendar, created calendar, holiday, etc.
// This function will clear all previous trigger from each calendar of user and create new trigger for remaining calendar of the user.
function createTriggers() {
  clearAllTriggers();
  let calendars = CalendarApp.getAllCalendars();
  calendars.forEach(cal => {
 
ScriptApp.newTrigger("calendarUpdate").forUserCalendar(cal.id).onEventUpdated().create();
  });
}

/* This trigger will provide us the calendar ID from which the event was fired, then you can perform your CalendarApp and sheet operation. If you want to synchronize new update more efficiently then use Calendar Advance Service, which will provide you with synchronization token that you can use to retrieve only updated and added events in calendar.
*/
function calendarUpdate(e) {
  logSyncedEvents(e.calendarId);
}

function clearAllTriggers() {
  let triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(trigger => {
    if (trigger.getEventType() == ScriptApp.EventType.ON_EVENT_UPDATED) ScriptApp.deleteTrigger(trigger);
  });
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多