2011年1月30日日曜日

Python CSVデータ 一括登録 bulkloader編

以前に書いたブログ。
Python CSVデータ 一括登録 TaskQueue編

なんだけど今回別の方法があると、こっちのほうが効率がいいのではと。

というかこっちを使うのが一般的ではないかと。

なので検証することに。

いろいろ検索するとたくさんの方が記載していました。Google本家も参考に。
Google App Engine データのアップロードとダウンロード
bulkloader.py を使って Google App Engine の本番サーバーから開発サーバーにデータを移す
他多数。

ただ今回実際にやってみて、整理するためにも書いときます。

まず、remote_apiの設定。app.yamlにて簡単に設定できます。

次に一括登録のために必要な登録するデータ(今回はCSV)とKindのPropertyとをマッピングすることが必要。

そのために下記ファイルを使用する。
bulkloader.yaml


自動生成が簡単にできます。もちろん生成後は多少編集が必要ですが。

下記コマンド。
C:\> appcfg.py create_bulkloader_config --filename=bulkloader.yaml --url=http://example.appspot.com/remote_api
実行結果。
Creating bulkloader configuration.
[INFO    ] Logging to bulkloader-log-20110129.101716
[INFO    ] Throttling transfers:
[INFO    ] Bandwidth: 250000 bytes/second
[INFO    ] HTTP connections: 8/second
[INFO    ] Entities inserted/fetched/modified: 20/second
[INFO    ] Batch Size: 10
[INFO    ] Opening database: bulkloader-progress-20110129.101716.sql3
[INFO    ] Opening database: bulkloader-results-20110129.101716.sql3
[INFO    ] Connecting to biyotest.appspot.com/remote_api
[INFO    ] Downloading kinds: ['__Stat_PropertyType_PropertyName_Kind__']
.[INFO    ] __Stat_PropertyType_PropertyName_Kind__: No descending index on __ke
y__, performing serial download
.........................................................
[INFO    ] Have 573 entities, 0 previously transferred
[INFO    ] 573 entities (223932 bytes) transferred in 78.1 seconds


これで取得完了。


yamlの中身は下記。

# Autogenerated bulkloader.yaml file.
# You must edit this file before using it. TODO: Remove this line when done.
# At a minimum address the items marked with TODO:
#  * Fill in connector and connector_options
#  * Review the property_map.
#    - Ensure the 'external_name' matches the name of your CSV column,
#      XML tag, etc.
#    - Check that __key__ property is what you want. Its value will become
#      the key name on import, and on export the value will be the Key
#      object.  If you would like automatic key generation on import and
#      omitting the key on export, you can remove the entire __key__
#      property from the property map.
# If you have module(s) with your model classes, add them here. Also
# change the kind properties to model_class.
python_preamble:
- import: base64
- import: re
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
- import: google.appengine.api.users

transformers:

- kind: Kind1
  connector: # TODO: Choose a connector here: csv, simplexml, etc...
  connector_options:
    # TODO: Add connector options here--these are specific to each connector.
  property_map:
    - property: __key__
      external_name: key
      export_transform: transform.key_id_or_name_as_string

    - property: password
      external_name: password
      # Type: String Stats: 1 properties of this type in this kind.

    - property: user
      external_name: user
      # Type: String Stats: 1 properties of this type in this kind.

データストアのKindの情報をダウンロードできる。



なので更新したいKindの #TODO~ となっている箇所を編集。



今回はCSVを使用する。
connector:csv

下記は設定しなくても良い。どんな場合に使うかはまだ不明。
connector_options: ← 削除
external_nameを編集する。


今回の場合はcsvを使用するので先頭行ににて使用している該当の項目名を設定する。


reference_propertyはreference_classを設定。
- property: user
      external_name: user
      # Type: Key Stats: 1286 properties of this type in this kind.
      import_transform: transform.create_foreign_key('User') reference_classを設定
      export_transform: transform.key_id_or_name_as_string
編集終了後データをアップロード。


下記コマンド。
C:\>appcfg.py upload_data --config_file=bulkloader.yaml --filename=example_data.csv
 --kind=Kind1 --url=http://example.appspot.com/remote_api
実行結果。
Uploading data records.
[INFO    ] Logging to bulkloader-log-20110129.151144
[INFO    ] Throttling transfers:
[INFO    ] Bandwidth: 250000 bytes/second
[INFO    ] HTTP connections: 8/second
[INFO    ] Entities inserted/fetched/modified: 20/second
[INFO    ] Batch Size: 10
[INFO    ] Opening database: bulkloader-progress-20110129.151144.sql3
[INFO    ] Connecting to biyotest.appspot.com/remote_api
[INFO    ] Starting import; maximum 10 entities per post
................................................................................
.................................................
[INFO    ] 1286 entites total, 0 previously transferred
[INFO    ] 1286 entities (3600417 bytes) transferred in 65.6 seconds
[INFO    ] All entities successfully transferred
これで一括登録完了。


気づき。

・blobpropertyは使用不可。
→登録できなかった。調べてみると方法があるみたいだけど、それでもうまくいかず。要調査。


・listpropertyは登録は完了するが、不正な値で登録されてしまう。
→こちらはサポート外?要調査


・下記propertyは登録しない。
 - property:_key_
 - property:_scatter_

→_key_は自動載番される。
→_scatter_はどのように使われているかは不明。


今回使用してみて、GAEのサーバーリソース軽減になるかと試してみたが、ほとんど一件一件登録する場合と変わらず。


ただ時間はかなりはやいので時間のメリットはあるかと。


リソースの使用量が同じぐらいならこちらを使うメリットはあるかなというところですね。


まあ細かいところはまだまだ勉強不足。


わからないことが多すぎて。


日々精進。


2011年1月25日火曜日

一世一代のお買い物

日曜に友達に買い物に付き合ってくれと。

男です。

なにかと思えば、婚約指輪を買いに行くと。

なんでオレを誘ったのか。

とりあえず不安だからついてきてくれと。

もちろん自分もそんなん買いに行ったことない。

まあおめでたいんで、幸せを分けてもらいに行ってきました。

全国に展開している専門店へ。

男二人で入るのもかなり変な感じだったけど。

友達はいろいろ話を聞いてましたが、その横で将来のため?に自分もいろいろ見物することに。

まあやっぱ高いですね。でもとってもきれい。自分もいつか送ってみたいと思いますが。。。

友達は引き続き営業トークに押され気味に商品を選んでいます。

ただ気に入ったものはあったようで迷い中。

店員さんも今買ってもらうならかなり安くすると。(ご優待と言うみたい)

ただ初の試みだし、他の店も見たかったみたいで、一度店を出ることに。

もう一軒見に行ったが、気に入ったものがなく、一軒目に言った店で買うことに。

今日中に戻って買えば、さらにご優待してくれるとのことだったので。

戻ると店員さんも笑顔で出迎え、友達も満を持して「買います」と。

やるときはやるなと思いつつ自分は、契約等が終わるまで椅子に座って待っていました。

契約の手続き等進めている様子。

しばらくすると、戻ってきた。

個人的には早く飲みに行きたかったので、笑顔で友達を見ると。

表情が暗い、とういか少し機嫌が悪い。

あまり怒る性格でもないのに。

なんでも書類には一度記入をしたのだが、買うのをやめたとのこと。

聞くと、見てるときは印象がよかった担当の店員さんが契約になると態度が変わったという。

それに頭にきて、断ってきたと。

自分が見る限り、そんな印象は受けなかったが。

本人が言うからそうなんだろう。

店員さんが必死に謝っているが、たぶん理由はわかっていない感じだった。

友達ももう聞く耳はもたない。

まさにこれからの人生を左右する、一世一代の決断なだけに怒りもおさまらない様子だった。

結局この日は買うのをやめ、予定通り飲みに行って終了。


自分も自分のコミニケーション力の無さでよく失敗する。特に仕事。

友達の勘違いかもしれない。

なかなか売れるもんでもないし、店員さんもそんな変は接客はしないかと。

それでもたぶん何か店員さんに隙があったんだと思う。

少しの言葉の捕らえ方の違いで、謝って伝えてしまったり。

聞き落としたり、間違って解釈してミスしたり。

コミニケーション技術ばかり意識してしまうと、言葉や文章に心がなくなったり。

そのつど単純な技術だけは人生生きてきて身につきますが。

生まれてこの方これには悩まされ続けてます。


友達を怒らせた店員さんも気の毒だと思いつつ。

まあその友達には幸せになってもらいたいですね。

なんだか変な、おもしろい休日でした。

自分も今週はどれだけ怒られるかな。

怒られても負けません。その都度落ち込んではいますが。

回復は早いです。

日々精進。

2011年1月15日土曜日

DNSキャッシュ

昨日はメールが受信できないとお客様から問合せ。

その対応に追われなんだかんだで午後が終了。。。

普段、環境まわりの対応はほとんどしないので、またつまずきっぱなしでした。

メールサーバーを管理しているサーバー会社に問い合わせると、サーバーダウンとのこと。

とりあえず復旧を待てばいいかと。

1時間ほどで復旧したと連絡が入ったので、お客様へ確認したが、

まだメールが受信できないとのことで、サーバー会社に再度確認。

すると既存のメールサーバーが完全にダウンしたので、別サーバーにメールサーバーの移行を行ったとのこと。

メールサーバーを認識させるにはメーラーを使用している端末の再起動を行って欲しいとのこと。

さて困った。メーラーを使用しているサーバーはシステム稼働中。

平日の昼間、業務ガンガン行っている時に、運用止めてサーバー再起動はなかなか難しい。

でもメールは送受信できないと、またそれも困る。

そこで再起動せずに対応する方法を調査。

すると端末のDNSキャッシュをクリアすれば新たなDNS情報を取得し、新メールサーバーに向くはずと。

そこでDNSキャッシュをクリアを試みる。

コマンドプロンプトから下記コマンドを実行。
c:\>ipconfig /flushdns

クリア後dnsの情報を確認。
c:\>ipconfig /displaydns


GUIからもやれるようです。
DNSサーバのキャッシュの内容を調査する

するとメールサーバーの情報がまだキャッシュに残っている。

もちろんまだ送受信は行えない。

サーバーのIP・DNSの設定を確認すると、DNSの設定がルーターになっている。

ルーターはYAMAHA RTX1100 を使用。

c:\>telnet 192.xxx.xxx.xxx   ルーターIP指定しtelnetで接続。

Password:              パスワードを指定

ルータでログイン後管理者アカウントに切り替え。
下記コマンドを実行。
# clear dns cashe     dnsキャッシュをクリア
# restart                   ルーター再起動

ルーターのDNSキャッシュはクリアさせたので、再度端末側のDNSキャッシュをクリア。

すると無事にメール送受信が可能に。

普段こういう対応はあまりしないし、DNS自体あまり意識しないので。

一般的には常識なのかもしれないですが、自分がやるとまたまた時間がかかってしまいました。

改めてメールがビジネスに欠かせないものだとも実感しつつ。

ネットワーク、環境周りの勉強不足を実感しつつ。

わからないことが多すぎて。

日々精進。

2011年1月11日火曜日

FireFoxブラウザからクライアントアプリを起動

あまりこういうケースは少ないかもしれませんが。

今回こんな場面に遭遇したので。

WEBシステム開発中に実現したい機能がどうしてもクライアントアプリでしか実現できなかったので。

呼び出しのリンクだけWEBページに追加して、そこからアプリ起動が必要に。

調査をするとブラウザからのクライアント資源へのアクセスはFireFoxだと可能とのこと。

今回使用したのはFirefox(バージョン 3.6.13)

WEBページにて設定したリンクは下記。
file:///C:/app/startup.xul#prm1=aaa

まずブラウザから呼び出す。ファイルにはXULファイルを使用。
startup.xul
---------------------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<page xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript">
<![CDATA[
    netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');


    var param = {};
    var pairs = location.hash.slice(1).split('&');
    var pair;
    var i;
    var I = pairs.length;
    for (i = 0; i < I; ++i) {
      pair = pairs[i].split('=');
      param[pair[0]] = pair[1];
    }
    var file = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
    file.initWithPath("c:\\app\\StartUp.exe"); クライアントアプリのパスを指定
    var process = Components.classes["@mozilla.org/process/util;1"].createInstance(Components.interfaces.nsIProcess);
    process.init(file);
    
    var args = [param['prm1']]
    process.run(false,args,args.length); パラメータ設定, コマンドラインから起動
    
    history.back();


]]>
</script>
</page>
---------------------------------------------------------------------------------

上記ファイルを実行できるようにするには、各ユーザーごとに保持する、設定ファイルを変更する必要あり。
ファイルパス
C:\Documents and Settings\[ユーザー]\Application Data\Mozilla\Firefox\Profiles\[任意の文字列].default\prefs.js


下記ソースを追加
user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");
user_pref("capability.policy.localfilelinks.sites", "http://example.com"); xulにリンクするWEBページの親サイト
user_pref("capability.policy.policynames", "localfilelinks");
user_pref("capability.principal.codebase.test.granted", "UniversalXPConnect");
user_pref("capability.principal.codebase.test.id", "file:///C:/app/startup.xul"); 

参考サイト
XPCOM活用術~高度な処理を実現する~
Running applications

理解はあまり深められなかったですが。

少し変わったことをやったかなと思うのでメモ程度。

ユーザー毎にFireFoxの設定ファイルがあるのでちょっと面倒。

ただ今回も結構な時間悩んでしまいました。

知らないことが多すぎて。

日々精進。

2011年1月7日金曜日

Python CSVデータ 一括登録 TaskQueue編

WEBサイト構築中につき大量のデータを登録することに。

大体数万件になりますか。

今回GAE+Pythonで開発中。

CSVファイルのアップロード機能を裏ページに実装し、登録を試みるが、

GAEの1リクエスト30秒という制約により一回に約200件ずつしか登録できない。

数万件のCSVファイルを数百件毎に分割するのも面倒、というか膨大な時間がもったいない。

そこで、登録処理を自動かつ大量に一括で走らせるために試行錯誤中。

今回はTask Queueをデータ登録用に作成。

一件ずつTaskを登録し、定期的に処理を実行することで自動化を実現することに。

肝心なCSVデータは一時的なものなので、GAEとは別の公開用のサーバーにアップ。

pythonライブラリ urllibを使用しCSVデータにアクセスした。

tasks.py
----------------------------------------------------------------------

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from google.appengine.ext import webapp
from google.appengine.api.labs import taskqueue
import urllib


class add_tasks(webapp.RequestHandler):
  def get(self):

    mytask = taskqueue.Queue('task-queue')
  
    #URLパス
    urlRequest="http://example.com/csvdata/data.csv"
    #CSVファイルにアクセス
    result = urllib.urlopen(urlRequest)
  
    #取得したデータを1行ずつTaskに追加
    for readline in result.readlines():
        row = readline.split(',')
        if len(row) > 0:
            task = taskqueue.Task(url='/data_ins' , params = {
                                                              'prm0':unicode(row[0], 'cp932'),
                                                              'prm1':unicode(row[1], 'cp932'),
                                                              'prm2':unicode(row[2], 'cp932'),
                                                              'prm3':unicode(row[3], 'cp932'),
                                                              })
            mytask.add(task)
----------------------------------------------------------------------

これだけのことなのにまたかなりの時間を要しちゃいました。

ただこの後、実際にTaskの実行で走らせる登録処理が動作せず。

簡単に直せそうだったけどまたはまっちゃいそうだし、力つきてTaskの登録までで終了。

pythonが全然慣れません!

わからないことが多すぎて。

日々精進。

2011年1月6日木曜日

2011年

お正月も終わり、仕事始めから二日。

今年も本格始動です。

正月は箱根駅伝を生観戦。

めちゃめちゃ速かった、脚が。

でも走る姿を見ていると、自然と涙がこぼれてきました。

学生長距離界のTOPがひたすら前に見えるライバルの背中を見据えて、決してあきらめることなく。

今にでも止まりたくなるくらいきつくて、苦しいはずなのに。

襷をつなぐとそこで倒れこんでしまう。

ただ絶対に自分に負ける選手がいない。どんな時代でも。


自分もこんな一年にしたいな。

2010年はなにかと周りに甘え、自分に甘え、負けっぱなしだったので。

信念をもって、何より自分に勝つこと。

そのために一日一日を大切に。

日々精進。