あなたの時間知覚を強化する：GASでカレンダーの説明欄をまとめるスクリプト

３行で
・いつ何があったか時間の流れをはっきりとさせたくない？
・Googleカレンダーで記録して把握していこうぜ
・手間をGoogleAppScriptで省くぜ


# 前置き
歳をとると時間の感覚があいまいになっていくじゃないですか、
「あの出来事あったのって去年だっけ？」
と思っても実際には数年前だったりするわけで、

気づけば1か月や1年が終わっており
「何もせずに終わったなぁ」
と悲しい気持ちになるものです。

かくいう私も近年は時系列がふわふわで、
こりゃなんとかしたいと思い立った次第であります。

問題なのは「この時期に何をしていたのか？」が把握できていない事だと仮定し、
毎週の出来事を記録した上で、1か月ごとに内容をまとめることにしました。

これで後から見返しても「xx年のxx月はこれやってたな～」としみじみできて
とてもうれしい気持ちになることができること間違いなし、完璧な作戦と言えましょう。

さて、勘の良い方ならお分かりのはず、この一か月の出来事をまとめるという作業がなんとまぁ絶妙に手間でありまして、今回GASでスクリプトを作るにあたった経緯でございます。

それでは早速見ていきましょう
# GASでカレンダーの説明欄をまとめるスクリプト
## 週間まとめweekの作成
![week.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/4d8531ae-ec52-3841-26cd-400a1fb2ed80.png)
まずは上記のように毎週の出来事をまとめるweekという繰り返し予定を、Googleカレンダーの好きな曜日に作っていきます。

自分は毎日カレンダーをチェックするので、「もしweekがあったら過去一週間の出来事をまとめる」という自分の脳内プログラムに従い、毎週の予定を書き記すことでしょう。

続いて脳内プログラムに従って一週間の内容をまとめた例を見ていきましょう
![discription.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/0933ff14-d8da-468a-f08c-dd1a1ccbe328.png)
上記のように説明欄に#から始まる出来事のカテゴリと、続けて内容を記入していきます。

このようにweekの説明欄に記載しておくことで、今回作成するスクリプトにより、毎月の出来事がカテゴリごとにまとめられるというわけです。

なお、ここで一点注意すべきことがありまして、
![save.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/7fe05272-780f-65e2-a87e-1c6b941c97b0.png)
保存するときは「この予定」を選択しないと他のweekが消えてしまったりするため注意が必要です。

次は本題のスクリプトを作成する準備をしていきます。
## スクリプトを作る準備をする
まずは「google apps script」でググり
![google search.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/7cf18c82-4c59-75b9-bec2-ebc4c3c3a699.png)

Start Scriptingに進み
![gas.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/b0c9150f-f5b0-0eff-dd01-5778fab592c0.png)

新しいプロジェクトを作成します
![gas create.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/03e9f0e4-ee83-481e-f7dc-aff1a0e572ef.png)

以下のようなページが表示されていれば準備は完了です。
![editor.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/c4d83cbd-de19-abeb-9030-ebe9bdc707c1.png)

では早速スクリプトを作っていきましょう
## スクリプトを作る
### スクリプト全文
```javascript
/*
カレンダーの先月のweekの説明欄をまとめ、実行月の1日にまとめた予定を出力する
動作にGoogleのCalendar APIと時間によるトリガーの設定が必要
*/

function main() {
  const calendarId = "カレンダーがあるアカウント@gmail.com";
  
  // 先月の月初日と月末日を取得
  const { monthStartDate, monthEndDate } = getLastMonth();  

  // 月初日と月末日から"week"というタイトルの予定を抽出
  const weekEvents = getWeekEvent(calendarId, monthStartDate, monthEndDate);

  // "week"というタイトルの予定から、説明欄を取得し#で記述したイベントごとに分類
  const eventCategories = getEventCategories(weekEvents);

  // イベントごとに分類したデータから1か月のまとめを作成
  const monthlySummary = getMonthlySummary(eventCategories);
  Logger.log(monthlySummary)

  // 作成した1か月のまとめをカレンダーに保存
  createMonthlySummaryEvent(calendarId, monthlySummary);
  Logger.log("実行完了");
}

function getLastMonth() { // 先月の月初日と月末日を取得
  var now = new Date();
  var monthStartDate = new Date(now.getFullYear(), now.getMonth() - 1, 1);
  var monthEndDate = new Date(now.getFullYear(), now.getMonth(), 0);

  return { monthStartDate, monthEndDate };
}



function getWeekEvent(calendarId, monthStartDate, monthEndDate) { //"week"というタイトルの予定を抽出
  var events = Calendar.Events.list(calendarId, {
    timeMin: monthStartDate.toISOString(),
    timeMax: monthEndDate.toISOString(),
    singleEvents: true,
    orderBy: 'startTime'
  });

  var weekEvents = [];
  if (events.items && events.items.length > 0) { // eventが存在するなら続行
    for (var i = 0; i < events.items.length; i++) {
      var event = events.items[i];
      var summary = event.summary || ''; // タイトルが存在しない場合は空文字列を使用
      if (summary.toLowerCase().includes("week")) {
        weekEvents.push(event);
      }
    }
  }

  return weekEvents;
}

function getEventCategories(weekEvents) { // カレンダーの説明欄を取得
  var eventCategories = {}

  weekEvents.forEach(event => { // eventごとにループ
    const description = event.description; // 説明のテキストのみ抽出
    const lines = description.split('\n'); // 行ごとに分割

    let currentCategory = null; // カテゴリを保持するための変数を追加

    lines.forEach(line => { // 行ごとにループ
      if (line.startsWith('#')) { // #で始まる場合、カテゴリを格納
        currentCategory = line.replace("#", "").trim(); // カテゴリ名を更新
        if (!eventCategories.hasOwnProperty(currentCategory)) { // カテゴリ名が存在しないなら作成
          eventCategories[currentCategory] = [];
        }

      } else if (line.trim() != "") { // #以外で始まる場合、データが空白かどうか
        if (!eventCategories[currentCategory].includes(line.trim())) { // 既に保存していないか
          eventCategories[currentCategory].push(line.trim()); // カテゴリに追加
        }
      }
    });
  });

  return eventCategories;
}

function getMonthlySummary(eventDescriptions) { // 月間のイベントまとめを作成
  let monthlySummary = '';

  Object.entries(eventDescriptions).forEach(([key, value]) => {
    monthlySummary += `#${key}\n`;
    monthlySummary += value.join('\n') + '\n\n';
  });

  return monthlySummary;

}

function createMonthlySummaryEvent(calendarId, monthlySummary) { //1か月のまとめをカレンダーに保存
  const title = "MonthlySummary";
  const create_date = new Date(); // 実行時の日付(トリガーによって1日となる)を取得
  const description = monthlySummary;
  const calendar = CalendarApp.getCalendarById(calendarId)

  calendar.createAllDayEvent(title, create_date,{description: description});
}

```
ということで事前に作成して冷蔵庫で寝かせておいたスクリプトがこちら
main関数を見ればおおよそ分かるのと、長くなってしまうため関数の細かい説明は割愛します。

これを先ほどのエディタに貼り付けてから
![mail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/5af42734-b333-d16d-dc15-dbca27f24d5d.png)
"カレンダーがあるアカウント@gmail.com";
という部分を自分のgoogleアカウントのgmailアドレスに変更し

![save2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/6d7f5e07-7a1e-7d8e-c1c0-62d254e847b7.png)
プロジェクトを保存します。

次に、GASでカレンダーを操作するために必要となるサービスを追加していきます。
### Calendar APIの追加
横にある十字を選択し
![service.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/699426ce-739e-9876-cf82-2ebee6268165.png)

Google Calendar APIを追加します
![api.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/6aa2b1f4-f2f0-7ed2-e5f0-abf83595e2ec.png)
これにより、カレンダーを操作する便利な機能を使えるようになりました。

最後にこのスクリプトを毎月実行させるためのトリガーを設定していきましょう。
### 自動で実行するためにトリガーを設定
横にある目覚まし時計のアイコンから
![timer1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/7339745b-5289-05ef-d0e3-1a44f790620b.png)

トリガーを追加
![timer2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/a23f270e-7206-2bb0-5f2d-34d2ed07a9ef.png)

以下のように毎月1日に実行されるよう設定してから
![timer3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/eea34662-8eef-4747-7a90-78bd6702a37e.png)

カレンダーへアクセスするためのスクリプトなので警告が出ますが、
許可していきます。

advancedから
![timer4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/8a7130b5-a584-a2af-35ec-8152f9460ba2.png)

許可画面へ
![timer5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/4ac526da-2335-ac6c-3bc6-48c56cec4550.png)

許諾を選択
![timer6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/92212cf6-89cd-0009-99b8-907510c939b1.png)

これで、weekの予定が自動でまとめられ
1か月の出来事をまとめたカレンダーの予定が毎月1日に自動作成されます。

それではどのような結果が出るのか見ていきましょう
## 出力結果

![month.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761860/c951a18f-0d8c-ea64-d0d0-a9c9b37765a6.png)
上記のような一か月の出来事をまとめた予定が追加されます。
これにより「xx年のxx月はこんな出来事があったんだなぁ」と、
時間と出来事を明確に結びつけやすくなることでしょう、すばらしい限りであります。

最後に使っている感想などを述べたいと思います。
# 終わりに
個人の感想にはなりますが
「以前よりも時間に流される感覚が減ったな～」
というのが一番大きなところでしょうか

何もしていなかったように感じても
1か月のまとめを見ると思ったより色々やっていることが分かり、
安心感を覚えることができて大変満足しております。

年末に知人と振り返って楽しんでもよし、
出来事の時期を特定するために使ってもよしと、
たいへん便利なスクリプトとルーティンですので、取り入れてみてはいかがでしょうか。

ご拝読いただきありがとうございました。