Google Apps Scrpitでスライドを自動生成


GoogleAnalyticsの[スプレッドシート出力機能](https://webtan.impress.co.jp/e/2015/12/08/21570)を使って作られたSpreadsheetや、作業タスクと進捗を記載したSpreadsheetを報告用にスライドに自動生成できないかと思ったらできたので簡単な例を用いて方法をまとめておきます。

## Google公式のマニュアル
1. [Google Apps Script - Slides Service](https://developers.google.com/apps-script/reference/slides/)
2. [Google Slides API](https://developers.google.com/slides/reference/rest/)

※基本的に２個目は参考程度で１個目のサイトしか使わなかったです。

## ゴール
スプレッドシートの内容を取得してスライドを特定のディレクトリ配下に新規生成する。

## 前準備
以下のファイルを作ります。

- スライドの保存先フォルダ 
- テンプレートのスライド
- 元ネタになるスライド
- GoogleAppsScript

以下のような感じです。
<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/70609522-7799-57ec-9f11-287e35eca610.png" width="450px">

### 元ネタとなるスプレッドシートの情報
スプレッドシートの情報は今回以下のようなものを用意します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/948a64d2-302d-3592-6137-69a42b9de18a.png" width="450px">

### テンプレート
スプレッドシートから報告用スライドを自動生成で埋め込む際のテンプレートファイルは以下のようなものを用意します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/7a9aad7b-f98e-a558-413e-c5dbcfdd3775.png" width="450px">
<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/80c72972-ebca-9a0b-6223-10ffde9e2561.png" width="450px">



## スクリプトのプロパティの設定
スクリプトファイルのプロパティを設定していきます。  
「ファイル＞プロジェクト」で
<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/f9742835-1298-c984-7d94-cb766664c422.png" width="300px">

<img src="https://qiita-image-store.s3.amazonaws.com/0/26911/d72199f6-a4dd-0db3-9b4a-f9492bfc3a8e.png" width="450px">

定義したプロパティは以下の通りです。

* 保存先のディレクトリのID
* 引用する元のスプレッドシートのID
* テンプレートのスライドのID

## スクリプトの作成
作成するスクリプトは以下のように分けます。メインの「コード.gs」（デフォルト生成されてるもの）とは別にスライドの１ページごとにスクリプトが用意されるイメージが整理しやすいかなと。

* コード.gs
* CreateTitlePage.gs
* CreateFirstPage.gs

### コード.gs
```javascript:コード.gs
function myFunction() {
  //プロパティの値を取得
  var saveDirId = PropertiesService.getScriptProperties().getProperty("SAVE_DIRECTORY_ID");
  var spreadSheetId = PropertiesService.getScriptProperties().getProperty("TARGET_SPREADSHEET_ID");
  var templateSlideId = PropertiesService.getScriptProperties().getProperty("TEMPLATE_SLIDE_ID");
  
  //テンプレートのスライドを保存先のフォルダにコピー
  var slideId = copySlideFile(templateSlideId,saveDirId);
  //タイトルページの編集
  createTitlePage(slideId);
  //果物の表とグラフページを編集
  createFirstPage(slideId,spreadSheetId);
}

/***
 * スライドを別のフォルダにコピー
 * @param {String} baseSlideId コピー元のスライドID
 * @param {String} targetDirId 保存先のフォルダのID
 * @return {String} コピー先のスライドID
 */
function copySlideFile(baseSlideId,targetDirId){
  //コピー先フォルダ取得
  var saveDir = DriveApp.getFolderById(targetDirId);
  
  //コピー元ファイル取得
  var baseFile = DriveApp.getFileById(baseSlideId);
  
  //ファイルコピー
  var copyFile = baseFile.makeCopy("【作成ファイル】"+getDate(),saveDir);
  
  //コピー先のファイルのIDを返却
  return copyFile.getId();
}

/***
 * 日付取得
 * @return {String} 日付（yyyy/MM/dd）
 */
function getDate(){
  //今日日付取得
  var today = new Date();
  //フォーマット指定して返却
  return Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy/MM/dd');
}
```

* `PropertiesService.getScriptProperties().getProperty()`で先程定義したプロパティを呼び出しています。
* `copySlideFile(baseSlideId,targetDirId)`でテンプレートのスライドファイルを今回作成するスライド用に複製しています。
	* `DriveApp.getFolderById(id)`でフォルダを取得
	* `DriveApp.getFileById(id)`でファイルを取得
	* 取得したファイルに対して`makeCopy(name, destination)`で名前指定して取得したフォルダに配置

### CreateTitlePage.gs
```javascript:CreateTitlePage.gs
/**
 * タイトルページの作成
 * @param {String} slideId スライドID
 */
function createTitlePage(slideId) {
  //置換対象文字列
  const titileKey = "{TITLE}";
  const subTitleKey = "{SUBTITLE}";
  
  //タイトルページを取得
  var titleSlide = SlidesApp.openById(slideId).getSlides()[0];
  
  //タイトルの置換
  titleSlide.replaceAllText(titileKey, "果物の値段");
  //サブタイトルの置換
  titleSlide.replaceAllText(subTitleKey, getDate()+"作成");  
}
```
* `createTitlePage(slideId)`は先程の複製されたスライドのIDを受け取ります
* `SlidesApp.openById(slideId).getSlides()[0];`でスライドの０ページ目を取得
* `replaceAllText`でスライドページ内にある文字列の置換を実施

### CreateFirstPage.gs
```javascript:CreateFirstPage.gs
/**
 * １ページ目の作成
 * @param {String} slideId スライドID
 * @param {String} spreadSheetId スプレッドシートID
 */
function createFirstPage(slideId,spreadSheetId) {
  //タイトル置換対象文字
  const titleKey = "{PAGETITLE}";
  
  //スプレッドシート取得
  var spreadsheet = SpreadsheetApp.openById(spreadSheetId);
  //ブラウザ利用状況シート取得
  var sheet = spreadsheet.getSheetByName('シート1');
  //スプレッドシートからグラフの一覧取得
  var charts  = sheet.getCharts();

  //1ページ目を取得
  var titleSlide = SlidesApp.openById(slideId).getSlides()[1];
  
  //タイトルの置換
  titleSlide.replaceAllText(titleKey, "果物の値段の表");
  
  //スライドに貼るグラフのサイズ指定
  var chartPosition = {left: 365, top: 125};
  var chartSize = {width: 350, height: 250};
  //スライドにグラフ貼り付け
  titleSlide.insertSheetsChart(
    charts[0],
    chartPosition.left,
    chartPosition.top,
    chartSize.width,
    chartSize.height);
  
  //スプレッドから対象エリア取得
  var range = sheet.getRange('A1:D4');
  var values = range.getDisplayValues();
  var rows = values.length;
  var columns = values[0].length;
  //スプレッドで定義された背景色取得
  var backgrounds = range.getBackgrounds();
  //スプレッドで定義された文字のボールド取得
  var fontWeights = range.getFontWeights();

  //スライドの表を取得
  var table = titleSlide.getPageElements()[1].asTable();
  
  for(var r = 0; r < rows; r++){
    if(values[r][1] != ""){
      //行の追加
      table.appendRow();
    }
    for(var c = 0; c < columns; c++){
      //セルの取得
      var cell = table.getCell(r,c);
      //値のセット
      cell.getText().setText(values[r][c]);
      //セルにスプレッドの背景色を引用
      cell.getFill().setSolidFill(backgrounds[r][c]);
      //セルにスプレッドの文字太字定義を引用
      cell.getText().getTextStyle().setBold(fontWeights[r][c] == "bold" ? true : false);
    }
  }
  //目安の列を削除
  table.getRow(table.getNumRows()-1).remove();
  table.getRow(table.getNumRows()-1).remove();
}
```
* 図の挿入を先に行ってます
	* `SpreadsheetApp.openById(id)`でスプレッドシートを取得
	* `getSheetByName(name)`でシートを指定
	* `getCharts()`でシート内にある図を取得
	* 取得した図を`insertSheetsChart(sourceChart, left, top, width, height)`で位置指定してスライドへ貼り付け
* 表の挿入
	* `getPageElements()[1].asTable()`でスライドに目安で配置していた表を取得
	* `appendRow()`で行を追加
	* `getCell(rowIndex, columnIndex)`で表の位置指定して取得
	* `getText().setText(text)`で値をセット
	* `remove()`で目安用の列は削除

※スプレッドシートの値取得などは他の方がよくまとめてるものがあるので割愛します。

# やりたかったけどできなかったこと
スライド中の表の操作は以下あたりを見ていけばなんとなくある程度装飾など含めてできるんですが、セル同士の結合がどうしてもできなかったです。スプレッドシートであればMerge関数でできるみたいなんですが、スライドの開発は遅れてるのか知らないですが用意されてないみたいです。[このページ](https://developers.google.com/apps-script/)に新しい機能は追加され次第記載されるようなので要チェックです。

* [Class Table](https://developers.google.com/apps-script/reference/slides/table)
* [Class TableCell](https://developers.google.com/apps-script/reference/slides/table-cell)