iT邦幫忙

2021 iThome 鐵人賽

DAY 7
2
Modern Web

整合 Google 服務的燃料——透過 Google Apps Script (GAS) 加速你的工作速度系列 第 7

D7 - 如何用 Google Apps Script 將 Google 表單的回應即時同步在多個行事曆上?

來到了第七天。老樣子,先講推薦的速解,如果你很急著用,這些 Add-On 可以幫上忙,第一是 Form to Calendar 但它十個 event 後就要收費; 第二是 Form Scheduler 有七天內要回應的限制。自己寫的好處是,如果你一天突然要客製化,那這篇文章應該幫的上忙。對於想知道怎麼做的人,讓我們開始吧!

先來個小測驗

答案會在今天的文章中!

今天的目標

電腦還不普及時,會到市立圖書館借電腦,那時需要用圖書證到櫃台,詢問哪個時段可以使用,然後就可以用個三十分鐘;又或是之前在做會議室的簡單借用時,老闆就問說能不能讓行事曆分開,不然這樣看得很累。所以今天的關鍵問題是...

  1. 如何將 Google 表單的結果同步在多個行事曆(Google Calendar)上?
  2. 如何即時將表單的結果同步到行事曆上?

特別提醒,這邊的「客製化」Email,因為會限定是要 Google 產品,一般人會是 Gmail,或是企業與組織的 Google Workspace(簡單來說你的學校、組織的基本上是用 Google Drive 傳檔案的話,基本上就是)。那我們就開始吧!


如何將 Google 表單的結果同步在多個行事曆(Google Calendar)上?

前三步跟 D6 差不多,會調整程式碼,但借用一下影片。

Step 1 開啟 Google 表單,並從回應的連結中串起 GAS

一樣開啟一個表單,並串回應與GAS。

一樣執行時會有「需要驗證」出現,讓我借用一下 D2 的影片。

Step 2 搜集回應並讀取資料

假設我們表單搜集到這樣的回應。

這個步驟也跟 D6 差不多,所以直接借用程式碼。

function readData() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let range = sheet.getRange(2,2,2,5);
  let display_data =  range.getDisplayValues();
  Logger.log(display_data);
  return data
}

讀起來的樣子,讀起來沒問題。

Step 3 將讀取的資料寫成 Google Calendar 要的格式

這步也跟 D6 很像,主要是做資料的轉換,這次我們先將要處理的時間用 Google Sheet 搞定。

同時調整 Step 2 的程式把,把 let range = sheet.getRange(2,2,2,5); 換成 let range = sheet.getRange(2,2,2,7); 。因為我們多抓了兩欄,也因為我直接把資料處理在 Google Sheet 上搞定,所以不用再 getDisplayValues,更改過後會長這樣。

function readData() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let range = sheet.getRange(2,2,2,7);
  let data =  range.getValues()
  return data
}

好,來看看能不能運作。

接著我們一樣來檢查要給 Calendar 的資料。因為比較複雜的 Date Object 已經讓 Google Sheet 搞定,這邊我們就可以簡化程式碼成...

function setUpCalendar() {
  let data = readData();
  for (let i = 0; i < data.length; i++) {
    let request = data[i];

    let user = request[0];
    let meeting_room = request[1];
    let title = user + '_Meeting_Room_' + meeting_room;
    let start_time = request[5];
    let end_time = request[6];
    Logger.log(title + '\n' + start_time + '\n' + end_time);
  }
}

一樣試跑看看能不能運作。

看起來可以!好,上面三步驟跟 D6 差不多,就比較快速帶過。接著會是今天的重點,開啟並設置不同的 Calendar。

Step 4 先建立好要用的 Google Calendar 並取得其 ID

首先,如果你還沒有固定給「會議室」的日曆的話,就先建立日曆。

建立好後,可以在「設定」這邊取得日曆的 ID。

接著,將兩個 ID 輸入到 GAS 的 environment.gs 當中。下面這段錄影片當時寫太快了,這邊我們用為全域變數,用 var 比較合適。

var room_A_ID = "icqs6thnruqt1fvfrqhel7glns@group.calendar.google.com"
var room_B_ID = "rnd3j7e3end2kjc60eo9ncpens@group.calendar.google.com"

然後在昨日基礎的程式碼上加上「分派給會議室 ID」的部分,就完成了。這邊簡單的寫,如果當會議室很多時,可以考慮另外用一個 Mapping 會議室 和 Calendar ID 的試算表。

function setUpCalendar() {
  let data = readData();
  for (let i = 0; i < data.length; i++) {
    let request = data[i];

    let user = request[0];
    let meeting_room = request[1];
    let title = user + '_Meeting_Room_' + meeting_room;
    let start_time = request[5];
    let end_time = request[6];

    let calendar_id;
    if (meeting_room=="A"){
      calendar_id = room_A_ID;
    } else if (meeting_room=="B"){
      calendar_id = room_B_ID;
    }
    let cal = CalendarApp.getCalendarById(calendar_id);
    let event = cal.createEvent(title, start_time, end_time);

    Logger.log('Create Calendar of '+ meeting_room +' \n'+ title + '\n' + start_time + '\n' + end_time);
  }
}

完整起來長這樣。

以上就完成了我們的 Q1。但我們實際使用上,更想看到的是一但有人填寫了表單,就更新到行事曆上,這個要怎麼做到?讓我們來看看 Q2。


Q2. 如何即時將表單的結果同步到行事曆上?

前面四個 Step 跟 Q1 一樣,這邊主要說明「要加上的部分」,也就是 Step 4 。

Step 4 使用 onEdit Trigger 來自動更新你的表單

onEdit(e) 是一個 Trigger,每當有人更新表單時,這個 Trigger 就會啟動。我們透過一段簡單的程式碼來看它的功能,以下這段程式碼是會跳出說,工作表中剛更動的數值。

function onEdit(e) {
  // Set a comment on the edited cell to indicate when it was changed.
  var range = e.range;
  SpreadsheetApp.getActiveSpreadsheet().toast(range.getValues());
}

實際用起來,會長這樣——

瞭解其基本功能後,我們來看要怎麼樣「結合」起我們 Q1 的部分。

Step 4-1 先把預處理的「公式」套用到整個表單

還記得 Step 2 我們有先對「時間」與「日期」做處理嗎?這邊我們要先設定說它的處理是會延續下去的。其實就是很單純的讓公式套用而已——

Step 4-2 讓新增的資料自動執行

那接下來,就是將 onEdit()setUpCalendar 進行整合。先給一個最簡單的整合方式,把兩個程式碼合併,這個合併需要一些調整。像是拿掉 setUpCalendar 第一段並加上個可傳入的變數。同時,請將 onEdit() 轉為其他「非預設」的 function Name(onOpen() 也是預設)。簡單來說是改名,主要原因是因為待會我們要用系統的 Trigger。如果用 onEdit(e) 會出現些 BUG(底了很久QQ)。我改成autoUpdate,並直接寫死說我要取最新更新的那列(e.range / range.getRow())的七個欄(let data = ...)。

function setUpCalendar(data) {
  // let data = readData();
  ...
  
}

function autoUpdate(e) {
  // Set a comment on the edited cell to indicate when it was changed.
  let range = e.range;
  let new_row = range.getRow();
  let data = SpreadsheetApp.getActiveSheet().getRange(new_row,2,1,7).getValues();
  setUpCalendar(data)
}

好,接著我們來設定 Trigger,這邊因為是示範,所以我是直接貼上一筆新的資料,如果是實際在使用表單,條件請改成「表單送出時」(要先確認表單有連接到 Google Sheet 喔,請看 Step 1)

好,設定完後我們來看看功能是不是有運作。

看起來有,恭喜!完成了自動設定表單囉!再讓我們看看今天的全部程式碼——

function readData() {
  let sheet = SpreadsheetApp.getActiveSheet();
  let range = sheet.getRange(2,2,2,7);
  let data =  range.getValues()
  return data
}

function setUpCalendar(data) {
  for (let i = 0; i < data.length; i++) {
    let request = data[i];

    let user = request[0];
    let meeting_room = request[1];
    let title = user + '_Meeting_Room_' + meeting_room;
    let start_time = request[5];
    let end_time = request[6];

    let calendar_id;
    if (meeting_room=="A"){
      calendar_id = room_A_ID;
    } else if (meeting_room=="B"){
      calendar_id = room_B_ID;
    }
    let cal =CalendarApp.getCalendarById(calendar_id)
    let event = cal.createEvent(title, start_time, end_time);

    Logger.log('Create Calendar of '+ meeting_room +' \n'+ title + '\n' + start_time + '\n' + end_time);
  }
}


function autoUpdate(e) {
  // Set a comment on the edited cell to indicate when it was changed.
  let range = e.range;
  let new_row = range.getRow();
  let data = SpreadsheetApp.getActiveSheet().getRange(new_row,2,1,7).getValues();
  setUpCalendar(data)
}

一樣提醒,行事曆有 Quota 限制。好,那今天就是我們的 D7,明天 D8 會繼續介紹結合如何將 Google Calendar 上的事件與更新全部列出到 Google Sheet 上。如果有疑問可以到 Facebook Group,想開很久這次鐵人賽才真的開起來哈哈哈,歡迎來當 Founding Member。如果不想錯過可以訂閱按讚小鈴鐺(?),也歡迎留言跟我說你還想知道什麼做法/主題。我們明天見。


上一篇
D6 - 如何用 Google Apps Script 將 Google 表單收到的時間同步在 Google Calendar 上?
下一篇
D8 - 如何用 Google Apps Script 將 Google Calendar 上的事件與更新全部列出到 Google Sheet 上?
系列文
整合 Google 服務的燃料——透過 Google Apps Script (GAS) 加速你的工作速度30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

2 則留言

0

您好,非常謝謝您清楚的教學

正在使用教學試著操作

但遇到一個問題

我使用表單回復時會觸發trigger的方式

但當我讀取表單的getLastRow() //讀取最新一筆回復

會顯示[, , , ]

但讀取getLastRow()-1就能讀取到前一次輸入的資料 //讀取上一筆回復

0
asleave
iT邦新手 5 級 ‧ 2022-11-24 15:43:45

https://ithelp.ithome.com.tw/upload/images/20221124/20155394B9L5xCOPxC.jpg

您好,想詢問一下,關於上面這段程式碼,我是參考您的做法下去微調,但卻一直無法成功,我是想做到google表單可以直接同步行事曆的狀態,再麻煩您指教,謝謝。

我要留言

立即登入留言