jirametrics 1.2.1 → 1.3
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/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
|