UserCreationFormでemailも必須項目に

元々のUserCreationForm。これを元にemailの登録を必須にする(パスワードをリセットするときにメールアドレスが必要なので)。
class UserCreationForm(forms.ModelForm):
"""
A form that creates a user, with no privileges, from the given username and password.
"""
username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^[\w.@+-]+$',
help_text = _("Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only."),
error_messages = {'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters.")})
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput,
help_text = _("Enter the same password as above, for verification."))

class Meta:
model = User
fields = ("username",)

def clean_username(self):
username = self.cleaned_data["username"]
try:
User.objects.get(username=username)
except User.DoesNotExist:
return username
raise forms.ValidationError(_("A user with that username already exists."))

def clean_password2(self):
password1 = self.cleaned_data.get("password1", "")
password2 = self.cleaned_data["password2"]
if password1 != password2:
raise forms.ValidationError(_("The two password fields didn't match."))
return password2

def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user

アカウント作成アプリ - shisogohanの日記とUserCreationFormのusernameをあたりを参考にしてMyUserCreationFormを作ると以下のようになる。
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User


class MyUserCreationForm(UserCreationForm):
email = forms.EmailField(label=_("Email"), required=True)

class Meta(UserCreationForm.Meta):
fields = ("username", "email")

def clean_email(self):
email = self.cleaned_data["email"]
try:
User.objects.get(email=email)
except User.DoesNotExist:
return email
raise forms.ValidationError(_("A user with that email already exists."))

使うときは元のUserCreationFormは消してこんなかんじでimportすればOK。エラーテキストもちゃんと表示されていいかんじ。
from forms import MyUserCreationForm as UserCreationForm
posted by 右京 | Python

django開発してます

django + twitter bootstapで色々と作ってますよ。まともに作る予定な(忘れっぽい)のでドキュメントという名のメモも作ったりして。このドキュメント作りが意外と楽しかったりするんだよね(sphinxは情報教育で必修にしてほしいくらい)。

作成中のドキュメント:
http://dl.dropbox.com/u/142237/docs/djangomemo/_build/html/index.html
posted by 右京 | Python

StreamLDAで日本語

もともとのStreamLDAは正規化、文字列のパースを行う部分でアルファベット以外の文字を取り除く仕様(128行目付近)。
つまり、このままでは日本語が使えない。
streamlda.py at master from jessykate/streamLDA - GitHub

そこで、正規化とパースを行う関数を登録できるようにすれば(日本語使いたい奴は勝手に関数を作る)良さそうなので該当部分を関数化して分離。
streamlda.py at master from ukyo/streamLDA - GitHub

日本語でパースに使うものといったらMeCab。
perl製のMeCab辞書正規化ツールで生成した辞書を使うためのツールのpythonバインディングを作ったのでよかったらどうぞ。
pybin/mecab_sample.pyをみれば使い方がわかるかと思います。
(実はまだStreamLDAで試していなかったりする)
ukyo/mecab-dic-overdrive - GitHub
posted by 右京 | Python

OnlineLDAの改良版のStreamLDAを使ってみた

ちょっと動かそうとしてイテレーション回数を200にしたら意外と時間がかかったのでその間に記事を書く。

OnlineLDAの元ソース、論文
David M. Blei
www.cs.princeton.edu/~blei/papers/HoffmanBleiBach2010b.pdf

StreamLDA
jessykate/streamLDA - GitHub

元々のLDAはバッチ学習を行っていて、OnlineLDAは名前通りオンライン学習をする。
ただし、オリジナルのOnlineLDAは最初に読み込む単語を決め打ちするので単語数を増やせないという仕様(オンラインの意味ねぇ)。
StreamLDAは単語数が増えていっても対応するように拡張されて、加えて分かりやすいサンプルまで付属されている。

そういうわけで、その分かりやすいサンプルを実行してみるまでのメモを載せておきます。

#準備
#必要なパッケージをインストール
#pip使うと時間かかります
sudo apt-get install -y python-numpy python-scipy python-matplotlib python-nltk

#インタラクティブシェルを開く
python
>>> import nltk
>>> #コーパスをダウンロード
>>> nltk.download()
#インタラクティブシェルを閉じる

#ソースを取ってくる
git clone https://github.com/jessykate/streamLDA.git
#準備終

#とりあえず試してみる
cd streamLDA
python tests.py
#パープレキシティが表示される(下図)

#実際のデータで試してみる(二つ目の引数はイテレーション回数。多すぎると時間かかるよ)
#dataフォルダの書庫(20_news.tar.gz)は事前に解凍しておく
./stream_corpus.py 20news 50

streamldatest01.png
・・・この図はあまり参考にならないかも。元論文で雰囲気を感じ取ってください。
posted by 右京 | Python

PyQtでウィンドウの外枠を消す

タイトルバーとかウィンドウの枠を消したいときもあるよね。

import sys

from PyQt4 import QtCore, QtGui

app = QtGui.QApplication(sys.argv)
#QtCore.QtにWindowFlags関係の定数が入ってます
w = QtGui.QWidget(None, QtCore.Qt.FramelessWindowHint)
w.show()

app.exec_()
posted by 右京 | Python

「第6回 MongoDB JP 勉強会 in Tokyo」に参加しました

「第6回 MongoDB 勉強会 in Tokyo」 : ATND
20110924_「第6回 MongoDB 勉強会 in Tokyo」( #mongotokyo ) - Togetter
参加しました。

まず最初に、mongodb勉強会はだれでもWelcomeな雰囲気の良い勉強会なので、勉強会に参加したいけどちょっと難しそうだな・・・みたいな人におすすめです。

色々と面白い発表があったのですが、それは上のリンクを参考にしてもらって、@bibrostさんのDBRefをいっぺんに取ってくる話(Bulk joinでMongoDBのDBRefを2倍速くする | Fungoing LLC)について、pymongoのAutoReferenceにパッチを当てるやりかたについて書きます(AutoReference使っている人がいるのかな?)。

やりかたは、pythonだと元コードを直接変更せずにsetattrで動的にコードを書き換えられるのでそれでいきます。
autoreference_patch.py(横に長くてすまん。詳しくはukyo/pymongo-autoreference-patch - GitHub)

#coding: utf8

"""pymongoのAutoReferenceにパッチ当てるやつ

リスト内のDBRef全部にクエリを発行せずに{$in: [1,2,...,n]}する。

Example:
>>> from pymongo.son_manipulator import AutoReference, NamespaceInjector
>>> import autoreference_dbref_patch
"""

from bson.dbref import DBRef
from bson.son import SON
from pymongo.son_manipulator import AutoReference


def transform_outgoing(self, son, collection):
"""Replace DBRefs with embedded documents.
"""

def transform_value(value):
if isinstance(value, DBRef):
return self._AutoReference__database.dereference(value)
elif isinstance(value, list):
#listの先頭がDBRefだったら1クエリで取ってくるようにする
if value and isinstance(value[0], DBRef) and len(value) > 1:
return [o for o in self._AutoReference__database[value[0].collection].find({"_id": {"$in": [v.id for v in value]}})]
else:
return [transform_value(v) for v in value]
elif isinstance(value, dict):
return transform_dict(SON(value))
return value

def transform_dict(object):
for (key, value) in object.items():
object[key] = transform_value(value)
return object

return transform_dict(SON(son))


#AutoReferenceにmonkey patch
setattr(AutoReference, 'transform_outgoing', transform_outgoing)

使い方は簡単で(Exampleに書いてあるが)、AutoReferenceをimportしたあとにこれをimportするだけです。他の部分を変更する必要はありません。

というわけで、もしAutoReferenceを使っている人がいたら試してみてください。

ソースコード:
ukyo/pymongo-autoreference-patch - GitHub
posted by 右京 | Python

Full Ajaxな掲示板 #python #javascript #kay

使っているライブラリとかフレームワークとか:
Kay
pjax
jQuery Form Plugin

特徴:
ページ遷移がpjax
コメント投稿したときは、ページに訪れたor投稿した時間以降のコメントのみをajaxで取ってくる
スレッド投稿は手抜き
GAE

ソースコード:
汚コードですが・・・
ukyo/KayPjaxSample - GitHub

そうそう、Form Pluginが予想以上に良かったのでおすすめ。
posted by 右京 | Python

Kay+pjaxのコーディング例 #python #gae

pythonコードとかテンプレートの中にpjaxのためだけに条件分岐を書くのが面倒なので、こうすれば楽できるんじゃないかなという書き方の一例。多分他にも方法はあると思う。

まずはベースになるテンプレートを用意する。これはbase.htmlにしておく。base.htmlではpjaxでのアクセスかどうかを判断して、pjaxだったらpjaxbase.htmlというpjax用のテンプレートを継承するようにする。そうじゃない場合は、普通に表示。

base.html
{% if 'X-PJAX' in request.headers %}
{% extends "myapp/pjaxbase.html" %}
{% else %}
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>{{ title }}</title>
{{ compiled_css('style.css') }}
{{ compiled_js('script.js') }}
</head>
<body>
<div id="wrapper">
<ul id="menu">
<li><a href="/" class="pjax">Top</a>
<li><a href="/menu1" class="pjax">menu1</a>
<li><a href="/menu2" class="pjax">menu2</a>
<li><a href="/menu3" class="pjax">menu3</a>
</ul>
<div id="content">
{% block content %}{{ content }}{% endblock %}
</div>
</div>
</body>
</html>
{% endif %}

pkax用のテンプレートにはコンテンツと、タイトルに細工をするスクリプトを仕込む。

pjaxbase.html
{% block content %}{{ content }}{% endblock %}
<script>$(function(){$("title").text("{{ title }}");});</script>

実際に使うときはbase.htmlを継承して、contentブロックを適当に書き換えればOK。
{% extends "myapp/base.html" %}
{% block content %}{{ content }}へようこそ{% endblock %}
posted by 右京 | Python

Kay用のPydevプロジェクトを作成するスクリプト作った

人柱用です。とりあえず、Windowsでは出来ました。使い方は下のような感じで。まず、pitのアレでエディタが開きます。kay_pathにはkayをインストールしたパス、gae_pathにはappengineをインストールしたパスを設定します。その後にプロジェクト名を聞かれるのでなんか入力してください。
$ python key_eclipse.py

すると、入力したプロジェクト名のディレクトリができて、その中に.project、.pydevproject、とsrcディレクトリができていると思います。srcの中にはmanage.py startprojectで生成しているやつができていると思います。あとは、eclipseでインポートしてね。

ソース:https://gist.github.com/1186169
#!/usr/bin/python
#coding: utf8

import sys
import os
import platform
from pit import Pit

PROJECT = """\
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>%s</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.python.pydev.PyDevBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.python.pydev.pythonNature</nature>
</natures>
</projectDescription>
"""

PYDEVPROJECT = """\
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?eclipse-pydev version="1.0"?>

<pydev_project>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python 2.5</pydev_property>
<pydev_variables_property name="org.python.pydev.PROJECT_VARIABLE_SUBSTITUTION">
<key>GOOGLE_APP_ENGINE</key>
<value>%s</value>
</pydev_variables_property>
<pydev_pathproperty name="org.python.pydev.PROJECT_SOURCE_PATH">
<path>/%s/src</path>
</pydev_pathproperty>
<pydev_pathproperty name="org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH">
<path>${GOOGLE_APP_ENGINE}</path>
<path>${GOOGLE_APP_ENGINE}/lib/django</path>
<path>${GOOGLE_APP_ENGINE}/lib/webob</path>
<path>${GOOGLE_APP_ENGINE}/lib/yaml/lib</path>
</pydev_pathproperty>
</pydev_project>
"""

def main():
if not 'EDITOR' in os.environ:
os.environ['EDITOR'] = 'notepad' if platform.system() == 'Windows' else 'vi'
conf = Pit.get('kay_eclipse', {'require': {'kay_path': 'you\'r kay path', 'gae_path': 'you\'r gae path'}})
project_name = raw_input('project_name: ')
os.mkdir(project_name)
os.chdir(project_name)
project = open('.project', 'w')
pydev_project = open('.pydevproject', 'w')
project.write(PROJECT % project_name)
pydev_project.write(PYDEVPROJECT % (conf['gae_path'], project_name))
project.close()
pydev_project.close()
sys.path.extend((conf['kay_path'], conf['gae_path'], conf['gae_path'] + '/lib/fancy_urllib'))
from kay.management.startapp import startproject
startproject('src')

if __name__ == "__main__":
main()
posted by 右京 | Python

statuses/media_timeline使ってみた #twitter

Twitterに最近の画像を表示する機能が実装されて、色々いじってみて気づいたんだが、statuses/media_timelineというAPIが追加されていた。これはTwitterと連携している画像サービスやyoutubeなどのURLが含まれているツイートを抽出してくれる(使った感じでは)。そこで、twitter公式のアップローダーからアップロードされた画像URLを取得するプログラムを書いてみた。

#coding: utf8

'''
twitter公式のアップローダーからアップロードされた画像URLの取得

Example:
>>> from tw_media_fetcher import fetch_media_urls
>>> for url in fetch_media_urls(11091562):
>>> print url
http://p.twimg.com/AXhFJIBCAAAwgJA.png
http://p.twimg.com/AXbzldcCMAAXaRd.png
http://p.twimg.com/AXYEkcDCEAADx5X.jpg
http://p.twimg.com/AXQQd7mCAAAYbwH.png
http://p.twimg.com/AXHLGeOCQAAzuHI.png
http://p.twimg.com/AW75mIUCAAAfTv9.jpg
http://p.twimg.com/AW6eVdOCEAEY3G-.jpg
http://p.twimg.com/AWtfSR8CEAEb7tb.jpg
http://p.twimg.com/AWtfCAQCQAEGsUd.jpg
http://p.twimg.com/AWtdRVICAAENn2M.jpg
http://p.twimg.com/AWq-rX_CIAAdIan.jpg
http://p.twimg.com/AWq7LNfCMAALOmF.jpg
http://p.twimg.com/AWexfcvCMAA-zzV.png
'''

import urllib
import urllib2
try:
import json
except:
import simplejson as json

def fetch_media_urls(user_id, offset=0, count=100):
params = {
'offset': offset,
'count': count,
'score': 'true',
'filter': 'false',
'include_entities': 'true',
'user_id': user_id
}
res = urllib2.urlopen('http://api.twitter.com/1/statuses/media_timeline.json?%s' % urllib.urlencode(params)).read()
for tweet in json.loads(res):
try:
for media in tweet['entities']['media']:
yield media['media_url']
except:
pass

説明:
公式アップローダーからアップロードされた画像URLを含むツイートにはentitiesという要素が含まれていて、その中のmediaという配列の中からmedia_urlを抜き出している。

youtubeやその他画像サービスだと、urlsの中に色々と情報が入ってる(これも対応させるのは面倒かも)。
posted by 右京 | Python

socketでtwitter apiにアクセス #python

pythonのsocketモジュールの練習。下の例はtwitterのpublic_timelineにアクセスしてユーザ名と本文を表示するサンプル。

import socket
try:
import json
except:
import simplejson as json

host = 'api.twitter.com'
port = 80
chunk_size = 1024


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((host, port))
s.send("""GET /1/statuses/public_timeline.json HTTP/1.1
Host: api.twitter.com

""")

message = ''
try:
while 1:
data = s.recv(chunk_size)
if not data:
break
message += data
finally:
s.close()

header, body = message.split('\r\n\r\n')
print header, '\n'
for tweet in json.loads(body):
try:
print '[%s]' % tweet['user']['screen_name'], tweet['text']
except:
print 'error'
posted by 右京 | Python

plda使ってみた(パラレルではないが)

まずは参考ページから:
Tokyotextmining#1 kaneyama genta
ldaとtwitterの合わせ技のネタはここから仕入れてきた。
plda - A parallel C++ implementation of fast Gibbs sampling of Latent Dirichlet Allocation - Google Project Hosting
plda本家
PLDAQuickStart - plda - A Quick Start Manual for plda - A parallel C++ implementation of fast Gibbs sampling of Latent Dirichlet Allocation - Google Project Hosting
pldaのチュートリアル

次に想定として、データはmongoDBに入っているとして、textデータだけdumpする。
#!/usr/bin/python
#coding: utf8

import re
import pymongo
import MeCab

sub_url = re.compile('(https?|ftp)(:\/\/[-_.!~*\'()a-zA-Z0-9;\/?:\@&=+\$,%#]+)').sub
sub_user_hash = re.compile('(@|#)[a-zA-Z0-9_]+').sub

def delete(text):
return sub_user_hash('', sub_url('', text))

conn = pymongo.Connection()
db = conn.twitter
tweets = db.tweets

m = MeCab.Tagger('-u /home/ukyo/mecab-dic-overdrive/misc/dic/wikipedia.dic')

for t in tweets.find():
fuga = delete(t['text']).encode('utf8')
hoge = []
for f in m.parse(fuga).split('\n')[:-2]:
if '名詞' in f and not '名詞,数' in f and not '非自律' in f:
hoge.append(f.split('\t')[0])
print ','.join(hoge)

dumpしたデータを受け取って頻度を計算する。
#!/usr/bin/python
# coding: utf8

import sys
import re
sub = re.compile('\n').sub

def hoge(text):
fuga = sub('', text).split(',')
foo = {}
if len(fuga) < 5:
return
while(len(fuga) > 0):
n = fuga.count(fuga[0])
s = fuga[0]
for i in range(n): fuga.remove(s)
foo[s] = n
print ' '.join([str(k) + ' ' + str(n) for k, n in foo.items()])

for line in sys.stdin:
hoge(line)

実際使うときは以下のようなかんじで
$ dump.py | make_train_data.py > training.txt

最後に、ldaのチュートリアルにしたがってちょっと改変したりしてやっていけばモデルが完成する。
ちなみに、完成したモデルはソースコード一式にview_lda.pyというものがついているのでそれでどんな具合に分類できたのかを見ることができる(これは結構感動する)。
posted by 右京 | Python

物欲センサー

あったらこんなかんじかなーというのを擬似コードで書いてみた。何のことについてかは、あまり言及しません。

MIN = 10
MAX = 50

scheme = {
'item1': 20,
'item2': 40,
'item2': 30,
}

def butuyoku_senser(item_name, user):
butuyoku_data = get_data_from_memcache(user, scheme)
for k, v in butuyoku_data.items():
if item_name == k && butuyoku_data[k] > MIN:
butuyoku_data[k] -= 1
elif item_name != k && butuyoku_data[k] < MAX:
butuyoku_data[k] += 1
set_data_to_memcache(user, scheme, butuyoku_data)

def walk_in_the_quest(request, user):
return select_item_from_butuyoku_data(get_data_from_memcache(user, scheme))

def update_senser(request, user):
if request.params['get_item?']:
butuyoku_senser(request.params['item_name'], user)
posted by 右京 | Python

python-mecab 分かち書き

ただ分かち書きしたいだけなのにできねぇ。
from MeCab import Tagger

'''
Example:
>>> import MeCab
>>> import mecab_wakachi_patch.py
>>> m = Mecab.Tagger()
>>> m.wakachi('僕と契約して魔法少女になってよ')
'''

def wakachi(self, text):
return [line.split('\t')[0] for line in self.parse(text).split('\n')[:-2]]

setattr(Tagger, 'wakachi', wakachi)
posted by 右京 | Python

ローマ字のnグラム作るやつ #python

https://github.com/ukyo/roman-ngram

コーパスを青空文庫からとってくる場合(bigram)はこんな感じでやればOKだね。
cat *.txt | iconv -f shift_jis -t utf-8 > aozora-utf8.txt
text2ngram aozora-utf8.txt 2

https://github.com/ukyo/roman-ngram/tree/master/example
にあるのはtwitterのsample streamから一晩くらい落としてきたやつから作ったもの。
posted by 右京 | Python

テキストを全てひらがなに変換する #python

textconverterはテキストのエンコードを変換するUtility #python :右京webを参照。

なぜかeuc_jpに変換しているのはお察しください。
#coding: utf8

import sys
import re
from textconverter import convert
import MeCab

p_ruby = re.compile('《.+?》')
p_hira = re.compile('[ぁ-ゞー]+')
sub = p_ruby.sub
match = p_hira.match
m = MeCab.Tagger()
kigou = set(['「','」','、','・','?','…',' '])

argvs = sys.argv

if len(argvs) != 3:
print 'error'
quit()

input_name = argvs[1]
output_name = argvs[2]

input_file = open(input_name, 'r')
output_file = open(output_name, 'w')

for line in input_file:
tmp = sub('', line)
tmp = m.parse(convert.utf8_to_eucjp(tmp))
tmp = convert.eucjp_to_utf8(tmp)
sq = tmp.split('\n')
for _line in sq[:len(sq)-2]:
yomi = _line.split(',')[5]
if yomi == '。':
output_file.write('\n')
elif yomi in kigou:
pass
elif match(yomi):
output_file.write(yomi)
output_file.write('\n')

output_file.close()
posted by 右京 | Python
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。