Blogaomu

WEBアプリケーション開発とその周辺のメモをゆるふわに書いていきます。

Google Calendar APIでカレンダーを取得するスクリプト

参考

Google Calendar API – Google Apps Platform — Google Developers

特定のカレンダーのイベント一覧を取得して、CSVに出力するスクリプトを書いてみました。Githubはこちら→TAKAyukiatkwsk/google_calendar_util

環境の構築

% cd path/to/project
% vi Gemfile
source :rubygems
gem "google-api-client"

% bundle install --path vendor/bundle

次にGoogle APIs Consoleでアプリケーションを登録します。詳しくはここに書いてあります。https://developers.google.com/google-apps/calendar/firstapp?hl=ja#register アプリケーション登録後、APIs ConsoleのAPI AccessでClient IDとClient secretを取得します。

OAuth

% bundle exec google-api oauth-2-login --scope=https://www.googleapis.com/auth/calendar --client-id=CLIENT_ID --client-secret=CLIENT_SECRET
% cp ~/.google-api.yaml .

認証に成功すると、ホームディレクトリに.google-api.yamlというファイルができるので、このファイルをプロジェクトルートにコピーすします。

このyamlファイルにはClient ID, Client secret, scope(アクセス可能なAPIを示す), access token, refresh tokenが書かれています。セットアップとしてyamlを読み込んで認証情報を設定します。 Configure your APPを参照のこと。

APIメソッドは以下のように実行します。

client.execute(:api_method => 'method_name',
               :parameters => {'key' => 'value', …})

認証から一定時間経ち、client.execute()を行うと以下のようにエラーとなる場合があります。

#<Google::APIClient::Schema::Calendar::V3::CalendarList:0x3fc275e1b348 DATA:{"error"=>{"errors"=>[{"domain"=>"global", "reason"=>"authError", "message"=>"Invalid Credentials", "locationType"=>"header", "location"=>"Authorization"}], "code"=>401, "message"=>"Invalid Credentials"}}>

こういう場合は、client.authorization.fetch_access_token!を行うと再認証できます。

ハマったところ

APIのパラメータはgoogle/api-clientライブラリ内部でURLエンコードされているのだけど、datetimeのパラメータのエンコード結果がうまくいきませんでした。datetimeのパラメータはRFC 3339のフォーマットで指定するようになっていて2012-09-30T00:00:00+09:00のように指定します。しかし、このままだとBad Requestとなってしまいます。

time_max = DateTime.new(2012, 9, 1).rfc3339
# => "2012-09-01T00:00:00+00:00" 
result = client.execute(:api_method => service.events.list,
                        :parameters => {"timeMax" => time_max})

これはタイムゾーン+09:00+の部分がスペースに置換されてしまうことが原因のようです。詳しくはissuesを参照のこと。http://code.google.com/p/google-api-ruby-client/issues/detail?id=56

根本的な解決策はまだ見出されていませんが、タイムゾーンの部分をZに置き換えることによってこの問題を回避することができます。

time_max = Time.utc(2012, 9, 1, 9).iso8601
# => "2012-09-01T09:00:00Z" 
result = client.execute(:api_method => service.events.list,
                        :parameters => {"timeMax" => time_max})

実行結果

自分のアカウントに登録されているカレンダー、出力したい年月を選択すると、以下のようなCSVファイルが出力されます。(まだSJISエンコードしてません)

% ruby app.rb
1. 連絡先の誕生日と予定
2. 日本の祝日
3. ユーザー名@gmail.com
please input number you want to get events 
> 3
what year? 
> 2012
what month? (1-12) 
> 9

% cat data/event_201209.csv
2012-09-03,11:00,2012-09-03,11:30,予定1
2012-09-04,14:00,2012-09-03,19:00,予定2
2012-09-05,12:00,2012-09-03,13:00,予定2
2012-09-05,19:00,2012-09-03,19:30,予定3
2012-09-06,12:00,2012-09-04,12:30,予定1
2012-09-07,11:00,2012-09-04,12:00,予定2