You are viewing an old version of this page. View the current version.

Compare with Current View Page History

Version 1 Next »

Report Synopsis

Given a JIRA project name, a start date and and end date, find total counts of issues completed before, on or after the due date, per priority:

 TotalUnfinishedFinished On DueFinished Before DueFinished After Due
Major, with due date     
Major, without due date  ---
Critical, with due date     
Critical, without due date  ---

Implementation

In Ruby, using the jira-ruby gem.

First we set up a $client object, using HTTP Basic authentication;

require 'jira'
require 'parallel'

HOST='https://REDACTED.atlassian.net'
$options = {
  :site => HOST,
  :context_path => '',
  :username => 'myusername',
  :password => %q{REDACTED},
  :auth_type => :basic
}

$client = JIRA::Client.new($options)

Next, we fetch the issues we're interested in:

issues = $client.Issue.jql("project=UX and updated>='2015-10-01' AND updated<='2015-10-27'", max_results:1000) { |i| i.fetch; i }

Now for the interesting part. Issues with a due date will have a resolutiondate field, which we can parse wtih strptime:

rdate = issues.find { |i| i.resolutiondate  }.resolutiondate
=> "2015-10-26T09:23:07.000-0700"
rdate = DateTime.strptime(rdate, '%Y-%m-%dT%H:%M:%S.%L%z')
=> #<DateTime: 2015-10-26T09:23:07-07:00 ((2457322j,58987s,0n),-25200s,2299161j)>
rdate = rdate.to_date                # Discard time portion
=> #<Date: 2015-10-26 ((2457322j,0s,0n),+0s,2299161j)>

We will also have a duedate, which we can parse similarly:

ddate = issues.find { |i| i.duedate  }.duedate
=> "2015-11-05"
Date.strptime(ddate, "%Y-%m-%d")
=> #<Date: 2015-11-05 ((2457332j,0s,0n),+0s,2299161j)>

and a priority, which is actually an object, so we'll just use the name part of it:

[14] pry(main)> ddate = issues.find { |i| i.priority  }.priority.name
=> "Critical"

 

Now we need to:

  • group issues by priority
    • for each priority's group, group again by classification:
      • if there is no resolution date, classify as "Unfinished"
      • if there is a resolution date, but no due date, classify "Finished, no due date"
      • If the resolution date and due date match, classify as "On Due"
      • If the resolution date is earlier than due date, classify as "Before Due"
      • If the resolution date is after the due date, classify as "After Due"

The Ruby Enumerable module's group_by method does the group-into-buckets job nicely, giving us a hash-of-hashes data structure.

data = issues.group_by { |i|
                i.priority.name + ", " + (i.duedate ? "with" : "without") + " due date" }
        .inject({}) { |h, (k, v)|
                h[k] = v.group_by { |i|
                        resdate = i.resolutiondate && DateTime.strptime(i.resolutiondate, '%Y-%m-%dT%H:%M:%S.%L%z').to_date
                        duedate = i.duedate && Date.strptime(i.duedate, "%Y-%m-%d")
                        if !resdate then "Unfinished"
                                elsif !duedate then "Finished, no due date"
                                elsif resdate == duedate then "On Due"
                                elsif resdate < duedate then "Before Due"
                                else "After Due"
                        end
                }
         h  }

data.keys   # Show our top-level groupings (this will be rows)
=> ["Critical, without due date", "Major, without due date", "Minor, without due date", "Major, with due date", "Blocker, without due date"]
cols = data.collect { |(k,v)| v.keys  }.flatten.uniq  # Identify unique columns.
=> ["Unfinished", "Finished, no due date"]

Then for reporting. I have yet to find a nice easy way of formatting a 2d array of numbers with arbitrary column numbers. The pretty print pp module might be good enough:

result = [[nil] + cols] # First row is a list of columns, starting with a nil
# Add rows, consisting of an array beginning with 'rowname', followed by the number of issues, or zero
result += data.collect { |(rowname,v)|
        [rowname] + cols.collect { |col| v[col] ? v[col].size : 0 }
        }
require 'pp'
pp result
[[nil, "Unfinished", "Finished, no due date"],
 ["Critical, without due date", 1, 3],
 ["Major, without due date", 133, 143],
 ["Minor, without due date", 27, 10],
 ["Major, with due date", 3, 0],
 ["Blocker, without due date", 0, 4]]

or some primitive HTML output:

puts "<table border=1>\n" + 
        result.map.with_index { |r, i| "<tr>" + 
                 r.map.with_index { |c, j|
                        el = (i==0 || j==0 ? "th" : "td")
                        "<#{el}>" + (c ? c.to_s : "\t") + "</#{el}>" }.join + 
                         "</tr>"
                         }.join("\n") +
      "</table>"
=>
<table border=1>
<tr><th>    </th><th>Unfinished</th><th>Finished, no due date</th></tr>
<tr><th>Critical, without due date</th><td>1</td><td>3</td></tr>
<tr><th>Major, without due date</th><td>133</td><td>143</td></tr>
<tr><th>Minor, without due date</th><td>27</td><td>10</td></tr>
<tr><th>Major, with due date</th><td>3</td><td>0</td></tr>
<tr><th>Blocker, without due date</th><td>0</td><td>4</td></tr></table>

which renders as:

 UnfinishedFinished, no due date
Critical, without due date13
Major, without due date133143
Minor, without due date2710
Major, with due date30
Blocker, without due date04
  • No labels