10分でできる ~ Googleカレンダー[ 自社版 ]を作ってみよう ~ [ Google Apps Script ]


なんか公式のやり方があるっぽいのでメモがてら書いておく。

https://cloud.google.com/blog/products/g-suite/g-suite-pro-tip-how-to-automatically-add-a-schedule-from-google-sheets-into-calendar

# Google Apps Script [GAS] ガチ勢3日目
Google Apps Scriptすげえ！楽しい！ってなって3日目です。
このScript便利だよ、自社のJavaScript書けるエンジニアさんの大切な時間を奪えば~~なんだって~~いろいろできるよって話をしていきます。

##内容
* 完成品の確認
* コピペ編
* コード編

以上3つで構成しようと思っています。

# 完成品の確認
マイカレンダー >> [ ◯◯株式会社 ] これを作ります。
<img width="1293" alt="スクリーンショット 2017-03-05 16.40.22.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/8de4c6f7-a758-f382-75fe-ce61780f7437.jpeg">
## POINT
Googleの[ 日本の祝日 ]を元として、自社だけの[ 創立記念日 ]とか[ ~~△休日出勤~~ ]
とか追加したカレンダーが作れます。
作るだけならコピペで10分くらいで作れるかと思います。
一度このスクリプトに慣れれば[声優さんイベントカレンダー]とか[アイドルライブカレンダー]とか[◯◯イベントカレンダー]とかをすぐ作れるようになれます。私、某J系アイドルのコンサート日程をGoogleマイカレンダーに登録しているんですけど、普段は非表示(今日は◯でライブか…いきたい！！ってならないように。)必要なときに表示にしています。

## 使用するサイト・技術など
* Google Apps(スプレッドシート、カレンダー)を使うので、Googleアカウント
* Google Apps Script( ≒ JavaScript)

## カレンダーを作成した後の発展先
後でQiita公開しようと思ってる記事
> 自社の営業日だけSlackに投稿したりするボット作成
これに似たのさっき公開しました[最終営業日に月末の報告しっかりしろよってSlackに投げるやつ](http://qiita.com/ykhirao/items/782e20ab0465533c48f6)

```省略しまくってます.js
CalendarApp.getCalendarById();
holidays = cal.getEvents();
return holidays.length != 0;
```
自社カレンダーがあるとこれくらいのコード量で、[ 自社が休みかどうか ]の営業日判定がすごくやりやすくなります。
詳しくはこちら[GoogleAppsScriptで日本の祝日を取得する](http://qiita.com/kamatama_41/items/be40e05524530920a9d9)

> スクレイピングしてせっせとカレンダー作りまくる

先程の[ [◯◯株式会社](https://calendar.google.com/calendar/embed?src=2mjt9uiftvsjtqrrt2tdtq89do%40group.calendar.google.com&ctz=Asia/Tokyo)のカレンダー ]
<img width="862" alt="スクリーンショット 2017-03-05 17.47.10.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/3b866b96-d458-4040-42dc-4ea806275ce9.jpeg">

実は一般公開も簡単で、趣味のイベント情報の管理人にもすぐなれます。画像右下の[ + Google Calendar ]からカレンダーの追加もすぐできます。趣味友のためにイベント情報ちょっとスクレイピングしてくるわ(どやっ)ってしてくれるといいですね。

# コピペ編

## スプレッドシート作成
[Google Drive](https://drive.google.com/drive/my-drive)からスプレッドシートを作成してください。
<img width="1145" alt="スクリーンショット 2017-03-05 17.54.17.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/cacc47b2-c845-822b-19ec-bad7bbb8ee1c.jpeg">

## 表のスタイルをあわせる

| 1 | 2 | 3 | 4 | 5 |
|:-:|:-:|:-:|:-:|:-:|
| Googleカレンダーからの取得 |  | 手動追加 |  | カレンダーの名前 |
| 日付  |  タイトル | 日付  |  タイトル | ◯◯株式会社  |
まずこちらをコピーして、[ 値のみ貼り付け ]をして、図のような形式にしてください。
<img width="606" alt="スクリーンショット 2017-03-05 18.01.38.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/e705475b-2f77-187c-07d1-2fe5eb68c89b.jpeg">

## スクリプトエディタを開く
ツール >> スクリプトエディタ 
こちらをクリック。
<img width="602" alt="スクリーンショット 2017-03-05 18.04.38.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/6c4f950a-b088-9bb1-261f-fcb1c7350d41.jpeg">

## スクリプトエディタにコードを貼り付ける
<img width="784" alt="スクリーンショット 2017-03-05 18.08.15.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/c7dd83d1-6299-90b8-c372-fba586be855b.jpeg">
1. 左上の名前変更
1. コード.gsに以下のコードをコピペ

```js
function createGoogleCalendar(){
  var title, date;
  
  // カレンダー名の取得
  var sheet = SpreadsheetApp.getActiveSheet();
  var calendarName = sheet.getRange(2, 5).getValue();
  
  // カレンダーの作成と、オブジェクトcalの生成
  var cal = CalendarApp.createCalendar(calendarName);
  
  // Google[日本の祝日]の全日イベントの追加
  for (var i = 3; i <= sheet.getLastRow(); i++) {
    title = sheet.getRange(i, 2).getValue();
    date = sheet.getRange(i, 1).getValue();
　　　　　　　　if (title=="" && date == "") break;
    cal.createAllDayEvent(title, date);
  }
  
  // [手動追加]の全日イベントの追加
  for (var i = 3; i <= sheet.getLastRow(); i++) {
    title = sheet.getRange(i, 4).getValue();
    date = sheet.getRange(i, 3).getValue();
    if (title=="" && date == "") break;
    cal.createAllDayEvent(title, date);
  }  
}

// Google[日本の祝日]の全日イベントからスプレッドシート作成
function createByGoogleHoliday() {

  var start = new Date(2017, 0, 1, 0, 0, 0, 0);
  var end = new Date(2019, 0, 1, 0, 0, 0, 0);

  var sheet = SpreadsheetApp.getActiveSheet();
  var holidays = getHoliday(start, end);

  var startRow = 3;

  var range = sheet.getRange(startRow, 1, holidays.length, holidays[0].length);
  range.setValues(holidays);
  range.sort([{column: 1, ascending: true}]); // 昇順ソート
  
}

/**
 * startDate〜endDateまでの祝日をgoogleカレンダーの「日本の祝日」から取得
 * [日付,祝日名]の多次元配列にした上で返す
 */
function getHoliday(start, end) {
  var cal = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");

  var holidays = cal.getEvents(start, end);
  var values = [];

  for(var i = 0; i < holidays.length; i++) {
    values[i] = [holidays[i].getStartTime(), holidays[i].getTitle()];
  }
  
  /**
   * 追加と削除機能あり
   * values.push([new Date("2017/1/4"), "俺の日"]);
   * values = deleteHoliday(values, "2017/1/4");
   */
  
  return values;
}

function deleteHoliday(values, date){
  var target = new Date(date);
  values.some(function(v, i){
    if (Date.parse(v[0]) == Date.parse(target)) values.splice(i,1);
  });
  return values;
}

```

今回は自動化はしていませんが、コードはこちらの記事[ [google spread sheetに自動更新される日本の祝日シートを作る](http://qiita.com/ik-fib/items/6c35640954c2b04a9287) \]から3割くらいお借りしています。月に一度の自動更新で正確に祝日カレンダーをGoogleから同期させられる手法なのでぜひ読んでみてください。追加点は`コード内でイベントの追加と削除機能を実装`と`ソート機能`と`スプレッドシートからのカレンダー作成`です。

## コードの実行
実行 >> `createByGoogleHoliday`

<img width="711" alt="スクリーンショット 2017-03-05 18.21.25.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/daaa1aec-0f01-fe75-66d4-35d93cb8c8ee.jpeg">
このスクリプトからスプレッドシートなどのGoogle Appsへのアクセス許可を聞かれるのでOKしてください。

## スプレッドシートに戻ってみる
こんな感じでGoogleカレンダーから取得できてると正解

<img width="701" alt="スクリーンショット 2017-03-05 18.24.17.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/01443978-82f9-fac8-c957-de216d55e5b9.jpeg">

## 手作業入力
* 手動追記 >> 日付とタイトル
    * 一応どちらも入力してください
    * 空白行があるとそこで終了
* カレンダーの名前 >> 私の会社
    * 私の会社の部分がカレンダーのタイトルになります。

<img width="670" alt="スクリーンショット 2017-03-05 18.28.02.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/ec627db7-a43f-1f7a-5dd8-2ff1ee601271.jpeg">

## Script Edit

ツール >> スクリプトエディタ
からスクリプトエディタに戻って
実行 >> createGoogleCalendar

<img width="699" alt="スクリーンショット 2017-03-05 18.32.53.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/59eeb8d2-3e92-6ce1-c423-f9a1efb24437.jpeg">

作成に時間はかかりますが、カレンダーができました！！！！

## Google Calendarを見る
<img width="1369" alt="スクリーンショット 2017-03-05 18.36.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/112929/9486696a-44c9-d2b5-b9cb-716612a0ecd3.png">

これでカレンダーができました！！無事[ △休日出勤 ]も春分の日も出力されてるので作成ができました。
共有とか削除はカレンダー設定からいろいろいじれます。

## 蛇足ボタンの追加
挿入 >> 図形描画
<img width="1213" alt="スクリーンショット 2017-03-05 18.41.19.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/7ef59bc6-b723-e8ea-75a1-4d73a902d6b1.jpeg">

ここからボタン作成ができます。

図形右クリック >> 右上のボタン >> スクリプトの割り当て
<img width="865" alt="スクリーンショット 2017-03-05 18.42.43.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/38911a4e-7615-7330-74db-51e720be65c9.jpeg">

ここで先程つくったスクリプトを割り当てられます。かっこいらない系。
[ createGoogleCalendar ]　
[ createByGoogleHoliday ]

<img width="869" alt="スクリーンショット 2017-03-05 18.45.05.jpg" src="https://qiita-image-store.s3.amazonaws.com/0/112929/338d08b0-a3ac-e105-6ca0-c0ddfa95aab6.jpeg">

こうするとデータの更新とカレンダーの作成がワンクリックでできるようになります。

## 以上、10分でできる専用カレンダーの作成方法でした。

次はこの自社カレンダーを使って、営業日だけ動くSlackボットの作成がしたい~
あ、Google Apps Scriptだと、コード10行くらいで定期実行してくれるbotが簡単につくれます。コード編終わって落ち着いたら記事書きます。

***
# コード編
簡単な解説とかいれていきます。私がGAS知ってからチュートリアル的なサンプル作りつつ、1日くらいで作れたので、ゆっくり丁寧にコードを読みといていけばすぐにGAS書けるようになると思います。

## Googleカレンダーから日本の祝日の取得方法

```コード.js

// Google[日本の祝日]の全日イベントからスプレッドシート作成
function createByGoogleHoliday() {

  var start = new Date(2017, 0, 1, 0, 0, 0, 0);
  var end = new Date(2019, 0, 1, 0, 0, 0, 0);

  var sheet = SpreadsheetApp.getActiveSheet();
  var holidays = getHoliday(start, end);

  var startRow = 3;

  var range = sheet.getRange(startRow, 1, holidays.length, holidays[0].length);
  range.setValues(holidays);
  range.sort([{column: 1, ascending: true}]); // 昇順ソート
  
}

/**
 * startDate〜endDateまでの祝日をgoogleカレンダーの「日本の祝日」から取得
 * [日付,祝日名]の多次元配列にした上で返す
 */
function getHoliday(start, end) {
  var cal = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");

  var holidays = cal.getEvents(start, end);
  var values = [];

  for(var i = 0; i < holidays.length; i++) {
    values[i] = [holidays[i].getStartTime(), holidays[i].getTitle()];
  }
  
  /**
   * 追加と削除機能あり
   * values.push([new Date("2017/1/4"), "俺の日"]);
   * values = deleteHoliday(values, "2017/1/4");
   */
  
  return values;
}

function deleteHoliday(values, date){
  var target = new Date(date);
  values.some(function(v, i){
    if (Date.parse(v[0]) == Date.parse(target)) values.splice(i,1);
  });
  return values;
}

```

### `function createByGoogleHoliday()`

```js
var start = new Date(2017, 0, 1, 0, 0, 0, 0);
var end = new Date(2019, 0, 1, 0, 0, 0, 0);
```

Googleの日本の祝日だと2年分くらいしか取得することができないので、この期間で設定しています。
またミリ秒までセットしている理由は、`deleteHoliday()`の部分でミリ秒まで同じなら削除をするみたいな判定しているからです。(ここ書き方わかんなくて適当に実装しました。アドバイスいただけたらうれしいです。)

```js
var sheet = SpreadsheetApp.getActiveSheet();
```

こちらスプレッドシート上のGASから操作しているので簡単にシートのオブジェクトがとれます。
別のGASからでも`SpreadsheetApp.openByUrl(SHEET_URL)`とかを使えばオブジェクトを取ってこれます。もしくはopenByIdとか。

```js
var holidays = getHoliday(start, end);
```
後で書きますがgetHoliday関数で、ある期間の祝日を、日付とタイトルの配列として返します。`[[日付, タイトル], [日付2, タイトル2]]`こんな感じ。

```js
var startRow = 3;
```

今回は3行目からデータを挿入していくので。

```js
var range = sheet.getRange(startRow, 1, holidays.length, holidays[0].length);
range.setValues(holidays);
range.sort([{column: 1, ascending: true}]); // 昇順ソート
```

まず範囲オブジェクトを作ります。配列の行列と範囲オブジェクトを対応させて、下の表の3行目(日付、タイトルの下)から、値をrange.setValues(holidays)で一気に入れます。

| 1 | 2 |
|:-:|:-:|
| Googleからの取得  |  |
| 日付  | タイトル  |
| day1  |  title1 |
| day2  |  title2 |
| day3  |  title3 |
| day4  |  title4 |
| day5  |  title5 |
| day6  |  title6 |


そして`range.sort([{column: 1, ascending: true}])`でソートしてます。(実は削除も追加もしなかったらもともと昇順です)
column: で何列目を基準にするか
ascending: trueが昇順、falseが降順

### function getHoliday(start, end) {}

```js
function getHoliday(start, end) {
  var cal = CalendarApp.getCalendarById("ja.japanese#holiday@group.v.calendar.google.com");

  var holidays = cal.getEvents(start, end);
  var values = [];

  for(var i = 0; i < holidays.length; i++) {
    values[i] = [holidays[i].getStartTime(), holidays[i].getTitle()];
  }

  /**
   * 追加と削除機能あり
   * values.push([new Date("2017/1/4"), "俺の日"]);
   * values = deleteHoliday(values, "2017/1/4");
   */

  return values;
}
```

* (start, end)で範囲指定する
* CalendarApp.getCalendarByIdで[日本の祝日]を取得
* cal.getEvents(start, end)で[ 日本の祝日 ]のイベント取得
    * 単数データにすると
    * .getStartTime()で始まる時間
    * .getTitle()でタイトルを取得できるようになる
* ここで追加したい時は配列にプッシュするだけ
* deleteHolidayは関数として実装


### function deleteHoliday(values, date){}

```js
function deleteHoliday(values, date){
  var target = new Date(date);
  values.some(function(v, i){
    if (Date.parse(v[0]) == Date.parse(target)) values.splice(i,1);
  });
  return values;
}
```

詳細はJavaScriptのsomeを。
targetに消したい日のDateオブジェクトを。
valuesには[ 日本の祝日 ]データが入っているので、それをすべて検索
[ Dateオブジェクト, title ]という形式なのでv[0]
Date.parse()でDate型を変換して比較できるようにしている(もっと良い書き方あるはず)

### function createGoogleCalendar(){}

```function.js
function createGoogleCalendar(){
  var title, date;

  // カレンダー名の取得
  var sheet = SpreadsheetApp.getActiveSheet();
  var calendarName = sheet.getRange(2, 5).getValue();

  // カレンダーの作成と、オブジェクトcalの生成
  var cal = CalendarApp.createCalendar(calendarName);

  // Google[日本の祝日]の全日イベントの追加
  for (var i = 3; i <= sheet.getLastRow(); i++) {
    title = sheet.getRange(i, 2).getValue();
    date = sheet.getRange(i, 1).getValue();
　　if (title=="" && date == "") break;
    cal.createAllDayEvent(title, date);
  }

  // [手動追加]の全日イベントの追加
  for (var i = 3; i <= sheet.getLastRow(); i++) {
    title = sheet.getRange(i, 4).getValue();
    date = sheet.getRange(i, 3).getValue();
    if (title=="" && date == "") break;
    cal.createAllDayEvent(title, date);
  }  
}
```

まずはこちら。

```js
var sheet = SpreadsheetApp.getActiveSheet();
var calendarName = sheet.getRange(2, 5).getValue();
var cal = CalendarApp.createCalendar(calendarName);
```

現在のアクティブシートからシートオブジェクトを作成して
カレンダー名を2行5列のとこから取得して、カレンダーを作成

```js
  for (var i = 3; i <= sheet.getLastRow(); i++) {
    title = sheet.getRange(i, 2).getValue();
    date = sheet.getRange(i, 1).getValue();
    if (title=="" && date == "") break;
    cal.createAllDayEvent(title, date);
  }
```
3行目からシートの最終行まで繰り返す処理
タイトルを2列目から
日付を1列目から所得して
もしどちらも空白ならfor文終了
タイトルと日付を使って全日イベントを作成。

# Google Apps Script

GASはいかがでしたか。かなり便利でいろんな機能が使えそうだと思ってます。

シンタックスハイライト追加プルリクありがとうございます。追加しました！！


.