Googleカレンダーからスプレッドシートにイベントを出力する

# はじめに
仕事で１週間の作業内容を記録したい！という要望を受け作成しました。
Googleカレンダーに登録されているイベントから以下のような表とグラフが作れます。
<img width="600" alt="myFunction.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/4ccfb7f1-7d8d-4967-397e-f95091c800fc.png">

プログラミングが全くわからない人でも作業ができるように丁寧に書いているつもりです。
分かる人は適当に改良して使ってください。

JS, GAS共に初心者が作ったものなので粗があるのは勘弁してください:bow_tone1:
それではどうぞ。

# 設定方法
1. Googleカレンダーを読み取りたいユーザでログインします。

2. 以下のGoogleスプレッドシートにアクセスします。
https://docs.google.com/spreadsheets/

3. 「新しいスプレッドシートを作成」から「空白」を選びます。
<img width="300" alt="スプレッドシート作成" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/86a65f58-27ca-a50b-a225-92224d1e8018.png">

4. 「ツール」 > 「スクリプトエディタ」 を押します。
<img width="400" alt="ツール" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/be03cda1-c06b-3f17-6de1-c7db6d4958e5.png">

5. 以下のようなエディタが出てくるので中身を消して後ほど出てくるコードをコピペします。
<img width="400" alt="スクリプトエディタ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/a11469cc-b535-acd4-40c7-6405c93ace93.png">
<img width="500" alt="スクリプトエディタ2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/1b5d090f-c50f-ffbb-2bfa-43572bb0f59e.png">

6. 三角の実行ボタンを押します。
<img width="200" alt="スクリプトエディタ2" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/dbcbd844-308c-e815-263f-fe83199c1c82.png">

7. プロジェクト（スクリプト）を保存するか聞かれるので「はい」を押します。
<img width="400" alt="保存" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/529f66b4-7dd4-4ee0-138e-ce5d30986671.png">

8. 適当なプロジェクト名をつけて「OK」を押します。
<img width="400" alt="Edit Project Name" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/cb60bf0d-64d1-c90c-0a64-37ec7fbb07a9.png">

9. コードに権限を与えていいか聞かれるので「許可を確認」を押します。
<img width="400" alt="Authorization required" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/58edbafc-11d0-360e-5069-c0b7598c1b39.png">

10. アカウントを選択します。
<img width="400" alt="アカウント選択.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/27d9fd97-5b6f-2b6b-bcaf-c072e94e55bc.png">

11. 「このアプリは確認されていません」が出たら「詳細」を押します。
<img width="400" alt="このアプリは確認されていません.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/e53e472c-602a-c9cf-c84f-f9a1a725c861.png">

12. なんか出るので「作業記録（安全ではないページ）に移動」を押します。（安全です。）
<img width="400" alt="このアプリは確認されていません2.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/dc50c80d-64a6-d77f-ef35-0346cf0c57b4.png">

13. アカウントへのリクエストを要求してくるので「許可」を押します。
<img width="400" alt="アクセスリクエスト.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/02089fe1-55aa-acac-1471-3d3c86f93bd0.png">

14. そしたらエディタに戻るとこんなのが出るのでもう一度6の実行ボタンを押しましょう。
<img width="400" alt="myFunction.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/7338fe11-8e5f-464d-5de1-49dbdb5585f1.png">

15. 3で開いたスプレッドシートに戻ると…
<img width="600" alt="myFunction.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265565/4ccfb7f1-7d8d-4967-397e-f95091c800fc.png">
やったね！
いい感じに表とグラフができてます。

場合によってはアプリの確認とかは出ないかもしれませんがそこは適当にやってください。

# 作ったもの
詳細はコード内に書いてありますが、集計する期間を設定できたりします。

```js

/*
！！注意！！　１日を超えるイベントが正しく取得できません!!

実行ボタンを押すことでログインしているユーザのカレンダーから
現在の週（日〜土）のイベントの時間を足し上げ新しいシートに出力します。
同じ週で実行すると結果を上書きします。

以下の値を設定することで集計する期間を設定することができます。（形式: YYYY/MM/DD, YYYY/MM/DD HH:mm:ss）
start: 開始日時
end: 終了日時
例: var start = "2019/11/20";
例: var end = "2020/1/2 9:30:00";
*/
var start = "";
var end = "";

function CalendarToSpreadSheet() {
  // 所要時間を整形するクラス
  var DurationToStr = function(duration) {
    this.duration = duration;
    this.culc = function() {
      var dur_h = Math.floor(this.duration / 60);  // 時
      var dur_m = this.duration - (dur_h * 60);    // 分
      // スプレッドシートの表示形式で経過時数01、経過分数01にすると桁がそろって見栄えが良い
      var dur_h_str = ('00' + dur_h).slice(-2);
      var dur_m_str = ('00' + dur_m).slice(-2);
      return dur_h_str + ':' + dur_m_str;
    }
  }
  
  // カレンダーから取得したイベントを整形して保存しておくクラス
  var SimpleEvent = function(title, duration) {
    this.title = title;
    this.duration = duration;
    this.getDurationStr = function() {
      return new DurationToStr(this.duration).culc();
    };
  };
  
  // ログインしているユーザのカレンダーを取得
  var calendar = CalendarApp.getCalendarById(Session.getActiveUser().getEmail());
  
  // 現在の週を取得
  var today = new Date();
  var thisYear = today.getFullYear();
  var thisMonth = today.getMonth();
  var thisDate = today.getDate();
  var thisDay = today.getDay();
  var thisSunday = thisDate - thisDay;
  var nextSunday = thisSunday + 7;
  // 日曜日の年月日
  if (start === "") {
    var startDate = new Date(thisYear, thisMonth, thisSunday);
  } else {
    var startDate = new Date(start);
  }
  // 次の日曜日の年月日
  if (end === "") {
    var endDate = new Date(thisYear, thisMonth, nextSunday);
  } else {
    var endDate = new Date(end);
    endDate.setDate(endDate.getDate() + 1);
  }
  // シート名を作成
  var startDateStr = startDate.getFullYear() + "/" + (startDate.getMonth() + 1) + "/" + startDate.getDate();
  var endDateStr = endDate.getFullYear() + "/" + (endDate.getMonth() + 1) + "/" + (endDate.getDate() - 1);
  var week = startDateStr + " - " + endDateStr;
  
  // アクティブなスプレッドシートを取得
  var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  // 今週のシートが既にあれば消す
  var oldSheet = spreadSheet.getSheetByName(week);
  if (oldSheet) {
    spreadSheet.deleteSheet(oldSheet);
  }
  // 新しいシートを作成
  spreadSheet.insertSheet(week);  
  // シートを取得
  var sheet = SpreadsheetApp.getActiveSheet();

  // イベントを取得
  var events = calendar.getEvents(startDate, endDate);
  // 読み出したイベントを登録しておく配列
  var readEvent = [];
  // イベントの読み出し
  for (var i = 0; i < events.length; i++) {
    var title = events[i].getTitle();         // 予定のタイトル
    var startTime = events[i].getStartTime(); // 開始時刻
    var endTime = events[i].getEndTime();     // 終了時刻
    // 所要時間の計算
    var duration = (endTime - startTime) / (1000 * 60);
    // SimpleEvent クラスを生成
    var simpleEvent = new SimpleEvent(title, duration);
    
    // 配列に追加済みか判定するフラグ
    var added = false;
    // 所要時間を足し上げ
    for (var j = 0; j < readEvent.length; j++) {
      if (title == readEvent[j].title) {
        readEvent[j].duration = readEvent[j].duration + duration;
        added = true;
        break;
      }
    }
    if (!added) {
      readEvent.push(simpleEvent);
    }
  }
  
  // 各行のタイトルを指定
  sheet.getRange('A'+(1)).setValue('予定名');
  sheet.getRange('B'+(1)).setValue('所要時間');
  // シートに出力
  for (var i = 0; i < readEvent.length; i++) {
    sheet.getRange('A'+(i+2)).setValue(readEvent[i].title);
    sheet.getRange('B'+(i+2)).setValue(readEvent[i].getDurationStr());
  }
  
  // 合計を出力
  var sumDuration = 0;
  for (var i = 0; i < readEvent.length; i++) {
    sumDuration = sumDuration + readEvent[i].duration;
  }
  sheet.getRange('A'+(readEvent.length+2)).setValue("合計");
  sheet.getRange('B'+(readEvent.length+2)).setValue(new DurationToStr(sumDuration).culc());
  
  // 円グラフを作成
  var range=sheet.getRange("A1:B" + String(readEvent.length+1));
  var chart=sheet.newChart()
    .addRange(range)
    .setChartType(Charts.ChartType.PIE)
    .setOption("pieSliceText", "value")
    .setPosition(1,4,0,0)
    .build()
  sheet.insertChart(chart);
}

```

# 参考
https://tonari-it.com/gas-calendar-spreadsheet/
https://yokonoji.work/google-apps-script-650
https://kiyotatsu.com/gas-script_get-calender/