2012/01/06 06:00:46
MoinMoin 2.0のビッグニュースに16個あげられていたんだが気になったものを
Sphinx-based docs
Sphinxベースのドキュメントになって読みやすい。でもmake pdflatexはこけた
Storage Layers: stores, backends, middlewares
ストレージにファイルだけではなくsqlalchemyで使えるRDBやKyoto Cabinetなんかも使える。
Tree based transformations
これに惹かれた
input -> converter -> DOM tree -> converter -> output
という変換経路をたどるのでフォーマットの変換ができる。つまりwikiのコンテンツを好きなフォーマットで取り出せるので、特にReSTで取り出してSphinxにもっていけるようになるのでデータの再利用がしやすそう。
GUI editor update and different approach
ckeditorが使える。WYSIWYGはイントラのサービスでは必須。
Themeing with Jinja2 templating engine
Flask-Themesを使っているので、自分用のテーマを作ってみる予定
Packaging
virtualenvのおかげで、環境を汚さずすむので便利。あとquickinstallスクリプトがインストールの面倒をほぼすべて見てくれるので超楽チン
2012/01/04 21:29:48
コミュニティーベースのコラボレーションツールを探している。
個人的にはSphinxのWeb Supportがいいんじゃないかと思っているんだが、うちのITリテラシーを考えるとちょっと無理すぎるかなぁと断念した。
論文が電子化されているけどpdfは単なる紙の模倣でつまんないがHTML化されてパラグラフ単位とかfigure単位でコメントしたり編集できたりすれば、レビューも作りやすいし、実験はいいけど解釈はクズみたいなトータルでの判断に悩むような論文も切り取れるし、レターみたいな内容が薄いんだか知識を入れ込めなかったんだか分からないような中途半端な文章も好き勝手に補足できる。つまり真のマッシュアップが待っているわけです。
単語に分解して相関を取るとかそういうのとは逆方向に、文脈をつなぎあわせてより抽象とか本質を目指すようなことがやりやすくなるわけですな。正直論文の最後に載っているリファレンスは、島根の場所が知りたいのに日本のあたりを指してるわけで残念な仕組みだよなと思ってるのでもう少し、位置情報を高精度化すれば面白いのにとずっと思っているんだけどなかなかそうはならないのはみんな著者のストーリーを読むのが好きなのかね?他人事なのでどうでもいいけど。
で、音でいうところのサンプリングというか、パラグラフ単位でコメントを入れられる仕組みっていうのは未来があると思っている、RWHで未来を見たというかちょっと感動した。まぁ、そういうのをイントラに構築できたら良かったんだけど現状は難しそうだ(ヨーヨーヨー)。
かといってwikiはなぁ、、、Sphinxっぽく文書出力できる良い感じのwikiが欲しいなぁと思いながら調べていたらmoin2がFlaskベースでフォーマットの変換がしっかりしてそう。ReSTでもOKなのでSphinx->pdfってのもやりやすそう。

あとは知っているフレームワークなので手を入れやすそうってのもある。化学構造のエディタとか組み込まないといけないし。それからmoin2のソースコード読むのも勉強になるし、モチベーション的にもよろしいです。
まぁそんな感じで今年は、wikiベースの創薬用コラボレーションツールを作ってみるかなと。
2011/11/11 06:45:57
Express+Jadeで幾つか書いていたらJadeの読みやすさに慣れてしまった。
FlaskでもHamlっぽい記法を使いたいなぁとググッてみたらhaml and flaskというエントリをみつけたので、この通りに入れてみた。
-extends "layout.html"
-block title
Page Not Found
-block body
%h1 << Page Not Found
%p
%a href="{{ url_for('show_entries') }}" << go somewhere nice
%p << or find by tag
%div.tags
%script type="text/javascript"
|$.getJSON('{{ url_for('show_jsontags') }}', null,
| function(json){
| for (i in json.tags.sort()){
| $('div.tags').append("<a href=\"/tag/" + json.tags[i] + "\">" + json.tags[i] + "</a> ");
| }
| }
|);
かなり読みやすくなった。入れ子をインデントで表現するのでPythonistaにはありがたいですね。ちなみにこっちがもとのjinjaのテンプレート。
{% extends "layout.html" %}
{% block title %}Page Not Found{% endblock %}
{% block body %}
<h1>Page Not Found</h1>
<p><a href="{{ url_for('show_entries') }}">go somewhere nice</a></p>
<h1>or find by tag</h1>
<div class=tags></div>
<script type="text/javascript">
$.getJSON('{{ url_for('show_jsontags') }}', null,
function(json){
for (i in json.tags.sort()){
$('div.tags').append("<a href=\"/tag/" + json.tags[i] + "\">" + json.tags[i] + "</a> ");
}
}
);
</script>
{% endblock %}
pipとかeasy_installで0.1を入れるとdiv#idとかdiv.classという記法が使えないので、GitHubの最新版を入れたほうがよいです。
ところで、HTML5においてセマンティクスが重視されると、デザインまわりはCSSに集約されていきHTMLタグとデザインっていうのは分離されていくと思うんですね(rdfとか昔っからあるし)。
セマンティクスってデザイナーというよりはプログラマー側が考えることだろうから、マークアップエンジニアとか呼ばれているヒト達はCSSエンジニアとかそういう名称になっていくの?
よくわからんので、今度誰かに聞いてみよう
静岡(東部のあたり)ではHTML5の入門書の読書会をしています(宣伝)
2011/11/09 18:45:46
自作のブログシステムのなかにデータベースに頻繁にアクセスしすぎて困るAPIがあるので、Flask-Cacheを使ってキャッシュするようにした。
設定を読ませてからcacheの設定をしないといけない(まぁ当たり前か)のだけどapp.config.from_objectで読ませる前にCacheしてたらキャッシュが効かなくてちょっとはまった。
CACHE_TYPE = 'simple'
app = Flask(__name__)
app.config.from_object(__name__)
cache = Cache(app) # 設定読ませたあとに
ソースは読んでたらキャッシュのタイプはwerkzeugに任せているらしいので、そっちのドキュメントを読めば大丈夫な感じですね。
2011/08/17 20:18:44
blohgってのが面白そうだったのでコードを読みつつ手元のmacbookにインストールして触ってみた。
sudo easy_install-2.7 blohg
mkdir myblohg
cd myblohg/
blohg initrepo
hg commit -Am 'initial commit'
blohg runserver
エントリを追加する場合にはcontent/post/にあたらしくrstファイルを追加してaddしてコミットする。

で、コード読んでたら
from werkzeug.contrib.atom import AtomFeed, FeedEntry
ってあって、werkzeugにフィード生成用の仕組みが用意されていることを知った。自分のブログ(Flask製)ではfeedgeneratorを使っているのだけど、こっちでもいいかなと思った。
おまけ
fujinismもblohgベースに変えようかなぁ
2011/07/27 20:02:13
HTMLガイドブックはphpなのでFlaskでやってみた。この本は良書ですね、オススメです(最後のほうにNode.jsのサンプルも載ってるし)。
青い囲みの中にドラッグドロップするとアップロードされるが、ドラッグドロップAPIのためにスペースを用意するっていうのはなんかいまいちだなぁ。もうちょっと洗練されたドラッグドロップのインターフェースはないものかね。

ディレクトリ構成
$ tree
.
├── static
│ └── uploads
├── templates
│ └── index.html
└── uploader.py
サーバー側
from flask import Flask, request, url_for, render_template, make_response
import os
DEBUG = True
SECRET_KEY = 'development key'
UPLOAD_FOLDER = 'static/uploads'
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif'])
app = Flask(__name__)
app.config.from_object(__name__)
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in ALLOWED_EXTENSIONS
@app.route('/')
def show_index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def do_upload():
file = request.files['xhr2upload']
if file and allowed_file(file.filename):
filename = file.filename
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
response = make_response(url_for('static', filename='uploads/'+filename, _external=True))
response.headers['Access-Control-Allow-Origin'] = '*'
return response
if __name__ == '__main__':
app.run()
クライアント側(templates/index.html)
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>File Uploader</title>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script>
<style>
#dropbox {
width: 500px;
height: 200px;
border: 1px solid blue;
background: #eee;
}
#urllists {
margin-top: 30px;
width: 400px;
height: 300px;
overflow: auto;
floar: left;
boader: 1px solid blue;
}
#currentimage {
margin-left: 420px;
margin-top: 30px;
}
</style>
</head>
<body>
<h1>File Uploader</h1>
<div id=dropbox></div>
<div id=urllists></div>
<div id=currentimage></div>
<script>
var DnDUploader = function (base_id) {
if(typeof(base_id) != "string" || base_id.length == 0 || document.getElementById(base_id) == null)
return false;
var __body = document.getElementsByTagName('body')[0];
var parent = document.getElementById(base_id);
__body.addEventListener("drop", function(e){e.stopPropagation();e.preventDefault();}, false);
__body.addEventListener("dragenter", function(e){e.stopPropagation();e.preventDefault();}, false);
__body.addEventListener("dragover", function(e){e.stopPropagation();e.preventDefault();}, false);
parent.addEventListener("drop", function(e){e.stopPropagation();e.preventDefault();_handleDrop(e);}, false);
parent.addEventListener("dragenter", function(e){e.stopPropagation();e.preventDefault();}, false);
parent.addEventListener("dragover", function(e){e.stopPropagation();e.preventDefault();}, false);
var _handleDrop = function(e) {
var x = e.layerX, y = e.layerY;
var dt = e.dataTransfer, files = dt.files, count = files.length;
var types = [
'image/png',
'image/gif',
'image/jpeg'
];
for (var i=0; i < count; i++) {
if (files[i].fileSize < 1048576) {
var file = files[i];
var type = file.type;
var filename = file.fileName;
if($.inArray(file.type, types) == -1) {
alert(file.type + 'はサポート外です。');
continue;
}
var reader = new FileReader();
reader.readAsDataURL(file);
_upload(file);
reader.onload = function(e) {
var fileData = e.target.result;
_drawImage(x, y, fileData);
}
} else {
alert('ファイルが大きすぎます');
}
}
};
var _drawImage = function(x, y, file) {
var imgElement = document.createElement('img');
imgElement.src = file;
imgElement.style.position = 'absolute';
imgElement.style.display = 'none';
parent.appendChild(imgElement);
setTimeout(function(e) {
var o_w = imgElement.width;
var o_h = imgElement.height;
imgElement.width = o_w > 100 ? 100 : o_w;
imgElement.height = parseInt( o_h * imgElement.width / o_w);
var w = imgElement.width;
var h = imgElement.height;
imgElement.style.left = (x-w / 2)+'px';
imgElement.style.top = (y-h / 2)+'px';
imgElement.style.display = 'block';
},1);
};
var _upload = function(file) {
var fd = new FormData();
fd.append("xhr2upload", file);
var xhr = new XMLHttpRequest()
xhr.open("POST", "http://www.kzfmix.com:5000/upload");
xhr.send(fd);
xhr.onload = function(e) {
var url = e.target.responseText;
$('#urllists').prepend('<p><a href="' + url + '">'+url+'</a></p>');
$('#currentimage').html('<img src="' +url+ '">');
}
}
}
DnDUploader('dropbox');
</script>
</body>
</html>
2011/07/26 20:09:09
XHR2はJSONPと比べてどんなメリットがあるんだろうか?
JSONPで行っている事は、外部のJavascriptを読み込んでいることに他ならず、不用意に利用することは大変危険な行為です。
まぁ、JSONPはハック色が強いですからね。
というわけで、クロスドメイン間で通信ができるというXHR2を試してみた。
サーバーのコード
ログ見てわかったんだがPOSTメソッドの時はOPTIONSで問い合わせないっぽい。なのでコメントアウトしても動いた。Access-Control-Allow-Originは必須で、コレがないと動かない。
from flask import Flask, make_response, request
app = Flask(__name__)
app.debug = True
#@app.route('/events', methods=['OPTIONS'])
#def view_events():
# response = make_response()
# response.headers['Access-Control-Allow-Origin'] = '*'
# return response
@app.route('/events', methods=['POST'])
def show_events():
u = request.form['username']
p = request.form['password']
response = make_response("user: %s, pass: %s" % (u, p))
response.headers['Access-Control-Allow-Origin'] = '*'
return response
if __name__ == '__main__':
app.run(host='www.kzfmix.com')
クライアントのコード
普通にXMLHttpRequestをnewしてPOSTメソッドでsendする
<!DOCTYPE html>
<html>
<head>
<title>xhr2 test</title>
</head>
<body>
<script>
document.addEventListener("DOMContentLoaded", function(){
var formData = new FormData();
formData.append('username', 'myuser');
formData.append('password', 'mypass');
var xhr = new XMLHttpRequest();
xhr.open("POST", "http://www.kzfmix.com:5000/events");
xhr.send(formData);
xhr.onerror = function(e) {
console.log("ERROR");
console.log(e);
}
xhr.onload = function(e) {
console.log("LOAD");
console.log(e);
console.log(xhr.status);
console.log(xhr.statusText);
console.log(xhr.responseText);
alert(xhr.responseText);
}
}, false);
</script>
</body>
</html>
実行結果

異なるドメイン(オリジン)間でデータの受け渡しが出来てる
$ python xhr2.py
* Running on http://www.kzfmix.com:5000/
* Restarting with reloader...
124.41.xx.xxx - - [26/Jul/2011 19:23:51] "POST /events HTTP/1.1" 200 -
124.41.xx.xxx - - [26/Jul/2011 19:24:04] "POST /events HTTP/1.1" 200 -
2011/07/19 10:28:12
昨日作ったものだけど、成果発表をしていないのでこのエントリでしてみたいと思う。
こんな感じのサイトで、認証はTwitterのOAuth、本の管理はbooklogの本棚APIを使ってます。
譲れる本の一覧を表示して、欲しい本があったらreplyしてもらって、手渡しで渡すと。
そんだけ。
まぁ、譲るぜオーラを漂わせるわけではなくて、あくまでイベントのついでに、物々交換的な機能があれば便利なんじゃないかと常々考えてたのでShizuDevつくる会で作ってみたと。
それからGAEも触りたかったしね。
xooqってどっからつけたかというと、はじめに本の交換だからbooxって名づけようと思っていたのだけど、やはりいありがちすぎて既に存在していたので、ひっくり返したらまぁOKだったという(一意性を担保するにはひっくり返せという教えが役に立った)。
前回ちょっと書いて放置してたら半年ほど経過してしまったが、今回みんなで集まったおかげで集中して作業できてよかった。
以下、自分で考えたオススメポイント。
住所バレしない
郵送だと住所書かないといけないのでちょっと嫌ですよね。なので会って渡す、コレ基本。イベントに行くついでにちょっと渡してくるというお手軽感。
Androidのbooklogアプリ便利
読み終わったらAndroidのアプリでスキャンして既読管理しているので、ついでにもう読まないであろう本は「交換してもいい本」カテゴリに入れてしまう、そんだけでOK。パソコン開いて作業しなくてよいので楽ちんですね。
ATNDでどのイベントに参加するかわかるようになってる
とりあえず表示するようにしただけ。やっつけ仕事っぽいがそのうちどうにかする。
2011/07/03 21:16:59
18日にやろっかなーと思っていたのだけど、今日時間があったのでOAuth認証を実装してみた。
というわけで、18日はなにしようかなぁ。jQMobileで血圧管理システムをつくってみようか、中断してる文献管理システムの構築再開しようか。
2011/06/14 21:27:36
Flaskにはjsonifyって関数があるから、いま作ってるサービスをRESTにしようと思ったんだけど細かいところでうまい実装が思いつかなかったり、そもそもきちんと理解してないことも発覚してRESTful Webサービスを読み直している。
とりあえずJSON用のRESTを実装して、クライアントとしてのwebはそっちをアクセスするようにしたほうがいいのかなぁと。
ただ、そうするとURIがぶつかるからJSON用のAPIのほうは/v1/をpathの先頭につけたけど。
あと、FlaskのjsonifyってSqlalchemyの結果をそのまま渡すと駄目で、dictionaryを組み立てて渡さないとあかんのね。これがちょっとめんどくさい
7章のブックマークサービスをRoRで実装する章をFlaskで再実装するという修行をしないとあかんのかなぁ、、、