PyCon mini JPに参加した

運営担当者、発表者の方大変お疲れさまでした。超楽しかったです。

全てのセッション楽しめた。Sphinxの話は良かった。Sphinxでプレゼンのスライドつくるの便利そう。

NVDAの紹介と日本語化プロジェクトの活動状況

超早口音声とか視覚に頼らないデバイスというかインターフェースとか。

テンプレートエンジンの高速化と失敗談について

感覚にたよらず、きちんとベンチマークをとろう

Pythonでフィジカルコンピューティング

python-on-a-chipすごい

最速WSGIサーバー研究会

WSGIサーバーの実装に際して気をつけるところとか

ライトニングトーク

ランチのLTも含めて盛りだくさんだった。blockdiag気になるのであとで試す。

オライリー積んでます

ぬー

1296043552

GAEとNLTKはおそらく読むからいいとしてHadoopは実機で動かさないと面白くないと思うんだけどみんなどうやってサンプルコードを試しているんだろうか?

ProductName プログラミング Google App Engine
Dan Sanderson
オライリージャパン / 3570円 ( 2011-01-24 )


ProductName 入門 自然言語処理
Steven Bird
オライリージャパン / 3990円 ( 2010-11-11 )


ProductName Hadoop
Tom White
オライリージャパン / 4830円 ( 2010-01-25 )


Flaskでrobots.txt

Yetibotうざいっす。あと404でるのでrobots.txtのほうもちゃんと書いておいた。

@app.route("/robots.txt")
def display_robots_txt():
    return app.send_static_file("robots.txt")

feedparserとfeedgeneratorを使ってRSSを加工する

職場でオープンソース版「Fastladder」を入れて使っている。

最近、最も保守的だと言われている知財の部署からエラー報告や、要望とかがちょくちょく寄せられるようになってきて、おー結構普及したなぁと延べ人数数えたら7割超えてた。Fastladderの前に一年くらい違うRSSリーダー使ってから3年半くらいかかってここまで来た感じ(RSSごときで3年もかかんのか?なんて思うんだけど、一般的に製薬業界はITリテラシー低いですね。なんでだろうかね?)。

地道な啓蒙活動に付き合って講習会を手伝ってくれたK君とかN君とか、別のサイトで普及活動に励んでくれたA君に感謝しないとな。

さて、登録されてるフィードの内容見てるとPubmedWIPOのPatentscopeが多いが、知財のヒトからエラー報告として挙げられた内容の中に400件くらいエントリのあるフィード(WIPO)を新規登録できないってのがあった。

研究者はプロジェクトの内容で検索かけるのだけど、知財のヒトは会社毎の検索式を追いかけたりするらしい。例えばPA/TAKEDA PHARMACEUTICALなんて検索かけると700件以上ヒットして、その検索式をRSSにすると500件のエントリが含まれるRSSが出力される。これがfastladderだと新規登録で弾かれる。ちなみに研究者の検索する状況ではそんな多量のエントリーの含まれるフィードは出てこなかったりするので今まで問題にならなかったみたい。

ソース見てないからわかんないんだけど、fastladderの未読記事のmax200件にしてるからそれで引っかかってんのかなぁと

んだよWIPOのRSSはmax指定できないの?->できないっぽい

とりあえず、feedのエントリ数を減らすような中継かませばいいんじゃないかと、休日を利用してササッと書いてみた。

import feedparser
import feedgenerator
from time import mktime
from datetime import datetime

MAX_ENTRIES = 10
wipo_url = "http://www.wipo.int/pctdb/en/rss.jsp?C=1&QUERY="
query    = "(FP%2FPA%2FTAKEDA+AND+FP%2FPHARMACEUTICAL)"

d = feedparser.parse(wipo_url+query)

feed = feedgenerator.Rss201rev2Feed(
    title       = d.feed.title,
    link        = d.feed.link,
    feed_url    = d.url,
    description = d.channel.description
    )

for entry in d.entries[:MAX_ENTRIES]:
    feed.add_item(
        title       = entry.title,
        link        = entry.link,
        description = entry.description,
        pubdate     = datetime.fromtimestamp(mktime(entry.updated_parsed))
        )

print feed.writeString('utf-8')

あとは、CGIかFlaskで動かせばOKでしょう。

pubdateのとこで若干はまったけどsofのパワーで解決。

今は、イントラQAサイトを定着させるのにちょっと力を入れてる。

Pythonのクラスシステムはどうなっているのか

#expertpython エキスパートPythonプログラミング読書会05

エキスパートPython 3.5.2

class MyClass(object):
    def mymethod(self):
        return "hello world"

これは

>>> def method(self): return "hello world"
... 
>>> klass = type('MyClass', (object,), {'mymethod':method})
>>> ins = klass()
>>> ins.mymethod()
'hello world'

__metaclass__はtypeが読み出される前後に様々な処理を挟み込むことができる

ProductName エキスパートPythonプログラミング
Tarek Ziade
アスキー・メディアワークス / 3780円 ( 2010-05-28 )


一度理解してしまえば分かりやすい

11.01.18追記

もちろんlamda式を使ってもいいわけだ

>>> klass = type('MyClass', (object,), {'mymethod': lambda self: 'hello world'})
>>> ins = klass()
>>> ins.mymethod()
'hello world'

luminescenceでjavascriptを埋め込んだプレゼン資料をつくる

機能がちょっとシンプルすぎて、凝ったことするのは大変かもしれないけど、yaml+markdownでスライドを作れるluminescenceが便利。

luminescence input(yaml) output(html) [template(html)]

でスライド用のhtmlが出力される、こんだけ。

yamlのソースのほうにjavascript埋め込めるのでスライド中に表示したソースコードを直接実行したりできるし、jQuery Growl使えばハッシュタグ付きのtweetをプレゼンにストリーム表示させたりできる。

それから、このブログシステムもMarkdownでコンテンツ書いているので、過去のエントリから使いたいコードをコピペするとそのままスライドでも動くことが多いために自分の作業環境との相性がいい。

なにより、プレゼン資料が単なる資料作りではなく、javascriptの学習も兼ねるので時間の使い方的に非常によいだろうと。ソースをGitで管理できるので心理的にも安心(重要)。

とりあえず、最初の資料にgoogle maps埋め込んでおいた

js読書会

第1回JavaScript読書会

JGP読みます

ProductName JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
Douglas Crockford
オライリージャパン / 1890円 ( 2008-12-22 )


beautifulsoupでJGPの電車ダイアグラムを取ってくる

ちょっと必要になったので

import urllib2,os
from BeautifulSoup import BeautifulSoup
url = "http://oreilly.com/javascript/excerpts/javascript-good-parts/syntax-diagrams.html"

soup = BeautifulSoup(urllib2.urlopen(url).read())

for _img in soup.findAll('img'):
    if 'excerpts' in _img.get('src'):
        img_url = _img.get('src')
        with open(os.path.basename(img_url), 'wb') as f:
            f.write(urllib2.urlopen(img_url).read())

宣伝

来月、コミュニテイf@富士でJavascript The Good Partsの読書会があります。

ProductName JavaScript: The Good Parts ―「良いパーツ」によるベストプラクティス
Douglas Crockford
オライリージャパン / 1890円 ( 2008-12-22 )


Flaskをthreadで動かす

MLより

multiprocessingを使えばいいらしい。

import time
from multiprocessing import Process
from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
        return "Hi there!!!"

def run():
    app.run()

if __name__ == '__main__':

    app.debug = True

    print "starting process"
    server = Process(target=run)
    server.start()

    print "sleeping"
    time.sleep(5)

    print "ending process"
    server.terminate()
    server.join()

    print "bye..."

どういうシチュエーションでこういう必要性が出るのかいまいちわからんが覚えておこう。

Flaskをgaeで動かす

さっきのやつをgaeで動かしてみる。

git clone https://github.com/gigq/flasktodo.git gmaptweetgae
cd gmaptweetgae

でapplication.pyをFlask用のコードに置き換えてapp.yamlを適当に設定したら

appcfg.py update .

で、できたサイト。多分自分はよく使うので満足。

参考

ProductName Google App Engineプログラミング入門
中居 良介
アスキー・メディアワークス / 1890円 ( 2009-12-23 )


FlaskとXDMつかってgooglemapとtwitterを連動させるサンプル

HTML5 APIガイドブック コミュニケーション系API編が面白い。ひと通り読んだので、コード書いてみてる。

2章のサンプルコードをFlaskで。サンプルコードは3つのHTMLファイルが必要だけどFlask使えば一つのファイルに全部押し込めるので管理が楽。

gmap2twitter

初期値は富士市役所のあたりから半径5キロ以内のtweetを探すようにした。で地図をドラッグすると、それに伴いtweetも連動して表示されるので、ほーこんなユーザーいるのか!と新たな発見もあった。

この本は入門書として最適かもしれん。

ProductName 徹底解説 HTML5 APIガイドブック コミュニケーション系API編
小松 健作
秀和システム / 2730円 ( 2010-12 )


Flaskコード

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

from flask import Flask

app = Flask(__name__)

@app.route("/")
def processing():
    response = """
<!doctype html>
<html lnag=ja>
<head>
<meta charset="utf-8">
<style type="text/css">
.page { margin: 0px auto; border: 0px; padding: 0px; text-align: center;}
</style>
</head>
<body>
<div class=page>
<h1>gmap2tweet</h1>
<iframe src="/googlemap" id=gmap width="500px" height="500px"></iframe>
<iframe src="/twitter" id=twitter width="500px" height="500px"></iframe>
<div id=mesg></div>
</div>
<script>
  var initialize = function(){
    var origin = location.protocol + "//" + location.host;
    var iframes = document.querySelectorAll('iframe');

    for(var i = 0; i < iframes.length; i++){
      iframes[i].contentWindow.postMessage('init', origin);
    }

    window.addEventListener('message', function(e){
        if (e.origin == origin) {
          document.getElementById('mesg').innerHTML = "receive data via xdm::" + e.data;
          document.getElementById('twitter').contentWindow.postMessage(e.data, origin);
        }
    }, false);
  }
  window.onload = function(){ initialize(); }
</script>
</body>
</html>
"""
    return response

@app.route("/twitter")
def twitter_processing():
    response = """
<!doctype html>
<html lang=ja>
<head>
<meta charset="utf-8">
</head>
<body>
<div id=mesg></div>
<div id=results></div>
<script>
var origin = location.protocol+"//"+location.host;
var parentWin = null;
var api="http://search.twitter.com/search.json?callback=show&rpp=50&geocode=";
var jsonpObj =null;

var sendJsonp = function(latlng, radius){
  if(jsonpObj)
    document.body.removeChild(jsonpObj);
  var scr = document.createElement('script');
  scr.src = api+encodeURIComponent(latlng+","+radius+"km");
  scr.type = "text/javascript";
  jsonpObj = document.body.appendChild(scr);
};

var show = function(obj){
  var results = obj.results;
  var out = '';
  var template = '<img src="#{img}"> <a href="http://twitter.com/\
#{from_user}" target="_blank"><b>#{from_user}</b></a> #{text}<br />\
#{created_at}<hr />';

  for(var i = 0; i < results.length; i++){
    var res = results[i];
    var tmp = template.replace("#{img}", res.profile_image_url) \
.replace("#{from_user}", res.from_user) \
.replace("#{from_user}", res.from_user);
    tmp = tmp.replace("#{text}", res.text) \
.replace("#{created_at}", res.created_at);
    out += tmp;
  }

  document.getElementById('results').innerHTML = out;
};

window.addEventListener('message', function(e){
  if(e.origin == origin) {
    if (e.data == 'init') {
      parentWin = e.source;
    } else {
      document.getElementById('mesg').innerHTML = e.data;
      document.getElementById('results').innerHTML = "読み込み中";
      sendJsonp(e.data, 5);
    }
  }
}, false);
</script>
</body>
</html>
"""
    return response

@app.route("/googlemap")
def google_processing():
    response = """
<!doctype html>
<html lang=ja>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body style="padding:0;margin:0;height:500px">
<div id=map_canvas style="width: 100%; height: 100%; border: 0px"></div>

<script>
var parentWin = null;
var origin = location.protocol + "//" + location.host;

var start = function() {
  var latlng = new google.maps.LatLng(35.164190, 138.678609);
  var myOptions = {zoom: 13, center: latlng, mapTypeId: google.maps.MapTypeId.ROADMAP};

  var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);

  var getCenter = function(){
    var center = map.getCenter();
    var lat = center.lat(); 
    var lng = center.lng();
    if(parentWin) {
      parentWin.postMessage(lat+","+lng, origin);
    }
  };

  window.addEventListener('message', function(e){
    if(e.origin == origin) {
      if(e.data == 'init') {
        parentWin = e.source;
        getCenter();
      }
    } else {
      alert("illegal message from " + e.origin);
    }
  }, false);

  google.maps.event.addListener(map, 'dragend', function(e){getCenter();});
  google.maps.event.addListener(map, 'zoom_changed', function(e){getCenter();});

};

window.onload = function(){start();};
</script>
</body>
</html>
"""
    return response

if __name__ == "__main__":
    app.run()