コマンドライン引数の扱い方にはいくつかの方法がありますのでメモしておきます。 もちろん sys.argv を自前で解析すれば何とでもなりますが、その解析を頑張りたくはありませんね、という動機です。
- 標準ライブラリ
- フレームワークに特有の方法 - Django, Twisted, Tornado
- 独立したライブラリ - gflags
コマンドライン引数の扱い方にはいくつかの方法がありますのでメモしておきます。 もちろん sys.argv を自前で解析すれば何とでもなりますが、その解析を頑張りたくはありませんね、という動機です。
個人的な紙のノートに記録するような内容ですが、毎日のように余震が続いていることを記録しておきたいな、と思ったのでメモしておきます。 震災から2週間が経とうとしていますが、未だに余震が続くことに驚きを隠せません。地震酔い?と感じることもしばしば。 輪番停電は引き続き断続的に実施されていますが、都内の生活は少し落ち着いてきた印象です。 「普通」の日常を取り戻すべく社会が動いている、そんな気がします。 それにしても、社会インフラを支えてくれている人には脱帽です。
世間的には福島にある原子力発電所の放射能に関する話題が大きなトピックでした。 金町の浄水場で基準値を大幅に超えるヨウ素が検出され、「水道水を飲めないの?」という不安が広がりました。 煮沸すれば... と思ったら、余計に悪いそうです。 成人にとっては許容範囲らしいですが、乳幼児には問題があるそうで、またしても飲料水の「買い占め」騒動。 情報の断片だけが急速に広がりますので、似たようなことは今後も続きそうです。 立場に沿った情報を発信/受信できる仕組みがあると良いのですが。
復興に向けての動きも形になり始め、グルーポンのマッチングギフトが2億円に達したそうです。 企業や著名人からの寄付も続々と発表されており、日本だけでなく、外国からもたくさんの援助があります。 サッカーの欧州各国リーグからも応援メッセージがありました。 わざわざユニフォームの背番号をカタカナ表記にして試合に臨んでくれたバレンシアにはとても和みました。 日本代表と J リーグ選抜のチャリティーマッチも決まりました。 また、中止されていた J リーグも、4月下旬からの再開が決まりました。 電力消費を抑えるためにデイゲーム主体になりますが、それでも少しずつ「普通」が戻ってきています。
そんな感じのつらつらとした記録。なお、「東北関東大震災」と呼称は赤十字社より。
Apache Shindig を使えるようにしてみます。 Shindig に関しては Web サイトのトップページに簡単な説明があります。
Apache Shindig is an OpenSocial container and helps you to start hosting OpenSocial apps quickly by providing the code to render gadgets, proxy requests, and handle REST and RPC requests.
Apache Shindig's goal is to allow new sites to start hosting social apps in under an hour's worth of work.
Shindig がガジェットの表示、リクエストのプロキシ、REST と RPC リクエストの処理を担当してくれますので、 その仕様に従えばアプリケーションを素早く提供できますよ、という感じでしょうか。 ここでの「仕様」が OpenSocial と呼ばれるもので、 Google や mixi で使われている OpenSocial のコンテナが Apache Shindig だそうです。 同じ仕様のコンテナを提供することで、Google 用に作ったガジェットを mixi でも使えますし、その逆もまた然りとなります。
Shindig に関する説明は各所にありますが、2010年の10月にバージョン 2.0 がリリースされました。 メジャーバージョンアップですので、インストールするときの違いもあるでしょう。 ということで、いくつかの記事を参考にさせて頂きながら、自分でも動作させてみます。
Shindig 自体は2007年12月頃から ASF の Incubator 下で活動が開始され、2009年1月にトップレベルのプロジェクトになりました。
ローカルのマシンで動かしても構いませんが、クリーンな環境の方が失敗する要素が少ないと思いますので、Amazon EC2 を使います。 東京リージョンが追加されていますので、接続速度にこだわる場合には選択肢のひとつとして有望です。 まぁ、輪番停電が実施されている中で東京のデータセンターを選択する必要があるとは思えませんが。。
少し前に EC2 で Apache HTTP と Solr を使えるようにしました (Amazon EC2 を使ってみる)。 インスタンスの起動、接続方法はだいたい一緒で、標準の AMI を使います。
ファイアーウォールの設定では 20 番ポート (SSH) と 80 番ポート (HTTP) を開放しておきます。 アプリケーションサーバの動作確認用に 8080 番ポートなどを確認しても構いませんが、 ちょっと確認するだけであれば SSH トンネリングを利用すれば良いと思います。
.ssh/config の Host は ec2shindig にしておきます。
接続例
$ ssh -L8080:localhost:8080 ec2shindig
なお、インスタンスの扱いには Amazon EC2 API Tools が便利そうです。 時間があれば設定しておきたいところです。
SSH で接続したら、Apache と Tomcat をインストールしておきます。
$ sudo yum -y install httpd tomcat6 $ sudo chkconfig httpd on $ sudo chkconfig tomcat6 on
次に、Apache へのリクエストを Tomcat にプロキシさせるように設定し、サーバを起動しておきます。
$ cat <<'EOF' |sudo tee -a /etc/httpd/conf.d/proxy.conf
ProxyPass / http://{YOUR-INSTANCE-PUBLIC-DNS}:8080/
EOF
$ sudo service httpd start
Apache Shindig の サイト からアーカイブをダウンロードします。 Java の実装と PHP の実装がありますが、単に配置するだけならば Java の .war アーカイブを使う方法が簡単に思えましたので、Java 版を使います。 shindig-server-2.0.0.war は予め取得しておきます。
$ sudo mv shindig-server-2.0.0.war /usr/share/tomcat6/webapps/ROOT.war $ sudo chown tomcat /usr/share/tomcat6/webapps/ROOT.war
.war ファイルを展開し、 web.xml を編集します。 ついでに、Tomcat が 8080 番ポートで動作することを確認しておきます。
$ sudo service tomcat6 start $ wget -SO /dev/null "http://localhost:8080/" $ sudo service tomcat6 stop $ sudo vim /usr/share/tomcat6/webapps/ROOT/WEB-INF/web.xml --- (*) $ sudo service tomcat6 start
これで EC2 インスタンスの Public DNS にアクセスすると、次の画面が表示されます。
http://{YOUR-INSTANCE-PUBLIC-DNS}/gadgets/ifr?url=http://miyakemusi.googlecode.com/svn/trunk/tv_gadget/tv.xml
設定に関して注意すべきことが2点挙げられます。
いずれもリソースの URL 解決という意味で根っこは同じです。 OpenSocial ではクロスドメイン通信を回避するために、コンテナが HTTP リクエストをバイパスさせます。 このときに、コンテンツの URL とプロキシ用の URL を解決しなければなりませんが、 これが Java から JavaScript に変換されています。 ガジェットをうまく表示できない場合は、取得したページのソースを表示してみると問題解決の端緒となるかもしれません。 localhost の記述が存在する場合には shindig.host の設定間違いかもしれませんので、 web.xm を見直します。
ROOT コンテキスト以外で動作させようとするとパスの整合性が崩れてしまいますので、うまく動きません。 たとえば、 .war ファイルの名前を変更せずに webapps ディレクトリに配置すると、 アプリケーションにアクセスする場合には /shindig-server-2.0.0 というパスが必要になります。 一番最初のリクエストはそれで成功しますが、その後に JavaScript を取得する部分に /shindig-server-2.0.0 が渡っていないため、ガジェットを表示できません。
さらに、Apache のプロキシでこのエンドポイントを自由に変更できます。 たとえば proxy.conf に次の定義を入れると、外部からは http://{YOUR-INSTANCE-PUBLIC-DNS}/shindig でアクセスできますが、 JavaScript の通信がうまくいかずにガジェットを表示できません。
ProxyPass /shindig http://{YOUR-INSTANCE-PUBLIC-DNS}:8080/shindig-server-2.0.0
いずれの問題も将来的な実装の変更、あるいはちょっと複雑な Shindig の設定で制御できるのかもしれませんが、 ちょっと始めてみるには遠回りすぎる気がしました。 ROOT/WEB-INF/classes/containers/default/container.js が関係していると思いますが、 とりあえずは ROOT コンテキストで動かしてみることが間違いが少ないはずで、 もっと突っ込んだことを実現する場合にはソースコードを取得して mvn を使って自前でビルドした方が良さそうです。
また、Apache をフロントに置く必然性もありませんので、Tomcat を 80 番ポートで動かすか、 ファイアーウォールで 8080 番ポートを許可すれば良いのかもしれません。 ここでは、EC2 のセキュリティポリシーを使いまわすために上記のようにしています。
NOTE 1: web.xml における shindig.host と shindig-port の設定例です。
<context-param>
<param-name>system.properties</param-name>
<param-value>
shindig.host={YOUR-INSTANCE-PUBLIC-DNS}
aKey=/shindig/gadgets/proxy?container=default&url=
shindig.port=80
</param-value>
</context-param>
NOTE 2: PHP 版は mod_php を使って構築します。
標準の AMI には PHP はインストールされていませんが、 yum でインストールできます。
他のモジュール (curl の PHP 拡張など) に関してもきっとインストールできるでしょう。
パスの問題に関しては分かりません。
OpenSocial の仕様に基づいた XML を用意し、Web サーバにアップロードします。 例えば、震災発生日からの経過日数を表示するガジェットは次のようになります。
show-elapsed-days-from-east-japan-earthquake.xml
<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="Pray for Japan!">
<Require feature="opensocial-0.8" />
</ModulePrefs>
<Content type="html">
<![CDATA[
<script>
function showElapsedDays() {
var earthquake = new Date(2011, 2, 11),
today = new Date(),
diff = (today - earthquake) / 86400000;
document.getElementById('elapsedDays').innerHTML = Math.floor(diff);
}
gadgets.util.registerOnLoadHandler(showElapsedDays);
</script>
<p>
今日は東日本大震災から<span id="elapsedDays"></span>日目です。<br />
1日も早い復興に向けて頑張りましょう!
</p>
]]>
</Content>
</Module>
XML とその中身の JavaScript のインデントの数に悩みますが、 XML は構造が分かるようにインデント2、JavaScript は XML とは異なることを明示するためにもインデント4にしてあります。
「Web サーバを持っていない!」という場合には Dropbox の Public フォルダを利用できます。 たとえば、上記の XML は http://dl.dropbox.com/u/1880322/os/show-elapsed-days-from-east-japan-earthquake.xml で参照できるようにしてあります。 Shindig の /gadgets/ifr エンドポイントの url パラメータにこの URL を指定することで、ガジェットを表示できます。
Apache Shindig をインストールし、簡単なガジェットを追加しました。 ガジェットの仕様は OpenSocial で定義されていますので、仕様に沿っておけば、 コンテナの提供者も利用者も同じものを再利用できます。
仕様がひとつで同じソフトウェアを使うなら、みんなでコンテナを共有すれば良いのでは?という疑問もあるかもしれませんが、 サービスごとにコンテナを提供することにより、サービス固有のユーザー情報を参照できるのは大きなメリットです。 サービスごとにペルソナを使い分けているユーザーには特に顕著な違いとなります。 最近流行の「ソーシャルグラフ」が違うためです。 ただ、実際にサービス提供側がバックエンドのユーザー情報と繋いでいくのは大変かもしれませんが。
既存のサービスにガジェットを追加していく、という観点でも仕様が共有されたコンテナは有効です。 「よそのサービスにあるあの機能が足りない」という場合にも、それぞれが OpenSocial に対応していれば簡単に流用できます。 たとえば、災害後の緊急事態用のガジェットが考えられます。 災害後の非常体制での生活を送る中では「大きなシステム改修/移行に踏み切るほどでもないが、一時的に機能を追加したい」 という要望もあると思います。 OpenSocial でガジェットをプラガブルにしておけば、こうした要望には対応できるのではないでしょうか。 今からでは遅い... かもしれませんが、次に役立てることはできると思います。
ガジェットに関しては、すでに多数の iGoogle ガジェットが開発されていますし、XML で記述されていますから時間をかければ中身を確認できます。 エンタープライズシステムだから、うちは SNS じゃないから、といった認識ではなく、今出来ることを少しでも考えていきたいですね。
Hack For Japan が開催されています。
三連休を利用したスプリントで、
ということで、アイデアを見てみます。 モデレーターのデータから生成した HTML はこちらに置いてあります。
http://dl.dropbox.com/u/1880322/hack4jp-moderators.html
アイデアは Google モデレーター で管理されています。 モデレーターは、個人的な感覚だと、閲覧には不向きな印象です。 「プレゼンテーション表示」だと一覧性が向上しますので、ザーッと眺めるだけだとこちらが便利です。
CSV で出力できますので、Excel などで閲覧することもできます。。。 と思いましたが、ダウンロードしたファイルはユニコードをエンコードしていませんでしたので、 普通の人だと日本語を読むことができません。 あと、カラムもズレている印象ですね。
それは困った... ということで、データだけを抜き出して HTML に整形し直します。 とはいえ、この辺りは将来的には改善されることでしょう。
Google スプレッドシートのデータ や プロジェクトリスト (となっていますが、実際には議論のまとめ) もあります。
モデレーターにも API が用意されており、 google-api-python-client もこれに対応しています。 サンプルも同梱されていますので、これをベースにしてデータを抜き出します。 moderator に関するサンプルは2つあります。
OAuth 2.0 系の方をベースにします。 認証フローに関しては Google API Python Client (2010年11月の記事) で書いたものとほぼ一緒です。 認可を許可するスコープが異なりますので、認可の画面は「モデレーターのデータにアクセスしても良いですか?」という表示になります。 認証部分のコードはこんな感じです。 認証キーなどをセットしてから run を呼び出すだけです。
from oauth2client.file import Storage
from oauth2client.client import OAuth2WebServerFlow
from oauth2client.tools import run
FLOW = OAuth2WebServerFlow(
client_id='XXXXXXXX',
client_secret='XXXXXXXX',
scope='https://www.googleapis.com/auth/moderator',
user_agent='moderator-cmdline-sample/1.0')
def main():
storage = Storage('moderator.dat')
credentials = storage.get()
if credentials is None or credentials.invalid == True:
credentials = run(FLOW, storage)
次に、サービスディスカバリの機構を使って moderator 用のサービスオブジェクトを構築します。 (ディスカバリに関しては GData 3 の discovery の中身 (2010年11月の記事) も参考に)
import httplib2
from apiclient.discovery import build
def main():
# .. 上記の認証フロー
http = httplib2.Http(cache=".cache")
http = credentials.authorize(http)
service = build("moderator", "v1", http=http)
モデレーターのデータは次の4種類から成ります。 (Moderator concepts)
それぞれのデータを扱うためにサービスからリソースを生成します。 今回はシリーズとサブミッションを扱います。 シリーズとトピックの ID は、取り扱うモデレーターのページの URL を参考にします。 (10進数表記と16進数表記に注意)
def main():
# .. 上記の続き
sid = 0x6441b # seriesId of target contens.
tid = 0x40 # topicId of target contens.
def print_votes(vote):
print 'Votes: +%d, -%d' % (vote['plusVotes'], vote['minusVotes'])
series = service.series().get(seriesId=sid).execute()
counts = int(series['counters']['submissions'])
print series['name']
print series['description']
print 'Submissions: %d' % (counts,)
print_votes(series['counters'])
per_page = 20
start = 0
def printer(items):
for item in items:
print item['text']
print_votes(item['counters'])
while start < counts:
submissions = service.topics().submissions().list(seriesId=sid,
topicId=tid, max_results=per_page, start_index=start).execute()
printer(submissions['items'])
start += per_page
標準出力に print しただけだと見栄えが悪いので HTML に整形し直します。 ここでは、テンプレートエンジンとして Jinja2 を使います。
次の4種類の情報を表示します。
リンクを生成するときに、ID の文字列を16進数表記にマッピングする必要があります。
テンプレート部分
import jinja2
BASE = u'''
<!DOCTYPE HTML>
<html lang="ja"><head>
<meta charset="UTF-8" />
<meta name="viewport" value="width=device-width,user-scalable=no" />
<title>Hack For Japan Moderators - Mirror</title>
</head><body><div id="wrapper">
<div id="header">{% block series %}{% endblock %}</div>
<div id="main">{% block submissions %}{% endblock %}</div>
<div id="footer">Generated around {{ now }}.</div>
</div></body></html>
'''
TEMPLATE = u'''
{% extends "base.html" %}
{% block series %}
<a href="{{ series.id|to_original_url }}">
{{ series.name }}</a> - Mirror
<p>{{ series.description }}</p>
<p>Submissions: {{ series.counters.submissions }},
Total Votes: +{{ series.counters.plusVotes }},
-{{ series.counters.minusVotes }}</p>
{% endblock %}
{% block submissions %}
{% for item in submissions %}
<div>
<p>{{ item.text }}</p>
<p>Votes: +{{ item.voteCounters.plusVotes }}
-{{ item.voteCounters.minusVotes }}
<a href="{{ item.id|to_original_url }}">->Go</a>
</p>
</div>
{% endfor %}
{% endblock %}
'''
TEMPLATES = {'base.html': BASE, 'template.html': TEMPLATE}
書き出す部分
from datetime import datetime
def main():
# .. 上記の続き
url = 'https://www.google.com/moderator/#15/e=%s&t=%s.%s' % (hex(sid)[2:],
hex(sid)[2:], hex(tid)[2:])
def to_original_url(id):
if 'submissionId' in id:
return '%s&q=%s.%s' % (url,
hex(int(id['seriesId']))[2:], hex(int(id['submissionId']))[2:])
return url
params = {'now': datetime.now(),
'series': series, 'submissions': submissions}
env = jinja2.Environment(loader=jinja2.DictLoader(TEMPLATES))
env.filters['to_original_url'] = to_original_url
tpl = env.get_template('template.html')
writer = open('hack4jp-moderators.html', 'wb')
writer.write(tpl.render(params).encode('utf-8'))
writer.close()
あとは出力された HTML ファイルのデザインなどを調整すれば冒頭の HTML の出来上がりです。 使い捨てスクリプトなので jinja2.DictLoader を使っていますが、 通常は jinja2.FileSystemLoader などを使って HTML は別ファイルで管理した方が良いでしょう。
NOTE:
Google モデレーターのデータを HTML に変換しました。 ざっくり拝見したところ、1日で成果が出そうなことから中長期的に継続していかなくてはいけないことまで、 多種多様なアイデアがありそうです。
一方でバックエンドの仕組みとしては、汎用的に使えることと、ローカルな情報を絞り込むことの兼ね合いかなぁ、と思います。 いずれにしても、相互に交換できるデータを使って、みんなで共有できると良さそうです。
Google App Engine / Java の開発では Eclipse が必須のような扱いですが、 HTML / JavaScript / CSS を書き換えて表示を確認したいだけの場合には面倒に感じます。 SDK をインストールすると、コマンドラインツールを使って開発用サーバを立ち上げることができます。 Eclipse を起動する必要がありませんので、マシンのメモリ使用量を抑えられます。
手順は Google App Engine のページに記載されていますが、 しばらくすると存在自体を忘れてしまいそうなのでメモしておきます。
「プログラミング Google App Engine」の "2.1 SDK のセットアップ" の最後にある "Eclipse なしでの Java SDK のインストール" でも少しだけ触れられています。
手元の環境では SDK を /usr/local/appengine-java-sdk-1.4.2 に移動させ、 バージョン番号を隠すようにシンボリックリンクを用意しました。 mv でも構わないと思いますが、複数の SDK を使えるようにしておくためには ln の方が良さそうです。 また、SDK のバージョンによって demos などが異なりますので、参照用に残しておくこともできます。
$ cd /usr/local $ sudo ln -s appengine-java-sdk-1.4.2 appengine-java-sdk
中身は次の構成になっています。
$ tree -d -L 2 /usr/local/appengine-java-sdk
/usr/local/appengine-java-sdk
|-- bin
|-- config
| |-- sdk
| `-- user
|-- demos
| |-- guestbook
| |-- helloorm
| |-- helloxmpp
| |-- jdoexamples
| |-- mandelbrot
| |-- mediastore
| |-- new_project_template
| |-- shardedcounter
| |-- sticky
| |-- taskengine
| `-- taskqueueexamples
|-- docs
| |-- javadoc
| |-- testing
| `-- tools
|-- lib
| |-- agent
| |-- impl
| |-- shared
| |-- testing
| |-- tools
| `-- user
`-- src
`-- orm
29 directories
bin ディレクトリには4つのスクリプトが用意されています。 .cmd は Windows 用、 .sh は Linux などのためですから、実質的には2種類のスクリプトです。
$ ls /usr/local/appengine-java-sdk/bin appcfg.cmd appcfg.sh* dev_appserver.cmd dev_appserver.sh*
開発用サーバを起動するスクリプトは dev_appserver で、引数には war ディレクトリを渡します。 ここではプロジェクトのディレクリに移動してあるものとして考えていますので war だけですが、 必要ならば絶対パスの方が間違いが少ないかもしれません。
$ /usr/local/appengine-java-sdk/bin/dev_appserver.sh war
スクリプトは -h オプションでヘルプが表示され、 -p オプションでポート番号を指定できます。 -a オプションでバインドするアドレスを指定できますので、他のマシンから表示を確認するためにも使えます。
$ /usr/local/appengine-java-sdk/bin/dev_appserver.sh -a 192.168.0.2 -p 8888 war
コマンドラインから開発用サーバを立ち上げました。 まぁ、今どきそんなメモリ使用量を気にするようなマシンで開発するな、という話かもしれませんが、 たまにはそういうこともあります。たぶん。 あと、フロントエンドの開発者に Eclipse を強要しない、という点では良いと思います。 CI (Continuous Integration) との相性も良いでしょうね。
昨年末に Google Buzz で Activity Streams (2010年12月の記事) を試してみました。 当時は認可の形式が OAuth 1.0 ベースでしたが、最近になって Google が OAuth 2.0 (Draft 10) に対応し始めました。 それに合わせて google-api-python-client にもモジュールが追加されましたので、試用してみます。
なお、InfoQ の記事にもある通り、OAuth 2.0 の最新版は Draft 13 なので、ライブラリのコードは変更されていくかもしれません。
とりあえず、何もデータが存在しないと寂しいので Buzz にデータを投稿します。 Gmail を Web ブラウザから使っている場合には、Buzz を有効化すると「受信箱」(Inbox) の下に Buzz が出現します。 メニューで Buzz を選択すると投稿用のフォームがありますので、そこに適当なメッセージを入力して送信できます。 送信するときには公開範囲を選択できます。
いちいちブラウザでメールボックスを開かないよ... という場合には Twitter へのポストを Buzz に流すこともできます。 クローリング (?) も早く、おそらくリアルタイム検索と同じくらいのタイムラグ (つまり、すぐさま) で反映されます。
もうひとつ、自分がちょっと良いかも、と感じているのが、Google Reader からの共有設定です。 新聞の代わりにフィードを読んでいるような人だと、1日当たりに数百から千件くらいの情報 (もっと多いかも) に目を通すと思いますが、 情報量が多くなるとそれを選別することが大変になります。 「あとで読む」という場合にはスターを付けることが一般的だと思いますが、 「読み終わってスクラップブックに保存したい」場合にはどうでしょうか。 ひとつの方法は、ブックマークに登録することになるでしょう。 ブラウザのブックマーク、ブラウザ間の同期サービスや Evernote、ソーシャルブックマークサービスなど、いくつかの方法があります。 Google Reader からの共有がちょっと便利なのは、ブラウザ間で同期させる必要がなく、携帯電話からも使えて、別のサービスにジャンプする必要がないことです。 あと、キーボードショットカット ("S") が使えるのも嬉しいところです。 記事を読むときは一気にまとめて読んでしまい、ちょっと気になったことを共有しておくと、 一日の終わりや週の終わりにその期間のことを振り返りやすくなります。
ということで、こじつけ感が強いことは否めませんが、Buzz にデータを流せるようにしておきます。
Google API Python Client (2010年11月の記事) をインストールします。 Mercurial で pull (https://google-api-python-client.googlecode.com/hg/) したディレクトリで、サンプルを実行します。 README は更新が追いついていないと思いますので読まなくとも。
実行するのは次の二行 (PYTHONPATH=. python samples/oauth2/buzz/buzz.py なら一行) です。
$ source setpath.sh $ python samples/oauth2/buzz/buzz.py
基本的なフローは前述のブログ記事にある通りですが、OAuth 2.0 では「確認コード」を要求されません。 代わりに次のメッセージが表示され、コマンドライン側では認証が完了しています。
The authentication flow has completed.
これが OAuth 2.0 の嬉しいところですが、細かい部分は省略します。 実装は oauth2client.tools.run にあると思いますので、時間があるときにでも。 リダイレクトして戻って来た場合の HTTP サーバをローカルで立ち上げています。
認証フローが終わると buzz.dat というファイルが生成されます。 認証トークンなどを保持している Python オブジェクト (oauth2client.client.OAuth2Credentials) を pickle でシリアライズしたものです。
ひとつ注意すべきなのが、このサンプルは認証が成功すると書き込み処理も実行することです。
とはいえ Web 画面からすぐに削除も変更もできますので、ちょっと buzz を撒き散らしてスミマセン、ということで。
NOTE:
Python で OAuth を扱う上で紛らわしいのが、モジュール名です。 oauth は OAuth 1.0 系のレファレンス実装で、 oauth2 はその改良版です。 モジュール名に 2 が付きますが、仕様のバージョンは 1 系です。 Google のライブラリにおける OAuth 2.0 用のモジュール名は oauth2client です。 ソースツリーには oauth2 と oauth2client の両方が存在します。
それでは Activity Streams の解析を... といきたいところですが、 Google の新しい API は discovery の仕組みを使います。 これに関しては GData 3 の discovery の中身 (2010年11月の記事) を少しだけのぞいて、 apiclient.discovery.build の使い方をつかめれば良いと思います。
さて、 build() でサービスのインスタンスを受け取ると、 activities() メソッドの戻り値を使って Activity Streams を操作できます。
def main():
storage = Storage('buzz.dat')
credentials = storage.get()
http = httplib2.Http()
http = credentials.authorize(http)
service = build("buzz", "v1", http=http, developerKey="XXXXXXXX")
activities = service.activities()
Activity Streams の Resource に対しては CRUD を基本とした操作 を実行できます。 一覧を取得するためには list 、新規にポストするためには insert などです。
たとえば、自分が Buzz に投稿した Activity を10件取得するためには次のコードを実行させます。 scope='@consumption' にすると、自分がフォローしているユーザーの Activity も取得します。
activitylist = activities.list(
max_results='10', scope='@self', userId='@me').execute()
Activity のストリーム (=流れ) ですから、10件取得してもそれで終わりだとは限りません。 たいていの場合は、何らかの条件を満たすまで、同じように Activity を手繰り寄せたくなると思います。 Resource クラスでは list_next() がこれを実現してくれます。
if activitylist: activitylist = activities.list_next(activitylist).execute()
取得した activitylist は Python の辞書形式になっており、 items キーで Activity の一覧にアクセスできます。
writer = sys.stdout
for item in activitylist['items']:
print >>writer, item['updated'], item['object']['content'].encode('utf-8')
item には source キーがあり、これによって投稿元の情報が分かります。 例えば、Twitter から流しているアイテムの元のリンクを表示させたい場合は次のようになります。
if 'source' in item and item['source']['title'] == 'Twitter':
tag = item['crosspostSource']
href = tag[tag.rfind(':') - 4:] # 4-char back for "http"
print >>writer, "Original:", href
あとはこれらを整形してあげれば活動のまとめを生成できます。 旅行の記録や日常の日記の下地としては便利ではないでしょうか。
Google Buzz にデータを投稿し、Activity Streams を受け取りました。 google-api-python-client を使うと簡単に API を実行できます。 XML の細部に立ち入る必要がないのは嬉しいことです。
Google でも OAuth 2.0 をサポートし始めたことにより、ライブラリにも機能が追加されています。 ライブラリのレベルでキレイに抽象化されていますので、アプリケーション開発者視点では特に違いを意識することなく認証フローを実装できます。 アプリケーションの利用者にとっては、OAuth 2.0 によって PIN コードを入力する必要がありませんので、ひと手間省けて導入の敷居が下がることが期待できます。
情報の流量が増加するとそれを整理することが大変になってしまい、たくさんの Web サービスを使い始めると情報が発散しがちです。 サービスによっては、操作感の違いに戸惑うこともあるかもしれませんし、サポートされるデバイスが少ないかもしれません。 しかし、最近はどのサービスも API を整備してきており、複数のサービスの情報をひとつのストリームに束ねることも現実的になってきました。 その選択肢のひとつとして Google Buzz は便利だな、と思います。 あとはソーシャルグラフなのかなぁ、と思いますが、それが一番大変だったり...
とはいえ、今回の大震災は多くの人が連絡手段の冗長性について考える契機になったはずです。 電話がダメならメール、メールがダメなら Twitter、Twitter がダメなら PFIF (People Finder Interchange Format)、 PFIF で追いつかなければ人力での文字起こし... 結局、最後は人と人とのつながりですが、それをまとめる一助として Activity Streams が使えるかもしれませんね。
Python の pprint モジュールを使って activitylist 変数を書き出してみると次のデータを得られます。 コマンドラインからポストしたものと、Twitter から流したものです。
{u'id': u'tag:google.com,2010:buzz-feed:self:posted:XXXXXXXX',
u'items': [{u'actor': {u'id': u'XXXXXXXX',
u'name': u'Shigeru Kitazaki',
u'profileUrl': u'XXXXXXXX',
u'thumbnailUrl': u'XXXXXXXX'},
u'id': u'tag:google.com,2010:buzz:XXXXXXXX',
u'kind': u'buzz#activity',
u'links': {u'alternate': [{u'href': u'XXXXXXXX',
u'type': u'text/html'}],
u'liked': [{u'count': 0,
u'href': u'XXXXXXXX',
u'type': u'application/json'}],
u'replies': [{u'count': 2,
u'href': u'XXXXXXXX',
u'type': u'application/json',
u'updated': u'2011-03-17T09:32:07.656Z'}],
u'self': [{u'href': u'XXXXXXXX',
u'type': u'application/json'}]},
u'object': {u'content': u'XXXXXXXX',
u'links': {u'alternate': [{u'href': u'XXXXXXXX',
u'type': u'text/html'}]},
u'originalContent': u'XXXXXXXX',
u'type': u'note'},
u'published': u'2011-03-17T09:28:09.000Z',
u'source': {u'title': u'Google API Client Example App'},
u'title': u'XXXXXXXX',
u'updated': u'2011-03-17T09:28:09.580Z',
u'verbs': [u'post'],
u'visibility': {u'entries': [{u'id': u'G:@me:@public',
u'title': u'Public'}]}},
{u'actor': {u'id': u'XXXXXXXX',
u'name': u'Shigeru Kitazaki',
u'profileUrl': u'XXXXXXXX',
u'thumbnailUrl': u'XXXXXXXX'},
u'crosspostSource': u'tag:twitter.com,2007:XXXXXXXX',
u'id': u'tag:google.com,2010:buzz:XXXXXXXX',
u'kind': u'buzz#activity',
u'links': {u'alternate': [{u'href': u'XXXXXXXX',
u'type': u'text/html'}],
u'liked': [{u'count': 0,
u'href': u'XXXXXXXX',
u'type': u'application/json'}],
u'replies': [{u'count': 0,
u'href': u'XXXXXXXX',
u'type': u'application/json',
u'updated': u'2011-03-17T09:02:12.000Z'}],
u'self': [{u'href': u'XXXXXXXX',
u'type': u'application/json'}]},
u'object': {u'content': u'XXXXXXXX',
u'links': {u'alternate': [{u'href': u'XXXXXXXX',
u'type': u'text/html'}]},
u'originalContent': u'XXXXXXXX',
u'type': u'note'},
u'published': u'2011-03-17T09:02:12.000Z',
u'source': {u'title': u'Twitter'},
u'title': u'XXXXXXXX',
u'updated': u'2011-03-17T09:02:12.000Z',
u'verbs': [u'post'],
u'visibility': {u'entries': [{u'id': u'G:@me:@public',
u'title': u'Public'}]}}],
u'kind': u'buzz#activityFeed',
u'links': {u'next': [{u'href': u'XXXXXXXX'}],
u'self': [{u'href': u'XXXXXXXX',
u'type': u'application/json'}]},
u'title': u'Google Buzz Self Feed for Shigeru Kitazaki',
u'updated': u'2011-03-17T09:32:07.656Z'}
XML の名前空間の違いを意識することなく変換してくれますので、 とっかかりとしては分かりやすいと思います。
記憶が風化しないように、引き続き Google Buzz からの転記です。 東北沖太平洋地震のメモ の続きで、Twitter でのメモ + いくつかのニュースです。
今回の大震災で被災された方には心よりご冥福をお祈りします。 原子力発電所、電気などの社会インフラ、流通関係、そして何より被災地での復興に尽力している方々がたくさんいます。 自分の現実的な行動としては募金くらいしかできませんが、何が起きているか、何を感じたかを時系列で並べておくことも後々に対して必要だと感じます。 日常的なことも織り交ぜながら、少しでも早い復旧を願い、できることをやっていきたいですね。
交通機関のマヒだけかと構えていましたが、大震災を冠する被害の規模になってしまいました。 ただひたすら情報を収集して一日を過ごしました。
テクノロジー系企業の素早い対応が公開されてきました。 また、外国のイベントなどを見ているサイトで写真をまとめて見ると、まるで外国の出来事かのように思えます。 けれども、それが現実に起こっている、ということが二重に衝撃的です。
心強かったのは、たくさんの外国が支援を早々に表明してくれて、救援にかけつけてくれたことです。 情けは人のためならず、という言葉が頭をよぎります。
フットサルに行く予定でしたが、すべての予定をキャンセルしました。 この日は実家に行って休憩することに。気楽に帰れる範囲が幸いしました。 将来的にも電話、メールはパンクする恐れがありますので、インターネット越しで非同期に連絡をとれるようにしておきます。
翌日から「輪番停電」という措置が取られることに決まりました。 一週間の始まりを前に、情報が錯綜しています。
普段から自転車通勤なので、この日は会社に出社しました。 交通網が麻痺しており、緊急業務以外では出社しない会社もあったようです。 会社の業態、規模によって様々な考え方があるでしょうから、判断に迷いながら世の中が動いているように感じました。 「一律で休みにしてしまえ」という論調もありましたが、業務凍結によって業績悪化ではなく会社が傾いてしまっては大変です。難しい問題です。
各種イベントの中止がアナウンスされ始めます。 また、散乱してきた情報をまとめる流れも出てきました。 まとめのまとめ、そのまたまとめ、という状況です。 こうした動きは2ちゃんねるが最も早かった印象です。
三寒四温とは言いますが、急に冬に逆戻りです。 天候も思わしくなく、暖房による電力消費が増えそうです。
夜に大きな地震がありました。 静岡県では震度5〜6程度になったそうです。東京も震度3でした。 原発に関する不安もこの日くらいから強くなってきました。
会社として完全に在宅勤務の日でした。 普段から準備をしておかなかった分、やや不便でしたが、部屋の片付けをしながら気持ちも整理しつつ、といった感じです。
ヨーロッパチャンピンズリーグの決勝トーナメントがありました。 FC東京から移籍した長友選手が所属するインテル対バイエルンの試合では "You'll never walk alone" が合唱されたそうです。 元々はリバプールのホームゲームで歌われてきたものですが、味の素スタジアムでも毎試合歌います。 今回は味スタのバージョンだったそうで、FC東京サポーターは歓喜したことでしょう。スポーツの力ですね。
「買い占め」がちょっとした社会問題化してきました。 トイレットペーパーとガソリンが最も品薄になり、オイルショックの話を彷彿とさせます。 また、為替が大きく動き、1ドル76円台前半に突入しました。 水曜日に予定されていた雑誌の発売が一日遅れました。
そして、三日連続で夜に地震がありました。 「余震」と呼んでよいのか別々の地震なのかが分かりません。 プレートは繋がっているのかもしれませんが、震源地は同じ地域ではありません。 ちょっとした揺れには多くの人が慣れ切ってしまっており、このタイミングで大きな地震が発生した場合が心配です。 また、「地震酔い」の人もいるかもしれません。
今日で震災発生から1週間です。 悲観していても前には進めませんので、できることからコツコツと。 ダライラマのツイートにもある通り、祈りにおいてだけでなく、日々の生活でもお互いに助け合いながら。
It is necessary to help others, not only in our prayers, but in our daily lives
Google Buzz から転記。Twitter からのメモ + いくつかのニュース。
職場の人と顔を見合わせて、やけに揺れますねーみたいな会話。 その後も強烈な余震が続き、帰宅できる人は明るい内に帰りましょう、ということに。
思ったより遥かにヤバいんじゃないの?と思い始めてインターネットをブラウジング。 地震の情報を得ることと並行して、2週間くらい溜まってたニュースを読む。
何となく状況を把握。今回は異常、という認識。海外にも情報が伝わっており、Twitter のトレンドには津波などが増えてくる。 友達からもメールがちらほら。
すっかり暗くなってしまったので、ライフラインを確認する。明るい内にやっておけば良かった。 余震なのか新しい地震なのか分からないが、新潟 (祖父母がいる) でも地震があったとの情報。うーむ。
少しずつローカルなニュースも把握。グーグルが google.org にコンテンツを用意。 お腹がすいたのでコンビニへ行ったが、食料は何もない。
なんとなく家にあったものを食べながら、引き続きニュースを読む。 余震も続く。。
ぼちぼち、良い話も悪い話も流れてくる。 非常事態の時ほど、発信には差が出てくるのは仕方ないが、TSUTAYA と NHK はあまりにも対照的。 冗談を言って売上を考えるか、独断で制度を色々と破っている感じでも、善かれと思うことをやるかの違いは大きい。。
個人的な範囲での無事を確認。 日本は先進国とかの次元を超越していることも確認。
東北地方の被災地は復旧が大変だと思うけど、本当に頑張ってほしい。 先週は茨城と福島の県境辺りまで旅行に行っていたが、おそらく地震直撃。。 M 8.8 は恐るべし。 関東大震災クラス、というか、観測史上最大だったらしい。 天災を防止することはできないけど、これからどうやって復興していくかが大事。 寄付とかは出来るはずなので、ちょっと考える。 今日は地震酔い (?) のような状態で気持ち悪いので就寝zzz
あと、地震とは関係ないけど、ザッピング中に見つけた感動的な話。 気持ちを切り替えるときに読むと前向きになれそう。
今週末はいよいよ J リーグが開幕します!
今年のFC東京は残念ながら J2 ですが、年間チケット (SOCIO) も届いたし、J リーグが運営するマイページへの告知も出た (Jリーグワンタッチパスマイページのオープンについて) し、米本の背番号入りのユニフォームも出来上がった (まだ受け取ってない) ので、後はスカパラのライブとFC東京の開幕ダッシュを観るだけなのですが、開幕戦後のスケジュールを確認し忘れていました。
ということで、iCal ファイルを作って Google Calendar にインポートしました。ガジェット右下の「+ Google カレンダー」をクリックすると自分のカレンダーに登録できるはずです。なお、後半戦はキックオフ時間が未定なので終日予定になっています。
Google Calendar だと...
ということで、公開された情報を共有するには便利ですね。