CSVのデータをPythonでGoogleカレンダーに一括登録する（Google Calendar API）

グループウェアからエクスポートしたCSVファイル（会議室などの予約データ）を、Googleカレンダーに一括登録する用事があったので、Pythonでやった忘備録。
# GoogleCalendarAPIを使う準備
[Google本家による説明](https://developers.google.com/google-apps/calendar/quickstart/python) の Step1 と Step2 を行う。英語が苦手でどうしても日本語が良い方は [Developers.IO](http://dev.classmethod.jp/cloud/google-calendar-api-get-start/) で。
# APIを呼び出すPythonを作る
[Google本家による説明](https://developers.google.com/google-apps/calendar/quickstart/python) にあるサンプルそのままでイベントの一覧を取得できるのでこれを改造。
以下、改造の概要
1. APIの [リファレンス](https://developers.google.com/google-apps/calendar/v3/reference/events/insert) を見てパラメータを確認
2. 登録したいCSVファイルの項目とパラメータとのマッピングを考える
3. 全体構成考えとく
4. CSVファイルを読み込む
5. CSVファイルのデータを解析してパラメータに設定
6. イベント登録APIを呼び出し
## 1. APIのリファレンスを見てパラメータを確認
イベント登録APIの [リファレンス](https://developers.google.com/google-apps/calendar/v3/reference/events/insert) を見ると、Path parameters と Optional query parameters が並んでます。なんとなく感じます。Path parameters が必須。
 Path parameters の calendarId は、予定（events）を登録したい対象（人や会議室）のこと。
Optional query parameters と続きますが、今回は省略して次へ。
Authorization の Scope が `https://www.googleapis.com/auth/calendar` とあります。これは「APIでカレンダーの読み書きする」こと。
サンプルスクリプトはイベントの一覧取得なので、  `https://www.googleapis.com/auth/calendar.readonly` と「カレンダー読み取り」になってるので変更。
Request body が登録したいデータを設定するとこ。
##2. 登録したいCSVファイルの項目とパラメータとのマッピング

| 移行元CSVの列名 | APIパラメータ | 備考 |
|:-----------|:------------|:------------|
| 施設 | calendarId | email address に変換 |
| 開始日時 | start | フォーマット変換 |
| 終了日時 | end | フォーマット変換 |
| 作成者 | attendees | email address に変換 |

作成者はAPI実行ユーザーになってしまう。そこまでコントロールするのは大変なので、参加者に設定することに。
移行元データには　施設、開始日時、終了日時、参加者　しかありません。他にも設定必須っぽいですが、ないものはしょうがないので気にせず先に。
##3. 全体構成を考えとく
サンプルの構成は
def get_credentials():で

- 認証情報取得

def main():で

- Google認証
- calendarAPIのサービス生成

ここまではそのまま使える。ありがたい。その後は、

- パラメータ設定
- APIのevents().lis()呼び出し

なので後半は削除。　

足りないのは、

- CSVファイルの読み込み
- データを1行づつループ
- CSVファイルのデータからパラメータを作成

使い回せるようにしたいので、

- ファイル名は設定ファイルで外だしに

```py
def setup_config():
    # 設定ファイル読み込み
    conf_dir_path = os.path.join(os.path.abspath('..'), 'conf')
    conf_file_path = os.path.join(conf_dir_path, 'settings.ini')
    conf_file = ConfigParser.SafeConfigParser()
    conf_file.read(conf_file_path)

    # configの値はglobal変数に（これ良いのかな…）
    global CLIENT_SECRET_FILE_NAME
    global TMP_CREDENTIAL_FILE_NAME
    global USER_FILE_NAME
    global RESOURCE_FILE_NAME
    global EVENT_FILE_NAME

    # 設定ファイルから各ファイル名を取得
    CLIENT_SECRET_FILE_NAME = unicode(conf_file.get(
        'settings', 'CLIENT_SECRET_FILE_NAME'), 'UTF-8')
    USER_FILE_NAME = unicode(conf_file.get(
        'settings', 'USER_FILE_NAME'), 'UTF-8')
    EVENT_FILE_NAME = unicode(conf_file.get(
        'settings', 'EVENT_FILE_NAME'), 'UTF-8')
    RESOURCE_FILE_NAME = unicode(conf_file.get(
        'settings', 'RESOURCE_FILE_NAME'), 'UTF-8')
    TMP_CREDENTIAL_FILE_NAME = unicode(conf_file.get(
        'settings', 'TMP_CREDENTIAL_FILE_NAME'), 'UTF-8')

setup_config()
```

- 各ファイルを格納するディレクトリを決めとく
    - 設定ファイルを格納するフォルダ
    - CSVファイルを格納するフォルダ
    - 認証ファイルを格納するフォルダ

こんな感じ

project
├─src/
├─conf/
├─credentials/
├─csvfiles/
└─log/

他の人が使った際にエラーの情報を残してもらうために、

- logは出力

```py
def setup_logger():
    logger = logging.getLogger(__name__)
    logger.setLevel(logging.INFO)

    log_file_path = os.path.join(os.path.abspath('..'), 'log')
    log_file_path = os.path.join(log_file_path, 'calendarInsertEvent.log')
    fh = logging.FileHandler(log_file_path)
    logger.addHandler(fh)

    sh = logging.StreamHandler()
    logger.addHandler(sh)

    formatter = logging.Formatter(
        '%(asctime)s:%(lineno)d:%(levelname)s:%(message)s')
    fh.setFormatter(formatter)
    sh.setFormatter(formatter)

logger = setup_logger()
```


##4. CSVファイルを読み込む
「Python CSVファイル」で検索するとたくさん情報あります。Python標準のcsvというやつがありますが、もう少し見てみると pandas なるものがでてきます。サンプル見ると「使い易そう」なので pandas を使う。

```py
import pandas as pd

# 予定CSV読み込み
event_csv_file = os.path.join(csv_files_path, EVENT_FILE_NAME)
event_df = pd.read_csv(event_csv_file)
```

event_df の df はDataFrameの略で、pandasでCSVファイルを読み込んだ際のオブジェクト。
これが良い。後で使います。
予定CSV以外にも、会議室、出席者のID（mail address）と移行元のデータを紐づける情報をCSVファイルで作成しておいたので、それらのCSVファイルも同様に読み込んでおきます。

```py
# 施設CSV読み込み
resource_csv_file = os.path.join(csv_files_path, RESOURCE_FILE_NAME)
resource_df = pd.read_csv(resource_csv_file)

# 登録者CSV読み込み
user_csv_file = os.path.join(csv_files_path, USER_FILE_NAME)
user_df = pd.read_csv(user_csv_file)
```

##5. CSVファイルのデータを解析してパラメータに設定
読み込んだCSVファイル1行につき１つの予定データなので1行づつループ。

```py
for i, values in event_df.iterrows():

    calendar_id = get_calendar_id(resource_df, values)
    body = create_api_body(user_df, values)

```

APIパラメータの calendarID を取得。

- values は予定ファイルの1行分のデータ
- resource_df は会議室のid(email address)を取得するためのDataFrame。

```py
    # values['施設']と同じresource_df['姓名']のメールアドレスを取得
    calendar_id = resource_df.ix[resource_df['name'] == values['施設']]['email'].values[0:1][0]
```

この1行で 施設情報マスタ（CSV）を施設で検索し、会議室IDを取得する感じ。
pandas 素敵や。

APIパラメータの Request body を作成。

- values は予定ファイルの1行分のデータ
- user_df 出席者のメールアドレスを取得するためのDataFrame。

これもpandasで

```py
# values['登録者']の値でuser_df['姓名']を検索し、メールアドレスを取得
email = user_df.ix[user_df['姓名'] == values['登録者']]['メールアドレス'].values[0:1][0]
```

移行元データの日時のフォーマットがちょっと変わってるので、開始日時と終了日時のフォーマットを整形して Request body に設定。

```py
# 開始日時
start_time = values['開始日時'].replace('/','-')
start_time = start_time.replace('|','T')
start_time = start_time + '+09:00'

# 終了日時
end_time = values['終了日時'].replace('/','-')
end_time = end_time.replace('|','T')
end_time = end_time + '+09:00'

body = {
    "summary": "",
    "start": {
        "dateTime": start_time,
        "timeZone": "Asia/Tokyo",
    },
    "end": {
        "dateTime": end_time,
        "timeZone": "Asia/Tokyo",
    },
    "attendees": [
        {"email": email},
    ],
}
```

##6. イベント登録APIを呼び出し

```py
# API呼び出し
event = service.events().insert(calendarId=calendar_id, body=body).execute()
```
この1行CalendarAPIのevents().insert()を呼び出し。
完成したスクリプトを実行すると、Googleカレンダーに予定データが登録できました。
[ソースはGitHub。](https://github.com/ping2shi2/py2_GoogleAPI/blob/master/CalendarAPI/calendarInsertEvent.py)