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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e97ffb4b2217f2b52ad32442b150243308aed066d4d4cedea4e9ab00a54e2f60
4
- data.tar.gz: 4d722736c3494db4977ce2504afe69ba5dad00982b25069a5dea70ff2b7141bc
3
+ metadata.gz: 02be53200d02a09c1dd8829dba1a06147959d6fc7c14960e810497ab72c8c731
4
+ data.tar.gz: b3f79887cc78af19ec5199140bb655cb161a21fc548d52b98fee04c287fdc100
5
5
  SHA512:
6
- metadata.gz: bec679ade15523af3929183d52603d200a4dc42ca1cb34debbde314970d2883f93b40f1f05789497d16ba467b812fc9f9df90cfe34cd1dff81ad7f04b7cf9b35
7
- data.tar.gz: 578a6ef7ba619bfd63e3ff5c2653fddca52500ad3f03117b043c80eed039c0b437eae7536f6970f9b8bfa1b03a2fd78b706abe015167350a506d0844313200af
6
+ metadata.gz: d05d5ea737475206c127b3e1bde806a5fb98493f4cd5d75dbc7d0ce1d89093fb5778ae276f1189a37096a6f800550f7f1bb86a2bbf1e9433f80caa90cc905ba8
7
+ data.tar.gz: 82dd49447e33017bd5d0dfb527011d27ae018d32c60e310bb8cc30ca1bcc8265888264df7aa2387109d8fe822a75381e6687bd750923de0d742b28dfb153bdf6
@@ -83,6 +83,10 @@ class Board
83
83
  @raw['id'].to_i
84
84
  end
85
85
 
86
+ def project_id
87
+ @raw['location']['id']
88
+ end
89
+
86
90
  def name
87
91
  @raw['name']
88
92
  end
@@ -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'].each do |raw_subtask|
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: {}, default_board: nil
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>#{file_prefix}</H1>", type: :header
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
@@ -495,7 +495,7 @@ class Issue
495
495
  end
496
496
 
497
497
  def load_history_into_changes
498
- @raw['changelog']['histories'].each do |history|
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'].each do |comment|
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, :settings
11
- attr_accessor :time_range, :jira_url
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
- if existing_status
204
- if existing_status.category_name != status.category_name
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
 
@@ -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)
@@ -64,4 +64,5 @@ class StatusCollection
64
64
  def <<(arg) = @list << arg
65
65
  def empty? = @list.empty?
66
66
  def clear = @list.clear
67
+ def delete(object) = @list.delete(object)
67
68
  end
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.2.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: 2023-10-05 00:00:00.000000000 Z
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.2.15
166
+ rubygems_version: 3.5.7
167
167
  signing_key:
168
168
  specification_version: 4
169
169
  summary: Extract Jira metrics