Friday, September 30, 2011

Trac and Kanban - Redux

Hello there!

Since my last post before summer, the number of total visits has gone from 2000 to over 7000! One of the reasons for this was the  Trac and Kanban post, where I breifly describe how to use a Trac wiki page as a basic kanban board. (The main reason is this Stack Overflow question which asks for a bit more, such as drag-n-drop and the like.)

We'we worked with this for half a year now, and it still feels like a good thing. So good, actually, that I simply had to improve it a bit.

Trac and Kanban, v2.0

Mainly I now use the "format=table" option to get a table, which means we get color for the priority, we can show who is owning the item and what resolution it had (duplicate/invalid/worksforme/wontfix, etc) where suitable.

It looks like this (with no one working on anything at the moment, but you get the idea):

This could be even better if Trac supported sorting on several ticket fields, but that is not possible with 0.12. There is a ticket and patch available on the Trac home site, but I haven't tried it ... yet.

Code review

For the confused, note that I still use two custom fields for post-commit code review "reviewed_by" and "has_review_issues".

I will probably change the boolean review_issues to a checkbox that better handles the states and workflow for code review:

  • not_reviewed
  • reviewed_with_issues
  • review_issues_fixed
  • reviewed_and_approved

A custom TicketChangeListener could then manage the has_issues to issues_fixed transition, so that it's automatically visible that the code has changed.

Automatic subpage/dashboard lists 

The second new thing I've discovered and deployed is the TitleIndex macro, to simply list show all sub pages for a single wiki page. Since we have all the dashboards under Dashboard/ProductNameX.Y pages, it's easy to list them all using [[TitleIndex(Dashboard/ProductName,hideprefix)]] on a product's "home" wiki page.

Old/completed milestones could then be moved to DashboardOld container so that they're still there, but does not clutter the "current and upcoming milestones/sprints" list.

Limit right column to last two weeks

Adding modified=2weeksago.. to the queries for closed tickets makes the dashboard work over long time, since older (closed) tickets are not modified and thus removed from the table after some time.

This really helps with using the system in a "pure" Kanban setting, without sprints or milestones. It's also useful to keep a current status view during longer milestones with 100+ tickets.

Wiki code

The wiki page above was generated from the following code:

This is the ticket dashboard for **[milestone:"Orsync 1.0"]**
([/query?milestone=Orsync+1.0&status=new&status=reopened&status=testable&status=accepted&order=priority&col=id&col=summary&col=status&col=type&col=priority&col=milestone&col=component open tickets])
-- [wiki:Orcamp/FeatureList current feature list]

Create new
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=defect defect],
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=enhancement enhancement],
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=suggestion suggestion],
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=change change],
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=task task],
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync&type=demo demo] or
[/newticket?milestone=Orsync+1.0&component=Apps/Orsync generic] ticket.

#!div style="float:left; margin-right:1em; width:30%"

= Open Tickets =

Total: [[TicketQuery(status=new|reopened,type!=suggestion,milestone=Orsync 1.0,format=count)]]
([[TicketQuery(status=new|reopened,type=suggestion,milestone=Orsync 1.0,format=count)]])

== New & Reopened ==

[[TicketQuery(format=table,col=summary|type,status=reopened|new,type!=suggestion,milestone=Orsync 1.0,group=priority,order=type)]]

== Suggestions ==

[[TicketQuery(group=priority,status=new|reopened,type=suggestion,milestone=Orsync 1.0)]]

#!div style="float:left; margin-right:1em; width:30%"

= Work In Progress =

[[TicketQuery(status=accepted,order=priority,group=owner,milestone=Orsync 1.0,format=count)]]
([[TicketQuery(status=accepted|testable,milestone=Orsync 1.0,format=count)]])

== Started ==

[[TicketQuery(status=accepted,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]

== Unreviewed tickets ==

[[TicketQuery(review_issues!=1,reviewed=,status=testable,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]

== Reviewed tickets with issues ==

[[TicketQuery(review_issues=1,status=testable,group=owner,order=priority,format=table,col=summary|type|owner,milestone=Orsync 1.0)]]

== Ready for Test ==

[[TicketQuery(review_issues=1,status=testable,group=developertest,order=priority,format=table,col=summary|type,milestone=Orsync 1.0)]]

#!div style="float:left; margin-right:1em; width:30%"

= Closed Tickets =

Total: [[TicketQuery(status=closed,resolution=fixed,milestone=Orsync 1.0,format=count)]] 

([[TicketQuery(status=closed,resolution!=fixed,milestone=Orsync 1.0,format=count)]])

== Fixed tickets (last two weeks) ==

[[TicketQuery(status=closed,format=table,col=summary|type,resolution=fixed,order=priority,desc=1,milestone=Orsync 1.0,modified=2weeksago..)]]

== Denied tickets 
(last two weeks) ==

[[TicketQuery(status=closed,resolution=wontfix|worksforme,format=table,col=summary|type|resolution,order=priority,desc=1,milestone=Orsync 1.0

== Invalid/duplicate/other 
(last two weeks) ==

[[TicketQuery(status=closed,resolution!=fixed|wontfix|worksforme,format=table,col=summary|type|resolution,order=priority,desc=1,milestone=Orsync 1.0


#!div style="clear:both"

There you go. Hope it helps!