jirametrics 2.31 → 3.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 +42 -29
- data/lib/jirametrics/aging_work_bar_chart.rb +52 -40
- data/lib/jirametrics/aging_work_in_progress_chart.rb +65 -60
- data/lib/jirametrics/aging_work_table.rb +36 -33
- data/lib/jirametrics/anonymizer.rb +31 -86
- data/lib/jirametrics/atlassian_document_format.rb +11 -4
- data/lib/jirametrics/blocked_stalled_by_date_builder.rb +64 -0
- data/lib/jirametrics/blocked_stalled_change.rb +5 -1
- data/lib/jirametrics/blocked_stalled_change_stream_builder.rb +194 -0
- data/lib/jirametrics/board.rb +23 -9
- data/lib/jirametrics/board_config.rb +0 -7
- data/lib/jirametrics/board_movement_calculator.rb +24 -21
- data/lib/jirametrics/cfd_data_builder.rb +46 -44
- data/lib/jirametrics/change_item.rb +19 -7
- data/lib/jirametrics/chart_base.rb +50 -64
- data/lib/jirametrics/cumulative_flow_diagram.rb +59 -46
- data/lib/jirametrics/cycle_time_config.rb +35 -46
- data/lib/jirametrics/cycletime_histogram.rb +7 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +7 -0
- data/lib/jirametrics/daily_view.rb +83 -61
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +24 -9
- data/lib/jirametrics/daily_wip_chart.rb +72 -45
- data/lib/jirametrics/data_quality_report.rb +61 -63
- data/lib/jirametrics/dependency_chart.rb +40 -31
- data/lib/jirametrics/download_config.rb +0 -3
- data/lib/jirametrics/downloader.rb +8 -7
- data/lib/jirametrics/downloader_for_cloud.rb +108 -72
- data/lib/jirametrics/estimate_accuracy_chart.rb +13 -13
- data/lib/jirametrics/examples/standard_project.rb +2 -2
- data/lib/jirametrics/expedited_chart.rb +47 -37
- data/lib/jirametrics/exporter.rb +19 -10
- data/lib/jirametrics/file_config.rb +19 -12
- data/lib/jirametrics/file_system.rb +16 -10
- data/lib/jirametrics/flow_efficiency_calculator.rb +62 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +4 -2
- data/lib/jirametrics/github_gateway.rb +23 -9
- data/lib/jirametrics/groupable_issue_chart.rb +3 -0
- data/lib/jirametrics/html/index.css +6 -0
- data/lib/jirametrics/html_report_config.rb +9 -29
- data/lib/jirametrics/issue.rb +290 -389
- data/lib/jirametrics/issue_collection.rb +1 -0
- data/lib/jirametrics/issue_printer.rb +60 -23
- data/lib/jirametrics/jira_gateway.rb +29 -18
- data/lib/jirametrics/mcp_server.rb +225 -209
- data/lib/jirametrics/project_config.rb +158 -134
- data/lib/jirametrics/pull_request_cycle_time_histogram.rb +1 -20
- data/lib/jirametrics/pull_request_cycle_time_scatterplot.rb +11 -33
- data/lib/jirametrics/rules.rb +1 -0
- data/lib/jirametrics/self_or_issue_dispatcher.rb +1 -3
- data/lib/jirametrics/sprint_burndown.rb +142 -242
- data/lib/jirametrics/sprint_count_measure.rb +42 -0
- data/lib/jirametrics/sprint_issue_change_data.rb +1 -0
- data/lib/jirametrics/sprint_points_measure.rb +62 -0
- data/lib/jirametrics/sprint_summary_stats.rb +16 -0
- data/lib/jirametrics/status_collection.rb +2 -2
- data/lib/jirametrics/stitcher.rb +21 -16
- data/lib/jirametrics/throughput_chart.rb +38 -24
- data/lib/jirametrics/time_based_chart.rb +65 -0
- data/lib/jirametrics/time_based_histogram.rb +30 -23
- data/lib/jirametrics/time_based_scatterplot.rb +11 -2
- data/lib/jirametrics/trend_line_calculator.rb +2 -2
- data/lib/jirametrics/wip_by_column_chart.rb +73 -32
- data/lib/jirametrics.rb +6 -2
- metadata +13 -20
|
@@ -7,7 +7,8 @@ class FlowEfficiencyScatterplot < ChartBase
|
|
|
7
7
|
|
|
8
8
|
attr_accessor :possible_statuses
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
# Long only because of the inline description_text heredoc and one-time setup; splitting wouldn't help.
|
|
11
|
+
def initialize block # rubocop:disable Metrics/MethodLength
|
|
11
12
|
super()
|
|
12
13
|
|
|
13
14
|
header_text 'Flow Efficiency'
|
|
@@ -63,7 +64,8 @@ class FlowEfficiencyScatterplot < ChartBase
|
|
|
63
64
|
end
|
|
64
65
|
|
|
65
66
|
if data_sets.empty?
|
|
66
|
-
return "<h1 class='foldable'>#{@header_text}</h1
|
|
67
|
+
return "<h1 class='foldable'>#{@header_text}</h1>" \
|
|
68
|
+
'<div>No data matched the selected criteria. Nothing to show.</div>'
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
wrap_and_render(binding, __FILE__)
|
|
@@ -139,14 +139,18 @@ class GithubGateway
|
|
|
139
139
|
# fallback fetches the complete set rather than us caching a partial answer.
|
|
140
140
|
next if commits['totalCount'] && commits['totalCount'] > nodes.size
|
|
141
141
|
|
|
142
|
-
messages_by_number[number] = nodes
|
|
143
|
-
commit = node['commit'] || {}
|
|
144
|
-
[commit['messageHeadline'], commit['messageBody']].compact
|
|
145
|
-
end
|
|
142
|
+
messages_by_number[number] = commit_messages_from(nodes)
|
|
146
143
|
end
|
|
147
144
|
messages_by_number
|
|
148
145
|
end
|
|
149
146
|
|
|
147
|
+
def commit_messages_from nodes
|
|
148
|
+
nodes.flat_map do |node|
|
|
149
|
+
commit = node['commit'] || {}
|
|
150
|
+
[commit['messageHeadline'], commit['messageBody']].compact
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
150
154
|
def owner_and_name
|
|
151
155
|
# @repo may be a full URL (https://github.com/owner/name.git) or an owner/name slug.
|
|
152
156
|
@repo.sub(%r{\Ahttps?://[^/]+/}, '').delete_suffix('.git').split('/', 2)
|
|
@@ -195,6 +199,11 @@ class GithubGateway
|
|
|
195
199
|
unless status.success?
|
|
196
200
|
error_message = " GitHub CLI command failed for #{@repo} " \
|
|
197
201
|
"(attempt #{attempts}/#{MAX_RETRIES}): #{stderr.strip}"
|
|
202
|
+
# stderr is a String (from Open3.capture3), so this include? is String#include? — a substring
|
|
203
|
+
# match ("does the error text contain this phrase?"), not Array membership. Style/ArrayIntersect
|
|
204
|
+
# can't tell the two apart; its `.intersect?(stderr)` rewrite would be an exact-equality check
|
|
205
|
+
# that never matches, silently killing the retry-on-transient-error logic.
|
|
206
|
+
# rubocop:disable Style/ArrayIntersect
|
|
198
207
|
if attempts < MAX_RETRIES && TRANSIENT_ERROR_PATTERNS.any? { |pattern| stderr.include?(pattern) }
|
|
199
208
|
delay = 2**attempts
|
|
200
209
|
@file_system.log error_message
|
|
@@ -202,15 +211,20 @@ class GithubGateway
|
|
|
202
211
|
sleep delay
|
|
203
212
|
next
|
|
204
213
|
end
|
|
214
|
+
# rubocop:enable Style/ArrayIntersect
|
|
205
215
|
@file_system.warning error_message
|
|
206
216
|
raise "GitHub CLI command failed for #{@repo}: #{stderr}"
|
|
207
217
|
end
|
|
208
218
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
219
|
+
return parse_command_result(stdout)
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def parse_command_result stdout
|
|
224
|
+
result = JSON.parse(stdout)
|
|
225
|
+
if result.nil? || (result.is_a?(Array) && result.empty?)
|
|
226
|
+
@file_system.warning "No data was found in GitHub for #{@repo}. Is that what you expected?"
|
|
214
227
|
end
|
|
228
|
+
result
|
|
215
229
|
end
|
|
216
230
|
end
|
|
@@ -4,6 +4,9 @@ require 'jirametrics/rules'
|
|
|
4
4
|
require 'jirametrics/grouping_rules'
|
|
5
5
|
|
|
6
6
|
module GroupableIssueChart
|
|
7
|
+
attr_accessor :issue_hints, :issue_periods
|
|
8
|
+
attr_reader :group_by_block
|
|
9
|
+
|
|
7
10
|
def init_configuration_block user_provided_block, &default_block
|
|
8
11
|
instance_eval(&user_provided_block)
|
|
9
12
|
instance_eval(&default_block) unless @group_by_block
|
|
@@ -74,6 +74,10 @@
|
|
|
74
74
|
--sprint-burndown-sprint-color-6: #56B4E9; /* Okabe-Ito sky blue */
|
|
75
75
|
--sprint-burndown-sprint-color-7: #F0E442; /* Okabe-Ito yellow */
|
|
76
76
|
|
|
77
|
+
--cfd-triangle-color: #333333; /* dark triangle on light background */
|
|
78
|
+
--cfd-arrival-rate-line-color: rgba(255,138,101,0.85); /* orange */
|
|
79
|
+
--cfd-departure-rate-line-color: rgba(128,203,196,0.85); /* teal */
|
|
80
|
+
|
|
77
81
|
--sprint-color: #56B4E9; /* Okabe-Ito sky blue */
|
|
78
82
|
|
|
79
83
|
--daily-view-selected-issue-background: lightgray;
|
|
@@ -253,6 +257,7 @@ html[data-theme="dark"] {
|
|
|
253
257
|
--expedited-chart-dot-expedite-started-color: #E69F00;
|
|
254
258
|
--expedited-chart-dot-expedite-stopped-color: #2DCB9A;
|
|
255
259
|
--sprint-burndown-sprint-color-1: #56B4E9; /* sky blue */
|
|
260
|
+
--cfd-triangle-color: #ffffff; /* light triangle on dark background */
|
|
256
261
|
--sprint-burndown-sprint-color-3: #2DCB9A; /* lighter bluish green */
|
|
257
262
|
--sprint-burndown-sprint-color-4: #CC79A7; /* reddish purple (vermilion → orange conflicts with color-2) */
|
|
258
263
|
--sprint-burndown-sprint-color-5: #F0E442; /* yellow */
|
|
@@ -407,6 +412,7 @@ html[data-theme="light"] {
|
|
|
407
412
|
--expedited-chart-dot-expedite-stopped-color: #2DCB9A;
|
|
408
413
|
|
|
409
414
|
--sprint-burndown-sprint-color-1: #56B4E9; /* sky blue */
|
|
415
|
+
--cfd-triangle-color: #ffffff; /* light triangle on dark background */
|
|
410
416
|
--sprint-burndown-sprint-color-3: #2DCB9A; /* lighter bluish green */
|
|
411
417
|
--sprint-burndown-sprint-color-4: #CC79A7; /* reddish purple (vermilion → orange conflicts with color-2) */
|
|
412
418
|
--sprint-burndown-sprint-color-5: #F0E442; /* yellow */
|
|
@@ -8,23 +8,6 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
8
8
|
|
|
9
9
|
attr_reader :file_config, :sections, :charts
|
|
10
10
|
|
|
11
|
-
def self.define_chart name:, classname:, deprecated_warning: nil, deprecated_date: nil
|
|
12
|
-
lines = []
|
|
13
|
-
lines << "def #{name} &block"
|
|
14
|
-
lines << ' block = ->(_) {} unless block'
|
|
15
|
-
if deprecated_warning
|
|
16
|
-
lines << " file_system.deprecated date: #{deprecated_date.inspect}, message: #{deprecated_warning.inspect}"
|
|
17
|
-
end
|
|
18
|
-
lines << " execute_chart #{classname}.new(block)"
|
|
19
|
-
lines << 'end'
|
|
20
|
-
module_eval lines.join("\n"), __FILE__, __LINE__
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
define_chart name: 'daily_wip_by_type', classname: 'DailyWipChart',
|
|
24
|
-
deprecated_warning: 'This is the same as daily_wip_chart. Please use that one', deprecated_date: '2024-05-23'
|
|
25
|
-
define_chart name: 'story_point_accuracy_chart', classname: 'EstimateAccuracyChart',
|
|
26
|
-
deprecated_warning: 'Renamed to estimate_accuracy_chart. Please use that one', deprecated_date: '2024-05-23'
|
|
27
|
-
|
|
28
11
|
def initialize file_config:, block:
|
|
29
12
|
super()
|
|
30
13
|
@file_config = file_config
|
|
@@ -55,10 +38,7 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
55
38
|
end
|
|
56
39
|
|
|
57
40
|
def execute_chart_per_board klass:, block:, board_id:
|
|
58
|
-
|
|
59
|
-
ids = board_id ? [board_id] : issues.collect { |i| i.board.id }.uniq
|
|
60
|
-
ids = ids.sort_by { |id| all_boards[id]&.name || '' }
|
|
61
|
-
ids.each_with_index do |id, index|
|
|
41
|
+
board_ids(board_id).each_with_index do |id, index|
|
|
62
42
|
execute_chart(klass.new(block)) do |chart|
|
|
63
43
|
chart.board_id = id
|
|
64
44
|
# We're showing the description only on the first one in order to reduce noise on the report
|
|
@@ -67,6 +47,14 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
67
47
|
end
|
|
68
48
|
end
|
|
69
49
|
|
|
50
|
+
# The boards to draw this chart for, sorted by name: just the requested one, or every board the
|
|
51
|
+
# issues touch.
|
|
52
|
+
def board_ids board_id
|
|
53
|
+
all_boards = @file_config.project_config.all_boards
|
|
54
|
+
ids = board_id ? [board_id] : issues.collect { |issue| issue.board.id }.uniq
|
|
55
|
+
ids.sort_by { |id| all_boards[id]&.name || '' }
|
|
56
|
+
end
|
|
57
|
+
|
|
70
58
|
def respond_to_missing? name, include_private = false
|
|
71
59
|
class_name = name.to_s.split('_').map(&:capitalize).join
|
|
72
60
|
!resolve_chart_class(class_name).nil? || super
|
|
@@ -200,12 +188,4 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
200
188
|
</section>
|
|
201
189
|
HTML
|
|
202
190
|
end
|
|
203
|
-
|
|
204
|
-
def discard_changes_before status_becomes: nil, &block
|
|
205
|
-
file_system.deprecated(
|
|
206
|
-
date: '2025-01-09',
|
|
207
|
-
message: 'discard_changes_before is now only supported at the project level'
|
|
208
|
-
)
|
|
209
|
-
file_config.project_config.discard_changes_before status_becomes: status_becomes, &block
|
|
210
|
-
end
|
|
211
191
|
end
|