【Googleカレンダーからの情報取得編】Google Apps Scriptで作る予実管理システム

Google Apps Scriptで作る予実管理システム【Googleカレンダーからの情報取得】編になります。
## この記事を読んでわかること
- Googleカレンダーのタイトル部分からの情報取得
- Googleカレンダーの任意のカレンダーからの情報取得（マイカレンダーではない）
- Googleカレンダーの情報変更履歴を管理する方法


## プログラム全容
```gs:店舗実績.gs
function getShopResults(shopName, shopId, number, pasteStartColumn){

    const ss = SpreadsheetApp.getActiveSpreadsheet();
    // 取得するカレンダーの情報
    const targetCalendar = CalendarApp.getCalendarById(shopId);

    let startDate;
    let endDate;
    let lastDay = 31;
    if(number == 0){
        startDate = new Date(2024,LAST_MONTH-1,1,0,0,0);
        if( LAST_MONTH == 4 | LAST_MONTH == 6 | LAST_MONTH == 9 | LAST_MONTH == 11){
            lastDay = 30;
        } else if(LAST_MONTH == 2){
            lastDay = 28;
        }
        endDate = new Date(2024,LAST_MONTH-1,lastDay, 23,59, 59);
    } else if(number == 1){
        startDate = new Date(2024,THIS_MONTH-1,1,0,0,0);
        if( THIS_MONTH == 4 | THIS_MONTH == 6 | THIS_MONTH == 9 | THIS_MONTH == 11){
            lastDay = 30;
        } else if(THIS_MONTH == 2){
            lastDay = 28;
        }
        endDate = new Date(2024,THIS_MONTH-1,lastDay, 23,59, 59);
    }

    const calendarEvents = targetCalendar.getEvents(startDate, endDate);
    console.log(calendarEvents.length);
    // return
    
    let eventLists = [];
    calendarEvents.forEach( (oneEvent) => {
        var eventTitle = oneEvent.getTitle();

        // イベントタイトルが特定のワードを含んでいればリストから除外
        if(eventTitle.toString().match(/キャンセル|休み|ドタキャン|休憩/) != null ){
            return
        }
        
        // イベントの開始時刻を取得
        var eventStartTime = oneEvent.getStartTime();
            eventStartTime = Utilities.formatDate(eventStartTime, "JST", "yyyy/MM/dd HH:mm");


    // イベントのメニュー項目と金額を取得
        var treatmentMenu = "";
        var treatmentPrice = 0;
        if(eventTitle.toString().match(/メニューA/) != null){
                treatmentMenu = "メニューA"
                treatmentPrice = 10000;
            } else if(eventTitle.toString().match(/メニューB/) != null){
                treatmentMenu = "メニューB";
                treatmentPrice = 3000;
            }
            eventLists.push([shopName, eventStartTime, eventTitle, treatmentMenu]);
    })

    
    const pasteSheetName = "店舗実績_" + LAST_MONTH + "月分";
    const pasteSheet = ss.getSheetByName(pasteSheetName);
    pasteSheet.getRange(3,pasteStartColumn,eventLists.length, 4).setValues(eventLists)
}
```
:::note warn
一部matchメソッドなどは簡略化して掲載しています。
:::

このような感じで作成しました。

## プログラムの説明
```
function getShopResults(shopName, shopId, number, pasteStartColumn){
```
関数には引数として3つ用意しています。
1. shopName：実績を取得する店舗が複数あるので引数として用意
1. shopId：カレンダーIDになる部分
1. number：0にしたら前月データを取得、1のときは今月分のデータを取得できるようにと自分がわかりやすいように用意
1. pasteStartColumn：最終的には『店舗実績_n月』という一つのシートの中で、店舗毎に実績を吐き出すようにしたかったので店舗によって開始の列が変わるため引数として用意

```
    let startDate;
    let endDate;
    let lastDay = 31;
    if(number == 0){
        startDate = new Date(2024,LAST_MONTH-1,1,0,0,0);
        if( LAST_MONTH == 4 | LAST_MONTH == 6 | LAST_MONTH == 9 | LAST_MONTH == 11){
            lastDay = 30;
        } else if(LAST_MONTH == 2){
            lastDay = 28;
        }
        endDate = new Date(2024,LAST_MONTH-1,lastDay, 23,59, 59);
    } else if(number == 1){
        startDate = new Date(2024,THIS_MONTH-1,1,0,0,0);
        if( THIS_MONTH == 4 | THIS_MONTH == 6 | THIS_MONTH == 9 | THIS_MONTH == 11){
            lastDay = 30;
        } else if(THIS_MONTH == 2){
            lastDay = 28;
        }
        endDate = new Date(2024,THIS_MONTH-1,lastDay, 23,59, 59);
    }
```
日付情報についてのプログラムです。カレンダーから情報をとるときには
- CalendarAppオブジェクトから
- カレンダーIDに応じた情報をgetCalendarByIdで取得したのちに
- 開始日、終了日を指定した状態でgetEventsメソッドを利用して指定期間のすべてのイベント情報を取得
というステップになるため、開始日と終了日の指定が必要になります。
開始日は1日でいいのですが、終了日は月によって異なるため、上記のようなプログラムで何月かによって最終日を変えるようなものにしました。

```
    let eventLists = [];
    calendarEvents.forEach( (oneEvent) => {
        var eventTitle = oneEvent.getTitle();

        // イベントタイトルが特定のワードを含んでいればリストから除外
        if(eventTitle.toString().match(/キャンセル|休み|ドタキャン|休憩/) != null ){
            return
        }
        
        // イベントの開始時刻を取得
        var eventStartTime = oneEvent.getStartTime();
            eventStartTime = Utilities.formatDate(eventStartTime, "JST", "yyyy/MM/dd HH:mm");


    // イベントのメニュー項目と金額を取得
        var treatmentMenu = "";
        var treatmentPrice = 0;
        if(eventTitle.toString().match(/メニューA/) != null){
                treatmentMenu = "メニューA"
                treatmentPrice = 10000;
            } else if(eventTitle.toString().match(/メニューB/) != null){
                treatmentMenu = "メニューB";
                treatmentPrice = 3000;
            }
            eventLists.push([shopName, eventStartTime, eventTitle, treatmentMenu]);
    })
```
期間内のカレンダー情報を取得できたら、それを一つずつ見ていって必要なものだけピックアップします。
getTitleメソッドを利用してGoogleカレンダーのタイトル部分を取得し、特定のワードを含んでいて実績に関連しないようなものは除外するようにしました。
また、タイトル部分はスタッフさんが各々メニュー項目を入れてくれますが、人によって入力方法に違いが生まれていたため、ある程度の精度で入力してくれていれば実績としてカウントできるようにmatchメソッドでの抽出を追加しました。

最終的にはこれをシートにsetValuesして完了といった流れです。

これで対応する店舗が増えていったとしても追加工数は最小限で済むと考えています。

# カレンダーの変更履歴を取得する
Googleカレンダーは無料アカウントの場合、情報の変更履歴を取ろうとしても通常はうまくいきません。有料のworkspace会員であれば管理画面からほにゃららできるそうなのですが、そのために有料会員とはならず。
じゃあプログラムで何とか取れるんじゃね？と思って調べてみるとあるじゃないですか。
`getLastUpdated`メソッドが用意されているので、これを利用するとうまく情報が管理できそうです。

ちなみにこのメソッド、ただ情報を閲覧（鉛筆マークを押して保存する）だけだと更新の判定にはならず、タイトル他何らかの編集を加えてから保存することで最終更新日が更新される仕組みのようなので、思った通りの挙動になってくれました。
これを毎日定時に実行してシートに吐き出させることで、少なくともどの24時間のうちに編集があったのかというのはわかるようになります。
プログラミングの技術不足によるものなのか、何か情報の改竄があったのかというトラブルを未然に防ぐためにも考えておきたい処理の一つです。


あとはonOpen関数を作成してカスタムメニューを用意し、その中でトリガーを好きに作ってやれば完成です。