<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>#8 &#187; Code</title>
	<atom:link href="http://jinim.jp/topics/code/feed" rel="self" type="application/rss+xml" />
	<link>http://jinim.jp</link>
	<description>from Tokyo</description>
	<lastBuildDate>Mon, 30 Aug 2010 04:57:09 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Simple HTTP Checker &#8211; シンプルなHTTPサーバの監視ツール</title>
		<link>http://jinim.jp/archives/2136</link>
		<comments>http://jinim.jp/archives/2136#comments</comments>
		<pubDate>Thu, 25 Mar 2010 06:33:38 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/?p=2136</guid>
		<description><![CDATA[WEBサイトの死活監視のサービスはいろいろあるけれど、どれも監視できるURL数に制限があったり、制限を解除してもらうには費用がかかるわけなのだけれど求める以上の機能があってちょっと高くついたり。なかなかピッタリこない。
 &#8230; <a href="http://jinim.jp/archives/2136">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>WEBサイトの死活監視のサービスはいろいろあるけれど、どれも監視できるURL数に制限があったり、制限を解除してもらうには費用がかかるわけなのだけれど求める以上の機能があってちょっと高くついたり。なかなかピッタリこない。</p>
<p>仕方がない、さくらのレンタルサーバが1つあるのでそこで監視するようにしてみよう。社内のサーバ監視をしているNagiosは便利なんだけれど、さくらの500円/月のレンタルサーバに入れるのはちょっとアレだし、必要最小限のスクリプトをPythonで書くことにする。</p>
<p>求めた要件としては、</p>
<ul>
<li>HTTPで接続できなかったらメールでお知らせが届く。</li>
<li>一回メールを送ったら、しばらくはメールしないで欲しい。</li>
<li>HEADとGETとPOSTに対応。</li>
<li>HTTPステータスコードだけをチェックする（レスポンスに含まれるコンテンツのチェックは不要）。</li>
<li>設定ファイルで複数のURLをまとめてチェックして欲しい。</li>
<li>（さくらなので）常駐するデーモンではなくて、単独のコマンドで実行できる（cronで繰り返し実行）。</li>
<li>お知らせメールはcrontabのMAILTOで送るからstderrに出力してくれればいい。</li>
<li>OKだったときのログはstdoutに出力してくれればいい。</li>
<li>スクリプトは1ファイルで完結させる。</li>
<li>Python2.4以降、標準ライブラリだけで動く。</li>
</ul>
<p><span id="more-2136"></span><br />
使い方は、<br />
1. python htpchk.py URL<br />
とURLを引数に指定する方法。これだと細かい設定はできませんが、HEADでチェック、お知らせメールは多くて10分に1回、となります。</p>
<p>2. python htpchk.py htpchk.conf<br />
と引数に設定ファイルを指定する（あるいは引数指定を省略して、htpchk.pyと同じディレクトリにhtpchk.confという設定ファイルを置いておく）と、複数のURLを一括してチェックできるようになります。<br />
設定ファイルは、Windowsのiniファイルに良く似た書き方です。DEFAULTセクションは全セクションのデフォルト値として使われます。あとはチェックするURL毎にセクションを作ってurlパラメータを書いておくだけで1と同様にチェックはできます。セクション毎にDEFAULTセクションの値を上書きできるので、細かい変更がしたい場合はセクションに値を書きます。詳しい設定ファイルの書き方は後の方のサンプルをご覧ください。</p>
<p>cronにこのスクリプトを実行するように登録しておく（さくらのレンタルサーバではこんな感じ）。<br />
/home/example/bin/にhtpchk.pyとhtpchk.confを置いたとするとこんな感じ。</p>
<pre>
MAILTO=alert@example.com, mobile@example.com
# NAME: htpchck
*/5     *       *       *       *       /usr/local/bin/python /home/example/bin/htpchk.py >/dev/null
</pre>
<p>これで、5分に一回htpchk.confに書いてあるサイトをチェックして、HTTPステータスが200じゃなければ、標準エラーにメッセージが出力されて、そのメッセージはMAILTOに書いたメールアドレスに送られます。標準出力は/dev/nullに向けてあるので捨てられます。</p>
<p>手元では47のURLをチェックする設定ファイルを書いて運用中。概ね良好なんだけれど、まだチェックするURLを増やしたいのでもう少しチェックにかかる時間を短縮したい。HTTPのリクエストを送る部分をスレッドにして同時実行すれば速くなるのはわかっているので、困ったらスレッド化するかも。</p>
<p>以下はスクリプト本体（htpchk.py）と設定ファイル（htpchk.conf）のサンプルです。最新版は<a href="http://gist.github.com/343248">Gist:343248</a>に置いてあります。</p>
<p><strong>htpchk.py</strong></p>
<pre class="brush:python">
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""#8 Simple HTTP Checker
  Usage:
       htpchk (htpchk.conf is needed at same directory.)
       htpchk URL
       htpchk config-file
"""
__NAME__ = '#8 Simple HTTP Checker'
__VERSION__ = '1.0'
__ABOUT__ = 'http://jinim.jp/archives/2136'
__USER_AGENT__ = 'Mozilla/5.0 (compatible; %s/%s; +%s)' % (__NAME__, __VERSION__, __ABOUT__)
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"

import os
import sys
import time
import urllib2
from urllib2 import HTTPError
import urlparse
import tempfile
from ConfigParser import ConfigParser

#url to filename with sha hash.
try:
    import hashlib
    def _urlhash_filename(url): return hashlib.sha224(url).hexdigest()
except:
    import sha
    def _urlhash_filename(url): return sha.new(url).hexdigest()

opener = urllib2.build_opener()
opener.addheaders = [('User-agent', __USER_AGENT__)]
urllib2.install_opener(opener)

class ParamError(Exception): pass

class HeadRequest(urllib2.Request):
    def get_method(self): return "HEAD"

class Site(object):
    def __init__(self, name, url, notify_interval=60*10, method="HEAD", data=None, wait_seconds=0):
        """When medhod is POST, data is requred."""
        self.name = name
        self.url = url
        self.method = method
        self.data = data
        self.notify_interval = notify_interval
        self.wait_seconds = wait_seconds
        self.notified = os.path.join(tempfile.gettempdir(), "htpcheck-%s" % _urlhash_filename(self.url))

    def check(self):
        if self.method=="HEAD":
            u = urllib2.urlopen(HeadRequest(self.url))
        elif self.method=="POST":
            u = urllib2.urlopen(self.url, data=self.data)
        elif self.method=="GET":
            u = urllib2.urlopen(self.url)
        else:
            raise ParamError, "method: %s not supported. Supported methods are (HEAD,GET,POST)." % self.method
        return u.info()

    def recovered(self):
        os.rename(self.notified, os.path.join(os.path.dirname(self.notified), os.path.basename(self.notified) + time.strftime("-recovered.%Y%m%d%H%M%S")))

    def notify(self, err):
        msg = log_message(self, err)
        last_notified = 0
        if os.path.exists(self.notified):
            try:
                last_notified = time.mktime(time.strptime(file(self.notified).readlines()[-1].split("\t")[0], DATETIME_FORMAT))
            except ValueError:
                last_notified = 0
        if time.time() > (last_notified + self.notify_interval):
            sys.stderr.write(msg)
            file(self.notified, 'a').write("%s\tnotified\n" % msg[:-1])
        else:
             sys.stdout.write(log_message(self, "[SUPPRESSED] %s" % err))

def log_message(site, msg=None):
    """if msg is not None, message means error.
    """
    if msg:
        format = '%s\tNG\t%s\t%s\t%s\t%s\n'
        return format % (time.strftime(DATETIME_FORMAT), site.name, site.method, site.url, msg)
    else:
        format = '%s\tOK\t%s\t%s\t%s\n'
        return format % (time.strftime(DATETIME_FORMAT), site.name, site.method, site.url)

def config_parse(conf):
    parser = ConfigParser()
    parser.readfp(open(conf))
    sites = list()
    for section in parser.sections():
        url = parser.get(section, 'url')
        method = parser.get(section, 'method')
        if method=="POST":
            data = parser.get(section, 'data')
        else:
            data = None
        notify_interval = parser.getfloat(section, 'notify_interval')
        wait_seconds = parser.getfloat(section, 'wait_seconds')
        sites.append(Site(section, url, notify_interval, method, data, wait_seconds))
    return sites

def main(sites):
    for site in sites:
        try:
            site.check()
            sys.stdout.write(log_message(site))
            if os.path.exists(site.notified):
                site.recovered()
        except HTTPError, err:
            site.notify(err)
        if site.wait_seconds>0: time.sleep(site.wait_seconds)

if __name__=='__main__':
    if len(sys.argv)>1: config = sys.argv[1]
    else: config = os.path.splitext(__file__)[0]+'.conf'

    if not os.path.exists(config):
        if config.startswith('http://') or config.startswith('https://'):
            name = urlparse.urlparse(config)[1].split(':')[0]
            sites = (Site(name, config), )
        else:
            raise ParamError, "config file %s not found." % config
    else:
        sites = config_parse(config)

    main(sites)
</pre>
<p><strong>htpchk.conf</strong></p>
<pre class="brush:plain">
[test_HEAD]
url: http://example.com/

[test_GET]
url: http://example.com/
method: GET

[test_POST]
url: http://example.com/
method: POST
data: Hello World

[test_notfound]
url: http://example.com/notfound.html
notify_interval: 10

[DEFAULT]
#************************************************
;  DEFAULT values
#************************************************

#Target URL.
#url=http://example.com/

#HTTP method
method: HEAD

#POST data
#data: Hello World

#When HTTP error occuered, repeatedly notified with interval seconds.
notify_interval: 600

#after checking, wait bellow seconds.
wait_seconds: 0
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/2136/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>営業日を判断するOracle PL/SQLパッケージ</title>
		<link>http://jinim.jp/archives/1715</link>
		<comments>http://jinim.jp/archives/1715#comments</comments>
		<pubDate>Thu, 04 Feb 2010 01:52:07 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://jinim.jp/?p=1715</guid>
		<description><![CDATA[現実逃避から少しでもエンジンがかかるように、コードを公開してみよう。
OracleのPL/SQLで書いた営業日/稼働日判定用のストアード・パッケージ。いろいろと調べてみたのだけれど、ぴったりくるものが見当たらなくて書いて &#8230; <a href="http://jinim.jp/archives/1715">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>現実逃避から少しでもエンジンがかかるように、コードを公開してみよう。</p>
<p>OracleのPL/SQLで書いた営業日/稼働日判定用のストアード・パッケージ。いろいろと調べてみたのだけれど、ぴったりくるものが見当たらなくて書いてみたもの。まだリリースまで時間があって、それほどテストされていないけれど、そんなに複雑でもないので大丈夫ではないかと。IS_WORKDAYがCHARを返すのはカッコ悪いけれど、いろいろあってこういう仕様。直すのは簡単なので気になる人は直してください。</p>
<p>仕様としては、１）土曜日と日曜日は営業日じゃない、２）休日テーブル（holidays）に日付があったらそれは休日。wkd_pkg.is_workdayを直すことで、土曜日は営業日にしたり、水曜日を休日にしたりできるので、その辺りは適当に。仕様はExcelにあるworkdayとnetworkdayを参考にしているけれど、厳密には一緒じゃない（と思う）。</p>
<p>このパッケージには4つの関数があります。1つはオーバーロードしたものなので、実質的には３つ。</p>
<p>1. FUNCTION WORKDAY( i_startdate IN date, i_days IN number) RETURN DATE;<br />
  開始日からi_days営業日後の日付を返す、 i_daysが正数なら未来、負数なら過去、開始日は数えない。</p>
<p>2. FUNCTION IS_WORKDAY( i_date IN date) RETURN CHAR;<br />
  指定した日付が営業日ならYを返す。営業日じゃなければNを返す。</p>
<p>3. FUNCTION NETWORKDAY( i_startdate IN date, i_enddate IN date) RETURN NUMBER;<br />
  開始日と終了日の間に含まれる営業日の日数を返す。日数には開始日と終了日も含まれる。<br />
<span id="more-1715"></span><br />
コードはGistにおいてあります。 <a href="http://gist.github.com/294489">Gist: 294489</a></p>
<pre class="brush:sql">
CREATE OR REPLACE PACKAGE WKD_PKG AS
/*
 営業日パッケージ

 ※土曜、日曜が営業日の場合は、IS_WORKDAYを直す
 ※国民の祝日が営業日の場合は、休日テーブルを直す

-- 休日テーブル（祝祭日、お盆とか、都民の日とかも）
create table holidays (
    holiday date,
    primary key(holiday)
);
-- 2010年の国民の祝日を入れておく
-- ここが便利 http://cbdb.cybozu.co.jp/cgi-bin/db.cgi?page=DBView&#038;did=690
insert into holidays values(to_date('2010-01-01','YYYY-MM-DD')); --元日
insert into holidays values(to_date('2010-01-11','YYYY-MM-DD')); --成人の日
insert into holidays values(to_date('2010-02-11','YYYY-MM-DD')); --建国記念の日
insert into holidays values(to_date('2010-03-21','YYYY-MM-DD')); --春分の日
insert into holidays values(to_date('2010-03-22','YYYY-MM-DD')); --振替休日
insert into holidays values(to_date('2010-04-29','YYYY-MM-DD')); --昭和の日
insert into holidays values(to_date('2010-05-03','YYYY-MM-DD')); --憲法記念日
insert into holidays values(to_date('2010-05-04','YYYY-MM-DD')); --みどりの日
insert into holidays values(to_date('2010-05-05','YYYY-MM-DD')); --こどもの日
insert into holidays values(to_date('2010-07-19','YYYY-MM-DD')); --海の日
insert into holidays values(to_date('2010-09-20','YYYY-MM-DD')); --敬老の日
insert into holidays values(to_date('2010-09-23','YYYY-MM-DD')); --秋分の日
insert into holidays values(to_date('2010-10-11','YYYY-MM-DD')); --体育の日
insert into holidays values(to_date('2010-11-03','YYYY-MM-DD')); --文化の日
insert into holidays values(to_date('2010-11-23','YYYY-MM-DD')); --勤労感謝の日
insert into holidays values(to_date('2010-12-23','YYYY-MM-DD')); --天皇誕生日

*/

/*
|| 開始日からi_days営業日後の日付を返す
|| i_daysが正数なら未来、負数なら過去
|| ※開始日は数えない
*/
 FUNCTION WORKDAY( i_startdate IN date, i_days IN number) RETURN DATE;

/*
|| 指定した日付が営業日ならYを返す。営業日じゃなければNを返す。
*/
 FUNCTION IS_WORKDAY( i_date IN date) RETURN CHAR;
 -- 今日が営業日かどうか
 FUNCTION IS_WORKDAY RETURN CHAR;

/*
|| 開始日と終了日の間に含まれる営業日の日数を返す。
|| 日数には開始日と終了日も含まれる。
*/
 FUNCTION NETWORKDAY( i_startdate IN date, i_enddate IN date) RETURN NUMBER;
END WKD_PKG;
/

----------------------------------------------------------
--    PACKAGE BODY
----------------------------------------------------------
CREATE OR REPLACE PACKAGE BODY WKD_PKG AS

FUNCTION WORKDAY( i_startdate IN date, i_days IN number) RETURN DATE
IS
  i   NUMBER := 0;
  work_days NUMBER := 0;
BEGIN
  IF i_days = 0 THEN
     RETURN i_startdate;
  END IF;

  WHILE true LOOP
    IF i_days > 0 THEN
       i := i + 1;
    ELSE
       i := i - 1;
    END IF;

    IF IS_WORKDAY(i_startdate + i) = 'Y' THEN
      work_days := work_days + 1;
    END IF;

    IF abs(work_days) = abs(i_days) THEN
       exit;
    END IF;
  END LOOP;

  RETURN i_startdate + i;
END;

FUNCTION IS_WORKDAY(i_date IN date) RETURN CHAR
IS
  hol NUMBER := 0;
BEGIN
  -- 日曜日:1と土曜日:7に含まれない
  IF TO_CHAR(i_date, 'D') NOT IN ('1', '7') THEN
     -- 土日以外でholidaysに存在しなければ営業日
     SELECT COUNT(*) INTO hol FROM holidays WHERE holiday=trunc(i_date);
     IF hol = 0 THEN
        RETURN 'Y';
     END IF;
  END IF;
  RETURN 'N';
END;

FUNCTION IS_WORKDAY RETURN CHAR
IS
BEGIN
  RETURN IS_WORKDAY(SYSDATE);
END;

FUNCTION NETWORKDAY( i_startdate IN date, i_enddate IN date) RETURN NUMBER
IS
  i number := 0;
  diff number := 0;
  days number := 0;
BEGIN
  diff := trunc(i_enddate) - trunc(i_startdate);
  FOR i IN 0..diff LOOP
      IF IS_WORKDAY(i_startdate + i) = 'Y' THEN
        days := days + 1;
      END IF;
  END LOOP;
  RETURN days;
END;

END WKD_PKG;
/

/*
-- テスト

-- 適当に
SELECT to_char(WKD_PKG.workday(to_date('2010-01-09','YYYY-MM-DD'), 2),'YYYY-MM-DD') FROM dual;
SELECT to_char(WKD_PKG.workday(to_date('2010-01-08','YYYY-MM-DD'), 4),'YYYY-MM-DD') FROM dual;
SELECT to_char(WKD_PKG.workday(to_date('2010-01-07','YYYY-MM-DD'), 4),'YYYY-MM-DD') FROM dual;
SELECT to_char(WKD_PKG.workday(to_date('2010-01-07','YYYY-MM-DD'), -4),'YYYY-MM-DD') FROM dual;

-- 全部6のはず。9,10が土日。11日が祝日。
SELECT WKD_PKG.NETWORKDAY(to_date('2010-01-07','YYYY-MM-DD'),to_date('2010-01-15','YYYY-MM-DD')) FROM dual;
SELECT WKD_PKG.NETWORKDAY(to_date('2010-01-07','YYYY-MM-DD'),to_date('2010-01-16','YYYY-MM-DD')) FROM dual;
SELECT WKD_PKG.NETWORKDAY(to_date('2010-01-07','YYYY-MM-DD'),to_date('2010-01-17','YYYY-MM-DD')) FROM dual;
-- 7のはず。9,10が土日。11日が祝日。
SELECT WKD_PKG.NETWORKDAY(to_date('2010-01-07','YYYY-MM-DD'),to_date('2010-01-18','YYYY-MM-DD')) FROM dual;

*/
</pre>
<p>追記：GitHubがトラブってた。なんだかAnonymousのGistになっていたのでForkしたGistに変えました。</p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/1715/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Flickrから自分のアップロードした写真を全部ダウンロードする</title>
		<link>http://jinim.jp/archives/1527</link>
		<comments>http://jinim.jp/archives/1527#comments</comments>
		<pubDate>Wed, 18 Nov 2009 01:13:02 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/?p=1527</guid>
		<description><![CDATA[ふと思い立ってやってみようとしたけれど、これが結構大変。一括してダウンロードする方法がFlickrの機能としては見当たらない。
誰か他の人の写真じゃなくて自分のアップロードした写真ぐらい、機能があってもよさそうだけれど。 &#8230; <a href="http://jinim.jp/archives/1527">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>ふと思い立ってやってみようとしたけれど、これが結構大変。一括してダウンロードする方法がFlickrの機能としては見当たらない。<br />
誰か他の人の写真じゃなくて自分のアップロードした写真ぐらい、機能があってもよさそうだけれど。<br />
検索してみると、いくつかソフトウエアがあって、２つ試してみたけれど、認証に失敗して先に進めないのがひとつと、そもそもインストールできないのがひとつ（.Net Frameworkを使うやつ）。<br />
意地になってしまって、<a href="http://flickrpy.googlecode.com/svn/trunk/flickr.py">flickr.py</a>を使ったPythonスクリプトを書いてみた。<br />
Pythonとflickr.pyがあれば使えます。ユーザID（ユーザ名じゃない）、API KeyとかSecretとか難しいのが必要。やっぱり、Flickr自身で実装しないと普通の人には難しいんじゃないか。</p>
<p>下のスクリプトをローカルに保存して、設定を3ヶ所書き換えて（ユーザID、API Key、API Secret）、flickr.pyを同じディレクトリにおいて、実行するとサブディレクトリを作成してその中にズンズンダウンロードします。<br />
<span id="more-1527"></span></p>
<pre class="brush:python">
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""flickr.pyの利用サンプル。 http://code.google.com/p/flickrpy/
Flickrから指定したユーザの全ての写真を取得してローカルディスクに保存する。
自分の写真をダウンロードするために作ったので、他人の写真でも使えるかどうかは未確認。
"""
import os
import time
import flickr
import urllib

#######################################################
# ここから設定変更
#######################################################

#ユーザID
#これ変更必須。自分のアカウントページでURLをひょいと見ると書いてあった。@を含んだ12文字。
user_id = "XXXXXXXX@YYY"

#Flickr API Key + Secret
#これ必須。自分のアカウントのページで確認。
flickr.API_KEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
flickr.API_SECRET = "XXXXXXXXXXXXXXXX"

#これはオプション。
#取得する画像のサイズ。Large=オリジナル。 http://www.flickr.com/services/api/flickr.photos.getSizes.html
#Originalでもいけそうだけど、何故かダメだった。
size = 'Large'

#######################################################
# ここまで設定変更
#######################################################

#ダウンロード写真を保持するディレクトリの作成
MY_DIR = os.path.dirname(os.path.abspath(__file__))
IMAGE_DIR = os.path.join(MY_DIR, 'image_%s' % size)
if os.path.exists(IMAGE_DIR):
    os.rename(IMAGE_DIR, "%s.bak-%s" % (IMAGE_DIR, time.strftime('%Y%m%d%H%M%S')))
os.makedirs(IMAGE_DIR)

if __name__=='__main__':
    user = flickr.User(user_id)
    photo_count = int(user.photos_count)
    print("%s has %d photos." % (user.username, photo_count))

    retr_count = 0
    page = 1

    while retr_count < photo_count:
        photos = flickr.people_getPublicPhotos(user_id, per_page = 500, page = page)
        for photo in photos:
            url = photo.getURL(size = size, urlType = 'source')
            filename = os.path.join(IMAGE_DIR, '%s.jpg' % (photo.title))
            file(filename, "wb+").write(urllib.urlopen(url).read())
            retr_count += 1
            print("%d/%d: %s retrieved." % (retr_count, photo_count, os.path.basename(filename)))
        page += 1
</pre>
<p>スクリプトはGistにも置いてあります。 <a href="http://gist.github.com/237451">Gist:237451</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/1527/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>さくらのレンタルサーバで直近１週間分だけmysqlのバックアップを取っておく</title>
		<link>http://jinim.jp/archives/1344</link>
		<comments>http://jinim.jp/archives/1344#comments</comments>
		<pubDate>Tue, 20 Oct 2009 07:59:43 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[小道具]]></category>

		<guid isPermaLink="false">http://jinim.jp/?p=1344</guid>
		<description><![CDATA[
さくらインターネットのレンタルサーバを使っている。
毎日mysqlのバックアップを自動的に取りたい。
一週間分だけ取っておきたい（一週間以上前のバックアップは消えてほしい）。

と、聞かれたので、書いておく。

mys &#8230; <a href="http://jinim.jp/archives/1344">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<ul>
<li>さくらインターネットのレンタルサーバを使っている。</li>
<li>毎日mysqlのバックアップを自動的に取りたい。</li>
<li>一週間分だけ取っておきたい（一週間以上前のバックアップは消えてほしい）。</li>
</ul>
<p>と、聞かれたので、書いておく。</p>
<ul>
<li>mysqldumpでダンプする。</li>
<li>bzip2で圧縮する。</li>
<li>dateで取得した曜日をファイル名に含めて毎日上書きする。</li>
</ul>
<p>という方針でいく。<br />
<span id="more-1344"></span><br />
まずは、ディレクトリを作る。コントロールパネルでもいいし、sshで入って</p>
<blockquote><p>mkdir backup<br />
mkdir bin
</p></blockquote>
<p>とやってもいい。とにかく、ホームディレクトリに、backupとbinディレクトリを作る。</p>
<p>mysqldumpを実行するシェルスクリプトを書く（cshはよくわからないので、bash）。わからなくなっちゃうから、全部フルパスで書いておく。<br />
ファイル名は「/home/ユーザ名/bin/mysql-backup.sh」とする。</p>
<pre class="brush:bash">
#!/usr/local/bin/bash
DBHOSTNAME=mysqlXXX.db.sakura.ne.jp
DBUSERNAME=ユーザ名
DBPASSWORD=パスワード
DBNAME=データベース名
DAY=`date "+%w"`
BACKUPFILENAME=/home/ユーザ名/backup/mysql.dmp.$DAY.bz2

/usr/local/bin/mysqldump -h $DBHOSTNAME -p$DBPASSWORD -u $DBUSERNAME $DBNAME |/usr/bin/bzip2 >$BACKUPFILENAME
</pre>
<p>※ mysqlXXX.db.sakura.ne.jpのXXXの部分、ユーザ名、パスワード、データベース名を自分の契約しているレンタルサーバの情報にあわせて変更する。</p>
<p>あとは、</p>
<blockquote><p>/home/ユーザ名/bin/mysql-backup.sh 1>/dev/null</p></blockquote>
<p>という感じでサーバコントロールパネルの「CRONの設定」を使って書いておく（crontab -eで編集しても同じ）。<br />
DAYの値は0が日曜日で順に土曜日が6。<br />
毎日先週の同じ曜日のバックアップファイルが上書きされるので、常に１週間分のこっていることになる。</p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/1344/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PostgreSQLでアプリケーションから透過的に論理削除</title>
		<link>http://jinim.jp/archives/584</link>
		<comments>http://jinim.jp/archives/584#comments</comments>
		<pubDate>Mon, 15 Jun 2009 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[PostgreSQL]]></category>
		<category><![CDATA[Storm]]></category>
		<category><![CDATA[仕事]]></category>

		<guid isPermaLink="false">http://jinim.jp/2009/06/16/postgresql%e3%81%a7%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e3%81%8b%e3%82%89%e9%80%8f%e9%81%8e%e7%9a%84%e3%81%ab%e8%ab%96%e7%90%86%e5%89%8a%e9%99%a4/</guid>
		<description><![CDATA[PostgreSQLのルールシステムを使ってDELETEが発行されたときに論理削除をするようにしてみた。
Storm（のようなORM）を使うときに、store.remove(object)と削除は削除として実行できるから &#8230; <a href="http://jinim.jp/archives/584">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.postgresql.jp/document/pg837doc/html/rules.html">PostgreSQLのルールシステム</a>を使ってDELETEが発行されたときに論理削除をするようにしてみた。<br />
Storm（のようなORM）を使うときに、store.remove(object)と削除は削除として実行できるからよい感じ。</p>
<p><span id="more-584"></span></p>
<pre class="brush:sql">
-- PostgreSQLのルールシステムを使った論理削除の例
-- 削除フラグと削除日時を保持するフィールドを持ったテーブルを作成
CREATE TABLE a(
  id serial PRIMARY KEY,
  value text,
  deleted boolean DEFAULT False,
  deleted_on timestamp DEFAULT 'infinity'
);
-- DELETEが実行されたら代わりに削除フラグを立てて削除日時をセットするルール
CREATE RULE a_delete_rule AS ON DELETE TO a
  DO INSTEAD
  UPDATE a SET deleted=True, deleted_on=CURRENT_TIMESTAMP
      WHERE id = OLD.id;

-- ここからはテストデータを投入、削除
-- 三件件挿入
INSERT INTO a(value) VALUES('a');
INSERT INTO a(value) VALUES('b');
INSERT INTO a(value) VALUES('c');
-- 確認
SELECT * FROM a;
-- 削除してみる
DELETE FROM a WHERE id>1;
-- 確認 削除されないで、削除フラグと削除日時が更新されている
SELECT * FROM a;
-- 後始末
DROP TABLE a;
</pre>
<p><a href="http://gist.github.com/229566">Gist:229566</a></p>
<p>実行すると、こんな感じになります。</p>
<blockquote><p>
pguser=# &#8212; PostgreSQLのルールシステムを使った論理削除の例<br />
pguser=# &#8212; 削除フラグと削除日時を保持するフィールドを持ったテーブルを作成<br />
pguser=# CREATE TABLE a(<br />
pguser(#   id serial PRIMARY KEY,<br />
pguser(#   value text,<br />
pguser(#   deleted boolean DEFAULT False,<br />
pguser(#   deleted_on timestamp DEFAULT &#8216;infinity&#8217;<br />
pguser(# );<br />
NOTICE:  CREATE TABLE will create implicit sequence &#8220;a_id_seq&#8221; for serial column &#8220;a.id&#8221;<br />
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index &#8220;a_pkey&#8221; for table &#8220;a&#8221;<br />
CREATE TABLE<br />
pguser=#<br />
pguser=# &#8212; DELETEが実行されたら代わりに削除フラグを立てて削除日時をセット<br />
pguser=# CREATE RULE a_delete_rule AS ON DELETE TO a<br />
pguser-#   DO INSTEAD<br />
pguser-#   UPDATE a SET deleted=True, deleted_on=CURRENT_TIMESTAMP<br />
pguser-#       WHERE id = OLD.id;<br />
CREATE RULE<br />
pguser=#<br />
pguser=# &#8212; 三件件挿入<br />
pguser=# INSERT INTO a(value) VALUES(&#8216;a&#8217;);<br />
INSERT 0 1<br />
pguser=# INSERT INTO a(value) VALUES(&#8216;b&#8217;);<br />
INSERT 0 1<br />
pguser=# INSERT INTO a(value) VALUES(&#8216;c&#8217;);<br />
INSERT 0 1<br />
pguser=#<br />
pguser=# &#8212; 確認<br />
pguser=# SELECT * FROM a;<br />
 id | value | deleted | deleted_on<br />
&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;<br />
  1 | a     | f       | infinity<br />
  2 | b     | f       | infinity<br />
  3 | c     | f       | infinity<br />
(3 行)</p>
<p>pguser=#<br />
pguser=# &#8212; 削除してみる<br />
pguser=# DELETE FROM a WHERE id>1;<br />
DELETE 0<br />
pguser=#<br />
pguser=# &#8212; 確認 削除されないで、削除フラグと削除日時が更新されている<br />
pguser=# SELECT * FROM a;<br />
 id | value | deleted |         deleted_on<br />
&#8212;-+&#8212;&#8212;-+&#8212;&#8212;&#8212;+&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;-<br />
  1 | a     | f       | infinity<br />
  2 | b     | t       | 2009-09-09 16:51:16.491694<br />
  3 | c     | t       | 2009-09-09 16:51:16.491694<br />
(3 行)</p>
<p>pguser=#<br />
pguser=# &#8212; 後始末<br />
pguser=# DROP TABLE a;<br />
DROP TABLE
</p></blockquote>
<p>DELETEの結果件数が0なのが、ORM側でどうなるのかちょっと気になるけれど。</p>
<p>考えたり、調べると、いいことあるなあ。</p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/584/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bashシェルスクリプトで自分の絶対パスを取得</title>
		<link>http://jinim.jp/archives/571</link>
		<comments>http://jinim.jp/archives/571#comments</comments>
		<pubDate>Wed, 04 Mar 2009 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[小道具]]></category>

		<guid isPermaLink="false">http://jinim.jp/2009/03/05/bash%e3%82%b7%e3%82%a7%e3%83%ab%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88%e3%81%a7%e8%87%aa%e5%88%86%e3%81%ae%e7%b5%b6%e5%af%be%e3%83%91%e3%82%b9%e3%82%92%e5%8f%96%e5%be%97/</guid>
		<description><![CDATA[シェルスクリプトで自分の絶対パスを知る方法はこんな方法しかないんだろうか？

#!/bin/sh
D=`dirname $0`
echo `cd $D;pwd`

Pythonだとしょっちゅう書いてるアレ。

#!/bi &#8230; <a href="http://jinim.jp/archives/571">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>シェルスクリプトで自分の絶対パスを知る方法はこんな方法しかないんだろうか？</p>
<pre class="brush:bash">
#!/bin/sh
D=`dirname $0`
echo `cd $D;pwd`
</pre>
<p>Pythonだとしょっちゅう書いてるアレ。</p>
<pre class="brush:python">
#!/bin/env/python
from os import path
print(path.abspath(path.dirname(__file__)))
</pre>
<p>お、こういう書き方があるのか。</p>
<pre class="brush:bash">
#!/bin/sh
echo $(cd $(dirname $0);pwd)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/571/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>OracleでFIRST_DAY</title>
		<link>http://jinim.jp/archives/515</link>
		<comments>http://jinim.jp/archives/515#comments</comments>
		<pubDate>Tue, 11 Mar 2008 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Oracle]]></category>

		<guid isPermaLink="false">http://jinim.jp/2008/03/12/first_day/</guid>
		<description><![CDATA[先輩に聞く前に、

まずよく考えみて、
わからなければ調べて、
それでもダメなら聞く、

という癖をつけないとなあ。厳しいぞ。

/* Oracle PL/SQLで日付からその月の1日を求める*/

/* to_date &#8230; <a href="http://jinim.jp/archives/515">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>先輩に聞く前に、</p>
<ul>
<li>まずよく考えみて、</li>
<li>わからなければ調べて、</li>
<li>それでもダメなら聞く、</li>
</ul>
<p>という癖をつけないとなあ。厳しいぞ。</p>
<pre class="brush:sql">
/* Oracle PL/SQLで日付からその月の1日を求める*/

/* to_date、to_charを使えるなら、考えればできる */
create or replace function first_day(thedate date) return date is
begin
    return to_date(to_char(thedate,'YYYY-MM')||'-01','YYYY-MM-DD');
end;

/* 調べてみれば、こっちがスマートだとわかる */
create or replace function first_day(thedate date) return date is
begin
    return trunc(thedate,'MONTH');
end;

/***** ついでに *****/

/* 年の最初の日 1月1日 */
create or replace function first_day2(thedate date) return date is
begin
    return trunc(thedate,'YEAR');
end;

/* 週の始まり日曜日（※NLSの設定次第） */
create or replace function first_day3(thedate date) return date is
begin
    return trunc(thedate,'DAY');
end;
</pre>
<p>最後に問題。月の日数を求めるにはどうする？</p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/515/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>urllib2でプロキシを参照しないようにする</title>
		<link>http://jinim.jp/archives/511</link>
		<comments>http://jinim.jp/archives/511#comments</comments>
		<pubDate>Tue, 04 Mar 2008 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/2008/03/05/urllib2%e3%81%a7%e3%83%97%e3%83%ad%e3%82%ad%e3%82%b7%e3%82%92%e5%8f%82%e7%85%a7%e3%81%97%e3%81%aa%e3%81%84%e3%82%88%e3%81%86%e3%81%ab%e3%81%99%e3%82%8b/</guid>
		<description><![CDATA[urllib.urlopenはステータス404でも例外を発生してくれない。
urllib2.urlopenはそのままだと環境変数のプロキシ設定を参照するようで、ちょっと困る場合があった。
というわけで、urllib2.u &#8230; <a href="http://jinim.jp/archives/511">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>urllib.urlopenはステータス404でも例外を発生してくれない。</p>
<p>urllib2.urlopenはそのままだと環境変数のプロキシ設定を参照するようで、ちょっと困る場合があった。</p>
<p>というわけで、urllib2.urlopenでプロキシを設定|参照しないようにする方法。</p>
<pre class="brush:python">
#!/usr/bin/env python
"""Python urllib2 proxy usage sample
"""
import urllib2

#今回はプロキシ設定を空にしておく
#proxies = {'http': 'http://www.example.com:3128/'}
proxies = {}

#プロキシハンドラーを作成して
handler = urllib2.ProxyHandler(proxies)

#プロキシハンドラーを指定してURL Openerを作成して
opener = urllib2.build_opener(handler)

#作成したURL Openerをインストールしてから
urllib2.install_opener(opener)

#普通にurlopenすると例外が発生してくれる
try:
    u = urllib2.urlopen('http://www.example.com/404NotFound.html')
    print u.read()
except IOError,error:
    print error
</pre>
<p><a href="http://gist.github.com/227549">Gist:227549</a></p>
<p>Pythonカテゴリ作ってみた。</p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/511/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pythonで月末、月初。ついでに月を加減するadd_months</title>
		<link>http://jinim.jp/archives/510</link>
		<comments>http://jinim.jp/archives/510#comments</comments>
		<pubDate>Sun, 02 Mar 2008 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/2008/03/03/%e6%9c%88%e6%9c%ab%e3%80%81%e6%9c%88%e5%88%9d%e3%80%82%e3%81%a4%e3%81%84%e3%81%a7%e3%81%ab%e6%9c%88%e3%82%92%e5%8a%a0%e6%b8%9b%e3%81%99%e3%82%8badd_months/</guid>
		<description><![CDATA[
月初はまあいいとしても、月末、月の足し引きは、こんな方法しか思いつかず。
カッコ悪いが使えることは使える、と思う。

2008.3.14追記

calendarモジュールを使って月の日数を取得することで、月末計算をシン &#8230; <a href="http://jinim.jp/archives/510">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<div class="section">
<p>月初はまあいいとしても、月末、月の足し引きは、こんな方法しか思いつかず。</p>
<p>カッコ悪いが使えることは使える、と思う。</p>
<p><a name="seemore"></a></p>
<h4>2008.3.14追記</h4>
<ul>
<li>calendarモジュールを使って月の日数を取得することで、月末計算をシンプルに。</li>
<li>add_monthsも見直し。</li>
</ul>
<p>それほどカッコ悪くはなくなったんじゃないかと思う。<a href="http://jinim.jp/archives/516">修正の経緯</a></p>
<pre class="brush:python">
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""月末、月初のdatetime.dateオブジェクトを返す。
Oracleのadd_months互換の月加減。
"""
import datetime
from calendar import monthrange

def first_day(date):
	"""月初を返す"""
	return datetime.date(date.year, date.month, 1)

def is_last_day(date):
	"""月末日付ならTrueを返す"""
	return days_of_month(date.year, date.month) == date.day

def days_of_month(year,month):
	"""年,月の日数を返す"""
	return monthrange(year, month)[1]

def last_day(date):
	"""月末を返す"""
	return datetime.date(year=date.year, month=date.month, day=days_of_month(date.year, date.month))

def add_months(date,months):
	"""月を加減する。
	dateにはdatetime.dateクラスのオブジェクト
	monthsには整数で月数を指定する。
	月末をOracleのadd_months互換の方法で処理する。
	例えば、
	2007年2月28日（月末）に1ヶ月足すと3月31日（月末）。
	2008年2月29日（月末）に1ヶ月足すと2008年3月31日（月末）。
	2008年2月28日（月末ではない）に1ヶ月足すと2008年3月28日（同じ日）。
	"""
	if months==0:
		return date

	year, month = divmod(date.month + months, 12)
	year = year + date.year

	#ちょうど割り切れたら12月で、マイナス1年。
	if month == 0:
		month = 12
		year = year - 1

	#入力日付がその月の月末なら、加算後月の日数を。
	#そうじゃなければ入力日付の日。
	day = date.day
	if date.day > days_of_month(year, month):
		day = days_of_month(year, month)
	return datetime.date(year=year, month=month, day=day)
</pre>
<p>最新版は<a href="http://gist.github.com/227554">Gist:227554</a>をどうぞ。</p>
</div>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/510/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MoinMoinのカスタムユーザ認証スクリプトサンプル</title>
		<link>http://jinim.jp/archives/499</link>
		<comments>http://jinim.jp/archives/499#comments</comments>
		<pubDate>Mon, 04 Feb 2008 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/2008/02/05/moinmoin%e3%81%ae%e3%82%ab%e3%82%b9%e3%82%bf%e3%83%a0%e3%83%a6%e3%83%bc%e3%82%b6%e8%aa%8d%e8%a8%bc%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88%e3%82%b5%e3%83%b3%e3%83%97%e3%83%ab/</guid>
		<description><![CDATA[MoinMoinを仕事で使いたい。今すでに使っている社内の認証機構を使ってユーザ管理を楽するためにはどうすればいいか？ Pythonのちょっとしたスクリプトを書けばなんとかなるようだ。
ということで調べてみたら、

wi &#8230; <a href="http://jinim.jp/archives/499">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://moinmo.in/">MoinMoin</a>を仕事で使いたい。今すでに使っている社内の認証機構を使ってユーザ管理を楽するためにはどうすればいいか？ Pythonのちょっとしたスクリプトを書けばなんとかなるようだ。</p>
<p>ということで調べてみたら、</p>
<ol>
<li>wikiconfig.pyのConfigクラス内でカスタムスクリプトを使うように設定する。</li>
<li>ログイン用のカスタムスクリプトを書いて、MoinMoin/auth/以下に置く。</li>
</ol>
<p>の２つの手順でできることがわかった。<a href="http://moinmo.in/MoinMoinRelease1.6">MoinMoin-1.6</a>で動くことを確認した。</p>
<p>以下、やってみて上手くいった手順を記録しておく。</p>
<p><a name="seemore"></a></p>
<p>この例では、</p>
<ul>
<li>カスタムスクリプト内にユーザ情報を保持している。この部分を必要な認証機構を呼び出すようにすれば使えるはず。</li>
<li>メールアドレスと別名もMoinMoinのプロファイル外で管理していることにして、カスタムスクリプト内で設定管理している。</li>
</ul>
<p><b>wikiconfig.pyの編集</b></p>
<pre class="brush:python">
class Config(DefaultConfig):
    """MoinMoinのカスタム認証スクリプトmyauth.loginを使うようにwikiconfig.py
　　　 で認証方法を指定。
    """
    from MoinMoin.auth import moin_session,myauth
    auth = [myauth.login, moin_session]
    #MoinMoin内のPreferenceを自動的に作成する
    user_autocreate = True
</pre>
<p><a href="http://gist.github.com/232443">Gist:232443</a></p>
<p><b>カスタム認証スクリプト MoinMoin/auth/myauth.py</b></p>
<pre class="brush:python">
# -*- coding: utf-8 -*-
"""MoinMoin custom user authentication sample
"""
from MoinMoin import user
#User Database
_USER_MAP = {'user1,pass1' : ('user1@example.com',u'user1'),
    'user2,pass2' : ('user2@example.com','user2')}

def login(request, **kw):
	username = kw.get('name')
	password = kw.get('password')
	login = kw.get('login')
	user_obj = kw.get('user_obj')

	if not login:
		return user_obj, True

	u = None
	user_info = _USER_MAP.get("%s,%s" % (username,password),False)

	if user_info:
		u = user.User(request,
		name=username,
		auth_username=username,
		password=password,
		auth_method='myauth',
		auth_attribs=('name', 'auth_username', 'password', 'email', 'aliasname', ))
		u.email = user_info[0]
		u.aliasname = user_info[1]
		u.create_or_update(True)
		request.log("Login OK. user=%s, email=%s, aliasname=%s." % (username,user_info[0],user_info[1]))

	return u, True
</pre>
<p><a href="http://gist.github.com/227564">Gist:227564</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/499/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Windows ショートカット作成VBスクリプト</title>
		<link>http://jinim.jp/archives/471</link>
		<comments>http://jinim.jp/archives/471#comments</comments>
		<pubDate>Wed, 24 Oct 2007 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[小道具]]></category>

		<guid isPermaLink="false">http://jinim.jp/2007/10/25/windows-%e3%82%b7%e3%83%a7%e3%83%bc%e3%83%88%e3%82%ab%e3%83%83%e3%83%88%e4%bd%9c%e6%88%90vb%e3%82%b9%e3%82%af%e3%83%aa%e3%83%97%e3%83%88/</guid>
		<description><![CDATA[全員のデスクトップのショートカットを揃えたほうが電話でサポートしやすいね、ということで。
VBスクリプトを使ってショートカット作成スクリプトを作ってみた。
使い方は、こんな感じ。

C:&#92;&#62;create- &#8230; <a href="http://jinim.jp/archives/471">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>全員のデスクトップのショートカットを揃えたほうが電話でサポートしやすいね、ということで。<br />
VBスクリプトを使ってショートカット作成スクリプトを作ってみた。</p>
<p>使い方は、こんな感じ。</p>
<blockquote><p>
C:&#92;&#62;create-shortcuts.vbs shortcut.conf
</p></blockquote>
<p><b>create-shortcuts.vbs</b></p>
<pre class="brush:vb">
'#####################################################
' ショートカット作成スクリプト
' 設定ファイル複数受付版
'#####################################################
Const FOR_READING = 1
Const COL_FOLDER = 0
Const COL_SUB_FOLDER = 1
Const COL_TITLE = 2
Const COL_PATH = 3
Const COL_WORK = 4

Set objArgs = WScript.Arguments
'Wscript.Echo objArgs(0)
'Wscript.Echo objArgs.length

For arg_index = 0 to objArgs.length-1

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile(objArgs(arg_index), FOR_READING)

Do Until objTextFile.AtEndOfStream
  strNextLine = objTextFile.Readline
  if strNextLine <> "" then
    conf = Split(strNextLine , ",")
    if conf(COL_FOLDER) <> "" then
        Set Shell = CreateObject("WScript.Shell")
        CreatePath = Shell.SpecialFolders(conf(COL_FOLDER))
        if CreatePath = "" then CreatePath = conf(COL_FOLDER)
        if conf(COL_SUB_FOLDER) <> "" then
          CreatePath = CreatePath &#038; "\" &#038; conf(COL_SUB_FOLDER)
          if not objFSO.FolderExists(CreatePath) then
          	objFSO.CreateFolder(CreatePath)
          end if
        end if
        Set link = Shell.CreateShortcut(CreatePath &#038; "\" &#038; conf(COL_TITLE) &#038;".lnk")
        link.Description = conf(COL_TITLE)
        link.HotKey = ""
        link.IconLocation = conf(COL_PATH)
        link.TargetPath = conf(COL_PATH)
        '通常のウインドウ
        link.WindowStyle = 1
        link.WorkingDirectory = conf(COL_WORK)
        args = ""
        For i = COL_WORK+1 to Ubound(conf)
          args = args &#038; " " &#038; conf(i)
        Next
        link.Arguments = args
        ret = link.Save

'デバッグ情報
'        Wscript.Echo "タイトル:" &#038; conf(0)
'        Wscript.Echo "パス:" &#038; conf(1)
'        Wscript.Echo "作業ディレクトリ:" &#038; conf(2)
'        Wscript.Echo "引数:" &#038; args
'        Wscript.Echo ret
    end if
  end if
Loop
Next
</pre>
<p><b>shortcut.conf</b></p>
<pre class="brush:text">
,********************************************
, カンマで始まる行はコメント行
,********************************************

,カンマで区切って、作成場所、タイトル,パス,作業ディレクトリ,引数1,引数2,引数3,...の順に書いておくと
,作成場所（デスクトップなど）にショートカットを作る

,作成場所に指定できるのは次のものだけ
, AllUsersDesktop
, AllUsersStartMenu
, AllUsersPrograms
, AllUsersStartup
, Desktop
, Favorites
, Fonts
, MyDocuments
, NetHood
, PrintHood
, Programs
, Recent
, SendTo
, StartMenu
, Startup
, Templates

,****************************************************************
, ここからが実際のデータ
,****************************************************************
Programs,,Excel,C:\Program Files\Microsoft Office\Office11\excel.exe,H:\,
Programs,,Word,C:\Program Files\Microsoft Office\Office11\winword.exe,H:\,
Programs,,PowerPoint,C:\Program Files\Microsoft Office\Office11\powerpnt.exe,H:\,
Programs,,Visio,C:\Program Files\Microsoft Office\Visio10\visio.exe,H:\,
Programs,,Access,C:\Program Files\Microsoft Office\Office11\msaccess.exe,H:\,

Desktop,,Excel,C:\Program Files\Microsoft Office\Office11\excel.exe,H:\,
Desktop,,Word,C:\Program Files\Microsoft Office\Office11\winword.exe,H:\,
Desktop,,PowerPoint,C:\Program Files\Microsoft Office\Office11\powerpnt.exe,H:\,
Desktop,,Visio,C:\Program Files\Microsoft Office\Visio10\visio.exe,H:\,
Desktop,,Access,C:\Program Files\Microsoft Office\Office11\msaccess.exe,H:\,</pre>
<ul>
<li>スクリプトファイル <a href="http://gist.github.com/232452">Gist:232452 create-shortcuts.vbs</a></li>
<li>設定ファイル <a href="http://gist.github.com/232454">Gist:232454 shortcut.conf</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/471/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ActiveDirectoryのLDAP認証もどき</title>
		<link>http://jinim.jp/archives/470</link>
		<comments>http://jinim.jp/archives/470#comments</comments>
		<pubDate>Tue, 09 Oct 2007 15:00:00 +0000</pubDate>
		<dc:creator>eight</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Tech]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://jinim.jp/2007/10/10/activedirectory%e3%81%aeldap%e8%aa%8d%e8%a8%bc%e3%82%82%e3%81%a9%e3%81%8d/</guid>
		<description><![CDATA[python-ldapを使ってみた。
ついでに、以前から試そうと思って手をつけていなかった、ActiveDirectoryのLDAP認証も。試しに、bindしてみるだけ。平文でパスワードが流れるのでこのままではちょっと使 &#8230; <a href="http://jinim.jp/archives/470">続きを読む <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://python-ldap.sourceforge.net/">python-ldap</a>を使ってみた。</p>
<p>ついでに、以前から試そうと思って手をつけていなかった、ActiveDirectoryのLDAP認証も。試しに、bindしてみるだけ。平文でパスワードが流れるのでこのままではちょっと使えない。</p>
<p><a name="seeall"></a></p>
<pre class="brush:python">
#!/usr/bin/env python
# -*- coding: utf-8-*-
# python-ldapを使ってActiveDirectoryで認証してみる
# http://python-ldap.sourceforge.net/

def ADAuth(username,password,host,port=389):
    """ActiveDirectoryのドメイコントローラにユーザ名とパスワードでbindしてみる。
    bindできれば認証OK。認証NGなら例外が起きる。
    """
    import ldap
    url = "ldap://%s:%d" % (host,port)
    l = ldap.initialize(url)
    l.simple_bind_s(username,password)
    l.unbind_s()

if __name__=='__main__':
    if len(sys.argv)<3:
        print "Usage: %s username password" % (sys.argv[0])
    else:
        try:
            userid = sys.argv[1]
            password = sys.argv[2]
            ADAuth(userid,password,"adserver.example.com")
            print "OK"
        except:
            print "NG"
</pre>
<p><a href="http://gist.github.com/227565">Gist:227565</a></p>
]]></content:encoded>
			<wfw:commentRss>http://jinim.jp/archives/470/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
