Trac Appender

スクリーンショット


TracのチケットをAjaxで使いやすくしちゃおうという主旨で、Ajaxなツールを作りました。

業務実施の際、CCPMの考え方のひとつである、ABPとHPの見積もりを使って、小さめのタスク(30min〜4h程度)を設定し、集中して1個流しでつぶす方法で、生産性向上を狙ってます。

ツールとしては、ABP / HP と実績をカスタム属性とした、Tracを使用します。

ただ、Tracはチケット(タスク)の登録、着手、実績の入力、検索、といった作業について、小さめのタスクを「さくさく」扱うには、ユーザインタフェースが弱いと感じてきました。

そこで、TracAppender という、HTML+JavascriptAJAX)で作成したTracに一枚かぶせる形のページを開発しました。ABP/HPを追加する手順とともに、本ツールを公開します。本ツールの特徴は以下のとおり。

・チケット概要のリアルタイム検索
・複数チケットの一括登録
・カスタム数値属性(ここではABP/HP/実績)の合計値表示
・チケット一覧からの、着手、概要や担当ABP/HPなどの修正、完了

Trac月をある程度理解している前提で説明しています。

  • TracAppender1.0β1

 ↓以下で公開しておきます。
 http://www.box.net/shared/e50y1a9llq


(1) Trac月を、http://ultimania.org/trac/ を参考にインストールする。インストール先をここでは、c:\TracLight とする。

(2) 別ページからのPOSTを許可するために、以下のコードを修正します。(セキュリティが低下するので注意)

 c:\TracLight\python\Lib\site-packages\trac\web\main.py
 231行目〜

          if ctype in ('application/x-www-form-urlencoded',
                 'multipart/form-data') and \
              req.args.get('__FORM_TOKEN') != req.form_token:
             raise HTTPBadRequest('Missing or invalid form token. '
                       'Do you have cookies enabled?')

 を削除します。
 
 c:\TracLight\python\Lib\site-packages\trac\ticket\web_ui.py
 534行目

        if int(req.args.get('ts')) != ticket.time_changed:

 を、

        if int(req.args.get('ts')) != ticket.time_changed and int(req.args.get('ts')) != 0:

 に修正します。

(3) ここでTracを起動してください。(既に起動している場合には再起動)

(4) 初期プロジェクト(default)もありますが、ここでは別途新たにプロジェクトを作成します。スタート>プログラム>Tracコマンドプロンプトを起動し、下記を実行します。
  
  create-project.bat personal
  
  personalはプロジェクト名。異なる名前にする場合には以降を読み替えてください。
  
(5) c:\TracLight\projects\trac\personal\conf\trac.ini の最後に以下を追加し、カスタム属性を追加します。
  

[ticket-custom]
cost_expect = text
cost_expect.label = 見積ABP(h)

cost_hp = text
cost_hp.label = 見積HP(h)

cost_result = text
cost_result.label = 実績工数(h)

(6) Tracにログインし、関連するレポートを追加、修正します。ここでは、{1},{7},{9},{10}を使います。あとでTracAppender側で本レポートを指定します。ちなみに{11}は動的変数を使った概要検索なので、普通には動作しません。

{1} 未解決チケット

SELECT p.value AS __color__,
   (CASE status WHEN 'assigned' THEN 
      CASE owner WHEN '$USER' THEN 'background: #f9c973;' ELSE 'background: #c1e4e9;' END
    ELSE
      CASE owner WHEN '$USER' THEN 'background: #fff1cf;' ELSE '' END
    END ) AS __style__,
   id AS ticket,
   summary AS summary,
   component, version, milestone, t.type AS type, 
   owner AS owner,
  (CASE status WHEN 'assigned' THEN '★' ELSE '' END) as ★,
   time AS created,
   changetime AS _changetime, description AS _description,
   reporter AS _reporter, c.value AS abp, d.value AS rs, e.value AS hp
  FROM ticket t
     LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'cost_expect')
      LEFT OUTER JOIN ticket_custom d ON (t.id = d.ticket AND d.name = 'cost_result')
      LEFT OUTER JOIN ticket_custom e ON (t.id = e.ticket AND e.name = 'cost_hp')
  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
  WHERE status IN ('new', 'assigned', 'reopened') 
  ORDER BY p.value, milestone, t.type, time

{7} 私の未解決チケット

SELECT p.value AS __color__,
   (CASE status WHEN 'assigned' THEN 
      CASE owner WHEN '$USER' THEN 'background: #f9c973;' ELSE 'background: #c1e4e9;' END
    ELSE
      CASE owner WHEN '$USER' THEN 'background: #fff1cf;' ELSE '' END
    END ) AS __style__,
   id AS ticket,
   summary AS summary,
   component, version, milestone,
   t.type AS type, priority, time AS created, owner,
   (CASE status WHEN 'assigned' THEN '★' ELSE '' END) as ★,
  changetime AS _changetime, description AS _description,
   reporter AS _reporter, c.value AS abp, d.value AS rs, e.value AS hp
  FROM ticket t
     LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'cost_expect')
      LEFT OUTER JOIN ticket_custom d ON (t.id = d.ticket AND d.name = 'cost_result')
      LEFT OUTER JOIN ticket_custom e ON (t.id = e.ticket AND e.name = 'cost_hp')
  LEFT JOIN enum p ON p.name = t.priority AND p.type = 'priority'
  WHERE t.status IN ('new', 'assigned', 'reopened') AND owner = '$USER'
  ORDER BY (status = 'assigned') DESC, p.value, milestone, t.type, time

{9} 更新順(1週間)

SELECT p.value AS __color__,
   (CASE status 
      WHEN 'closed' THEN 'color: #777; background: #ddd; border-color: #ccc;'
      ELSE 
        (CASE owner WHEN '$USER' THEN 'font-weight: bold' END)
    END) AS __style__,
   id AS ticket, summary, milestone, status, 
   resolution, t.type AS type, priority, owner,
  (CASE status WHEN 'assigned' THEN '★' ELSE '' END) as ★,
   changetime as modified,
   time AS _time,reporter AS _reporter, c.value AS abp, d.value AS rs, e.value AS hp
  FROM ticket t
     LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'cost_expect')
      LEFT OUTER JOIN ticket_custom d ON (t.id = d.ticket AND d.name = 'cost_result')
      LEFT OUTER JOIN ticket_custom e ON (t.id = e.ticket AND e.name = 'cost_hp')
     ,enum p
  WHERE datetime('1970-01-01',changetime||'seconds') > datetime('now', '-7 days')
        AND p.name=t.priority AND p.type='priority'
        AND ( resolution<>'重複' or resolution is null )
  ORDER BY modified DESC        

{11} 概要検索

SELECT p.value AS __color__,
   (CASE status 
      WHEN 'closed' THEN 'color: #aaa; background: #ddd; border-color: #ccc;'
      ELSE 
   (CASE status WHEN 'assigned' THEN 
      CASE owner WHEN '$USER' THEN 'background: #f9c973;' ELSE 'background: #c1e4e9;' END
    ELSE
      CASE owner WHEN '$USER' THEN 'background: #fff1cf;' ELSE '' END
    END )

    END) AS __style__,
   id AS ticket, summary, milestone, status, 
   resolution, t.type AS type, priority, owner,
   (CASE status WHEN 'assigned' THEN '★' ELSE '' END) as ★,
  changetime as modified,
   time AS _time,reporter AS _reporter, c.value AS abp, d.value AS rs, e.value AS hp
  FROM ticket t
     LEFT OUTER JOIN ticket_custom c ON (t.id = c.ticket AND c.name = 'cost_expect')
      LEFT OUTER JOIN ticket_custom d ON (t.id = d.ticket AND d.name = 'cost_result')
      LEFT OUTER JOIN ticket_custom e ON (t.id = e.ticket AND e.name = 'cost_hp')
     ,enum p
  WHERE p.name=t.priority AND p.type='priority' AND summary like '$SUMMARY'
        AND ( (resolution<>'無効' and resolution<>'重複')or resolution is null )
  ORDER BY summary

(7) TracAppender1.0b1.lzh をダウンロードし、展開したファイル一式と、別途ダウンロードした prototype.js を、c:\TracLight\apache2\htdocs に配置してください。下記ファイルになります。
  
  indicator.gif
  TicketCreater.css
  TicketCreater.js
  TicketCreaterPersonal.html
  prototype.js
  
  ※TicketCreaterなのは昔のなごりです。。
  ※TicketCreaterPersonal.htmlがツールのページになります。
   名前は自由に変更して大丈夫です。その際には以降の説明を読み替えてください。
  
(8) プロジェクト名を変更した場合には、TicketCreaterPersonal.html を修正します。「personal」となっている文字列をすべて変更したプロジェクト名にしてください。また上部の変数名は変更できます。コメントを参考にしてください。

(9) http://TracのURL/TicketCreaterPersonal.html につなぐと使用できます。使用前にTracにログインしておいてください。検索結果を出して、一覧の概要以外の部分をクリックすると着手等ができるダイアログがあがります。