jirametrics 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/jirametrics/aggregate_config.rb +1 -1
- data/lib/jirametrics/aging_work_bar_chart.rb +18 -13
- data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
- data/lib/jirametrics/aging_work_table.rb +21 -16
- data/lib/jirametrics/anonymizer.rb +6 -5
- data/lib/jirametrics/chart_base.rb +35 -19
- data/lib/jirametrics/css_variable.rb +33 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +8 -9
- data/lib/jirametrics/daily_wip_by_age_chart.rb +43 -17
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +32 -17
- data/lib/jirametrics/daily_wip_by_parent_chart.rb +42 -0
- data/lib/jirametrics/daily_wip_chart.rb +67 -6
- data/lib/jirametrics/data_quality_report.rb +1 -1
- data/lib/jirametrics/dependency_chart.rb +18 -14
- data/lib/jirametrics/downloader.rb +13 -11
- data/lib/jirametrics/examples/aggregated_project.rb +17 -20
- data/lib/jirametrics/examples/standard_project.rb +10 -18
- data/lib/jirametrics/expedited_chart.rb +17 -15
- data/lib/jirametrics/exporter.rb +26 -20
- data/lib/jirametrics/grouping_rules.rb +7 -1
- data/lib/jirametrics/html/aging_work_bar_chart.erb +12 -6
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +8 -2
- data/lib/jirametrics/html/aging_work_table.erb +11 -19
- data/lib/jirametrics/html/cycletime_histogram.erb +9 -3
- data/lib/jirametrics/html/cycletime_scatterplot.erb +10 -4
- data/lib/jirametrics/html/daily_wip_chart.erb +18 -5
- data/lib/jirametrics/html/expedited_chart.erb +11 -5
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/index.css +186 -0
- data/lib/jirametrics/html/index.erb +8 -36
- data/lib/jirametrics/html/sprint_burndown.erb +11 -6
- data/lib/jirametrics/html/story_point_accuracy_chart.erb +9 -4
- data/lib/jirametrics/html/throughput_chart.erb +11 -5
- data/lib/jirametrics/html_report_config.rb +28 -3
- data/lib/jirametrics/issue.rb +5 -3
- data/lib/jirametrics/jira_gateway.rb +5 -2
- data/lib/jirametrics/project_config.rb +14 -19
- data/lib/jirametrics/settings.json +7 -0
- data/lib/jirametrics/sprint_burndown.rb +10 -4
- data/lib/jirametrics/status_collection.rb +1 -1
- data/lib/jirametrics/story_point_accuracy_chart.rb +20 -10
- data/lib/jirametrics/throughput_chart.rb +10 -2
- data/lib/jirametrics.rb +2 -0
- metadata +7 -3
@@ -13,7 +13,10 @@ class JiraGateway
|
|
13
13
|
|
14
14
|
def call_url relative_url:
|
15
15
|
command = make_curl_command url: "#{@jira_url}#{relative_url}"
|
16
|
-
|
16
|
+
result = call_command command
|
17
|
+
JSON.parse result
|
18
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
19
|
+
puts "Error #{e.inspect} when parsing result: #{result.inspect}"
|
17
20
|
end
|
18
21
|
|
19
22
|
def call_command command
|
@@ -29,7 +32,7 @@ class JiraGateway
|
|
29
32
|
|
30
33
|
def load_jira_config jira_config
|
31
34
|
@jira_url = jira_config['url']
|
32
|
-
raise
|
35
|
+
raise 'Must specify URL in config' if @jira_url.nil?
|
33
36
|
|
34
37
|
@jira_email = jira_config['email']
|
35
38
|
@jira_api_token = jira_config['api_token']
|
@@ -8,7 +8,7 @@ class ProjectConfig
|
|
8
8
|
|
9
9
|
attr_reader :target_path, :jira_config, :all_boards, :possible_statuses,
|
10
10
|
:download_config, :file_configs, :exporter, :data_version, :name, :board_configs,
|
11
|
-
:settings, :
|
11
|
+
:settings, :aggregate_config
|
12
12
|
attr_accessor :time_range, :jira_url, :id
|
13
13
|
|
14
14
|
def initialize exporter:, jira_config:, block:, target_path: '.', name: '', id: nil
|
@@ -22,22 +22,12 @@ class ProjectConfig
|
|
22
22
|
@name = name
|
23
23
|
@board_configs = []
|
24
24
|
@all_boards = {}
|
25
|
-
@settings =
|
26
|
-
'stalled_threshold' => 5,
|
27
|
-
'blocked_statuses' => [],
|
28
|
-
'stalled_statuses' => [],
|
29
|
-
'blocked_link_text' => [],
|
30
|
-
|
31
|
-
'colors' => {
|
32
|
-
'stalled' => 'orange',
|
33
|
-
'blocked' => '#FF7400'
|
34
|
-
}
|
35
|
-
}
|
25
|
+
@settings = load_settings
|
36
26
|
@id = id
|
37
27
|
end
|
38
28
|
|
39
29
|
def evaluate_next_level
|
40
|
-
instance_eval(&@block)
|
30
|
+
instance_eval(&@block) if @block
|
41
31
|
end
|
42
32
|
|
43
33
|
def run
|
@@ -58,6 +48,10 @@ class ProjectConfig
|
|
58
48
|
end
|
59
49
|
end
|
60
50
|
|
51
|
+
def load_settings
|
52
|
+
JSON.parse(File.read(File.join(__dir__, 'settings.json')))
|
53
|
+
end
|
54
|
+
|
61
55
|
def guess_project_id
|
62
56
|
return @id if @id
|
63
57
|
|
@@ -121,7 +115,7 @@ class ProjectConfig
|
|
121
115
|
board_id = $1.to_i
|
122
116
|
load_board board_id: board_id, filename: "#{@target_path}#{file}"
|
123
117
|
end
|
124
|
-
raise "No boards found in #{@target_path.inspect}" if @all_boards.empty?
|
118
|
+
raise "No boards found for #{@file_prefix} in #{@target_path.inspect}" if @all_boards.empty?
|
125
119
|
end
|
126
120
|
|
127
121
|
def load_board board_id:, filename:
|
@@ -243,7 +237,7 @@ class ProjectConfig
|
|
243
237
|
|
244
238
|
start = json['date_start'] || json['time_start'] # date_start is the current format. Time is the old.
|
245
239
|
stop = json['date_end'] || json['time_end']
|
246
|
-
@time_range = to_time(start)..to_time(stop)
|
240
|
+
@time_range = to_time(start)..to_time(stop, end_of_day: true)
|
247
241
|
|
248
242
|
@jira_url = json['jira_url']
|
249
243
|
rescue Errno::ENOENT
|
@@ -251,8 +245,9 @@ class ProjectConfig
|
|
251
245
|
raise
|
252
246
|
end
|
253
247
|
|
254
|
-
def to_time string
|
255
|
-
|
248
|
+
def to_time string, end_of_day: false
|
249
|
+
time = end_of_day ? '23:59:59' : '00:00:00'
|
250
|
+
string = "#{string}T#{time}#{@timezone_offset}" if string.match?(/^\d{4}-\d{2}-\d{2}$/)
|
256
251
|
Time.parse string
|
257
252
|
end
|
258
253
|
|
@@ -436,8 +431,8 @@ class ProjectConfig
|
|
436
431
|
else
|
437
432
|
message << "days of data from #{issue.changes.first.time.to_date} to #{cutoff_time.to_date}"
|
438
433
|
end
|
439
|
-
|
434
|
+
exporter.file_system.log message
|
440
435
|
end
|
441
|
-
|
436
|
+
exporter.file_system.log "Discarded data from #{issues_cutoff_times.count} issues out of a total #{issues.size}"
|
442
437
|
end
|
443
438
|
end
|
@@ -22,7 +22,13 @@ class SprintBurndown < ChartBase
|
|
22
22
|
|
23
23
|
@summary_stats = {}
|
24
24
|
header_text 'Sprint burndown'
|
25
|
-
description_text
|
25
|
+
description_text <<-TEXT
|
26
|
+
<div class="p">
|
27
|
+
Burndowns for all sprints in this time period. The different colours are only to
|
28
|
+
differentiate one sprint from another as they may overlap time periods.
|
29
|
+
</div>
|
30
|
+
#{describe_non_working_days}
|
31
|
+
TEXT
|
26
32
|
end
|
27
33
|
|
28
34
|
def options= arg
|
@@ -55,9 +61,9 @@ class SprintBurndown < ChartBase
|
|
55
61
|
end
|
56
62
|
|
57
63
|
result = +''
|
58
|
-
result <<
|
64
|
+
result << render_top_text(binding)
|
59
65
|
|
60
|
-
possible_colours =
|
66
|
+
possible_colours = (1..5).collect { |i| CssVariable["--sprint-burndown-sprint-color-#{i}"] }
|
61
67
|
charts_to_generate = []
|
62
68
|
charts_to_generate << [:data_set_by_story_points, 'Story Points'] if @use_story_points
|
63
69
|
charts_to_generate << [:data_set_by_story_counts, 'Story Count'] if @use_story_counts
|
@@ -65,7 +71,7 @@ class SprintBurndown < ChartBase
|
|
65
71
|
@summary_stats.clear
|
66
72
|
data_sets = []
|
67
73
|
sprints.each_with_index do |sprint, index|
|
68
|
-
color = possible_colours[index %
|
74
|
+
color = possible_colours[index % possible_colours.size]
|
69
75
|
label = sprint.name
|
70
76
|
data = send(data_method, sprint: sprint, change_data_for_sprint: change_data_by_sprint[sprint])
|
71
77
|
data_sets << {
|
@@ -32,7 +32,7 @@ class StatusCollection
|
|
32
32
|
next
|
33
33
|
else
|
34
34
|
all_status_names = @list.collect { |s| "#{s.name.inspect}:#{s.id.inspect}" }.uniq.sort.join(', ')
|
35
|
-
raise "Status not found: #{name_or_id}. Possible statuses are: #{all_status_names}"
|
35
|
+
raise "Status not found: \"#{name_or_id}\". Possible statuses are: #{all_status_names}"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -6,15 +6,20 @@ class StoryPointAccuracyChart < ChartBase
|
|
6
6
|
|
7
7
|
header_text 'Estimate Accuracy'
|
8
8
|
description_text <<-HTML
|
9
|
-
<p>
|
9
|
+
<div class="p">
|
10
10
|
This chart graphs estimates against actual recorded cycle times. Since
|
11
11
|
estimates can change over time, we're graphing the estimate at the time that the story started.
|
12
|
-
</
|
13
|
-
<p>
|
14
|
-
The completed dots indicate
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
</div>
|
13
|
+
<div class="p">
|
14
|
+
The #{color_block '--estimate-accuracy-chart-completed-fill-color'} completed dots indicate
|
15
|
+
cycletimes.
|
16
|
+
<% if @has_aging_data %>
|
17
|
+
The #{color_block '--estimate-accuracy-chart-active-fill-color'} aging dots
|
18
|
+
(click on the legend to turn them on) show the current
|
19
|
+
age of items, which will give you a hint as to where they might end up. If they're already
|
20
|
+
far to the right then you know you have a problem.
|
21
|
+
<% end %>
|
22
|
+
</div>
|
18
23
|
HTML
|
19
24
|
|
20
25
|
@y_axis_label = 'Story Point Estimates'
|
@@ -55,10 +60,15 @@ class StoryPointAccuracyChart < ChartBase
|
|
55
60
|
(hash[key] ||= []) << issue
|
56
61
|
end
|
57
62
|
|
63
|
+
@has_aging_data = !aging_hash.empty?
|
64
|
+
|
58
65
|
[
|
59
|
-
[completed_hash, 'Completed', '
|
60
|
-
[aging_hash, 'Still in progress', '
|
61
|
-
].filter_map do |hash, label,
|
66
|
+
[completed_hash, 'Completed', 'completed', false],
|
67
|
+
[aging_hash, 'Still in progress', 'active', true]
|
68
|
+
].filter_map do |hash, label, completed_or_active, starts_hidden|
|
69
|
+
fill_color = CssVariable["--estimate-accuracy-chart-#{completed_or_active}-fill-color"]
|
70
|
+
border_color = CssVariable["--estimate-accuracy-chart-#{completed_or_active}-border-color"]
|
71
|
+
|
62
72
|
# We sort so that the smaller circles are in front of the bigger circles.
|
63
73
|
data = hash.sort(&hash_sorter).collect do |key, values|
|
64
74
|
estimate, cycle_time = *key
|
@@ -9,7 +9,12 @@ class ThroughputChart < ChartBase
|
|
9
9
|
super()
|
10
10
|
|
11
11
|
header_text 'Throughput Chart'
|
12
|
-
description_text
|
12
|
+
description_text <<-TEXT
|
13
|
+
<div class="p">
|
14
|
+
This chart shows how many items we completed per week
|
15
|
+
</div>
|
16
|
+
#{describe_non_working_days}
|
17
|
+
TEXT
|
13
18
|
|
14
19
|
init_configuration_block(block) do
|
15
20
|
grouping_rules do |issue, rule|
|
@@ -25,7 +30,10 @@ class ThroughputChart < ChartBase
|
|
25
30
|
data_sets = []
|
26
31
|
if rules_to_issues.size > 1
|
27
32
|
data_sets << weekly_throughput_dataset(
|
28
|
-
completed_issues: completed_issues,
|
33
|
+
completed_issues: completed_issues,
|
34
|
+
label: 'Totals',
|
35
|
+
color: CssVariable['--throughput_chart_total_line_color'],
|
36
|
+
dashed: true
|
29
37
|
)
|
30
38
|
end
|
31
39
|
|
data/lib/jirametrics.rb
CHANGED
@@ -51,6 +51,7 @@ class JiraMetrics < Thor
|
|
51
51
|
require 'jirametrics/daily_wip_chart'
|
52
52
|
require 'jirametrics/groupable_issue_chart'
|
53
53
|
require 'jirametrics/discard_changes_before'
|
54
|
+
require 'jirametrics/css_variable'
|
54
55
|
|
55
56
|
require 'jirametrics/aggregate_config'
|
56
57
|
require 'jirametrics/expedited_chart'
|
@@ -65,6 +66,7 @@ class JiraMetrics < Thor
|
|
65
66
|
require 'jirametrics/sprint'
|
66
67
|
require 'jirametrics/issue'
|
67
68
|
require 'jirametrics/daily_wip_by_age_chart'
|
69
|
+
require 'jirametrics/daily_wip_by_parent_chart'
|
68
70
|
require 'jirametrics/aging_work_in_progress_chart'
|
69
71
|
require 'jirametrics/cycletime_scatterplot'
|
70
72
|
require 'jirametrics/sprint_issue_change_data'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jirametrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Bowler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: random-word
|
@@ -74,11 +74,13 @@ files:
|
|
74
74
|
- lib/jirametrics/change_item.rb
|
75
75
|
- lib/jirametrics/chart_base.rb
|
76
76
|
- lib/jirametrics/columns_config.rb
|
77
|
+
- lib/jirametrics/css_variable.rb
|
77
78
|
- lib/jirametrics/cycletime_config.rb
|
78
79
|
- lib/jirametrics/cycletime_histogram.rb
|
79
80
|
- lib/jirametrics/cycletime_scatterplot.rb
|
80
81
|
- lib/jirametrics/daily_wip_by_age_chart.rb
|
81
82
|
- lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb
|
83
|
+
- lib/jirametrics/daily_wip_by_parent_chart.rb
|
82
84
|
- lib/jirametrics/daily_wip_chart.rb
|
83
85
|
- lib/jirametrics/data_quality_report.rb
|
84
86
|
- lib/jirametrics/dependency_chart.rb
|
@@ -107,6 +109,7 @@ files:
|
|
107
109
|
- lib/jirametrics/html/data_quality_report.erb
|
108
110
|
- lib/jirametrics/html/expedited_chart.erb
|
109
111
|
- lib/jirametrics/html/hierarchy_table.erb
|
112
|
+
- lib/jirametrics/html/index.css
|
110
113
|
- lib/jirametrics/html/index.erb
|
111
114
|
- lib/jirametrics/html/sprint_burndown.erb
|
112
115
|
- lib/jirametrics/html/story_point_accuracy_chart.erb
|
@@ -118,6 +121,7 @@ files:
|
|
118
121
|
- lib/jirametrics/project_config.rb
|
119
122
|
- lib/jirametrics/rules.rb
|
120
123
|
- lib/jirametrics/self_or_issue_dispatcher.rb
|
124
|
+
- lib/jirametrics/settings.json
|
121
125
|
- lib/jirametrics/sprint.rb
|
122
126
|
- lib/jirametrics/sprint_burndown.rb
|
123
127
|
- lib/jirametrics/sprint_issue_change_data.rb
|
@@ -151,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
155
|
- !ruby/object:Gem::Version
|
152
156
|
version: '0'
|
153
157
|
requirements: []
|
154
|
-
rubygems_version: 3.5.
|
158
|
+
rubygems_version: 3.5.10
|
155
159
|
signing_key:
|
156
160
|
specification_version: 4
|
157
161
|
summary: Extract Jira metrics
|