ほげにっき

hogedigoの日記

ScrumもどきPlugin開発記その4

いやぁ。半年ぶりに開発再開です。オハズカシイったらありゃしない。

これまでのあらすじ

今後は弊社の有能中国人プログラマに手伝って貰うので、ガリガリ作っていくよ・・・多分^_^;;

前回迄でTracのPluginとして登録・動作させることは出来たので、今後は追加機能単位でピックアップしていくことにする。

概要

Pluginで管理するデータを保管する為に、Trac DBに独自テーブルを追加する。この辺もTimingAndEstimation pluginを参考にした。

テーブル構造は以下の通り

sprint_attrテーブル

基本情報。

milestone マイルストーン
start_date sprint開始日
end_date sprint終了日
weekend 週末
workhours 1日の稼働時間
sprint_holidayテーブル

祝日情報。

milestone マイルストーン
a_date 日付
sprint_workdayテーブル

週末だけど稼働する日。

milestone マイルストーン
a_date 日付

sprintはmilestoneで管理しようと思っているので、マイルストーン名が主キーになっている。これ、イマイチだなー。ADMINでマイルストーン名変更されたらどうしようかな。milestoneテーブル自体がマイルストーン名を主キーにしているのが元凶なんだけども。サロゲートキーがあれば良かったのに。


日付は全部intね。UNIX秒。Tracの基本DB構造もそうなってるので。


sprint_attr.weekendは、どの曜日を週末(非稼働日)としてあつかうかをビットマスクとして持つ。右端1〜7bitが月曜〜日曜。これはpython datetimeオブジェクトのweekdayメソッドと合わせている。ちょっとトリッキーだけど、↓みたいなカンジで判定する。

#today(datetime型)が週末かどうか
weekend & (1 << today.weekday())

実装

pluginをインストールまたはアップグレードした際に行う処理なので、IEnvironmentSetupParticipantを実装したクラスinitenv.pyに追加することにする。(参照:Trac Scrumもどきプラグイン開発記 - ほげにっき)

initenv.py(一部)
    def __init__(self):
        self.db_version_key = 'TracScrumHalf_Db_Version'
        self.db_version = 1
        self.db_installed_version = None
        
        # Initialise database schema version tracking.
        self.db_installed_version = self.get_system_value(self.db_version_key)
        
    def get_system_value(self, key):
        db = self.env.get_db_cnx()
        cursor = db.cursor()
        cursor.execute("SELECT value FROM system WHERE name='%s'" % key)
        
        rows = cursor.fetchall()
        if len(rows) == 1 :
            cursor.close()
            return int(rows[0][0])
        else :
            version_value = 0
            cursor.execute("INSERT INTO system (name,value) VALUES('%s', '%s')" %(key, version_value))
            cursor.close()
            db.commit()
            db.close()
            return version_value

まんまTimingAndEstimation pluginを参考にさせてもらいました。
systemテーブルにDBバージョン番号を保存しておくことで、pluginを動的にアップグレードできる様にする。重要なのは↓の属性。

        self.db_version_key = 'TracScrumHalf_Db_Version'
        self.db_version = 1
        self.db_installed_version = None
db_version_key systemテーブルに保存する為のキー
db_version 当DBバージョン
db_installed_version インストール済plugin DBバージョン

まずdb_installed_versionをsystemテーブルより取り出す。systemテーブルに存在しない場合は0とする。db_installed_version < db_versionの場合にDBをupgradeし、db_versionを保存する(後述)。

initenv.py(一部)
    def upgrade_environment(self, db):

        (中略)
            
        if self.system_needs_upgrade():
            self.do_db_upgrade(db)
            
        print "Done Upgrading"

    def do_db_upgrade(self, db):
        
        #get a database connection if we don't already have one
        if not db:
            db = self.env.get_db_cnx()
            
        cursor = db.cursor()
        
        # version 0 create tables: sprint_attr, sprint_holiday, sprint_workday
        if self.db_installed_version < 1:
            # create table 'sprint_attr'
            sqlSprintAttr = """
            CREATE TABLE sprint_attr (
                milestone text,
                start_date int,
                end_date int,
                weekend int,
                workhours int,
                UNIQUE (milestone)
            );
            """
            cursor.execute(sqlSprintAttr)
        
            # create table 'sprint_holiday'
            sqlSprintHoliday = """
            CREATE TABLE sprint_holiday (
                milestone text,
                a_date int,
                UNIQUE (milestone, a_date)
            );
            """
            cursor.execute(sqlSprintHoliday)
            
            # create table 'sprint_workday'
            sqlSprintWorkday = """
            CREATE TABLE sprint_workday (
                milestone text,
                a_date int,
                UNIQUE (milestone, a_date)
            );
            """
            cursor.execute(sqlSprintWorkday)

        # update the version, the db_version will be increased
        cursor.execute("UPDATE system SET value='%s' WHERE name='%s'" % (self.db_version, self.db_version_key))
        self.db_installed_version = self.db_version
        
        cursor.close()
        #db.commit()

これで必要なテーブルをCREATEして、当DBバージョンを保存している。最後のcommitがコメントになっているのは、Tracのupgrade_environmentメソッドに以下コメントが書かれていた為。

Implementations of this method should not commit any database
transactions. This is done implicitly after all participants have
performed the upgrades they need without an error being raised.

http://trac.edgewall.org/browser/trunk/trac/env.py

timingandestimation pluginはcommitしている様に見えるがbugか?まあエラーが起きなければ問題はないのだろうけど。


あとはenvironment_needs_upgradeメソッドに、DBアップグレードが必要かどうかの判定を追加。

initenv.py(一部)
    def environment_needs_upgrade(self, db):
        return self.ticket_fields_need_upgrade() or self.system_needs_upgrade()
            

    def system_needs_upgrade(self):
        return self.db_installed_version < self.db_version


いじょ!!


冗長な部分が増えてきたのでソース全文はのっけません。ある程度カタチになったらsubversionリポジトリを公開しますm(_ _)m