Adds support for saving Trac ID, for more accurate syncing
1 files changed, 82 insertions(+), 6 deletions(-)

M trac-sync.rb
M trac-sync.rb +82 -6
@@ 6,20 6,68 @@ 
 #   ditz sync: synchronize issues with Trac
 #
 # Usage:
-#   1. add a line "- trac-sync" to the .ditz-plugins file in the project root.
+#   1. Add a line '- trac-sync' to the .ditz-plugins file in the project root.
+#   2. Call 'ditz trac'
 
 require 'rubygems'
 require 'trac4r'
 require 'ditz'
 
 module Ditz
+
   class Issue
+    field :trac_id, :ask => false
+
     def log_at time, what, who, comment
       add_log_event([time, who, what, comment || ""])
       self
     end
   end
 
+
+  class ScreenView
+    add_to_view :issue_summary do |issue, config|
+      " Trac ID: #{issue.trac_id || 'none'}\n"
+    end
+    add_to_view :issue_details do |issue, config|
+      "Trac ID: #{issue.trac_id || 'none'}\n"+
+      "Trac URL: #{config.trac_sync_url || 'none'}\n"
+    end
+  end
+
+
+  class HtmlView
+    add_to_view :issue_summary do |issue, config|
+      next unless issue.trac_id
+      [<<EOS, { :issue => issue }]
+<tr>
+  <td class='attrname'>Trac ID:</td>
+  <td class='attrval'><%= issue.trac_id %></td>
+</tr>
+EOS
+    end
+
+    add_to_view :issue_details do |issue, config|
+      next unless issue.trac_id
+      [<<EOS, { :issue => issue, :config => config }]
+<h2>Trac Synchronization</h2>
+<table>
+  <tr>
+    <td class='attrname'>Trac ID:</td>
+    <td class='attrval'><%= issue.trac_id %></td>
+  </tr>
+  <tr>
+    <td class='attrname'>Trac URL:</td>
+    <td class='attrval'><%= config.trac_sync_url %></td>
+  </tr>
+</table>
+EOS
+    end
+  end
+
+
+
+  # A utility class for handling Trac tickets and milestones
   class TracUtil
     # If new types or dispositions are added to either ditz or trac, they'll need
     # to be mapped here

          
@@ 52,31 100,52 @@ module Ditz
     DSTATUS_TSTATUS[ :unstarted ] = "assigned" 
     DSTATUS_TSTATUS[ :paused ] = "assigned" 
 
+
     def initialize( project, config, trac )
       @project, @config, @trac = project, config, trac
     end
 
+
     def equal?( ticket, issue )
       ticket.summary == issue.title
     end
 
+
+    # Find matches between tickets and issues.  First matches by
+    # ticket.trac_id, and if that fails, by title
     def pair( tickets )
       rv = []
       issues = @project.issues.clone
       for ticket in tickets 
-        issue = issues.find { |i| equal?( ticket, i ) }
-        issues.delete(issue) if issue
+        issue = issues.find { |i| ticket.id == i.trac_id }
+        issue = issues.find { |i| equal?( ticket, i ) } unless issue
+        if issue
+          if !issue.trac_id.nil? and (issue.trac_id != ticket.id)
+            puts "ERROR! Found a match by title, but the IDs don't match!!"
+            puts "       issue has #{issue.trac_id}, ticket has #{ticket.id}"
+            # TODO do something useful here
+          end
+          puts "Found ticket/issue match #{ issue.trac_id ? "by id" : "by name"}"
+          issues.delete(issue)
+          issue.trac_id = ticket.id if issue.trac_id.nil?
+        end
         rv << [ticket,issue]
       end
+      # Now put the left-over, unmatched issues in with a nil ticket
+      # TODO check here that none of these issues have trac_ids.
       for issue in issues
         rv << [nil,issue]
       end
       rv
     end
 
+
+    # Syncs ditz issues -> Trac
     def create_tickets( issues )
     end
 
+
+    # Syncs Trac tickets -> issues
     def create_issues( tickets )
       tickets.each do |t,i|   # i will always be nil
         # trac4r doesn't yet support resolution

          
@@ 101,7 170,8 @@ module Ditz
                                      :component => t.component,
                                      :status => TSTATUS_DSTATUS[ t.status ],
                                      :disposition => resolution,
-                                     :references => []
+                                     :references => [],
+                                     :trac_id => t.id
           }, [@config, @project])
 
           @project.add_issue( issue )

          
@@ 110,6 180,7 @@ module Ditz
       end
     end
 
+
     def change_status status, issue
       if issue.status != status
         old_status = issue.status

          
@@ 119,6 190,7 @@ module Ditz
       nil
     end
 
+
     def group_by_time(changelog)
       rv = {}
       changelog.each do |log|

          
@@ 129,6 201,7 @@ module Ditz
       rv.sort
     end
 
+
     def update_issues( pairs, trac )
       pairs.each do |ticket, issue|
         puts "Working on #{ticket.id}/#{issue.id[0,4]}"

          
@@ 186,15 259,18 @@ module Ditz
     end
   end
 
+
   class Config
     field :trac_sync_url, :prompt => "URL of Trac project (without the login/xmlrpc)"
     field :trac_sync_user, :prompt => "The Trac user ID that has XMLRPC permissions"
     field :trac_sync_pass, :prompt => "The Trac password for the account"
   end
 
+
+
   class Operator
-    operation :sync, "Sync with a Trac repository"
-    def sync( project, config )
+    operation :trac, "Sync with a Trac repository"
+    def trac( project, config )
       unless config.trac_sync_url
         STDERR.puts( "Please run 'ditz reconfigure' and set the Trac URL" )
         return