這邊先打個預防針,再如何導入自動化管理,始終還是需要人的介入,所以最終還是會需要花些時間參與其中,但至少可以從部分重複性的勞動中解放出來,隨者我們很多行為都在電腦中操作,有時候我們會發現我們做的事情就是重複性的點擊和寄件,這時候其實這些行為也可以被自動化,這就是所謂的機器人流程自動化(Robotic Process Automation)(P.S: 假如要很…技術宅的來說明的話),從簡單的點擊自動化到判讀發報告工具等等都是這類的概念。
這是開始接手一點點行政工作的夥伴常常會遇到的情境,這邊就以醫院總醫師業務為例。
“新學期開始,你的科又要迎來新的Clerk,每週都有不同梯次的人,你也要提醒帶的醫師記得上課,又要在課後收集Clerk的作業和評分,光處理這些事物,大概就佔用到你一半以上的時間”
令人崩潰的迷因
相信身邊很多開始需要處理行政雜務的朋友,常需要做的事情就是各種提醒和收集評分表或是報告,一部分的人慢慢的會使用如Gmail, Google Form, Google doc, Google sheet等等工具導入電子化,但最煩的是日期到了你必須自己來寄信、彙整、收集。
但其實可以再往下多自動化一步。(假如你是Google工具重度使用者的話)
這邊分享一下你可以使用Google App Script來讓這些工具自動化完成,做到如下:
特定日期發信提醒上課講師,給予講師當天課程學生名單,且將評分表附在信中寄給學生或是相對應的老師!
什麼是Google App Script?
我們常常在用的如Gmail, Google Slides, Google Sheet, Google Form等等都是所謂的Google Apps,而我們使用的時候都是使用圖形化介面,在瀏覽器中開啟信箱,寫信和收信,附上郵件,在google doc上面協作,撰寫草稿,或是利用Google Slides來做簡報,使用Google Form來設計問券,這些工具就是所謂的Google App,正常情況下你就是利用滑鼠來使用,但其實最近兩年Google為了讓企業或是個人能更高效地利用,導入了所謂的Google App Script工具,讓你使用簡單的代碼,便能輕鬆的自動化這些當初用滑鼠來使用的情節,且可以輕鬆地串接每個App,讓工作流程自動化且排程化。
簡單來說,你每個建立的google doc, google slide, googlde sheet, googlde form都是一個可以用程式來調用的檔案,具有唯一的物件Id。很酷吧!
如何建立一個Google App Script檔案?
那會不會很麻煩,假如想要使用Google App Script來操控我們在google Apps裡的檔案?一點也不,你不需要安裝任何程式,或是建立整個編程環境,流程就像是你建立一個Google Doc檔案一樣。

編輯和撰寫Google Apps Scripts
點開你建立的Google Apps Script就會自動進入他的編輯器中,如同下面的畫面:

整個畫面就如同一個簡單的編輯器,裡面可以直接撰寫代碼來控制你的文件,主要是使用Javascript的語法,所以相對容易,你不用在自己學習新的語法和觀念,只需要搞懂Google App Scrip裡面調用Gmail, Google Sheet, Google Form的函式即可,以及你想要怎麼去串接資料來達成你想要的任務。
基本邏輯,每個Google檔案都是可以被取用的物件
第一個重要觀念在使用Google App Scripts是每一個你的google檔案,都有一串獨一無二的Id,且都是顯示在網址之中,非常一清二楚。

這邊是我每封信件的一個範本,這邊一點開,你從瀏覽器就能看到這個文件的Id,這就可以在App Script中調用,另一方面,因為Google在權限管理做得很好,所以不同帳號的人基本上是不能調用彼此文件的,所以我這邊露出這Id基本上不會讓別人可以任意修改(希望我的理解沒錯,不然….我的檔案就有危險惹)
基本範例:一個自動寄出提醒當天上課教師的Email
這個功能在做什麼呢?簡單描述一下,每天這個函數會被排程在早上八點的時候執行,執行時,他會去檢查我們的課程排程google sheet表單,看今天的日期是哪個老師上課,有老師上課的話,就是檢查另一個學生組別的google sheet,看這老師帶的組別,裡面有哪些學生,接者他再去抓取我們的問券範本google form,把這些學生的姓名和上課資料放進去問券,接者在抓取我們設計的Email範本,把相關老師姓名、學生姓名以及問券連結放上去,接者邊把這封信寄給這位老師,同時CC給我。
他會長什麼樣子呢?

基本的撰寫方式就是以寫一個函數(javascript function)的模式,然後函數內完成你想要的功能,這邊可以看到Google Apps Scripts的編輯器會自動抓取這份文件中定義的所有函數,你點選執行便能測試這些你自己撰寫的代碼。

你可以把所有要用到的檔案都放在同一個地方,這樣其實就可以很輕鬆的管理和建置你的流程,之後要交接給學弟妹也會非常方便,或是用舊的代碼來完成新的工作!
這邊分享原始代碼,給有興趣的人使用:
// 使用的代碼
// reference usage: http://www.eion.com.tw/Blogger/?Pid=1148
function autosentDOC(){
// ======================== Basic 參數設置 ================================
let today = new Date(); // 讀取今天的日期
Logger.log(today);
Logger.log(today.toDateString());
// 設定google drive中檔案的id
// 問券:一份google form問券:用來打分數
const form_id = '1XvhpcCV_UXDCJpIOsdaksjfo9LDwWKrkxP16sMvs' ;
// 資料:一份google spread sheet:裡面有所有clerk的資訊,包含:姓名、梯數、開始的日期、結束的日期等等
const sheetTeacher_id = '1tquDwugwMsy5al20f_hGChdfdsafdsGsCoXHUJF0';
const sheetStu_id = '1EdySuvRHo3iPqdfsdfsfdsfB9Cx3sRcmFnPfdsfdo';
// 文本:一份google doc:裡面有email的範本
const docmd_id = '11QphDQkYe8rdfsdfsdfeOfKlXhfQdH6bj5g'; // 內容:提醒有clerk,且要評分
//const email = 'dontellyou@gmail.com' // 測試時使用自己的信箱
// 寄信函數的參數
const param = {
method:'get',
headers: {
"Authorization":"Bearer "+ScriptApp.getOAuthToken()
},
muteHttpExceptions:true
}
// ======================== 今天上課的教學醫師 =============================
// 讀取教學醫師的排程
const gs_MT = SpreadsheetApp.openById(sheetTeacher_id);
const gs_MT_sheet = gs_MT.getSheetByName('schedule');
const gs_data = gs_MT_sheet.getDataRange().getValues();
// 讀取學生組別資料
const gs_Stu = SpreadsheetApp.openById(sheetStu_id);
const gs_Stu_sheet = gs_Stu.getSheetByName('member');
const gs_Stu_data = gs_Stu_sheet.getDataRange().getValues();
// ======================== 從google sheet取得 Clerk的名單資料 ======
// 讀取google drive中的google sheet 檔案
//const ss = SpreadsheetApp.openById(sheet_id);
// 讀取這個google sheet中的分頁,分頁名稱為2021_2022_Clerk_CPcourse
//const sheet = ss.getSheetByName('2021_2022_Clerk_CPcourse');
// 讀取這個分頁中的所有資料
//const data = sheet.getDataRange().getValues();
// 將資料中,所有clerk課程開始日期比今天小且clerk課程結束日期比今天晚的“所有”資料
let currentTeachers = gs_data.filter(function(item, index, array){
//Logger.log(item[0]);
let tmpDate = new Date(item[0]);
return tmpDate.toDateString() == today.toDateString()
});
// 將資料中,clerk課程開始日期比今天小且clerk課程結束日期比今天晚的“第一筆”資料
let currentTeacher = gs_data.find(function(item, index, array){
let tmpDate = new Date(item[0]);
return tmpDate.toDateString() == today.toDateString()
});
// 判斷今天有組別的話,組別名單
if(currentTeachers.length == 0){
Logger.log('今天沒有老師要上課');
}else{
// 假如本兩週有clerk學生,則執行下面的代碼來建立問券
//看一下currentClerk物件內容(debug使用)
Logger.log("todayTeacher");
Logger.log(currentTeacher[3]);
Logger.log("today group");
Logger.log(currentTeacher[2]);
Logger.log(currentTeachers);
// 這週有clerk,看是否是第一週
// 這梯有clerk,且本週為要上課的日子gs_Stu_data
const currentClerksNamesArray = gs_Stu_data.filter(function(item, index, array){
//Logger.log(item[0]);
return item[0] == currentTeacher[2]
});
const currentClerksNames = Array.from(currentClerksNamesArray, item => item[2]);
Logger.log("today group names");
Logger.log(currentClerksNamesArray);
Logger.log(currentClerksNames);
// ======================== 修改問券的資料,根據今天之clerk名單 ==============
// 讀取google drive中事先建立的google form
const form = FormApp.openById(form_id) ;
// 讀取這個google form的問題
const items = form.getItems();
// 一個個確認這問券中的問題
items.forEach((item)=>{
if(item.getIndex() == 0){
// 假如是這問券的第一個問題
const nameItem = item.asMultipleChoiceItem();
// 將這梯的醫學生名字放進去問券第一題的選項之中
nameItem.setChoiceValues(currentClerksNames);
Logger.log(currentClerksNames,'名字放入問券!');
}
if(item.getIndex() == 1){
// 假如是這問券的第二個問題
const nameItem = item.asMultipleChoiceItem();
// 將這梯的日期放到問券之中
nameItem.setChoiceValues([today.toDateString()]);
Logger.log([today.toDateString()],'日期放入問券!');
}
})
// 這梯有一學生
//讀取email範本:有clerk學生的版本
//const form = FormApp.openById(form_id) ;
const url = 'https://docs.google.com/document/d/'+docmd_id+'/export?format=html';
let html = UrlFetchApp.fetch(url, param);
html = html.getContentText();
// 將信件放入老師名稱
html = html.replace(/#teacher/g, currentTeacher[3]);
// 將信件放入clerk名稱
html = html.replace(/#name/g, currentClerksNames);
// 將信件放入google form的連結
html = html.replace(/#link/g, form.getPublishedUrl());
// 將信件放入這梯clerk的名字
html = html.replace(/#time/g, currentTeacher[2]);
// 信件的主旨
const subject = 2022 + ' 第'+currentTeacher[2]+'組'+' 檢驗醫學部-實診暨臨診-案例討論課當日評核';
const prop = {
htmlBody:html,
cc:'testemail@gmail.com'
};
// 寄信
GmailApp.sendEmail(currentTeacher[4], subject,'',prop);
}
}