HTML+JavaScriptで位置情報アプリ

位置情報を使ったサービスをスマホで使いたいとなるとネイティブアプリになってることが多いけど、HTMLアプリでいけんじゃね?と。
結構いけた。

路傍の史跡マップ

ロケタッチでも自由なスポットを登録できるけど、テーマ限定で独立したかった。もっと別テーマで量産する需要もありそうにも思う。目的に適うのはGoogleみんなで地図をつくろうだけど、Google+前提になるのと近くのスポットを見る方法がなさそうなのが残念。

技術的にはGoogle Maps JavaScript API様々。マイマップは操作できないみたいだけど、KMLを吐くことで同じ目的が達成できたし正当な方法である気がする。
スポットのデータは自前でMySQLに入れているが、写真は外部に置く。Tumblrを使いたかったけどAPIで投稿した時にエントリーIDが返らないので、Flickrを使用。flickrawでばっちり。
ただデザインがなー。スマホ向けにCSS弄るのめんどい。

ChordWikiに評価システム追加

おかしい(間違った)コード譜も多いよね、という声があったので五つ星の評価システムを付けた。
5段階評価 星型大
IPアドレスでユーザー識別。簡易なCSRF対策。


まだ今はそれぞれの楽曲ページに表示しているだけだが、将来的に多様な使い方をすることを考えるとRDBMSが必要だったのでMySQL稼働させた。
Rubyから使うライブラリは今ならMysql2でいいのかな。


楽曲横断でデータを使えるようになったからタグとかも実装したくなりそうだ。

サーバが落ちたメモ

とりあえず備忘録として。
uptimeが半年を超えたサーバのウェブサイトが落ちた。pingは通るがsshで入れない。さくらVPSのコントロールパネルではCPUが2.0Kに振り切ったまま。5時間落ちてた。コンパネから再起動で復帰。

Linux version 2.6.18-348.12.1.el5 (mockbuild@builder10.centos.org) (gcc version 4.1.2 20080704 (Red Hat 4.1.2-54)) #1 SMP Wed Jul 10 05:28:41 EDT 2013
/var/log/messages

Feb  8 17:28:27 www*****u kernel: Mem-info:
Feb  8 17:30:21 www*****u kernel: Node 0 DMA per-cpu:
Feb  8 17:30:22 www*****u kernel: cpu 0 hot: high 0, batch 1 used:0
Feb  8 17:30:25 www*****u kernel: cpu 0 cold: high 0, batch 1 used:0
Feb  8 17:30:26 www*****u kernel: cpu 1 hot: high 0, batch 1 used:0
Feb  8 17:30:30 www*****u kernel: cpu 1 cold: high 0, batch 1 used:0
Feb  8 17:30:49 www*****u kernel: Node 0 DMA32 per-cpu:
Feb  8 17:30:50 www*****u kernel: cpu 0 hot: high 186, batch 31 used:33
Feb  8 17:30:50 www*****u kernel: cpu 0 cold: high 62, batch 15 used:15
Feb  8 17:30:51 www*****u kernel: cpu 1 hot: high 186, batch 31 used:30
Feb  8 17:30:53 www*****u kernel: cpu 1 cold: high 62, batch 15 used:48
Feb  8 17:30:55 www*****u kernel: Node 0 Normal per-cpu: empty
Feb  8 17:31:48 www*****u kernel: Node 0 HighMem per-cpu: empty
Feb  8 17:33:40 www*****u kernel: Free pages:        6956kB (0kB HighMem)
Feb  8 17:39:58 www*****u kernel: Active:127037 inactive:102689 dirty:0 writeback:0 unstable:0 free:1739 slab:6080 mapped-file:1155 map
ped-anon:229203 pagetables:13134
Feb  8 17:41:48 www*****u kernel: Node 0 DMA free:2948kB min:36kB low:44kB high:52kB active:0kB inactive:0kB present:9684kB pages_scann
ed:0 all_unreclaimable? yes
Feb  8 17:42:01 www*****u kernel: lowmem_reserve[]: 0 994 994 994
Feb  8 17:42:02 www*****u kernel: Node 0 DMA32 free:4008kB min:4012kB low:5012kB high:6016kB active:507904kB inactive:410872kB present:
1018072kB pages_scanned:3244940 all_unreclaimable? yes
Feb  8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0
Feb  8 17:42:02 www*****u kernel: Node 0 Normal free:0kB min:0kB low:0kB high:0kB active:0kB inactive:0kB present:0kB pages_scanned:0 a
ll_unreclaimable? no
Feb  8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0
Feb  8 17:42:02 www*****u kernel: Node 0 HighMem free:0kB min:128kB low:128kB high:128kB active:0kB inactive:0kB present:0kB pages_scan
ned:0 all_unreclaimable? no
Feb  8 17:42:02 www*****u kernel: lowmem_reserve[]: 0 0 0 0
Feb  8 17:42:02 www*****u kernel: Node 0 DMA: 5*4kB 4*8kB 5*16kB 4*32kB 2*64kB 2*128kB 1*256kB 0*512kB 2*1024kB 0*2048kB 0*4096kB = 2948kB
Feb  8 17:42:02 www*****u kernel: Node 0 DMA32: 104*4kB 7*8kB 1*16kB 16*32kB 15*64kB 0*128kB 0*256kB 0*512kB 0*1024kB 1*2048kB 0*4096kB = 4008kB
Feb  8 17:42:02 www*****u kernel: Node 0 Normal: empty
Feb  8 17:42:02 www*****u kernel: Node 0 HighMem: empty
Feb  8 17:42:02 www*****u kernel: 6961 pagecache pages
Feb  8 17:42:02 www*****u kernel: Swap cache: add 9457575874, delete 9457570200, find 2144320127/3685532428, race 1898+6301
Feb  8 17:42:02 www*****u kernel: Free swap  = 0kB
Feb  8 17:42:02 www*****u kernel: Total swap = 2048276kB
Feb  8 17:42:02 www*****u kernel: Free swap:            0kB
Feb  8 17:42:02 www*****u kernel: 262141 pages of RAM
Feb  8 17:42:02 www*****u kernel: 5628 reserved pages
Feb  8 17:42:02 www*****u kernel: 25981 pages shared
Feb  8 17:42:02 www*****u kernel: 5768 pages swap cached
Feb  8 17:42:02 www*****u kernel: Out of memory: Killed process 9706, UID 48, (httpd).
Feb  8 17:42:02 www*****u kernel: httpd invoked oom-killer: gfp_mask=0x201d2, order=0, oomkilladj=0
Feb  8 17:42:02 www*****u kernel: 
Feb  8 17:42:02 www*****u kernel: Call Trace:
Feb  8 17:42:02 www*****u kernel:  [<ffffffff800cb2ee>] out_of_memory+0x8e/0x2f3
Feb  8 17:42:02 www*****u kernel:  [<ffffffff8002e4c7>] __wake_up+0x38/0x4f
Feb  8 17:42:02 www*****u kernel:  [<ffffffff8000f691>] __alloc_pages+0x27f/0x308
Feb  8 17:42:02 www*****u kernel:  [<ffffffff80013081>] __do_page_cache_readahead+0x96/0x17b
Feb  8 17:42:02 www*****u kernel:  [<ffffffff800139be>] filemap_nopage+0x14c/0x360
Feb  8 17:42:02 www*****u kernel:  [<ffffffff80008972>] __handle_mm_fault+0x1fd/0x103b
Feb  8 17:42:02 www*****u kernel:  [<ffffffff800183d7>] do_sync_write+0xc7/0x104
Feb  8 17:42:02 www*****u kernel:  [<ffffffff8006719c>] do_page_fault+0x499/0x842
Feb  8 17:42:02 www*****u kernel:  [<ffffffff800a3c28>] autoremove_wake_function+0x0/0x2e
Feb  8 17:42:02

ChordWikiにレコメンデーション機能追加

ChordWiki協調フィルタリングのレコメンデーション「この曲を見た人はこんな曲も見ています」を付ける。Cicindela要求スペックが高いとのことでさくらVPSのメモリ1Gでは無理だと思われ、Recommendifyで挑む。Rubyだし。


よくわからん。
情報というかコードも本家の他にこことかここくらいしか見当たらない。

class Recommender < Recommendify::Base
  max_neighbors 50
  input_matrix :chordwiki,
    :similarity_func => :jaccard,
    :weight => 5.0
end

recommender = Recommender.new

データ追加時も結果取得時も毎回このクラスを定義してnewするの? その先はRedisだからそういうものかもしれない。
max_neighborsとweightがどう使われているのかも読み取れない。レコメンド難しい。
本家サンプルに倣って

recommender.chordwiki.add_set(user_id, items)

して、最後に

recommender.process!

する。user_idはリモートホスト
結果取得はページ表示時にまたnewして

recommender.for(t).slice(0,5).each

する。chordwikiはどこ行った?と思うが、Redisのキーはrecommendify:chordwiki:itemsとrecommendify:chordwiki:ccmatrixとrecommendify:similaritiesが作られていて、結果はrecommendify:similaritiesを読めばいいらしい。じゃぁ複数のテーブルを持ちたくなったらどうするの?と思うがとりあえず関係ないのでスルー。


リアルタイムでデータ追加することはやめて、Apacheログから投入する。ログを保存してある4週間分を入れて、あとは週一回のcronを設定。1週間分で900MB程のログファイルの処理に数時間かかる。
負荷が心配だけど今のところ問題ない。
RedisのさくらVPSでの起動設定はこのへん参照した。

それでもなおレコメンド結果としては満足の行く出力が得られているとは言い難い。的外れな結果は出ていないが、アクセスが充分あるページに対しても結果0件ということが多い*1アルゴリズムやパラメータの問題かもしれないが、改良する知識がない。


協調フィルタリングによるレコメンデーションについては広告方式の無料ASPがあっても良いと思っていたけど、必要なデータ量と計算量の大きさを目の当たりにすると、難しいことがよく分かった。

*1:0件の時はテキスト検索で補完する。これまでも表示していたけど、Yahoo!の検索APIが有料化したのでGoogleに切り替え。http://ajax.googleapis.com/ajax/services/search/web?v=1.0&q=hoge&callback=fuga で行けるのね。あとキーフレーズ抽出APIが5万リクエスト/日の上限があって途中から表示されなくなっていたので使用中止。こっちは工夫しても限度があるしお茶を濁す

紀伊國屋書店アフィリエイトコード生成ツール

紀伊國屋書店 MyLink 作成

昔作って結局自分でも使わないから放置していたのだけど、紀伊國屋書店のサイトがリニューアルしたことと、Amazonのアソシエイト料率が変わったことを受けて、作り直した。

料率はこうか。

カテゴリ Amazon 紀伊國屋書店
3% 3%
電子書籍 3.5%-8% 3%
DVD・CD 2% 3%

紀伊國屋書店は自分買い(ショッパーズクラブ)ができて5%、さらにポイント1%(提携カードで2%)が付くように見える。

だが料率の問題だけでなく、Amazonアソシエイトと比べて紀伊國屋書店アフィリエイトコード取得環境が整備されていないので非常に手間がかかる。自動生成する仕組みを構築しないと、ブログに気軽に貼り付けるにはハードルが高い。このツールで少しは軽減できるけど、既に会員になっているかどうかという点でも差が大きいので、結局は収益化できず、使えない。
画像のURLも以前と変わっていて和書以外についても生成するのが面倒で、OGPが設定されているから取得するようにしたけど、APIがないと根本的にどうにもならん。

ブックマークレットも書いておくか。

javascript:(function(){var l=location.hostname;var u='http://worris3.sakura.ne.jp/KinoMyLink/?isbn=';if(l.match(/amazon/)){u=u+document.getElementById('ASIN').value;}else if(l.match(/kinokuniya/)){u=u+location.pathname.split(/dsg-\d\d-/)[1]}else{u=u+prompt('ISBN');}location.href=u;})();

新刊全点RSSフィードからジャンル別に取り出すYahoo! Pipe

bk1から移行したhontoが評判悪いが、中でも日別の新刊案内が消えたのが大きいらしい。
昔作った新刊全点RSSフィードスクレイピングの修正が必要となり、遅ればせながら対応した。
http://feeds.feedburner.com/TRCNewArrival


さらにジャンル別に見たいという人もいるっぽいのでYahoo! Pipesをpublishしておく。
http://pipes.yahoo.com/worris/trcnewarrival
いっそキーワードで取り出す。
http://pipes.yahoo.com/worris/trcnewarrival2


RSSで読むのなんか最近は流行らないのかなぁ。
コミックはもともと含まれていません。

ChordWikiのサーバをさくらのVPSに移転

前から脳内で計画とシミュレーションしてたけどむしゃくしゃしてたので実行した。
さくらのクラウドがリリースされるのを待ってみたけど、よく見たら必要なかった。

目的

アクセス負荷対策

さくらのレンタルサーバをライトからプレミアムに移行したのが2010年11月で90万PV/月くらいだったが、最近は180万PV/月まで来ていて503 Service Temporarily Unavailableが結構出ている。Webalizerで見ると3%台前半。他のサービスも共存してるけど。
304 Not Modifiedを明示的に返したりrequireを減らすために一ファイルに突っ込んだりと負荷軽減策を打って来たが、一時的に軽減したものの焼け石に水っぽい。
そしてさくらのレンタルサーバでは同一ファイルに対するアクセス制限が課せられているという話が本当なら影響が大きいシステムなので、引っ越せば軽減できると考えた。

Ruby 1.9

「その時」が来て慌てないように早めに書いておこうと思った。共有レンタルサーバで独自にRubyのバージョン上げるのも環境がややこしくなるのでVPSへ。
しかしほとんど書き換えないであっさり動作した。

Unicode

EUC-JPで上等だと思ってたんだけど、コンテンツ自体は問題ないのだが、URLエンコードしたEUC-JPを正しく扱えないサービスが存在する。bit.lyとかGoogle +1とか。勝手にUTF-8扱いするらしい*1。あとGoogle Analyticsはデコードは正しくしてくれるのだが、APIで呼び出す時など再エンコードUTF-8にしてしまう。
旧URLからのリダイレクトで文字コード変換できない文字が一部あるような気がしたがスルー。

副産物

独自ドメイン

chordwiki.orgを取った。

URL正規化

mod_rewriteの使用もコード変更多そうだから躊躇していたがそうでもなかった。代表ページのpermalinkだけ正規化して編集ページなどはパラメータを使うところや、サブドメインjaを無駄に使うなど、もろもろWikipediaに倣う。

ランキングをGoogle Analytics

これまで自分でカウントしていたが、上記のUTF-8化でGoogle Analyticsから生成できるようになった。

未達成

HTML5化とデザイン一新

Wikiエンジンに大きく関わるし、どうせデザインセンスないから諦めた。

ログイン機構

編集履歴IPアドレスでなくOpenIDで残したいなぁ。
Twitter IDで実装。


これまでの蓄積データを文字コード変換して移動させたり、301リダイレクトの処理も問題なく、概ね無事に移転作業完了した、と思う。
ChordWiki : コード譜共有サイト 〜無料の歌詞とコードをシェアしよう

*1:COMPLETEDって書いてあるけど嘘。