2011年6月21日

Google Tasks API を使ってみる

先月、Google Tasks の API が公開されました。 API 公開のアナウンスが出されたときはサンプルがありませんでしたが、 先週末に記事が公開されました。 ライブラリコードのサンプルディレクトリに同様のコードが存在します。

記事/サンプルを読む中では、 OAuth2Decorator クラスが導入されたこと、 コールバック URL のハンドラスクリプトとして oauth2client/appengine.py を使えるようになったこと、 この二点が大きな改善点だと思います。

しかし、それに気付かずに実装しちゃった... という寂しいお話。 いちおう、スクリプトはこちら。。。

認証の始まり

最近の Google のたいていの API は OAuth 2.0 を使います。 Google が提供している Python のライブラリには oauth2client モジュールがあり、これを使うと簡単にトークンを管理できます。 ちなみに oauth2 は OAuth 1.0a 用のライブラリ... というのは蛇足です。

認証には OAuth2WebServerFlowOAuth2Decorator を使うことになりますが、 どちらの場合でも次の4つの情報が必要になります。

  • client_id: アプリケーション ID のようなもの。
  • client_secret: アプリケーションのパスワードのようなもの。
  • scope: アプリケーションがユーザーに許可を求めるアクセス範囲。
  • user_agent: アプリケーションの名前。

client_id と client_secret は API Console から取得します。 API Console は画面表示が英語ですが、なんとなくの雰囲気でも使える気がします。 user_agent は適当な名前を付けた方が良いと思いますが、割と何でも動くはずです。 で、おそらく選択肢が存在するのが scope です。

API を使ったアクセスはユーザーから見えない部分で何をするか分かりませんので、 この範囲内で動作します/させます、と表明します。 最近の話題だと、Twitter が scope の扱いを見直し、ダイレクトメッセージへのアクセス権限を変更しました。 Google の API では、そんな感じの仕組みが細分化されて実現されています。

Tasks API では、次のふたつの scope から選択できます。

  • https://www.googleapis.com/auth/tasks: 読み書き (read/write access to Tasks)
  • https://www.googleapis.com/auth/tasks.readonly: 読み込み専用 (read-only access to Tasks)

なんで https:// から始まるような長い文字列なのか?という疑問はさておき、 API ドキュメント - Developer's Guide (v1): Using the API - の Authorizing requests に記載されています。 Google のそれぞれの API には類似のドキュメントがあり、たとえばモデレータの場合には、 tasks の部分が moderator になったような感じで記載されています。 モデレータについては Hack for Japan の Google モデレーターのデータを見る (過去記事) で同じような認証フローを書いています。

コールバック

先ほどまでの部分 (コードを掲載してないけど) で、OAuth 2.0 認証の準備が整いました。 OAuth は、認可するサービス (この場合は Google) と認可されるアプリケーション (この場合は自作アプリ) が別々に動いていますから、 認可が終わったら何らかの方法でユーザーをアプリケーションに戻す必要があります。 Web アプリケーションでの常套手段として HTTP リダイレクトがありますので、 アプリケーションはリダイレクト先のコールバック URL を準備しておきます。

  1. API Console に登録する。
  2. アプリケーションに特別な URL を用意し、GET パラメータを解析する。

コールバックは、たとえば flow という変数が OAuth2WebServerFlow のインスタンスであり、 コールバック URL のリクエストハンドラを AppEngine の webapp フレームワークの RequestHandler で実装した場合、 次のように記述できます。

credentials = flow.step2_exchange(self.request.params)

この戻り値がユーザーの認証トークンになります。

いちいちこのような仕組みを理解しながら記述するのは煩雑なので、 Google API Python Client では oauth2client/appengine.py というスクリプトを提供してくれています。 冒頭に挙げた Google Code の記事では、 app.yaml に次のハンドラを追加しています。

handlers:
- url: /oauth2callback
  script: oauth2client/appengine.py

ライブラリの中に実行コードも一緒にして配布しているわけです。 実際にコードを見てみると、最後に次の変数と関数が定義されています。

application = webapp.WSGIApplication([('/oauth2callback', OAuth2Handler)])

def main():
  run_wsgi_app(application)

URL が決め打ちになってしまいますが、簡単にコールバックを処理できるのは嬉しいと思います。

なお、API Console で登録していない URL をコールバックに指定すると認証エラーになります。 AppEngine の場合はローカル環境で開発してプロダクション環境にデプロイしますので、 その両方 (ふつうは localhost と app-id.appspot.com) の URL を登録しておきます。 ホスト名以降のパスも一致していなければなりませんので、最初の登録には注意が必要かもしれません。

タスクリストとタスク

認証トークンを使えるようになると、ようやく Tasks API を実行できます。 Tasks にはふたつのデータモデルがあります。

  • タスクリスト (task list): 複数のタスクをまとめるカテゴリのようなもの。
  • タスク (task): 個別の「やること」。期限や状態を設定できる。

ユーザーはデフォルト (@default) のタスクリストを含む複数のタスクリストを持つことができます。 個々のタスクはいずれかのタスクリストに所属します。 つまり、タスクを操作するためには次の二つのステップが必要になります。

  1. タスクリストの一覧を取得する。
  2. タスクリストを指定して、タスクの一覧を取得する。

実際に API 用のサービスを構築し、タスクリストを取得するコードは次のように記述できます。 credentials は OAuth 2.0 の認証トークンオブジェクトです。

http = httplib2.Http()
http = credentials.authorize(http)

service = build('tasks', 'v1', http=http)

tasklists = service.tasklists().list().execute()
for item in tasklists['items']:
    if item['title']:
        print '*', item['title']

build() は JSON のサービス定義ファイルからメソッドを構築するためのものです。 Discovery と呼ばれる仕組みで、要するに以下の JSON ファイルを読み込んでオブジェクトを生成します。

人間が読むような代物でもありませんが、 resources というキーに対応する値がインスタンス化されます (表現が微妙な気もしますが)。 いちおう読んでみると、メソッド名やそこで使われる HTTP メソッド、パラメータとして有効なもの、その順番などが記述されています。 メソッドごとのスコープも制限されていることが分かります。 tasklists には methods が定義されており、その中に list があります。 pathdescription と API ドキュメントを照らし合わせてみると、何となく意味が分かってくるかもしれませんね。

(JSON を一部抜粋)

"list": {
 "id": "tasks.tasklists.list",
 "path": "users/@me/lists",
 "httpMethod": "GET",
 "description": "Returns all the authenticated user's task lists.",
 "parameters": {
  "maxResults": {
   "type": "integer",
   "description": "Maximum number of task lists returned on one page. Optional. The default is 100.",
   "minimum": "-9223372036854775808",
   "maximum": "9223372036854775807",
   "location": "query"
  },
  "pageToken": {
   "type": "string",
   "description": "Token specifying the result page to return. Optional.",
   "location": "query"
  }
 },
 "response": {
  "$ref": "TaskLists"
 },
 "scopes": [
  "https://www.googleapis.com/auth/tasks",
  "https://www.googleapis.com/auth/tasks.readonly"
 ]
},

タスクを取得する

次に、タスクの一覧を取得します。 すべてのユーザーは @default というタスクリストを持つはず (意図的に削除しようとした場合などは未確認) ですので、 決め打ちにしてしまって一覧を取得します。

tasks = service.tasks().list(tasklist='@default').execute()
for task in tasks['items']:
    if task['title']:
        print '*', task['title']

タスクにはタイトルがありますが、空文字でも構わないようなので、if 文で確認しています。 個人的には必須で良いのにな、と思いますが、そこはそういう仕様のようです。

NOTE:

内部的に uritemplate モジュールを使いますが、バージョンによっては "@" の置換がうまくいかないかもしれません。 google-api-python-client に含まれる uritemplate と本家のものは safe が違う ためです (将来的には統一される?)。

タスクを登録する

登録するときは insert() メソッドを実行 (execute()) します。 insert() の引数には tasklistbody を指定します。 'notes' と 'due' は指定しなくても構いません。

task = {
    'title': 'ファミマカードの支払い',
    'notes': 'そろそろ完済したい。',
    'due': '2011-07-08T12:00:00.000Z'
}
result = service.tasks().insert(tasklist='@default', body=task).execute()
print "Created a new task,", result['id']

終わりに

Google Tasks API を使ってみました。 認証部分と Discovery あるいはドキュメントのどこを読むべきかが分かってくると、 どの API も似たようなイメージで利用できます。 ライブラリ自体も着々と改善されていますので、もうすぐ正式リリースとなるかもしれません。

タスク API は簡単に扱えますが、そもそも Google Tasks の位置付けが微妙... という考えもあります。 とはいえ、AppEngine にメールを送ったら勝手に登録してくれるようにできると、 ケータイメールで受け取ったことを転送するだけでタスク化できます。 また、メールとカレンダーの両方に統合されているため、タスク自体に気付きやすいように思います。 要は、どこまで Google Account に集約するか?という問題になるでしょう。

なお、最近は API ドキュメントのサンプルに PHP も追加されました。 そこは Go じゃ... という気がしなくもありませんが、幅広くサンプルが揃うのは良いことだと思います。

おまけとして、過去には、モバイル画面をスクレイピングする、という記事もありました。 もちろん、今となっては非推奨でしょうけど。

コメントを投稿