Finding all paths in a directed acyclic graph structure with CPAN Graph module
非循環有向グラフで、ある頂点から頂点までの全経路を探します。
This script finds all paths from a vertex to another.
(注意点: 循環構造があると無限ループします)
NOTE: cannot be used with cycles.
use strict; use warnings; use Graph::Directed; use Data::Dumper; sub find_all_paths { my ($from, $dest) = @_; return [[$dest]] if $from eq $dest; return undef unless $g->is_reachable($from, $dest); my @paths; foreach my $edge ( $g->edges_from($from) ) { my $child_paths = find_all_paths($edge->[1], $dest); if (defined($child_paths)) { foreach my $child_path (@$child_paths) { push(@paths, [$from, @$child_path]); } } } return \@paths; } # usage my $g = Graph::Directed->new; $g->add_edges(qw( b a c a d a e a f a g a h b i c j h k h l i m l n i o d o t p j q j q r q s r l r n s o )); my $paths = find_all_paths('q', 'a'); print Dumper($paths);
outputs:
$VAR1 = [ [ 'q', 's', 'o', 'd', 'a' ], [ 'q', 'r', 'n', 'i', 'c', 'a' ], [ 'q', 'r', 'l', 'i', 'c', 'a' ], [ 'q', 'j', 'h', 'b', 'a' ] ];
DjangoでShift-JISの携帯サイトを作る
Djangoのバージョンは1.0.2を使っています。
まず、settings.pyでDEFAULT_CHARSETをcp932に指定しておきます。
ここでShift_JISを指定してしまうと「〜」などの文字を出力しようとした時にUnicodeErrorが出てしまいます。
DEFAULT_CHARSET = 'cp932'
このままだとレスポンスのHTTPヘッダが Content-Type: text/html; charset=cp932 となってしまい良くないのでContent-TypeをShift_JISに付け替えます。middleware.pyを作ってその中でMiddlewareクラスを定義しておきます。
class SJISMiddleware(object): def process_response(self, request, response): response['Content-Type'] = 'text/html; charset=Shift_JIS' return response
特定のファイルはContent-Typeを変えたくない場合はif文で振り分けるなどすると良いでしょう。Content-Typeがtext/htmlの場合だけ付け替えるというのも良いかもしれません。
さらにContent-Lengthも付与するようにMiddlewareクラスを作っておくと良いと思います。
class ContentLengthMiddleware(object): def process_response(self, request, response): if not response.has_header('Content-Length') and response.content: response['Content-Length'] = len(response.content) return response
あと機種判別のため uamobile も使うケースが多いと思います。とても素晴らしいパッケージですね。
class UserAgentMobileMiddleware(object): def process_request(self, request): request.device = None try: request.device = detect(request.META) except Exception, e: print "Can't detect device: %s" % e
最後に上で作った3つのMiddlewareをsettings.pyで有効にします。こんな感じでしょうか。
MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'some.project.middleware.ContentLengthMiddleware', 'some.project.middleware.SJISMiddleware', 'some.project.middleware.UserAgentMobileMiddleware', )
some.projectの部分はmiddleware.pyの配置箇所によって適宜置き換えてください。
ちなみにMIDDLEWARE_CLASSESで指定したクラス群は、リクエストの時は上から順にprocess_request()関数が処理され、レスポンスの時は下から順にprocess_response()関数が処理されます。
PHPでユニコードエスケープ(unicode_encode, unicode_decode代替)
PHP6からは unicode_encode() 関数と unicode_decode() 関数が追加されるらしいのですが、PHP5やPHP4でユニコードエスケープをしたい時のために。
// UTF-8文字列をUnicodeエスケープする。ただし英数字と記号はエスケープしない。 function unicode_decode($str) { return preg_replace_callback("/((?:[^\x09\x0A\x0D\x20-\x7E]{3})+)/", "decode_callback", $str); } function decode_callback($matches) { $char = mb_convert_encoding($matches[1], "UTF-16", "UTF-8"); $escaped = ""; for ($i = 0, $l = strlen($char); $i < $l; $i += 2) { $escaped .= "\u" . sprintf("%02x%02x", ord($char[$i]), ord($char[$i+1])); } return $escaped; } // Unicodeエスケープされた文字列をUTF-8文字列に戻す function unicode_encode($str) { return preg_replace_callback("/\\\\u([0-9a-zA-Z]{4})/", "encode_callback", $str); } function encode_callback($matches) { $char = mb_convert_encoding(pack("H*", $matches[1]), "UTF-8", "UTF-16"); return $char; } // 使用例 $str = "東京"; // ユニコードエスケープする $decoded = unicode_decode($str); echo "$decoded\n"; // 「\u6771\u4eac」が出力される // UTF-8に戻す $encoded = unicode_encode($decoded); echo "$encoded\n"; // 「東京」が出力される
Movable TypeのPerl APIを使って移行作業
やりたいこと
(1) 移行元のMovable TypeのデータをYAMLでダンプ ↓ (2) YAMLを移行先サーバに転送 ↓ (3) 移行先のMovable TypeでYAMLをインポート
ここまでやります。
投稿者をダンプ&インポートする
まず全投稿者をダンプしてみます。
以降の説明では Movable Type を /var/www/html/mt3-cgi にインストールしたものと仮定しています。別のディレクトリにインストールした場合は読み替えてください。
すべての投稿者をダンプするスクリプト: dump_authors.pl
#!/usr/bin/perl use strict; use warnings; use lib qw(/var/www/html/mt3-cgi/lib); use MT; use MT::Author; use YAML::Syck; # カレントディレクトリがMTのインストールディレクトリではない(mt-config.cgiがない)場合は # mt-config.cgi へのパスを指定する my $mt = MT->new( Config => '/var/www/html/mt3-cgi/mt-config.cgi' ); # load() の () 内に検索条件を指定しなければ全件取得 my @authors = MT::Author->load(); # YAMLでダンプする print Dump @authors;
移行元サーバでこのスクリプトを実行します。
% ./dump_authors.pl > authors.yaml
この authors.yaml を移行先サーバに転送します。
次に、投稿者をインポートするスクリプト: import_authors.pl
#!/usr/bin/perl use strict; use warnings; use lib qw(/var/www/html/mt3-cgi/lib); use MT; use MT::Author; use YAML::Syck; # カレントディレクトリがMTのインストールディレクトリではない(mt-config.cgiがない)場合は # mt-config.cgi へのパスを指定する my $mt = MT->new( Config => '/var/www/html/mt3-cgi/mt-config.cgi' ); # 標準入力(STDIN)からYAMLデータを読み込む my $yamlstr = join('', <>); # オブジェクトに戻す my @authors = Load($yamlstr); foreach my $author (@authors) { # $author->save で投稿者が登録されます $author->save or die $author->errstr; }
さきほどの authors.yaml を読み込ませてスクリプトを実行します。
% ./import_authors.pl < authors.yaml
この要領です。そんなに難しくないですよね?
カテゴリをダンプ&インポートする
カテゴリは MT::Category を使います。
ダンプするスクリプトはこんな風になります。
# 全カテゴリを取得 my @cats = MT::Category->load(); # ダンプ print Dump @cats;
インポートするスクリプトも同様です。
# YAMLをロード my $yamlstr = join('', <>); my @cats = Load($yamlstr); print Dumper(@cats); foreach my $cat (@cats) { # $cat->save でカテゴリが登録されます $cat->save or die $cat->errstr; }
ほかにもいろいろありまっせ
同様にブログ一覧はMT::Blogで移行できます。使い方は MT::Blogのドキュメント にあります。
このほかにもテンプレートやプラグインデータなど一通りPerl APIが用意されていて大変お手軽です。
参考: Movable Type オブジェクト・リファレンス
大量のデータをインポートするとき
データが大量にあるときはYAMLのロードでたくさんメモリ食っちゃって大変です。
なので1件ずつ逐次パースしてインポートしていくといいと思います。こんな感じで
my $buf; while (<>) { if ($_ eq "--- \n" && $buf) { my $data = Load($buf); &process_data($data); $buf = ''; } $buf .= $_; } sub process_data { my $data = shift; # $data をインポートするなどの処理 }
結論
Movable Type の Perl API は大変よくできていて素晴らしいです。
Movable Typeに大量のエントリを投入テスト中
仕事でMovable Type 3.35に10万件ぐらいのエントリを投入中。
まだ5,300件なのでだいぶ時間かかる。しかもだんだん遅くなってる気がする。
CustomFieldsとかプラグイン使ってるのでAPI使わずWWW::Mechanizeで自動投入してる。あまりMT使ったことないのでもっといい方法があるのかもしんない。
一応ダイナミック・パブリッシングにはしたけど、1つのブログに大量にエントリ入れたらどうなるかパフォーマンス見てみます。