Google Blogger API v3.0を使ってエントリを書き込む

先日、C#からGoogle Blogger API v3.0を使用した。
検索してもサンプルが見当たらなかったので、ブログを書き込むサンプルをメモとして公開しておく。

準備

Google Blogger API v3.0はNuGetからインストールします。

コード

using Google.Apis.Auth.OAuth2;
using Google.Apis.Services;
using Google.Apis.Util.Store;
using Google.Apis.Blogger;
using Google.Apis.Blogger.v3;
using Google.Apis.Blogger.v3.Data;
//...
    static void Main(string[] args)
    {
       // OAuth 認証を行う
        UserCredential credential;
        using (var stream = new FileStream("client_id.json", FileMode.Open, FileAccess.Read))
        {
            credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                    GoogleClientSecrets.Load(stream).Secrets,
                    new[] { BloggerService.Scope.Blogger },
                    "user", CancellationToken.None);
        }

       // Bloggerのインスタンスを取得
        BloggerService service = new BloggerService(new BaseClientService.Initializer()
        { HttpClientInitializer = credential, ApplicationName = "Blogger Convert" });

       // Blogの一覧を取得
       var blogList = service.Blogs.ListByUser("self").Execute();

      // Blogに新しいエントリを作成する
       var newPost = new Post();
       newPost.Title = "blog title";
       newPost.Content = "blog body text
"; newPost.Published = DateTime.Prase("2016-01-01 12:00"); var updPost = service.Posts.Insert(newPost, blogList.Items[0].Id).Execute(); //...

ちなみにバグがあってブログのラベルを設定することは出来ない。もう随分長いこと放置されているので、修正の見込はないと思っておこう。
画像データをアップロードする機能は見当たらない。おそらくはPicasaと連携しろって事なのだとも思うけど・・・Picasaって2016年5月にサービス終了するんだよな。

Google App Engineでインデックスを使う

Google App Engineのキーバリューストア(DataStore)の特徴はSQLライクな構文による検索と、インデックスを使える事です。といっても、一般的なDBMSほどの機能を提供してくれるわけではありません。Google App EngineのDataStoreの特徴を踏まえて上手に使いましょう。
インデックスの制限
DataStoreのインデックスはstlのmapと同様の構造になっており、ソートの為に使用するインデックスと検索のために使用するインデックスは共有されているようです。そのために様々な制限が加わります。
制約1
検索条件で不等号を指定した場合には、検索条件と異なる並べ替えの指定をできません。where区で指定したフィールドは、order by区でも指定する必要があります。

×・・・WHERE col1 > 1 ORDER BY col2
○・・・WHERE col1 > 1 ORDER BY col1, col2

制約2
複数の不等号による比較条件を持つクエリーを実行できません。

×・・・WHERE col1 >= 10 AND col2 > 10

制約3
OR演算子がありません。代わりにIN演算子が用意されていなすが、異なるフィールドを指定したOR検索は出来ません。
制約4
ひとつのアプリケーションで作成できるインデックスの数は  個に制限されている。制限3と合わせて、無闇にインデックスを作成すると規模の大きなアプリケーションでは制約に引っかかる可能性があります。
制約5
セッション分離をサポートしません。一般的なDBMSは他のセッションから更新が行われても、他のセッションは新しいセッションを始めるまで影響を受けませんが、DataStoreでは即時に反映されます。
これらの制約を踏まえて如何にして旨く活用していくかが問題になります。
DataStoreを使いこなすには?
施策1 スクリーニングに留める
GQLによる検索はスクリーニングに留めます。例えば特定条件に一致するデータを日付順で取得したい場合には、日付によるソートのみをGQLで行い、条件に一致するか否かはコードで記述します。
施策2 頁番号フィールドを作る
大量のデータをすべて読み出しながら処理を行う場合には、一度に全てのレコードを読み出すことは出来ません。先頭レコードと読み出すレコード数を指定して、数百レコードずつ読み出して処理を行うことになります。この場合、処理中に他のプロセスからレコードの追加、削除が行われると、同じ行を二回処理したり、処理されないレコードが発生する恐れがあります。
これを防ぐために頁番号フィールドを作成して、数百レコード格納する毎に頁番号を繰り上げるようにします。読み出すときには、頁番号を指定することで一度に処理可能なレコード数を読み出します。頁番号を繰り上げながら順次読み出すことで、途中でレコードが追加されても最後まで読み出すことが出来ます。
施策3 検索条件、ソート条件の分離
検索条件やソート条件を別のエンティティに分離します。それぞれのエンティティ毎に検索・ソートを行うことで、全体として目的の結果を得ます。
例えば会員情報を格納したエンティティから年齢18歳以上の人を都道府県毎にソートして取得したい場合を考えてみます。都道府県を格納したエンティティからソートした状態でレコードを読み出します。次に会員情報に対して県名と年齢を検索条件として読み出します。ソートされた県名順にデータを読み出していくので、結果的に件名でソートされた18歳以上の会員一覧を得られます。
このように複数回の検索に分離することで望んだ結果を得ます。
施策4 検索を使わないように設計する
実は王道かもしれません。GQLに頼らなくても望みのデータを取得出来るようにエンティティを設計します。
この話は、別の記事にて。

Google App EngineのDataStoreインデックスの削除

Google App EngineのDataStoreに一度作成したインデックスを削除するには、下記のコマンドを実行します。
appcfg.py vacuum_indexes [プロジェクトパス名]
例:appcfg.py vacuum_indexes “C:\Users\narita\workspace\sample”
ただしこのコマンドはプロジェクトで作成したインデックスのうち、index.yamlに登録されていないインデックスを、すべて一括して削除してしまうので注意が必要です。

Google App EngineでCookieを使う

Google App EngineでCookieを使う場合には、Pythonの提供するCookie関連クラスを使うことは出来ない。変わりに「self.request.cookies」と「self.response.headers.add_header」を使うことになる。
Cookieの書き込みには「self.response.headers.add_header」を使用する。以下のようにheadersコレクションを直接に追加する以外に無いようだ。

myCookie = 'name=%s; expires=Tue, 1-Jan-2030 00:00:00 GMT;' % myName
self.response.headers.add_header('Set-Cookie', myCookie )

Cookieの読み込みには「self.request.cookies」を使用することが出来る。以下のようなコードで読み出すことが出来る。

myName = self.request.cookies.get('name', '')

Google App Engine 習作、オセロゲーム

Google App Engineを使ってみながら、オセロゲームを作ってみた。本当ならソースコードもと思ったが、弄ったり、戻したりを繰り返した汚いコードなので非公開です。
URLから適当に名前を入力してログインすると、対戦相手が現れるまでの待機画面になります。ほかの誰かがゲームにログインすると、見慣れた升目が表示されて、ゲームスタートです。

Google App EngineでのセッションID生成

意外なところで嵌るものだ。
Pythonでは乱数生成用のエンジンを何種類か積んでいる。Wichman-Hill(WichmannHillクラス)と、メルセンヌツイスタ(randomクラス)、OSの提供する乱数ジェネレータ(SystemRandomクラス)から選ぶことが出来る。セッションIDに使用するなら、予測不可能でビット数の多い乱数を生成できるSystemRandomクラスを選択したいところなのだが、Google App Engineでは対応していないらしい。セキュリティ上重要なハードウェア乱数が提供されていない辺り、本当に片手落ちだと思う。
デバッガ上だと普通に動くので、気が付くのが遅れた。
・・・と言うわけで、メルセンヌツイスタ(randomクラス)で生成する以外の方法はないようだ。

Google App EngineでDjangoメモ

Google App EngineではDjangoというWEBフレームワークを使った開発を行えます・・・そのはずです。とりあえず情報収集中で実現できていないけど、そのメモなどを。
Google App EngineでDjangoを使うには、いくつかの方法があるらしい。
1.Google App Engine Helper for Djangoを使う。
2.Use Django on App Engineを使う。
3.Google App Engine SDK標準のDjangoを使う。
4.標準のDjangoをGoogle App Engineで動かすべく自分で設定する。
3.が理想的な気がするが、標準のGoogle App EngineだとDjangoのver 0.96が起動するらしい。ver1.0やver1.1を動作させるには設定を変更すればよいらしい。でもGoogle App Engineに標準でインストールされているDjangoは古くてセキュリティホール持ちらしい。したがって、DjangoまるごとGoogle App EngineにUpしないと実用には耐えないようだ。
よって最新のDjangoを落としてきて、1.のGoogle App Engine Helper for Djangoを使うか、Use Django on App Engineのパッチを充てるのがもっとも現実的な解となるようだ。

Google App Engineで日本語を使う

日本語を有効にする
Google App Engineのソースコードに日本語を記述すると、下記のようなエラーが表示されます。Google App Engine Launcherでは正常に動作してしまうので、はまりどころです。

: Non-ASCII character 'x93' in file /base/data/home/apps/mikahosi-1/1.338233447893773564/helloworld.py on line 11, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details (helloworld.py, line 11)

日本語で動作させるためには、下記の一行をソースコードの先頭に記述します。

# -*- coding:utf-8 -*- 

UTF-8以外の日本語コードはGoogle App Engine RuntimeとPythonの都合で通らないらしいです。
またPythonで日本語定数文字列を記述するには下記のように先頭にuを追加する必要があります。

text = u"日本語を出力"

Google App EngineをDeployしてみる

Google App EngineにApplicationを作る
最初にGoogle App EngineにApplicationを作成します。Google App Engine LauncherのDashbordボタンをクリックしてGoogle App EngineのWEBサイトを開きます。
AppEngine1
Preiewの画面からCreate An Applicationをクリックして新規にアプリケーションを作成します。この時に指定するApplication Identifierは、ApplicationのNameと一致している必要があります。このアプリケーションの例ではmikahosi-1となります。Application Titleも必ず入力しなくてはなりませんが、こちらは任意の文字列で構いません。
Google App EngineにDeployする
Google AccountからApplicationを作成したら、Google App Engine LauncherのDeployボタンをクリックします。下図のログオン画面が表示されますので、ログオンアカウントパスワードを入力してログインします。ここで項目名がmailとなっていますが、指定するのはメールアドレスではありません。Googleのアカウント名(Google Mailと@より前の部分)を入力します。メールアドレスを指定するとログインできません。
AppEngine2
初めて使用する場合には、CAPTCHA認証を要求される場合があります。その場合にはDeployment To Googleのコンソール表示にURLを表示します。ブラウザで指定されたURLに接続して、CAPTCHA認証を行ってください。
AppEngine3
以上で無事にGoogle App Engineに作成したアプリケーションをアップロードできたはずです。Google App EngineのWEBサイトにて割り当てられたURLに接続してみてください。
ちなみに作成した掲示板サンプルは下記のURLにアップロードしてあります。
http://mikahosi-1.appspot.com/

DataStoreを使ってみる

オブジェクト指向データベース(ODBMS)
クラウド独特のデータストアを使う前に、オブジェクト指向データベース(ODBMS)について知っていると入りやすい。
ODBMSは1980年代後半から、1990年代前半にかけて、様々なオブジェクト指向開発の試行錯誤と研究の中で生まれ、そして殆ど実用される事無く消えつつあった技術です。要は「オブジェクト指向分析の中で表現したデータクラスを、データクラスのままストレージに格納して扱えば、オブジェクトをそのまま表現できて扱いやすいんじゃね?」という発想でした。それまでのストレージはRDBMSに代表される表形式の物で、そこにデータクラスを格納するには非効率な変換処理(ORマッピング)が必要だったのです。
でもODBMSという試みはあまり上手くいきませんでした。ODBMSはリソースを大量に消費するとか、検索が遅いと言った、実装上の問題を抱えている事が多かったのです。その試みはORDBMSやORマッピングやLINQなどとして現在も続いています。そしてクラウドもODBMSを実現するための一つの答えになるものと私は思っています。
初期のODBMSで問題となっていたリソースの消費は、クラウドが安価なリソースを提供することで解決されました。RDBMSとは全く異なるパラダイムの上に成り立っているので、当初は混乱するかもしれません。ですがODBMSはオブジェクト指向開発の効率を大きく高め得るものです。積極的に取り組んでいく価値は高いと思っています。
DataStoreの定義
データの定義は簡単です。以下のようなクラスを定義するだけです。”text”、”date”がメンバ変数です。この二つの値がDataStoreに保存されます。

class GuestComment(db.Model):
text = db.StringProperty(required=True)
date = db.DateTimeProperty(required=True)

データを保存したいときには、クラスメンバのput()を呼び出します。データを読み出す場合はall()で全データを読み出せます。

class MainPage(webapp.RequestHandler):
def post(self):
text = cgi.escape(self.request.get("content"))
text = text.replace("rn", "<br>")
text = text.replace("r", "<br>")
text = text.replace("n", "<br>")
if len(text) > 0:
com = GuestComment(text=text,date = datetime.datetime.now())
com.put()
self.makeHtml()
def get(self):
self.makeHtml()
def makeHtml(self):
self.response.out.write("""
<html>
<body>
<form action="/" method="post">
<div><textarea name="content" rows="3" cols="60"></textarea></div>
<div><input type="submit" value="Sign Guestbook"></div>
</form>""")
comList = GuestComment.all()
for com in comList:
self.response.out.write('Wrote Date : ')
self.response.out.write(com.date)
self.response.out.write("<br>")
self.response.out.write(com.text)
self.response.out.write("<br>")
self.response.out.write('<hr>')
self.response.out.write('</body></html>')

一部のデータだけを読み出したい場合や、並び順を入れ替えて読み出したい場合にはGQLで指定します。RDBMSで使用するSELECT文とほぼ同様の物なので、すぐに理解できると思います。

comList = GuestComment.gql(“ORDER BY date DESC”)

RDBMSのようにリンクを指定するにはどうするのかって?そこがODBMS独特の部分です。例えばユーザーに関するデータを掲示板への書き込みと同時に保存したい場合、そこにはユーザーを表すクラスへの参照を保存する事になります。ReferencePropertyメンバを使って以下のように記述します。

class UserData(db.Model):
name = db.StringProperty()
age  = db.IntegerProperty()
class GuestComment(db.Model):
text = db.StringProperty(required=True)
date = db.DateTimeProperty(required=True)
user = db.ReferenceProperty(UserData)