シフト管理アプリ「シフトボード」の予定をGoogleカレンダーに出力する

## まとめ

- [シフトボード](https://shiftboard.jp/)ってアプリは給与計算とかできて便利なのにGoogleカレンダーとの連携がいまいち
- PythonでCSV形式に直すプログラム作った
- それをGoogleカレンダーにインポートできる
- **みんな幸せ**

## シフトボードの問題点

とっても使いやすいアプリなんですが、普段使いカレンダーとの同期が不十分です。

一応iOSのみ本体カレンダーと同期できて、さらにそこからGoogleカレンダーに連携するという[荒技](https://ruxumu.net/archives/6939)もあるにはある。

が、非常に使いにくいし同期が間に合ってない時があるんですよね、要するに不安定

あとiCloud -> Googleカレンダーの時に、公開カレンダーを使ってるのでセキュリティが不安

## 作ったもの

シフトボードの共有テキストから、CSV形式に変換してGoogleカレンダーに予定追加できるプログラム


<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1623593/73edc938-9e37-1d3e-dee6-9be23a136be3.png" width="200px" alt="シフトボードのスクリーンショット">
このボタンから

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1623593/794e6211-58b1-b502-5a54-32daa6dc202d.png" width="200px" alt="シフトボードのスクリーンショット">
共有を押すと

```
07/21（水）00:00 - 18:00
- ほげほげレストラン
07/22（木）10:00 - 24:00
- ほげコンビニ
```
こういったシフトのデータが得られる

このテキストデータを、Googleカレンダーがインポート可能な形式に直すプログラムを作成しました。


## 開発環境
**PC**
macOS 11.4 
python3.6.8

**スマホ**
iPhone 8
iOS 14.1
シフトボード5.27.0

## ソースコード

https://github.com/taikis/shiftboad

## 解説

### 処理の流れ

[入力データのチェック](#入力データのチェック)
↓
[日付の処理](#日付の処理)
↓
[データを辞書形式で処理](#データを辞書形式で処理)
↓
[Googleカレンダーの形式にCSV出力](#googleカレンダーの形式にcsv出力)

### 入力データのチェック

シフトボードから出力される入力データは、行によって4つに分かれます

| 番号 | 内容                                             | 例                                     | 判定基準                                    | 
| ---- | ------------------------------------------------ | -------------------------------------- | ------------------------------------------- | 
| 1    | バイトのシフト                                   | 07/21（水）00:00 - 18:00               | 正規表現(後述)                              | 
| 2    | バイト先                                         | - ホゲレストラン                       | 正規表現<br>-\s.+                           | 
| 3    | 空白行<br>最後から二番目に存在                   |                                        | == None                                     | 
| 4    | 最終行<br>シフト管理アプリ「シフトボード」で作成 | シフト管理アプリ「シフトボード」で作成 | == "シフト管理アプリ「シフトボード」で作成" | 

2に関しては、出力画面でチェックを入れるか入れないかによって存在しない場合がある

1の正規表現は、少しややこしいのですがこのようになりました

```
\d{2}/\d{2}（(月|火|水|木|金|土|日)）\d{2}:\d{2}\s-\s\d{2}:\d{2}
```

コードは以下のようになります

```python
    timeRe = re.compile(
        "\d{2}/\d{2}（(月|火|水|木|金|土|日)）\d{2}:\d{2}\s-\s\d{2}:\d{2}")
    subRe = re.compile("-\s.+")

    if timeRe.fullmatch(dataString) is not None:
        stateData = TIME_DATA
    elif subRe.fullmatch(dataString) is not None:
        stateData = SUBJECT_DATA
    elif len(dataString) == 0:
        stateData = NO_DATA
    elif dataString == "シフト管理アプリ「シフトボード」で作成":
        stateData = END_LINE

    return stateData
```

### 日付の処理

元データには年が書かれていないため、今日の日付から推定する必要がある

```py
    year = today.year
    if month < today.month:
        year += 1
    date = datetime.datetime(year, month, day)
```

また、上がり時間が24時を超える場合、24時を超える表記となるため、修正する。

```py
    minute = int(minute)
    minutes = hour*60 + minute
    date += datetime.timedelta(minutes=minutes)    
```

### データを辞書形式で処理

辞書形式で`[バイト先の名前, 入り時刻, 上がり時刻]`を保存する

時刻は単純に文字数で区切って入れました

```py
    dataDict["startDate"] = setDate(
        dataStrings[0][0:2], dataStrings[0][3:5], dataStrings[0][8:10], dataStrings[0][11:13])
    dataDict["endDate"] = setDate(
        dataStrings[0][0:2], dataStrings[0][3:5], dataStrings[0][16:18], dataStrings[0][19:21])
```

バイト先の名前は、出力時の設定によってはない場合があるため、ない場合は「バイト」としました

```py
    if len(dataStrings) == 2:
        dataDict["subject"] = dataStrings[1][2:]
    else:
        dataDict["subject"] = "バイト"
```

## Googleカレンダーの形式にCSV出力

Googleカレンダーの形式に出力するために、まず仕様を確認しましょう
[.csv ファイルまたは iCal ファイルを作成または編集してから読み込む](https://support.google.com/a/users/answer/37118?hl=ja&co=GENIE.Platform%3DDesktop#zippy=%2Ccsv-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90%E3%81%A8%E7%B7%A8%E9%9B%86)

これを元に、下記の形式に直すことにします

|Subject|Start Date|Start Time|End Date|End Time|
|:----|:----|:----|:----|:----|
|ほげレストラン|07/21/2021|12:00 AM|07/21/2021| 6:00 PM|

CSV形式への出力はライブラリを使えば簡単ですね

```py
with open(outputPath, 'w') as f:
    writer = csv.writer(f)
    writer.writerow(
        ["Subject", "Start Date", "Start Time", "End Date", "End Time"])
    for data in self.dataDicts:
        writer.writerow(
        [data["subject"],
        data["startDate"].strftime("%m/%d/%Y"),
        data["startDate"].strftime("%l:%M %p"),
        data["endDate"].strftime("%m/%d/%Y"),
        data["endDate"].strftime("%l:%M %p"),
        ]
        )
```

## 今後やりたいこと

iOSのアプリとして開発して、共有ボタンからワンクリックでGoogleカレンダーに飛ばしたい

でもPythonで作っちゃった。どうしよ

## 参考文献

[Googleカレンダーに予定を読み込む](https://support.google.com/a/users/answer/37118?hl=ja&co=GENIE.Platform%3DDesktop)