Redmineに負けないようにTracのワークフロープラグインを作る
Redmineに負けないために必要なことをまず調べる。このページを見ると、RedmineはTracker(チケットの分類)と,role(権限でいい)の二つの種類別に、元->先ステータスを選んでいく仕組みになっているようです。ただ、チケットの分類が山ほどあれば山ほどクリックしていかないとダメなんじゃないかと思うんですけど…これが設定しやすいと言っている人が多いんだから、なんか、私が想像しているよりもすばらしいやり方があるんでしょうかね。
Tracでは、標準のConfigurableTicketWorkflowでは権限に対応していで、そのほかにAdvanced Ticket Workflow PluginのTicketWorkflowOpTriageでは(マイルストーンとか何でもアリなんだけど)チケットの分類で、ワークフローを分岐できるようになっている。次のように設定したとして、分岐先が、agreedでもfixedでも表示は実装済みとなる。agreedに行くには"合意済み"ならわかるのだが…
fix = accepted -> *
fix.name = 実装済み
fix.operations = set_resolution,triage
fix.permissions = TICKET_MODIFY
fix.triage_field = type
fix.triage_split = 要件->agreed,タスク->fixed
じゃぁチケットの分類を目的のものだけを書くようにするとどうだろうということで、次のような設定で確認してみましたが、結局だめで二つとも表示されます。triage_splitに書かれてないものは全部newになってしまいます。それもちょっとめんどくさくて、typeを追加するたびにワークフローのtriage_splitに追加していかないとダメってことになります。
fix = approved,accepted -> *
fix.name = 実装済み
fix.operations = set_resolution,triage
fix.permissions = TICKET_MODIFY
fix.triage_field = type
fix.triage_split = タスク->fixed
agree = accepted,approved -> *
agree.name = 合意する
agree.permissions = TICKET_AGREE
agree.operations = triage
agree.triage_field = type
agree.triage_split = 要件->agreed
なんでだろうということでソースを見てみると、TicketWorkflowOpTriageは通常のワークフローのConfigurableTicketWorkflowの機能を、生かす実装になっている。だから遷移先のステータスは'*'として、元のほうでは遷移しないようにして、ある程度の処理をまかせ、triage関連フィールドの値から遷移先を変えている。問題ないようにしようとするとreq_accepted/acceptedのようにacceptedの時点で分岐させておく必要が出てくるが、無意味なステータスが増えるだけのように思える。でも、Advanced Ticket Workflow Pluginはこういう使い方を想定しているのかもしれない。
accept = assigned -> *
accept.name = 着手する
accept.operations = set_owner_to_self,triage
accept.permissions = TICKET_MODIFY
accept.triage_field = type
accept.triage_split = 要件->req_accepted,バグ->accepted,その他->accepted
fix = accepted -> fixed
fix.name = 実装済み
fix.operations = set_resolution
fix.permissions = TICKET_MODIFY
agree = req_accepted -> agreed
agree.name = 合意する
agree.permissions = TICKET_AGREE
ちょっとしっくりこないので、プラグインを作ることにしました。どういうものを作るかというと
1. 分岐の条件は分類(type)に限定
->アクションにtypeというプロパティを作り対象の分類をカンマ区切りで指定する。
2. 分類(type)を追加してもそれなりに動くようにする
->指定した以外のtypeの時だけ遷移するような指定をできるようにする
->アクションにtypeexcludeというプロパティを作り除外する分類をカンマ区切りで指定する。
3. できるだけConfigurableTicketWorkflowの機能を利用し置き換える
->派生にしようとしたがダメっぽいのでメンバーにする。pythonよくわからない(こんなのでプラグイン作って大丈夫なのか?)
4. アクションの遷移先は基本ひとつとする
->アクションを表示するときにチケットの分類を確認して遷移できないものは表示しない
5. たくさんクリックしないと作れないような実装にはしない。
->アクションを主に考える/入力する仕組みにすればOk
じゃそのソースworkflowex.py
ライセンス等は省略してるのでZIPを確認してください。
from trac.core import implements, Component
from trac.env import IEnvironmentSetupParticipant
from trac.config import Configuration
from trac.ticket.api import ITicketActionController, TicketSystem
from trac.ticket.default_workflow import ConfigurableTicketWorkflowclass TracWorkflowEx(Component):
def __init__(self, *args, **kwargs):
self.controller = ConfigurableTicketWorkflow(self.env)
# デフォルトのワークフローでは入っているものはすべて読んでいてくれる
for action, attributes in self.controller.actions.items():
attributes.setdefault('type',[])
if attributes['type'] != []:
attributes['type'] = [a.strip() for a in
attributes['type'].split(',')]
attributes.setdefault('typeexclude',[])
if attributes['typeexclude'] != []:
attributes['typeexclude'] = [a.strip() for a in
attributes['typeexclude'].split(',')]
if attributes['type'] != [] and attributes['typeexclude'] != []:
self.log.error(u'typeとtypeexcludeのどちらかひとつのみ定義してください')implements(ITicketActionController, IEnvironmentSetupParticipant)
# IEnvironmentSetupParticipant methods
def environment_created(self):
self.controller.environment_created()def environment_needs_upgrade(self, db):
return self.controller.environment_needs_upgrade(db)def upgrade_environment(self, db):
self.controller.upgrade_environment(db)# ITicketActionController methods
def get_ticket_actions(self, req, ticket):
allowed_actions_base = self.controller.get_ticket_actions(req, ticket)
allowed_actions = []
# できるだけ標準機能を生かして、allowed_actionsから消していく仕組みとする
type = ticket['type'].strip()
for action in allowed_actions_base:
attr = self.controller.actions[action[1]]
if attr['type'] == [] and attr['typeexclude'] == []: # 無条件なので追加
allowed_actions.append(action)
continue
if attr['type'] != [] and type in attr['type']: # typeにあれば追加
allowed_actions.append(action)
continue
if attr['typeexclude'] != [] and type not in attr['typeexclude']: #typeexcludeなければ追加
allowed_actions.append(action)
continue
return allowed_actionsdef get_all_status(self):
return self.controller.get_all_status()def render_ticket_action_control(self, req, ticket, action):
return self.controller.render_ticket_action_control(req, ticket, action)def get_ticket_changes(self, req, ticket, action):
return self.controller.get_ticket_changes(req, ticket, action)def apply_action_side_effects(self, req, ticket, action):
self.controller.apply_action_side_effects(req, ticket, action)
全然テストしていませんがまとめたものを、公開しておきます。
「TracWorkflowExPlugin20110301.zip」をダウンロード
プラグインをビルド,インストール,有効化してtrac.iniに次のように設定すると
[ticket]
workflow = TracWorkflowEx[ticket-workflow]
accept = assigned,accepted,approved -> accepted
accept.name = 着手する
accept.operations = set_owner_to_self
accept.permissions = TICKET_MODIFY
leave = * -> *
leave.default = 1
leave.name = 変更しない
leave.operations = leave_status
reassign = new,assigned,accepted,reopened,agreed -> assigned
reassign.name = 担当者変更
reassign.operations = set_owner
reassign.permissions = TICKET_MODIFY
fix = accepted -> fixed
fix.name = 実装済み
fix.operations = set_resolution
fix.permissions = TICKET_MODIFY
fix.typeexclude = 要件, 要件2
aqgree = accepted -> agreed
aqgree.name = 合意する
aqgree.permissions = TICKET_MODIFY
aqgree.type = 要件, 要件2
approve = agreed -> approved
approve.name = 承認する
approve.operations = set_resolution
approve.permissions = TICKET_MODIFY
resolve = assigned,fixed,approved -> closed
resolve.name = 解決にする
resolve.permissions = TICKET_CLOSE
reopen = closed -> reopened
reopen.name = 差し戻す
reopen.operations = del_resolution
reopen.permissions = TICKET_CREATE
どうやって作るかですが、まずはアクションを次のように設定
まだ、この設定の取り込みには対応していませんが
「TracWorkFlow20110301.zip」をダウンロード
図を次のように作って、出力させればOk
こうするとうまく動作しているようです。
まずは"要件"チケットのステータスがアクセプトの場合
次は"バグ"チケットのステータスがアクセプトの場合
これでRedminiには負けてないですよね。
| 固定リンク
この記事へのコメントは終了しました。
コメント