jirametrics 1.2.1 → 1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/jirametrics/board.rb +4 -0
- data/lib/jirametrics/downloader.rb +1 -1
- data/lib/jirametrics/examples/aggregated_project.rb +4 -0
- data/lib/jirametrics/examples/standard_project.rb +26 -17
- data/lib/jirametrics/issue.rb +2 -2
- data/lib/jirametrics/project_config.rb +27 -8
- data/lib/jirametrics/status.rb +4 -3
- data/lib/jirametrics/status_collection.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02be53200d02a09c1dd8829dba1a06147959d6fc7c14960e810497ab72c8c731
|
4
|
+
data.tar.gz: b3f79887cc78af19ec5199140bb655cb161a21fc548d52b98fee04c287fdc100
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d05d5ea737475206c127b3e1bde806a5fb98493f4cd5d75dbc7d0ce1d89093fb5778ae276f1189a37096a6f800550f7f1bb86a2bbf1e9433f80caa90cc905ba8
|
7
|
+
data.tar.gz: 82dd49447e33017bd5d0dfb527011d27ae018d32c60e310bb8cc30ca1bcc8265888264df7aa2387109d8fe822a75381e6687bd750923de0d742b28dfb153bdf6
|
data/lib/jirametrics/board.rb
CHANGED
@@ -170,7 +170,7 @@ class Downloader
|
|
170
170
|
@issue_keys_pending_download << parent_key if parent_key
|
171
171
|
|
172
172
|
# Sub-tasks
|
173
|
-
issue.raw['fields']['subtasks']
|
173
|
+
issue.raw['fields']['subtasks']&.each do |raw_subtask|
|
174
174
|
@issue_keys_pending_download << raw_subtask['key']
|
175
175
|
end
|
176
176
|
|
@@ -1,5 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file is really intended to give you ideas about how you might configure your own reports, not
|
4
|
+
# as a complete setup that will work in every case.
|
5
|
+
#
|
6
|
+
# See https://github.com/mikebowler/jirametrics/wiki/Examples-folder for moreclass Exporter
|
3
7
|
class Exporter
|
4
8
|
def aggregated_project name:, project_names:
|
5
9
|
project name: name do
|
@@ -1,9 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file is really intended to give you ideas about how you might configure your own reports, not
|
4
|
+
# as a complete setup that will work in every case.
|
5
|
+
#
|
6
|
+
# See https://github.com/mikebowler/jirametrics/wiki/Examples-folder for more
|
3
7
|
class Exporter
|
4
|
-
def standard_project name:, file_prefix:, ignore_issues: nil, starting_status: nil, boards: {},
|
8
|
+
def standard_project name:, file_prefix:, ignore_issues: nil, starting_status: nil, boards: {},
|
9
|
+
default_board: nil, anonymize: false
|
10
|
+
|
5
11
|
project name: name do
|
6
12
|
puts name
|
13
|
+
self.anonymize if anonymize
|
7
14
|
|
8
15
|
settings['blocked_link_text'] = ['is blocked by']
|
9
16
|
file_prefix file_prefix
|
@@ -36,7 +43,7 @@ class Exporter
|
|
36
43
|
html_report do
|
37
44
|
board_id default_board if default_board
|
38
45
|
|
39
|
-
html "<H1>#{
|
46
|
+
html "<H1>#{name}</H1>", type: :header
|
40
47
|
boards.each_key do |id|
|
41
48
|
board = find_board id
|
42
49
|
html "<div><a href='#{board.url}'>#{id} #{board.name}</a></div>",
|
@@ -48,16 +55,7 @@ class Exporter
|
|
48
55
|
cycletime_scatterplot do
|
49
56
|
show_trend_lines
|
50
57
|
end
|
51
|
-
cycletime_scatterplot do # Epics
|
52
|
-
header_text 'Parents only'
|
53
|
-
filter_issues { |i| i.parent }
|
54
|
-
end
|
55
58
|
cycletime_histogram
|
56
|
-
cycletime_histogram do
|
57
|
-
grouping_rules do |issue, rules|
|
58
|
-
rules.label = issue.board.cycletime.stopped_time(issue).to_date.strftime('%b %Y')
|
59
|
-
end
|
60
|
-
end
|
61
59
|
|
62
60
|
throughput_chart do
|
63
61
|
description_text '<h2>Number of items completed, grouped by issue type</h2>'
|
@@ -79,14 +77,24 @@ class Exporter
|
|
79
77
|
aging_work_table
|
80
78
|
daily_wip_by_age_chart
|
81
79
|
daily_wip_by_blocked_stalled_chart
|
80
|
+
daily_wip_chart do
|
81
|
+
header_text 'Daily WIP by Parent'
|
82
|
+
description_text <<-TEXT
|
83
|
+
How much work is in progress, grouped by the parent of the issue. This will give us an
|
84
|
+
indication of how focused we are on higher level objectives. If there are many parent
|
85
|
+
tickets in progress at the same time, either this team has their focus scattered or we
|
86
|
+
aren't doing a good job of
|
87
|
+
<a href="https://improvingflow.com/2024/02/21/slicing-epics.html">splitting those parent
|
88
|
+
tickets</a>. Neither of those is desirable.
|
89
|
+
TEXT
|
90
|
+
grouping_rules do |issue, rules|
|
91
|
+
rules.label = issue.parent&.key || 'No parent'
|
92
|
+
rules.color = 'white' if rules.label == 'No parent'
|
93
|
+
end
|
94
|
+
end
|
82
95
|
expedited_chart
|
83
96
|
sprint_burndown
|
84
97
|
story_point_accuracy_chart
|
85
|
-
# story_point_accuracy_chart do
|
86
|
-
# header_text nil
|
87
|
-
# description_text nil
|
88
|
-
# y_axis(sort_order: %w[Story Task Defect], label: 'TShirt Sizes') { |issue, _started_time| issue.type }
|
89
|
-
# end
|
90
98
|
|
91
99
|
dependency_chart do
|
92
100
|
link_rules do |link, rules|
|
@@ -98,8 +106,9 @@ class Exporter
|
|
98
106
|
rules.merge_bidirectional keep: 'outward'
|
99
107
|
when 'Sync'
|
100
108
|
rules.use_bidirectional_arrows
|
101
|
-
# rules.line_color = 'red'
|
102
109
|
else
|
110
|
+
# This is a link type that we don't recognized. Dump it to standard out to draw attention
|
111
|
+
# to it.
|
103
112
|
puts "name=#{link.name}, label=#{link.label}"
|
104
113
|
end
|
105
114
|
end
|
data/lib/jirametrics/issue.rb
CHANGED
@@ -495,7 +495,7 @@ class Issue
|
|
495
495
|
end
|
496
496
|
|
497
497
|
def load_history_into_changes
|
498
|
-
@raw['changelog']['histories']
|
498
|
+
@raw['changelog']['histories']&.each do |history|
|
499
499
|
created = parse_time(history['created'])
|
500
500
|
|
501
501
|
# It should be impossible to not have an author but we've seen it in production
|
@@ -507,7 +507,7 @@ class Issue
|
|
507
507
|
end
|
508
508
|
|
509
509
|
def load_comments_into_changes
|
510
|
-
@raw['fields']['comment']['comments']
|
510
|
+
@raw['fields']['comment']['comments']&.each do |comment|
|
511
511
|
raw = {
|
512
512
|
'field' => 'comment',
|
513
513
|
'to' => comment['id'],
|
@@ -7,8 +7,9 @@ class ProjectConfig
|
|
7
7
|
include DiscardChangesBefore
|
8
8
|
|
9
9
|
attr_reader :target_path, :jira_config, :all_boards, :possible_statuses,
|
10
|
-
:download_config, :file_configs, :exporter, :data_version, :name, :board_configs,
|
11
|
-
|
10
|
+
:download_config, :file_configs, :exporter, :data_version, :name, :board_configs,
|
11
|
+
:settings
|
12
|
+
attr_accessor :time_range, :jira_url, :project_id
|
12
13
|
|
13
14
|
def initialize exporter:, jira_config:, block:, target_path: '.', name: ''
|
14
15
|
@exporter = exporter
|
@@ -39,8 +40,9 @@ class ProjectConfig
|
|
39
40
|
|
40
41
|
def run
|
41
42
|
unless aggregated_project?
|
42
|
-
load_status_category_mappings
|
43
43
|
load_all_boards
|
44
|
+
@project_id = @all_boards.first.last.project_id
|
45
|
+
load_status_category_mappings
|
44
46
|
load_project_metadata
|
45
47
|
load_sprints
|
46
48
|
end
|
@@ -176,7 +178,8 @@ class ProjectConfig
|
|
176
178
|
name: status_name,
|
177
179
|
id: snippet['id'].to_i,
|
178
180
|
category_name: category_config['name'],
|
179
|
-
category_id: category_config['id'].to_i
|
181
|
+
category_id: category_config['id'].to_i,
|
182
|
+
project_id: snippet['scope']&.[]('project')&.[]('id') # May have a value if this is a NextGen project
|
180
183
|
)
|
181
184
|
end
|
182
185
|
end
|
@@ -198,16 +201,32 @@ class ProjectConfig
|
|
198
201
|
end
|
199
202
|
|
200
203
|
def add_possible_status status
|
204
|
+
# If it's project scoped and it's not this project, just ignore it.
|
205
|
+
return if status.project_id && status.project_id != @project_id
|
206
|
+
|
201
207
|
existing_status = find_status(name: status.name)
|
202
208
|
|
203
|
-
|
204
|
-
|
205
|
-
raise "Redefining status category #{status} with #{existing_status}. Was one set in the config?"
|
206
|
-
end
|
209
|
+
# If it isn't there, add it and go.
|
210
|
+
return @possible_statuses << status unless existing_status
|
207
211
|
|
212
|
+
# If the existing one has a project id then it's already the most precise. Ignore the new one.
|
213
|
+
# No need to check categories as status_category_mapping can't add a project_id so by definition
|
214
|
+
# this data came from Jira.
|
215
|
+
return if existing_status && existing_status.project_id
|
216
|
+
|
217
|
+
# If the new one has a project_id then it's more precise so replace the old one with this,
|
218
|
+
# regardless of whether the categories match.
|
219
|
+
if status.project_id
|
220
|
+
@possible_statuses.delete(existing_status)
|
221
|
+
@possible_statuses << status
|
208
222
|
return
|
209
223
|
end
|
210
224
|
|
225
|
+
# This new status may have come from status_category_mapping so verify that categories match.
|
226
|
+
if existing_status.category_name != status.category_name
|
227
|
+
raise "Redefining status category #{status} with #{existing_status}. Was one set in the config?"
|
228
|
+
end
|
229
|
+
|
211
230
|
@possible_statuses << status
|
212
231
|
end
|
213
232
|
|
data/lib/jirametrics/status.rb
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Status
|
4
|
-
attr_reader :id, :type, :category_name, :category_id
|
4
|
+
attr_reader :id, :type, :category_name, :category_id, :project_id
|
5
5
|
attr_accessor :name
|
6
6
|
|
7
|
-
def initialize name:, id:, category_name:, category_id:
|
7
|
+
def initialize name:, id:, category_name:, category_id:, project_id: nil
|
8
8
|
@name = name
|
9
9
|
@id = id
|
10
10
|
@category_name = category_name
|
11
11
|
@category_id = category_id
|
12
|
+
@project_id = project_id
|
12
13
|
end
|
13
14
|
|
14
15
|
def to_s
|
15
16
|
"Status(name=#{@name.inspect}, id=#{@id.inspect}," \
|
16
|
-
" category_name=#{@category_name.inspect}, category_id=#{@category_id.inspect})"
|
17
|
+
" category_name=#{@category_name.inspect}, category_id=#{@category_id.inspect}, project_id=#{@project_id})"
|
17
18
|
end
|
18
19
|
|
19
20
|
def eql?(other)
|
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: 1.
|
4
|
+
version: '1.3'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Bowler
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: random-word
|
@@ -163,7 +163,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
163
|
- !ruby/object:Gem::Version
|
164
164
|
version: '0'
|
165
165
|
requirements: []
|
166
|
-
rubygems_version: 3.
|
166
|
+
rubygems_version: 3.5.7
|
167
167
|
signing_key:
|
168
168
|
specification_version: 4
|
169
169
|
summary: Extract Jira metrics
|