Gmailアカウントで受信したメールをRedmineにチケット登録する
Redmineを個人タスク管理に使うため、どこからでもメールでチケット登録したいと思って試してみた。
Redmine環境は BitNami :: Redmine の VMware Virtual Machines Ubuntu 10.10
まずはRedmine.JPのマニュアルを参照してコマンドを作成。
sudo /opt/bitnami/ruby/bin/rake --trace -f \
/opt/bitnami/apps/redmine/Rakefile \
redmine:email:receive_imap \
RAILS_ENV="production" \
host=imap.gmail.com \
port=993 \
ssl=1 \
project=XXX \
username=XXX@gmail.com \
password=XXX
メールを送ったあと上記コマンドを実行してみると、一応チケットが登録されたものの、タイトルが文字化けしている。
探してみたらパッチがあったので対策してみる。
メールでチケット登録時に題名が文字化けする問題を修正 | Redmine.JP Blog
wget http://blog.redmine.jp/assets/2010/07/16/farend_mail_handler_ja_subject_fix.rb
sudo mv farend_mail_handler_ja_subject_fix.rb /opt/bitnami/apps/redmine/config/initializers/
sudo /opt/bitnami/ctlscript.sh restart
定期的に自動でメールを取得するためcronに登録する。
sudo crontab -e
*/10 * * * * /opt/bitnami/ruby/bin/rake -f /opt/bitnami/apps/redmine/Rakefile redmine:email:receive_imap RAILS_ENV="production" host=imap.gmail.com port=993 ssl=1 project=XXX username=XXX@gmail.com password=XXX
これにて完了。
なお、このままだと送信元メールアドレスとRedmineのユーザのメールアドレスが一致してないといけないという制約がある。任意のメールアドレス(匿名ユーザ)でチケット登録するには unknown_user=accept を付ければいいらしいけど今のところ必要ないのでこのまま。
Twitterの声優クラスタ調査
先日、有志によるオタク達の河口湖合宿にて、ネット上の声優ファンコミュニティの勢い調査のプレゼンをしました。
その中から、Twitterの声優クラスタ調査結果の詳細を紹介します。
Twitterには、mixiのようにわかりやすいコミュニティが存在しません。
そこで今回は、リスト機能を利用して形成されているクラスタを調べました。
調査方法は下記の通りです。
こうして得られたクラスタ人数のランキングは下記の通りです。*2
リンク先は各クラスタのユーザリストです(スクリーンネーム,所属リスト数)。
- 水樹奈々 2166人 (80 lists) ユーザリスト
- 田村ゆかり 1621人 (81 lists) ユーザリスト
- スフィア 1566人 (105 lists) ユーザリスト
- 茅原実里 1102人 (90 lists) ユーザリスト
- 坂本真綾 775人 (40 lists) ユーザリスト
- 堀江由衣 456人 (21 lists) ユーザリスト
- 神谷浩史 437人 (7 lists) ユーザリスト
- 平野綾 298人 (4 lists) ユーザリスト
- 釘宮理恵 96人 (6 lists) ユーザリスト
この中には、複数の声優クラスタに所属しているユーザもいます。そこで、下記の通りユーザの重複度合いを調べてみました。
続いて重複率。重複率は A/(A+B-A∩B)A∩B/(A+B-A∩B) な感じで求めました。
クラスタ人数と重複率を考慮してクラスタを配置してみるとこんな感じ。
純粋に適当なデータ処理だけで出た結果ですが、割と体感通りなんじゃないでしょうか?
追記:
重複率はA∩B/Aの方が体感に合うんじゃね?的な意見があり、確かにと思ったので調べてみました。
新しく求めた重複率=浮気率を使ってクラスタ配置してみるとこんな感じになりました。
こっちの方が体感通りのような…?
迷路を解いてみた
http://okajima.air-nifty.com/b/2010/01/post-abc6.html
これを解いてみた。
50分かかりました。
# coding: utf-8 import sys data = """************************** *S* * * * * * * ************* * * * * ************ * * * * ************** *********** * * ** *********************** * * G * * * *********** * * * * ******* * * * * * **************************""" # 各マスの距離を算出 def solve(x, y, maze): # 現在のマスの上下左右を調べる for [dx, dy] in [[x, y-1], [x+1, y], [x, y+1], [x-1, y]]: if maze[dy][dx] > maze[y][x] + 1: maze[dy][dx] = maze[y][x] + 1 solve(dx, dy, maze) # ゴールから逆順にたどって$マークを付ける def route(x, y, maze, out): # 現在のマスの上下左右を調べる for [dx, dy] in [[x, y-1], [x+1, y], [x, y+1], [x-1, y]]: if maze[dy][dx] == maze[y][x] - 1 and maze[dy][dx] > 0: out[dy][dx] = "$" route(dx, dy, maze, out) break wall = -1 # 壁 inf = 100000 # 探索前の初期値 maze = [] # 距離データ out = [] # 出力データ start = [] # スタート位置 goal = [] # ゴール位置 # 入力をリスト構造に変換 iy = 0 ix = 0 for line in data.splitlines(): lmaze = [] lout = [] for c in line: lout.append(c) if c == "*": lmaze.append(wall) else: lmaze.append(inf) if c == "S": start = [ix, iy] elif c == "G": goal = [ix, iy] ix += 1 maze.append(lmaze) out.append(lout) iy += 1 ix = 0 # 距離を算出 [x, y] = start maze[y][x] = 0 solve(x, y, maze) # ゴールから逆順に辿る [x, y] = goal route(x, y, maze, out) # 標準出力に出力 for l in out: for c in l: sys.stdout.write(c) sys.stdout.write("\n")
↓出力結果
************************** *S* * $$$$ * *$* *$$* $************* * *$* $$* $$************ * *$$$$* $$$$$ * **************$*********** * $$$$$$$$$$$$$ * **$*********************** * $$$$$* $$$$$$$$$$$$$G * * * $$$$*********** * * * * ******* * * * * * **************************
- printだとスペースが入ってしまうのでsys.stdout.writeに修正
- xとy逆だった…
ニコニコ動画の検索結果を取得してリスト化する
# coding: sjis def login(opener): import urllib url = "https://secure.nicovideo.jp/secure/login?site=niconico" postdata = {} postdata["mail"] = "メールアドレス" postdata["password"] = "パスワード" postdata = urllib.urlencode(postdata) r = opener.open(url, postdata) html = r.read().decode("utf-8") return html def get_list_html(): import cookielib, urllib2 url = u"http://www.nicovideo.jp/tag/%E5%A0%80%E6%B1%9F%E7%94%B1%E8%A1%A3" cj = cookielib.LWPCookieJar("cookie.txt") cj.load() ch = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(ch) html = opener.open(url).read().decode("utf-8") try: html.index('form name="login"') login(opener) html = opener.open(url).read().decode("utf-8") except ValueError: pass cj.save() return html def parse(html): from BeautifulSoup import BeautifulSoup soup = BeautifulSoup(html) table = soup.findAll("table") td = table[8].findAll("td") list = [] for t in td: p = t.findAll("p") date = p[1].text length = p[3].text title = p[4].text a = p[4].find("a") url = "http://www.nicovideo.jp/" + dict(a.attrs)["href"] s = p[5].findAll("strong") play = s[0].text comment = s[1].text mylist = s[2].text if int(play.replace(",", "")) > 5000: video = {"date" : date, "length" : length, "title" : title, "url" : url, "play" : play, "comment" : comment, "mylist" : mylist} list.append(video) return list def main(): html = get_list_html() list = parse(html) print list if __name__=="__main__": main()
認証付き&要クッキーのサイトにログインする
baseurl = "https://trading1.sbisec.co.jp/ETGate/" def write(str): f = open("out.html", "w") f.write(str.encode("sjis")) f.close() def build_opener_cookie(cj): import urllib2 ch = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(ch) return opener def get_form(opener): r = opener.open(baseurl) html = r.read().decode("sjis") return html def parse_form(html): from BeautifulSoup import BeautifulSoup soup = BeautifulSoup(html) q = {} form = soup.findAll("form")[1] for i in form.findAll("input"): d = dict(i.attrs) t = d["type"] if t == "hidden" or t == "text" or t == "password": q[d["name"]] = d["value"] return q def login(opener, query): import urllib query["user_id"] = u"hogehoge" query["user_password"] = u"hagehage" query = urllib.urlencode(query) r = opener.open(baseurl, query) html = r.read().decode("sjis") return html def main(): import cookielib cj = cookielib.LWPCookieJar("cookie.txt") opener = build_opener_cookie(cj) html = get_form(opener) q = parse_form(html) html = login(opener, q) write(html) cj.save() if __name__=="__main__": main()
- ログインページのformを抽出
- input要素を抽出してコピー
- id, passを入れてurlencodeしてpost
- LWPCookieJarを使うとCookieの内容がファイルとして保存できる
Webサイトから特定の情報を抽出する
def get(url, code): import urllib f = urllib.urlopen(url) return f.read().decode(code) def parse(html): from BeautifulSoup import BeautifulSoup soup = BeautifulSoup(html) entry_table = soup.findAll("table")[3].findAll("td") return {"title": entry_table[6].text,"body": entry_table[9].text,"datetime": entry_table[10].text} if __name__=="__main__": url = "http://www.starchild.co.jp/artist/horie/diary/index.php" html = get(url, "eucjp") entry = parse(html) print entry
- BeautifulSoup を使ってみた
- いろいろ試したけど結局力技に落ち着いた