Couchbase Server 2.0 ドキュメント翻訳に参加しました

Couchbaseへの理解と英語強化の為に、Couchbase Server 2.0のドキュメント翻訳に参加しました。
途中仕事が忙しくなったこともあり、当初自分が予定していたほど翻訳活動に参加できませんでしたが、他メンバの方々のおかげで無事公開されました。

Couchbase Server 2.0 ドキュメント

翻訳チームに参加したのは初めてでしたが、自社ソフトウェアのプルーフリード作業にも活かしていきたいと思います。

卒業と入学

先週末で大学院修士課程を修了しました。科目聴講生時代を含めると2年半通ったことになります。
会社勤めをしながらの大学院であった為、途中半年間の出張もあったりと非常に苦しい時もありましたが、無事乗り切ることができました。何よりもこのような状況を支えてくれた妻に感謝します。

2年間の修士課程から何を得たのかを考えてみると、技術的なことは当然ながら、研究の進め方や考え方・プレゼンテーションといったソフトスキルまで多くのことを得たと実感しています。
自分のスキルを縦軸と横軸で考えると、2年前は縦横どちらも経験相応に伸びていたと思います。但し、複数に伸びている縦軸同士が上手くリンクしていなかったり、横軸の伸びが甘いことから曖昧な知識のもとで何となく進めていた部分がありました。このような状況を打破する為に、もう一度学び直そうと考えました。
結果的に、当初の目的はクリアでき、業務を進めていく中でも自分の様々なスキルが向上していることが日々実感できています。
また、研究として「SNSにおけるプライバシー侵害」をテーマとして、現代社会のプライバシー侵害への考え方、機械による侵害要因分析と検知手法を研究しました。入学前は自分がプライバシー侵害の研究をするとは考えても見なかったですが、今後更なる技術革新が進んでいくなかで本研究テーマは非常に良いテーマであったと思います。当然、1年間の研究からは大きな成果が出せておらず、改善点も多くありますので博士後期課程で同テーマの研究を継続することにしました。
博士課程ではこれまで以上に困難があると推測しますが、3年後に学位が取れるよう研究に邁進していきます。
最後に2年前と同様に博士課程へ進むことに対して、快諾してくれた妻に感謝します。

CouchConf に行ってきました

CouchConf Tokyoに行ってきました。
仕事の都合で午前の部のみとなりましたが、Couchbaseが良く出来ていることを実感したカンファレンスでした。
自分の会社製品とよく似たコンセプトである為、今後の製品としての伸びや自分のスキルの振り方判断になればと思い、少し触っていました。Couchbaseユーザグループでver2.0のマニュアル翻訳に参加したので、これからもっと機能を確認していこうと思います。
それにしても、コンソールの機能・デザインが秀逸です。馬鹿高い自社製品も見習って欲しいところです。
couchbase_top

CouchDB on AWS 最速インストール

CouchDBをインストールをする際に何度かハマってしまったので、AWS上で動かすまでの最速インストール手順を残します。
以下の手順を実行する前に、EC2インスタンスをLaunchしておきます。なお、今回はAmazon Imageを対象としています。

ssh -i [your pem-file] ec2-user@[your ec2-instance]
sudo yum -y update
sudo yum -y install git expect
mkdir git
git clone https://gist.github.com/1171217.git
cd git

vi couchdb-ec2-install.sh
##  you need to change 2 lines for version of couchdb and erlang.
##  For checking CouchDB ver: https://github.com/apache/couchdb/tags
##  For checking Erlang ver: http://www.erlang.org/doc/apps/erts/notes.html

sudo sh couchdb-ec2-install.sh
sudo couchdb start -b
Apache CouchDB 1.2.1 (LogLevel=error) is starting.
Apache CouchDB has started. Time to relax.

Gitからcloneしたら、インストール用のshellスクリプト内のCouchDB, Erlangのバージョンだけ最新に変更して実行します。
(この実行は少し時間がかかる)
あとFutonにアクセスする時に使うAdminパスワードはスクリプト実行結果に出力されますが、
変更する場合は /usr/local/etc/couchdb/local.ini を変更します。事前にshellを変更しておいても良いです。
最後にFutonにアクセスして起動を確認して完了です。

編集後のcouchdb-ec2-install.sh はこのような感じです。(今回はCouchDB:1.2.1, Erlang: 5.9.3.1とした)

#!/bin/bash

#
# This script installs and configures couchdb on a fresh Amazon Linux AMI instance.
#
# Must be run with root privileges
# Tested with Amazon Linux AMI release 2011.02.1.1 (ami-8c1fece5)
#

export BUILD_DIR="$PWD"

# install gem dependencies
yum install gcc gcc-c++ libtool curl-devel ruby-rdoc zlib-devel openssl-devel make automake rubygems perl git-core
gem install rake --no-ri --no-rdoc

if [ ! -e "/usr/local/bin/couchdb" ]
then

  if [ ! -d "$BUILD_DIR/build-couchdb" ]
  then
    # get build-couch code
    git clone git://github.com/iriscouch/build-couchdb
    cd $BUILD_DIR/build-couchdb/
    git submodule init
    git submodule update
  fi

  # run build-couch
  cd $BUILD_DIR/build-couchdb/
  rake git="git://git.apache.org/couchdb.git tags/1.2.1" install=/usr/local
fi

# install our .ini
cat << 'EOF' > /usr/local/etc/couchdb/local.ini
[couchdb]
delayed_commits = false

[httpd]
port = 80
bind_address = 0.0.0.0
socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay, true}]
WWW-Authenticate = Basic realm="administrator"
;WWW-Authenticate = bummer

[couch_httpd_auth]
require_valid_user = true

[log]
level = error

[admins]
EOF

# generate & set the initial password
export ADMIN_PASSWORD=`mkpasswd`
echo "admin = ${ADMIN_PASSWORD}" >> /usr/local/etc/couchdb/local.ini

# allow beam to bind to port 80 (not necessary if you make httpd.port >=1024)
setcap 'cap_net_bind_service=+ep' /usr/local/lib/erlang/erts-5.9.3.1/bin/beam

if [ ! -e "/etc/logrotate.d/couchdb" ]
then
  # add couch.log to logrotate
  ln -s /usr/local/etc/logrotate.d/couchdb /etc/logrotate.d/
  # change to daily rotation
  sed -e s/weekly/daily/g -i /usr/local/etc/logrotate.d/couchdb
  #logrotate -v -f /etc/logrotate.d/couchdb 
fi

# add couchdb user
adduser --system --home /usr/local/var/lib/couchdb -M --shell /bin/bash --comment "CouchDB" couchdb

# change file ownership
chown -R couchdb:couchdb /usr/local/etc/couchdb /usr/local/var/lib/couchdb /usr/local/var/log/couchdb /usr/local/var/run/couchdb

# run couchdb on startup
ln -s /usr/local/etc/rc.d/couchdb /etc/init.d/couchdb
chkconfig --add couchdb
chkconfig --level 345 couchdb on

# done!
echo
echo
echo "Installation complete!"
echo "Couchdb admin password was set to: ${ADMIN_PASSWORD}"
echo
echo "Couchdb is ready to start. Run:"
echo "    sudo service couchdb start"

InfoTalk Summer Workshop 2012(Amazon DynamoDB Hackathon)に行ってきました

InfoTalk Summer Workshop 2012(Amazon DynamoDB Hackathon)に行ってきました。
DynamoDBにさわるのは初めてでしたが、非常に有意義な時間でした。今回はチームに分かれてDynamoDBを使用したアプリを作ったりしました。
このDBの一番の特徴は読み書きのThroughputをコントロール出来る点です。これにより運用コストを低減することが出来ます。
但し、現状はThroughputのダウンが1日1回のみの制限がありますので、注意が必要です。

ハッカソンで作ったものではないですが、DynamoDBがどのように動くのかを確認する為に、
CouchDBに流し込んでいるPythonスクリプトを変更し、DynamoDBに入れ込むようにしてみました。
PythonからDynamoDBを扱う場合は、botoを使用します。なお、本スクリプトを実行前にテーブルは作成済みであることとします。

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

import sys
import tweepy
import json
import time
import jsonpickle
from datetime import datetime
import re
import boto

CONSUMER_KEY = 'YOUR CONSUMERKEY'
CONSUMER_SECRET = 'YOUR CONSUMER_SECRET'
ACCESS_TOKEN = 'YOUR ACCESS_TOKEN'
ACCESS_TOKEN_SECRET = 'YOUR ACCESS_TOKEN_SECRET'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

conn = boto.connect_dynamodb()
table = conn.get_table('twitter')
count = 0

class CustomStreamListener(tweepy.StreamListener):

    def on_status(self, status):
        results = {}
        try:
            if re.match(u"[ァ-ヶーぁ-ん]" , status.text): 

                pickled = jsonpickle.encode(status)
                results = json.loads(pickled)
                del results['_api']

                item = table.new_item(
                    hash_key=int(results['id_str']),
                    range_key=20120729,
                    attrs={'user': results['author']['screen_name'],
                           'text': results['text']})

                item.put()
                #print item

        except Exception, e:
            print >> sys.stderr, "Encountered Exception:", e
            pass

    def on_error(self, status_code):
        print >> sys.stderr, "Encountered error with status code:", status_code
        return True

    def on_timeout(self):
        print >> sys.stderr, "Timeout..."
        return True 


streaming_api = tweepy.streaming.Stream(auth, CustomStreamListener(), timeout=60)

try:  # sample(): streaming_api.sample()
    #streaming_api.filter(follow=None, locations=QUERY)
    #streaming_api.filter(follow=None, track=QUERY)
    streaming_api.sample()

except Exception, e:
    print >> sys.stderr, "Error: '%s' '%s'" % (str(datetime.now()), str(e))

finally:
    print >> sys.stderr, "disconnecting..."
    streaming_api.disconnect()

Facebookから友人の投稿写真を取得するPythonスクリプト

プライバシー侵害の研究の一環として、FacebookからFQLを使用して友人の投稿写真を取得するPYthonスクリプトを作りました。取得した写真の取り扱いには注意してください。

https://github.com/shmachid/facebook_mining/blob/master/getPic_from_fb.py

スクリプト実行手順は以下となります。

  1. 下記のサイトにアクセスし、Facebookアプリの登録をする
  2. http://miningthesocialweb.appspot.com/
    全ての権限を与える必要はないが、スクリプトが動かなくなる可能性もあるので注意してください。
    求められた権限はそのまま許可し、不要になったら、Facebookアプリを削除すれば良いです。

  3. 権限を与えたら、query実行ページに遷移する
  4. http://miningthesocialweb.appspot.com/query

  5. ページ遷移時に表示される ACCESS_TOKEN をコピーする
  6. ディレクトリ配下の out/facebook.access_token に3のTOKENを貼り付ける
  7. プログラムを実行する
  8. 自分のコネクション数にもよるが、画像1万枚で4時間ほどかかります。

  9. 処理終了後、ディレクトリ配下に picture/ が出来るので写真を確認する
  10. 一枚ずつ見るのが面倒な場合は、 out/get_picture.html を開いても良です。

ディレクトリ構成は以下のようにします。out/以下にトークン情報を格納します。

[shmachid@hoge:~/fb] $ ls
getPic_from_fb.py  out
[shmachid@hoge:~/fb] $ cd out
[shmachid@hoge:~/fb/out] $ ls
facebook.access_token
# -*- coding: utf-8 -*-

import os
import sys
import json
import urllib
import urllib2
from urllib import urlencode
import webbrowser

class FQL(object):

    ## This class was made by ptwobrussell.
    ## please check original code.
    ## https://github.com/ptwobrussell/Mining-the-Social-Web.git

    ENDPOINT = 'https://api.facebook.com/method/'

    def __init__(self, access_token=None):
        self.access_token = access_token

    def _fetch(cls, url, params=None):
        conn = urllib2.urlopen(url, data=urlencode(params))
        try:
            return json.loads(conn.read())
        finally:
            conn.close()

    def query(self, q):
        if q.strip().startswith('{'):
            return self.multiquery(q)
        else:
            params = dict(query=q, access_token=self.access_token, format='json')
            url = self.ENDPOINT + 'fql.query'
            return self._fetch(url, params=params)

    def multiquery(self, q):
        params = dict(queries=q, access_token=self.access_token, format='json')
        url = self.ENDPOINT + 'fql.multiquery'
        return self._fetch(url, params=params)


if __name__ == '__main__':
    try:
	    ## First, you need to assign facebook application and get access-token.
        ## please get the access token from following web-site and copy&paste to file.
        ## http://miningthesocialweb.appspot.com/
        ACCESS_TOKEN = open('out/facebook.access_token').read()
        if ACCESS_TOKEN == '':
            print >> sys.stderr, "Check your token"
            exit()

        if not os.path.isdir('picture'):
            os.mkdir('picture')

        q = "select target_id from connection where source_id = me() and target_type = 'user'"

    except IOError, e:
        try:
            print >> sys.stderr, "IO Error"
            exit()
        except IndexError, e:
            print >> sys.stderr, "Index Error"
            exit()

    fql = FQL(access_token=ACCESS_TOKEN)

    ## 1. get persons id
    print "get person id start"
    persons = [str(t['target_id']) for t in fql.query(q)]
    print json.dumps(persons, indent=4)


    ## 2. get album id
    print "get album id start"
    cnt = 1
    work = []
    for i in persons:
        u = 'select aid from album where owner = %s' % (i)
        work += fql.query(u)
        print cnt
        cnt += 1

    albums = [str(t['aid']) for t in work]
    print json.dumps(albums, indent=4)


    ## 3. get photo id
    print "get photo id start"
    cnt = 1
    work2 = []
    for al in albums:
        e = 'select object_id from photo where aid = "%s"' % (al)
        work2 += fql.query(e)
        print cnt
        cnt += 1

    photos = [str(t['object_id']) for t in work2]
    while 'error_code' in photos: photos.remove('error_code')
    while 'request_args' in photos: photos.remove('request_args')
    print json.dumps(photos, indent=4)
    print "Abount photo count: %s" % (len(photos))


    ## 4. get photo url
    print "get photo url start"
    cnt = 1
    photo_srcs = []
    N = 30
    for k in range(len(photos) / N + 1):
        r = 'select src, width, height from photo_src where photo_id in (%s)' \
               % (','.join(photos[k * N:(k + 1) * N]))
        photo_srcs += fql.query(r)
        print cnt
        cnt += 1


	## 5. select picture : width=320 only
    print "get photo via urllib start"
    cnt = 1
    photo_src = []
    for photo in photo_srcs:
        if photo['width'] == 320:
            photo_src.append('<img src="' + photo['src'] + '">')
            filename = photo['src'].split('/')

            ## get picture
            try:
                urllib.urlretrieve(photo['src'], os.path.join(os.getcwd(), 'picture', filename[-1]))
            except:
                print >> sys.stderr, "Can't get this  picture '%s'" % (filename[-1])

            print cnt
            cnt += 1 


    ## 6. create HTML
    print "create HTML start"
    OUT = os.path.basename('get_picture.html')
    html = "<html><body>%s</body></html>" % (' '.join(photo_src[0:]))
    f = open(os.path.join(os.getcwd(), 'out', OUT), 'w')
    f.write(html)
    f.close()

    ## if you want to show these pictures in a browser, please execute following  command at your terminal.
    ## `open out/get_picture.html`
    #webbrowser.open('file://' + f.name)

    print "Done!"

MapReduce Views in CouchDB

前回に続いて、MapReduceによるView作成、View実行を確認してみました。
Javascript以外の言語でMapReduceを行う場合は、couchDBのiniファイルに以下のようなイメージで追記します。
正常反映されれば、FutonのView Codeセグメントでlanguageとして使用可能となります。

[query_servers]
python = /usr/bin/couchpy

下記はサンプルとして、tweetのユーザ情報でlocation = ‘Tokyo’であるtweetを出力しています。
また、本サンプルではReduce処理を使用していません。
https://github.com/shmachid/twitter_mining/blob/master/define_view_in_couch.py

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

import couchdb
from couchdb.design import ViewDefinition


SERVER_URL = 'YOUR COUCHDB URL'    #ex: http://localhost:5984
DB_USER = 'YOUR USER'
DB_PASSWD = 'YOUR PASSWORD'
DB = 'YOUR DB NAME'

server = couchdb.Server(SERVER_URL)
server.resource.credentials = (DB_USER, DB_PASSWD)


try:
    db = server.create(DB)

except couchdb.http.PreconditionFailed, e:
    db = server[DB]

    def mapper(doc):
        if doc['author']['location'] == "Tokyo":
            yield (doc['id'], doc['text'])

    ## if you need to use reduce function, please remove bellow the comment-tag.
    #def reducer(keys, values, rereduce):
    #    return max(values)

    view = ViewDefinition('index', 'map_location', mapper, language='python')
    #view = ViewDefinition('index', 'map_location', mapper, reducer, language='python')
    view.sync(db)


    records = [(row.key, row.value) for row in db.view('index/map_location')]

    for record in records:
        print record[1]

Viewを作成・更新後、初回実行時にViewの更新処理が走る為、View実行完了まで時間が掛かりました。(対象件数は25万件であった)
データ更新が絶えず入っている状況でViewへアクセスがあった場合は、どのよう挙動となるのかが気になります。
ログは以下のように出力されます。何か良い方法あるのかな、探したけど分からなかった。

[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244494 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244520 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244548 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244579 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244606 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244634 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244664 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244693 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244721 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244747 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244773 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244797 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244821 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244848 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244877 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244903 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244934 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244961 for tweets _design/index
[Sat, 05 May 2012 17:01:30 GMT] [info] [<0.6123.0>] checkpointing view update at seq 244991 for tweets _design/index

Twitter Streaming API to CouchDB with tweepy and jsonpickle

Twitter Streaming API: filter()からCouchDBへ流しこむサンプルスクリプトを作りました。
返ってきたStatus情報等を全て格納しておりテーブルサイズが大きくなりやすいので、適度にフィルタを掛けると良いです。
連休前と連休中で画像共有サービスの利用頻度が変わるのかを確認する為にFilter条件をtwippleで行なったところ、ゴールデンウィーク中は1日で19万件程度tweetされていました。
連休前として2週間程前に収集した際は10万件程でしたので、行楽中は画像共有サービスの利用が増えるようです。
https://github.com/shmachid/twitter_mining/blob/master/stream2couch.py

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

import sys
import time
from datetime import datetime
import tweepy
import json
import jsonpickle
import couchdb


if (len(sys.argv) < 2):
    print "Usage: please check your parameter"
    sys.exit()


QUERY = sys.argv[1:]

SERVER_URL = 'YOUR COUCHDB URL'    #ex: http://localhost:5984
DB_USER = 'YOUR USER'
DB_PASSWD = 'YOUR PASSWORD'
DB = 'YOUR DB NAME'

CONSUMER_KEY = 'YOUR CONSUMERKEY'
CONSUMER_SECRET = 'YOUR CONSUMER_SECRET'
ACCESS_TOKEN = 'YOUR ACCESS_TOKEN'
ACCESS_TOKEN_SECRET = 'YOUR ACCESS_TOKEN_SECRET'

auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_TOKEN_SECRET)

server = couchdb.Server(SERVER_URL)
server.resource.credentials = (DB_USER, DB_PASSWD)


try:
    db = server.create(DB)

except couchdb.http.PreconditionFailed, e:
    db = server[DB]

class CustomStreamListener(tweepy.StreamListener):

    def on_status(self, status):
        results = {}
        try:
            if status.id_str in db:
                return True

            pickled = jsonpickle.encode(status)
            results = json.loads(pickled)
            del results['_api']

            db[results['id_str']] = results

        except Exception, e:
            print >> sys.stderr, "Encountered Exception:", e
            pass

    def on_error(self, status_code):
        print >> sys.stderr, "Encountered error with status code:", status_code
        return True

    def on_timeout(self):
        print >> sys.stderr, "Timeout..."
        return True 


streaming_api = tweepy.streaming.Stream(auth, CustomStreamListener(), timeout=60)
print >> sys.stderr, 'Filtering parameters: "%s"' % (' '.join(sys.argv[1:]),)

try:  # sample(): streaming_api.sample()
    streaming_api.filter(follow=None, track=QUERY)

except Exception, e:
    print >> sys.stderr, "Error: '%s' '%S'" % (str(datetime.now()), str(e))

finally:
    print >> sys.stderr, "disconnecting..."
    streaming_api.disconnect()

RICOH賞を受賞しました

大学院の有志チームで参加していたRICOH & Java developer challenge2011でRICOH賞をいただきました。
アプリケーション機能は非常にシンプルでしたが、顔認識技術とビジネスモデルとの組み合わせ、アプリケーションとしての完成度が評価されたのではと思います。また、水着の三愛がリコーの傘下といこともあり、画像解析 × ファッションレポート のアプリケーションが受け入れられたのもあるようです。

グランプリの北陸先端技術大学院さんも顔画像技術を使用されており、エンタープライズアプリケーションにおいてもこれらの技術は注目されているのだと感じました。
また、他チームのアイデアや実装は素晴らしく、若い方々の発想力に驚きました。もう少しブラッシュアップすればRICOHからアプリケーションとして出ても良いのではと思われるアプリケーションもあり良い刺激をもらえました。

約8ヶ月ほど、チームで活動をしてきましたが、最後に良い結果が出て良かったです。チームの皆さんありがとうございました。