iT邦幫忙

2021 iThome 鐵人賽

DAY 23
0
Modern Web

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

D23 - 如何用 Apps Script 自動化地創造與客製 Google Slides?(四)一次抓出所有簡報中的「特定文字」與備註

今天的目標:

要怎麼快速搜集在 Slides 中出現的特定文字,並在 Sheet 上標示其出現的頁碼?今天的結果預計是這樣——

今天的主題情在在於,現代溝通很常會用 PPT。很多時候會需要來回傳 PPT,而為了傳達說「這一頁我想改什麼」,我們會使用「註解」如下圖(以 D21 生成的文為例,在拿給主管後收到了回饋),大大的被寫上了「距離太遠」。

傳達上的另一種方式,是「另外開一個新的文字方塊」來溝通,容易看到被大大的貼上了「主標換行」。

那當我們在「檢查簡報時」會有個重複的行為,就是每次要改簡報,我們就要從頭到尾的簡報都看過是不是還有「待辦事項」,有時還是難免漏掉一些小部分。有沒有什麼方式,可以幫助我們自動地搜集所有的 Comment 或標示上「TO-DO」的文字方塊,並整理成 Google Sheet 呢?而對應的關鍵問題是——

  1. 要怎麼一次性的搜集在 Google Slides 中出現的特定文字/註解,並在 Google Sheet 上標示出其頁碼?

好,那就讓我們開始吧!


Q1 要怎麼一次性的搜集在 Google Slides 中出現的特定文字/註解,並在 Google Sheet 上標示出其頁碼?

輸入 Input

  • Google Sheet 作為 GAS 的入口
  • 一份已經在溝通的 Google Slides 作為目標
    • 上面寫上的溝通項目、要改的部分,都有在最前面寫上 TO-DO

輸出 Output

  • Google Sheet 上的一個表格,標示著「Slides 頁碼」與「待辦事項文字內容」

預計會變成這樣——

Step 1 從 Google Sheet 進入 GAS 並設定指定資料夾 ID

今天我們用 Google Sheet 作為連結 GAS 的管道,讓我們借用 D14 的影片。

一樣第一次按下 GAS 中的「執行」會有「存取驗證」需要大家按一下。這邊仍是借用一下 D2 的影片。

接著,我們抓出要核對的 Presentation ID,這邊我們用 D21 的「自動化爲鐵人賽的每一篇貼文生封面圖」 的結果 Slides 為例。抓出 ID 的示範如下——

並將這 ID 到我們的 GAS 當中設定為一個參數。

var target_slide_ID = "your_pres_ID_here"

有夥伴反應這邊不確定怎麼做,就一起準備了影片——

接著,就是讀取簡報了,直接作為 Step 2

Step 2 用 Slide ID 讀取投影片們

這邊我們就用 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)
  }
}

跑起來長這樣——

Step 3 讀取投影片中的「特定文字」們

這次我們要讀的特定文字是「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 中了。

Step 4 寫入 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)

Step 5 讀取備註 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)
}

跑起來長這樣——

好,那今天就是我們的結果了。

好,那今天就到這邊。今天我們主要學了:

  1. 如何讀取 Google Slides 中的特定文字與所在頁面
  2. 如何讀取 Google Slides 中的 Comments
  3. 如何將上述兩者寫到 Google Sheet 中

注意的是,今天第 2 點的「讀 Comment」其實是 Google Drive 中的檔案都可以用,但細節的程式碼會需要再修正就是。


今天進入了 Slide 的最後一天,希望對大家有所幫助。如果還有問題,透過留言之外,也可以到 Facebook Group,想開很久這次鐵人賽才真的開起來,歡迎來當 Founding Member。如果不想錯過可以訂閱按讚小鈴鐺(?),也歡迎留言跟我說你還想知道什麼做法/主題。我們明天見。


上一篇
D22 - 如何用 Apps Script 自動化地創造與客製 Google Slides?(三)一次看完所有檔案的預覽
下一篇
D24 - 如何用 Apps Script 自動化地創造與客製 Google Sheet?(ㄧ)自動化創造圖表並放到報告中
系列文
整合 Google 服務的燃料——透過 Google Apps Script (GAS) 加速你的工作速度30

尚未有邦友留言

立即登入留言