2012年7月31日火曜日

Google Apps Scriptで連絡先を編集する

Google Apps Scriptで連絡先(Contacts)を編集する方法をメモしておきます。
  • 連絡先を作る
var contact = ContactsApp.createContact("名前", "苗字", "target@youraddress.com");

これだけだと、インスタンスが生成されるだけで、実際に連絡先が
登録されるわけではないので注意です。
実際に登録するには、ContactsApp.addContacts()を呼び出さないといけません。

  • 連絡先のグループを作る
var contactgroup = ContactsApp.createContactGroup("Sample Group");

連絡先のグループを新規に作成します。

  • 連絡先のグループを取得する
var contactgroup = ContactsApp.getContactGroup("Sample Group");

連絡先のグループを名前から検索して取得します。

  • システム標準のグループを取得する
var myContacts = ContactsApp.getContactGroup("System Group: My Contacts");

グループ名に"System Group: "をつけるようです。

ちなみに、
getContactGroup()は、引数の名前のグループが存在しない時はnullを返してくるようです。
getContactGroupById()を使えば、ContactGroupのIDを指定してグループを取得できます。
  • グループを取得する
var contactgroups = ContactsApp.getContactGroups();
for(var i in contactgroups) {
  Logger.log(contactgroups[i].getGroupName());
}

getContactsGroups()を呼び出すと、登録されているグループが取得されます。
戻り値は配列です。標準のグループも取得されるので、システム標準かどうかは
ログに出力すれば、判断できると思います。

  • 連絡先をメールアドレスから検索して取得する。

var contact = ContactsApp.findByEmailAddress('target@youraddress.com');
var contact = ContactsApp.getContact('target@youraddress.com);

注意点としては、リテラル文字列を指定する時は''(シングルクオート)を使わないと
正しく動作しないようです。
  • 連絡先、グループを削除する

連絡先を削除するには、ContactsApp.deleteContact(contact)
グループを削除するにはContactsApp.deleteContactGroup(group)を
呼び出せば削除可能です。


2012年7月25日水曜日

Go言語のWebフレームワーク"goweb"をGAE/Gで動かす

昨日、公開された(?)Go言語のWebフレームワークの"goweb"を早速GAE/Gで動作するか
試してみました。

導入方法は、以下のように行います。

  1. gowebのソースコードをダウンロードする。
    1. go get code.google.com/p/goweb/goweb
    2. Google Codeからダウンロードする。
    • 1の方法を使った場合は、$GOPATH配下か、GOPATHがなければ、Go言語のコマンドがあるディレクトリにダウンロードされます。
    • ダウンロードを行った場合は、任意のディレクトリに展開して下さい。
  2. ダウンロード後展開 or go getで取得したディレクトリ配下のgowebディレクトリをGAE/Gのアプリケーションディレクトリにコピーする。(GAE/Gの場合は、GOPATH変数を認識しないので、手動でコピーしておく必要があります)
  3. 以下のようなプログラムを作成し、実行する。

package testapp

import (
 "appengine"
 "goweb"
 "fmt"
 "net/http"
)

type PeopleController struct {}
func (p *PeopleController) HandleRequest(c *goweb.Context) {
 context := appengine.NewContext(c.Request)
 context.Debugf("PeopleController#HandleRequest Called.")
 fmt.Fprintf(c.ResponseWriter, "You are looking for person with ID %s", c.PathParams["id"])
 fmt.Fprintf(c.ResponseWriter, "\nServerSoftware = %s", appengine.ServerSoftware())
}

func init() {

 var peopleController *PeopleController = new(PeopleController)
 goweb.Map("/people/{id}", peopleController)

 goweb.ConfigureDefaultFormatters()
 http.Handle("/", goweb.DefaultHttpHandler)
}

gowebパッケージはapp.yamlがあるディレクトリにコピーしておけば良いです。

URLは、http://localhost:8080/people/testみたいな感じにすると、結果が返ってくる事が
わかると思います。

また、appengineのAPIを実行する場合にappengine.Contextを取得したい時は、
HandleRequestメソッドの引数cからappengine.Contextを生成できます。

context := appengine.NewContext(c.Request)

ソースコードもこれから読んでみようと思っていますが、フレームワークを利用せずに
作成するよりはだいぶ楽かもしれないですね。

ちなみに、gowebのディレクトリの中に、goweb_testappディレクトリがあって、その中には
以前のGAE/Gのバージョンで試していた形跡があります。
api_version: 2なので、1.5.3以前かな?


2012年7月16日月曜日

Ubuntu 12.04にgolangを”ソースから”インストールする。

久しぶりにデスクトップPCを使おうと思ったので、
眠っていたPC(Ubuntu11.10→12.04にアップグレード)に

golangの環境をインストールしました。

ちなみに、ソースからのインストールです。

以下が手順です。

1. まず、コンパイルのための環境をインストール
$ sudo apt-get install gcc libc6-dev

2. Go言語のソースコードはMercurialで管理されているので、コマンドをインストール。
$ sudo apt-get install mercurial

3. 環境変数の設定
export GOROOT=[ソースコードがあるディレクトリ]
export PATH=$PATH:$GOROOT/bin

4. Go言語のコンパイル
$ cd $GOROOT/src
$ ./all.bash

5. Go言語コンパイラの動作確認

$ go version
go version go1.0.2(という感じの出力があればOK)

package main

func main() {
    println("Hello World")
}

とりあえず、Hello Worldを出力する、適当なサンプルを作って。

$ go run hello.go
Hello World

となれば、成功です。

以前の知識と変わっている(?)点

  1. $GOARCHがなくてもgolangのコンパイルが可能。(つけたらつけたで何か変化するかな?)

ということで、Let's try golang!!

そして、Enjoy golang!!


2012年7月12日木曜日

DevFestX Japan 2012 Summerの中国会場を担当しました。

7/8(日)にDevFestX Japan 2012 Summerが開催されました。
参加者の皆様、スタッフ、協力してくれた福山のみなさん
お疲れさまでした。そしてありがとうございました。
今後もよろしくお願いします。

会場の様子は中国GTUGのサイトに写真を掲載していますので
ごらんください。

そこで、中国GTUGの名前が、GDG中国に変更される事が発表されました。
他にもGoogle Developersブランドのいろんなサービス、イベント、コミュニティが
新設されています。
(詳しくは、DevFestX Japan 2012 Summerの講演スライドをご覧ください)

日本だけ存在していた「Google API Expert」が世界中で認定されることになり、
名称も「Google Developer Expert」に変わっています。
中国会場には、小川真一(@shin1ogawa)さんに来ていただいて、
いろいろな話が聞けて良かったと思います。
いい刺激にもなりました。

本当は、イベント中の講演をじっくり聞きたいところだったのですが、
主催者のジレンマ(?)として、会場の空気、状況、その他色々な対応を
するために、話をほとんど聞けない(聞いているが、頭には残らない)ので、
後で、Google I/Oの動画(英語)をじっくり見ようと思います。

小川さんと話をしている中で、印象に残ったのが、
「おがわさんに質問する97のこと」について、

回答が

  1. 97も質問するな
  2. ググレカス(これは、私が言ったか)
  3. ソース読め

ものすごく、厳しい正しい回答で、面白かったと思います。

後は、「GAEはGoが最強」というお言葉が聞けて、大満足しております。
が、ネックは「テスト」(これも私も同じ意見でした。テストができるから
仕事では、Javaを選択してます)
※Python版のテストはどうしているんだろうか。小川さんに聞くのを忘れていた。

脱線すると、Go言語にもgo testという機能があって、
言語自体にテスティングフレームワークが含まれているのですが、
パッケージのテストしかできないことと、GAE/GのSDKのgoコマンドに
パスを通して、go testを実行し、仮に動作したとして、GAEのAPIを
実行してくれるのかどうかがよくわかっていない(どこかで試してみないとなー
と思っています。)ですが、試してみる価値はあるかもしれません。

あと、「appengine ja nightを開催してくれれば、(参加者が20名以上であれば)また来れる」というありがたいお言葉を頂いたので、今は西は京都までなので、
是非、中国地方でも開催を!と言われました。
やっぱり、「night」だから、夜やるのかな…?

さて、運営サイドの話題に戻すと

中国会場の参加者は、事前申し込み27人/当日参加24人/懇親会参加17人と
いう結果でした。
今回は前日に岡山で大きなイベントがあったこともあり、岡山での告知は
少し抑えました(週末両方をイベント参加にするのは大変だと思ったので)

また、本当は岡山で開催かな?と思っていたのですが
条件に合う場所が空いておらず、たまたま、福山大学の先生から別件の用事を
頼まれたので、交換条件で、ついでに会場をお借りしました。

会場が福山に決まったので、目的としては

  1. 福山の技術者の発掘
  2. 勉強会等のスタートアップ
  3. 会場の選択肢を増やす
  4. (会場を借りた見返りとして)福山大学の宣伝

と言った、目的をもって準備を進めました。

実際には広島(市内)、岡山、東京の方、福山大学の生徒が大半で、
技術者の参加が少なかったので、残念な点でした。
原因は、告知が行き届かなかったのか、曜日が悪かったのか。。
(土曜日開催の方が、傾向として良いみたい(?))

当日は、事前テストではうまくいっていた機材のトラブルがあり、
バックアップ担当の松原さんの機材を借りました。
そして、Webカメラも6/23のイベントでは超安定していたのに、
なぜか映像が白くなって、使えなかったので、小川さんが持参した
Webカメラを使わせていただきました。
ネットワークに関しては超安定していたので、中国会場で
回線がおちるなどの現象には遭遇しませんでした。
アンケートに関しても、全体的に満足していただいたようで、
大成功だったと思っています。

良い点、悪い点色々ありましたが、今後のイベントにつながれば
良いのではないかと思っています。

福山の技術者の盛り上がりに関しては、福山のポテンシャルに
期待しています。

2012年7月11日水曜日

第14回勉強会@広島を開催しました。

blogに残すのが、だいぶ遅くなってしまいましたが、
第14回中国GTUG勉強会を広島で開催してきました。

今回は「Google Docs」をテーマに
.NET勉強会/ヒーロー島の上本さんにGoogle DocsとMicrosoft Office Web Appsを
比較していただく話題を展開していただきました。

ポイントを整理すると、
  • Google Apps Scriptはすばらしい。
  • Googleの技術はAPIが充実している
  • Microsoft Officeの方もそのうちマクロ機能は実装されるだろう。
という話がありました。Google DocsもMicrosoft Office Web Appsも
双方に利点があるし、今後の機能拡張に注目ではないでしょうか。

第14回の勉強会では、Hangoutを使って、東京のGoogle Apps API Japan Group
皆さんとつないで、ハンズオンにチューターとして協力していただいたり、
GASを利用してできることを発表していただきました。
(夜を徹して資料を作成していただいた(?)みたいで、本当にありがとうございます)

さて、今回は私がハンズオンの講師として
「イベント管理ツール」をテーマにして、メールの送受信、トリガーあたりを
使ってみました。
率直な感想として、「GASは遅い」というイメージがついてしまったかな?と
思ったり思わなかったり。
(実際、遅延したのが原因かわからないが、ソースコードが反映されずにうまく動作しないということもあって、フォローが大変でした。)

で、現場にいた私も感じていましたが、参加者の皆さんが疲れてしまって、
「詰め込みすぎ」感がでてしまったかな?と思います。
(実際にそういう意見も聞こえました)

Go言語のハンズオン(第13回勉強会)の時は「シナリオ」がなかったので、
置き去り感がでてしまったので、シナリオを重視したのですが、
ちょっと、ボリュームが多かった(休憩が少なかった?)ようです。。
やっぱりハンズオンは難しい。

当日の資料に関しては、中国GTUGのサイトを見て頂ければ公開してありますので
参考にしてみてください。

Google Apps ScriptもGoogle I/Oでだいぶ進化したので、
新しく追加された機能を利用するか、これまでの機能について
紹介するか。
イベントを行う上での幅は広がったかな?と思っています。

Google Apps Scriptは無料だし、質問に答えてくれる方もたくさんいらっしゃるので
Twitter / Google+どちらでも、#gasjaを付けて質問をすれば
困ることはないと思います。

2012年6月5日火曜日

GAE/Gで時間のチェック(Datastore編)

GAE/GのSDK 1.6.4からGo Ver 1が適用され、
時間をデータストアに保存する時は、datastore.Time(int64のtype。今は削除されている)では無く、time.Timeが利用できるようになっています。(参考Blog記事

time.Timeをデータストアに格納しようと思うと、普通に日時が設定されていれば問題ないのですが、
例えば、データストアのプロパティの追加した時など、日時のプロパティに時間が設定されていない時があります。

日時が設定されていないまま、Putを行うと、"time value out of range"のエラーが
発生してしまいます。

これを回避するためには、

  1. PropertyLoadSaverのLoadかSaveで日付をセットする。
  2. リリース前にデータの移行をしておく。(未設定の日時が発生しないようにしておく)
のどちらかになると思います。2のケースだと全件Putされるので問題ないと思いますが、
移行プログラムでデータを移行させない事の方が多いと思います。

したがって、PropertyLoadSaverインターフェイスを実装して、以下のように、
時間がZeroかどうかを判定するロジックを入れておかないと、Putが成功しません。
判定メソッドはGo言語標準で用意されているIsZero()を利用します。
LoadとSaveどちらで設定するのも構わないと思いますが、ここでは、Load時に設定する
方針にしてみます。(if文1つなら処理コストはあまり変わらないだろうと言っても、読み込み時に毎回実行するのは無駄だ。と思われる場合はSaveで実装するのが良いでしょう)

func(hoge *Hoge) Load(c <-chan datastore.Property) error {
        // データストアから読み出し。
 if err := datastore.LoadStruct(hoge, c); err != nil {
  return err
 }

 if hoge.Date.IsZero() {
  // データがない(0000/01/01状態)ものは現在時刻にするなど。
  hoge.Date = time.Now().Format(TimeFormat)
 }

 return nil
}

データが既に入っている場合はIsZero()がfalseになると思われるので、
特に時間の設定は不要(データストアから読みだした時点で代入されている)でしょう。


GAE/GでバッチGetをした時の注意点

最近、Androidアプリ「ええことなかったわー(β)」を開発したのですが、
実は、データを貯めているサーバ側はGAE/Gで開発しています。

本当に、レスポンスとしては早いですよ。
今のところ、リクエストに対するレスポンスは、
ms単位の時間でレスポンスを返しています。
(GAE/Jでも仕事でサービスを利用していますが、レスポンスの内容が全然違いますが、1s前後のオーダだったりするので…)

先ほど、Admin ConsoleでのPerformanceを最弱にしていなかったので、
まだユーザ数も少ないので、最弱(Max Idle Instance = 1+Min Pending Latency = automatic)にしてみました。その結果は後日…ということで。

さて、本題のバッチGetを利用するときの注意点なのですが、
結論から言うと、「指定したキーのエンティティが1件でも無いと、appengine.MultiErrorを返す」ということです。
実際には、以下のようなコードになると思いますが

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
// 何かのエラー
}

全てのキーのエンティティがあれば、エラーの判定にならない(戻り値=nil)なのですが、
1つでもエンティティが存在しない場合、全てエラー(戻り値 = appengine.MultiError)となります。
(おかげで、全部エラーレスポンスに…)

で、これは困ったという事で、以下のようなチェックを行うことになります。

data := make([]HogeEntity, len(keys))
if err := datastore.GetMulti(c, keys, data); err != nil {
 if _, ok := err.(appengine.MultiError); ok {
  // バッチGetだと指定したキーのエンティティが1個でもない場合、MultiErrorが返る。
  c.Infof("Hoge NoSuchEntity")
  return keys, data, nil
 } else {
  c.Errorf("Batch Get Error %s", err.Error())
  return keys, nil, err   
 }
}

実際、appengine.MultiErrorが返されても、存在しているデータは取得できるので
取得したデータが構造体にセットされている部分と、空の部分がsliceに存在する
状態になるので、後で、空の部分を除外するなど、編集処理が必要となります。

ちなみに、appengine.MultiErrorはtype MultiError []errorと定義されていますので
複数のエラーが含まれた状態になることも考慮しておく方が良いかもしれません。
私はGetだけの利用だったので、今のところ、正常なものは利用しています。

MultiErrorの戻り値はGetMulti/PutMulti/DeleteMultiで戻される事があるという
ことなので、(ドキュメントのBasic Operationsに明記)利用するときはappengine.MultiErrorの中身を確認してみてください。