[GAS]平日は毎朝スプレッドシートのテンプレからコピーを作成する

## はじめに
最近GAS(Google Apps Script)をさわりはじめました。
とある Google スプレッドシートで、その日使うための新しいシートを、テンプレ用シートから複製する、というのを毎日手動でやっていたのでGASで自動化してみよう、という経緯です。まあわりとよくありそうな話です。

そんなに複雑なことはしていませんが、これを実現させるだけでも勉強になったので整理も兼ねて記事にしてみました。

## 仕様
- 同一スプレッドシート内にテンプレート用のシートがある
- 毎朝テンプレート用シートを複製し、シート名をその日の日付・曜日に変更する
- 土日は必要ない

## コード
https://github.com/JPSERN/GAS/blob/master/scripts/AutoSheetCreator/main.gs

今回はコンテナバインドスクリプトです。
該当のスプレッドシートから｢ツール｣→｢スクリプトエディタ｣を開いて、上記のスクリプトを丸っと貼り付ければ準備完了です。
公式のクイックスタートとおなじフローですね。
https://developers.google.com/apps-script/quickstart/custom-functions

あとは、`setTrigger()` を実行するトリガーを設定すれば日々の動作が開始されます。
トリガーの設定内容は [後述](https://qiita.com/tommy_aka_jps/items/e726df27a9a807e828bb#%E3%83%88%E3%83%AA%E3%82%AC%E3%83%BC%E4%BD%9C%E6%88%90%E7%94%A8%E9%96%A2%E6%95%B0) をご参考ください。


## 簡易解説

### メインの関数
```javascript
function run() {
  var book = SpreadsheetApp.getActiveSpreadsheet();
  var tplSheet = book.getSheetByName("※テンプレ用シートの名前");
  var sheetName = generateSheetName();

  if(tpl == null) {
    return;
  }

  if(book.getSheetByName(sheetName) == null) {
    //テンプレ用シートを選択する
    book.setActiveSheet(tplSheet);

    //選択したシートを一番左に移動 
    book.moveActiveSheet(0); 

    //さらに一番左にテンプレ用シートを元に作ったコピーを挿入
    book.insertSheet(sheetName, 0, {template: tplSheet}); 
  }
}
```

テンプレからのシート作成と移動を行っています。
関数の名前は `run` だと抽象的すぎたのでもうちょっと具体的なものにすべきだったと反省。名前って大事。
ちなみにテンプレ用シートを都度移動しているのはテンプレを編集したいことも多々あるので目に見えるところに置いておきたいからです。なので特に必要なければテンプレシートの移動はいらないとは思います。


### シート名生成用関数

```javascript
function generateSheetName() {
  var week = ["日","月","火","水","木","金","土"];
  var date = new Date();
  return Utilities.formatDate(date, "Asia/Tokyo", "M/d ") + week[date.getDay()];
}
```

`M/d 曜日` という文字列を返すだけの関数です。
JavaScriptで日付フォーマットの処理をするのは少々面倒なんですが、
世界のGoogle先生は便利なものを用意されていたのでこちらを使っています。
https://developers.google.com/apps-script/reference/utilities/utilities#formatDate(Date,String,String)

### 休日判定用関数

```javascript
function _isHoliday() {
  var today = new Date();

  //土日なら true
  var weekInt = today.getDay();
  if(weekInt <= 0 || 6 <= weekInt){
    return true;
  }

  //国民の祝日を丸ごと取得
  if(!_isHoliday.cacheCalendar) {
    _isHoliday.cacheCalendar = CalendarApp.getCalendarById(
      "ja.japanese#holiday@group.v.calendar.google.com"
    );
  }

  //今日が国民の祝日に該当していたら true
  var todayEvents = _isHoliday.cacheCalendar.getEventsForDay(today);
  if(todayEvents.length > 0) {
    return true;
  }

  return false;
}
```

こちらのコードをほぼそのまま使わせていただきました。感謝。
https://qiita.com/jz4o/items/d4e978f9085129155ca6#%E4%BC%91%E6%97%A5%E7%A5%9D%E6%97%A5%E3%82%92%E5%88%A4%E5%AE%9A%E3%81%99%E3%82%8B%E9%96%A2%E6%95%B0

国民の祝日が変わることはそうそうないので、カレンダーをキャッシュするようにだけ変えています。


### トリガー作成用関数

```javascript
function setTrigger() {
  if (_isHoliday()) return;
  deleteTriggers("run"); //実行済みのトリガーを削除
  var setTime = new Date();
  setTime.setHours(7);
  setTime.setMinutes(0);
  ScriptApp.newTrigger("run").timeBased().at(setTime).create();
}

function deleteTriggers(target) {
  var triggers = ScriptApp.getProjectTriggers();
  triggers.forEach(function(trg) {
    if (trg.getHandlerFunction() == target) {
      ScriptApp.deleteTrigger(trg);
    }
  });
}
```

単純に毎日朝7時に動かすだけならスクリプトは必要なかったのですが、平日のみという制限をかけたかったので、`setTrigger` は毎日動かすようにして、土日なら `setTrigger()` は実行されても何もしない、というようにしています。

こんなかんじです。
![settrigger.png](https://qiita-image-store.s3.amazonaws.com/0/70638/00a24e66-5907-d249-38e8-db37cf43ba9f.png)




なお、スクリプトで生成されたトリガーはずっと残っているので、`setTrigger()` 実行時に古いトリガーの掃除を行うようにしています。

トリガーの作成と導入はこちらを参考にさせていただきました。
https://tonari-it.com/gas-trigger-set/


## 感想
環境構築の必要もなく手軽にスクリプトを書いて、面倒な作業をひとつ減らすことができたのがいいですね。
今回はスプレッドシートに紐づけたコンテナバインドスクリプトですが、次はスタンドアロンスクリプトを作ってみようかなと思います。GithubのAPIを使って何かやってみようかな。

おしまい