要怎麼快速搜集在 Slides 中出現的特定文字,並在 Sheet 上標示其出現的頁碼?今天的結果預計是這樣——
今天的主題情在在於,現代溝通很常會用 PPT。很多時候會需要來回傳 PPT,而為了傳達說「這一頁我想改什麼」,我們會使用「註解」如下圖(以 D21 生成的文為例,在拿給主管後收到了回饋),大大的被寫上了「距離太遠」。
傳達上的另一種方式,是「另外開一個新的文字方塊」來溝通,容易看到被大大的貼上了「主標換行」。
那當我們在「檢查簡報時」會有個重複的行為,就是每次要改簡報,我們就要從頭到尾的簡報都看過是不是還有「待辦事項」,有時還是難免漏掉一些小部分。有沒有什麼方式,可以幫助我們自動地搜集所有的 Comment 或標示上「TO-DO」的文字方塊,並整理成 Google Sheet 呢?而對應的關鍵問題是——
好,那就讓我們開始吧!
TO-DO
預計會變成這樣——
今天我們用 Google Sheet 作為連結 GAS 的管道,讓我們借用 D14 的影片。
一樣第一次按下 GAS 中的「執行」會有「存取驗證」需要大家按一下。這邊仍是借用一下 D2 的影片。
接著,我們抓出要核對的 Presentation ID,這邊我們用 D21 的「自動化爲鐵人賽的每一篇貼文生封面圖」 的結果 Slides 為例。抓出 ID 的示範如下——
並將這 ID 到我們的 GAS 當中設定為一個參數。
var target_slide_ID = "your_pres_ID_here"
有夥伴反應這邊不確定怎麼做,就一起準備了影片——
接著,就是讀取簡報了,直接作為 Step 2
這邊我們就用 SlidesApp.openById()
搭配 getSlides()
來取得所有的投影片。接著再用一個 for 迴圈 Print 出所有檔案們。
function listAllTodos(){
// READ SLIDES
let pres = SlidesApp.openById(target_slide_ID);
let slides = pres.getSlides();
Logger.log("The number of slides are "+ slides.length)
for(let i = 0; i<slides.length; i++){
Logger.log("Read Slides No."+ i)
}
}
跑起來長這樣——
這次我們要讀的特定文字是「To-Do
」,邏輯是,搜尋每一張投影片的元素(Page Elements)確認,如果是屬於含有文字方塊的(Shape),那就確認他呈現的文字中,前五個是不是 "TO-DO"
,是的話就抓出來並寫上頁數。程式碼結合前面步驟就會變成——
function listAllTodos(){
// READ SLIDES
let pres = SlidesApp.openById(target_slide_ID);
let slides = pres.getSlides();
// LIST TARGET TEXT and page
for(let i = 0; i<slides.length; i++){
let slide = slides[i]
let page_elements = slide.getPageElements();
for(page_element of page_elements){
if(page_element.getPageElementType() == SlidesApp.PageElementType.SHAPE){
let text = page_element.asShape().getText().asRenderedString();
if (text.slice(0,5)=="TO-DO"){
Logger.log("Slide No."+i+ " has a to-do:"+ text)
}
}
}
}
}
讓我們來看看會跑怎麼樣——
OK,那卻時有讀到我們在 Slide 第 12 與 13 頁中的 TO-DO
以及其文字。接著就要寫入 Google Sheet 中了。
出現我們的老朋友 writeData()
拉!程式碼讓大家複習——
function writeData(data){
let sheet = SpreadsheetApp.getActiveSheet();
let starting_row = 2;
let starting_col = 1;
let num_row = data.length;
let num_col = data[0].length;
let range = sheet.getRange(starting_row, starting_col, num_row, num_col);
range.setValues(data);
}
再結合並改寫我們的前面的部分
function listAndRecordAllTodos(){
// READ SLIDES
let pres = SlidesApp.openById(target_slide_ID);
let slides = pres.getSlides();
let result=[];
let update_time = new Date().toLocaleDateString();
// LIST TARGET TEXT and page
for(let i = 0; i<slides.length; i++){
let slide = slides[i]
let page_elements = slide.getPageElements();
for(page_element of page_elements){
if(page_element.getPageElementType() == SlidesApp.PageElementType.SHAPE){
let text = page_element.asShape().getText().asRenderedString();
let starting_text = text.slice(0,5)
if (starting_text=="TO-DO"){
Logger.log("Slide No."+(i+1)+ " has a to-do:"+ text);
result.push([update_time, i+1, text]);
}
}
}
}
writeData(result)
}
讓我們來看看會跑怎麼樣——
好,那看起來有寫入成功!但其實我們還有件事沒做,就是「讀取備註」(Read Comment)
會把「讀取備註」分開寫,是因為它相對比較複雜。目前查到能「讀備註」的 Google 功能並不是現有的,而是舊版 API 中的 Drive.comment
換句話說,要另外設置。設置方式如下——
好,當我們設置完後,我們就可以取得 Comment 了!以下是我們這次 Comment 的資料——
那不囉唆,直接上程式碼——
function readComments(){
let comments = Drive.Comments.list(target_slide_ID)
for(comment of comments.items){
Logger.log("Comment Content is : "+ comment.content)
Logger.log("Comment Anchor is : "+comment.anchor)
Logger.log("Comment Anchor page is : "+comment.anchor['page'])
Logger.log("Comment Anchor page is : "+JSON.parse(comment.anchor)['page'])
}
}
這段程式碼執行起來長這樣——
而輸出的圖放大在這邊——
可以核對到 comment.content
讀到的發現確實是我們要的 Comment 沒錯。但為什麼要對 comment.anchor
另外再取 JSON 呢?這邊說明一下。comment.anchor
的意思是指,「這個 Comment 的錨⚓️」,也就是位在哪張 Slide 當中。但因為它原本 API 會讓我們取得這樣的資訊——
Logger.log(comment.anchor)
// {"type":"shape","uid":1632396166038,"page":"SLIDES_API791698242_104","targets":["SLIDES_API791698242_105"]}
Logger.log(comment.anchor['page'])
// null
明明是 Dict,卻不讓我們用 Dictionary 的取法,這是因為它現在在物件中這一整串不被理解為 Dictionary。需要另外再透過 JSON.parse()
把它變成我們可以讀的 Dict。所以才會用這樣的讀法——
Logger.log(JSON.parse(comment.anchor)['page'])
// SLIDES_API791698242_96
所以最後我們能讀到 Comment 和所在位置了,要怎麼結合起來?這邊我們使用 {}
,也就是 Dictionary,它的好處是「提取」與「確認是否存在」上使用的時間相對其他資料結構檢疫。但因為 Googel Apps Script 通常是小量做,所以相對時間與空間複雜度不用想太多。但對 Dict 不熟者可以參考 MDN 的介紹。
function readAndWriteCommentsWithDict(){
let comments = Drive.Comments.list(target_slide_ID)
let page_id_content={}
let update_time = new Date().toLocaleDateString();
for(comment of comments.items){
let page_content = comment.content;
let page_id = JSON.parse(comment.anchor)['page']
if ((page_id in page_id_content)){
page_id_content[page_id].push(page_content)
}else{
page_id_content[page_id] = [page_content]
}
}
let pres = SlidesApp.openById(target_slide_ID);
let slides = pres.getSlides();
let result = [];
for(let i = 0; i<slides.length; i++){
let slide = slides[i];
let slide_id = slide.getObjectId();
if (slide_id in page_id_content){
result.push([update_time, i+1, page_id_content[slide_id]])
}
}
Logger.log(result)
}
跑起來長這樣——
好,那今天就是我們的結果了。
好,那今天就到這邊。今天我們主要學了:
注意的是,今天第 2 點的「讀 Comment」其實是 Google Drive 中的檔案都可以用,但細節的程式碼會需要再修正就是。
今天進入了 Slide 的最後一天,希望對大家有所幫助。如果還有問題,透過留言之外,也可以到 Facebook Group,想開很久這次鐵人賽才真的開起來,歡迎來當 Founding Member。如果不想錯過可以訂閱按讚小鈴鐺(?),也歡迎留言跟我說你還想知道什麼做法/主題。我們明天見。