jirametrics 2.5 → 2.30
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/bin/jirametrics-mcp +5 -0
- data/lib/jirametrics/aggregate_config.rb +16 -3
- data/lib/jirametrics/aging_work_bar_chart.rb +193 -133
- data/lib/jirametrics/aging_work_in_progress_chart.rb +138 -42
- data/lib/jirametrics/aging_work_table.rb +63 -19
- data/lib/jirametrics/anonymizer.rb +81 -6
- data/lib/jirametrics/atlassian_document_format.rb +160 -0
- data/lib/jirametrics/bar_chart_range.rb +17 -0
- data/lib/jirametrics/blocked_stalled_change.rb +6 -4
- data/lib/jirametrics/board.rb +73 -20
- data/lib/jirametrics/board_config.rb +10 -2
- data/lib/jirametrics/board_feature.rb +14 -0
- data/lib/jirametrics/board_movement_calculator.rb +155 -0
- data/lib/jirametrics/cfd_data_builder.rb +108 -0
- data/lib/jirametrics/change_item.rb +54 -18
- data/lib/jirametrics/chart_base.rb +203 -30
- data/lib/jirametrics/css_variable.rb +2 -2
- data/lib/jirametrics/cumulative_flow_diagram.rb +208 -0
- data/lib/jirametrics/cycle_time_config.rb +137 -0
- data/lib/jirametrics/cycletime_histogram.rb +17 -38
- data/lib/jirametrics/cycletime_scatterplot.rb +18 -87
- data/lib/jirametrics/daily_view.rb +306 -0
- data/lib/jirametrics/daily_wip_by_age_chart.rb +5 -8
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +15 -5
- data/lib/jirametrics/daily_wip_by_parent_chart.rb +4 -6
- data/lib/jirametrics/daily_wip_chart.rb +36 -16
- data/lib/jirametrics/data_quality_report.rb +251 -42
- data/lib/jirametrics/dependency_chart.rb +8 -6
- data/lib/jirametrics/download_config.rb +17 -2
- data/lib/jirametrics/downloader.rb +177 -108
- data/lib/jirametrics/downloader_for_cloud.rb +287 -0
- data/lib/jirametrics/downloader_for_data_center.rb +95 -0
- data/lib/jirametrics/estimate_accuracy_chart.rb +75 -14
- data/lib/jirametrics/estimation_configuration.rb +25 -0
- data/lib/jirametrics/examples/aggregated_project.rb +5 -8
- data/lib/jirametrics/examples/standard_project.rb +54 -38
- data/lib/jirametrics/expedited_chart.rb +10 -9
- data/lib/jirametrics/exporter.rb +51 -16
- data/lib/jirametrics/file_config.rb +21 -6
- data/lib/jirametrics/file_system.rb +96 -4
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +115 -0
- data/lib/jirametrics/github_gateway.rb +115 -0
- data/lib/jirametrics/groupable_issue_chart.rb +12 -4
- data/lib/jirametrics/grouping_rules.rb +26 -4
- data/lib/jirametrics/html/aging_work_bar_chart.erb +8 -17
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +24 -5
- data/lib/jirametrics/html/aging_work_table.erb +13 -4
- data/lib/jirametrics/html/collapsible_issues_panel.erb +2 -2
- data/lib/jirametrics/html/cumulative_flow_diagram.erb +503 -0
- data/lib/jirametrics/html/daily_wip_chart.erb +41 -15
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +4 -12
- data/lib/jirametrics/html/expedited_chart.erb +7 -24
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +81 -0
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/index.css +336 -62
- data/lib/jirametrics/html/index.erb +16 -21
- data/lib/jirametrics/html/index.js +164 -0
- data/lib/jirametrics/html/legacy_colors.css +174 -0
- data/lib/jirametrics/html/sprint_burndown.erb +18 -25
- data/lib/jirametrics/html/throughput_chart.erb +43 -21
- data/lib/jirametrics/html/time_based_histogram.erb +123 -0
- data/lib/jirametrics/html/{cycletime_scatterplot.erb → time_based_scatterplot.erb} +16 -21
- data/lib/jirametrics/html/wip_by_column_chart.erb +250 -0
- data/lib/jirametrics/html_generator.rb +32 -0
- data/lib/jirametrics/html_report_config.rb +83 -76
- data/lib/jirametrics/issue.rb +481 -97
- data/lib/jirametrics/issue_collection.rb +33 -0
- data/lib/jirametrics/issue_printer.rb +97 -0
- data/lib/jirametrics/jira_gateway.rb +96 -16
- data/lib/jirametrics/mcp_server.rb +531 -0
- data/lib/jirametrics/project_config.rb +374 -130
- data/lib/jirametrics/pull_request.rb +30 -0
- data/lib/jirametrics/pull_request_cycle_time_histogram.rb +77 -0
- data/lib/jirametrics/pull_request_cycle_time_scatterplot.rb +88 -0
- data/lib/jirametrics/pull_request_review.rb +13 -0
- data/lib/jirametrics/raw_javascript.rb +17 -0
- data/lib/jirametrics/self_or_issue_dispatcher.rb +2 -0
- data/lib/jirametrics/settings.json +7 -1
- data/lib/jirametrics/sprint.rb +13 -0
- data/lib/jirametrics/sprint_burndown.rb +47 -39
- data/lib/jirametrics/sprint_issue_change_data.rb +3 -3
- data/lib/jirametrics/status.rb +84 -19
- data/lib/jirametrics/status_collection.rb +83 -38
- data/lib/jirametrics/stitcher.rb +81 -0
- data/lib/jirametrics/throughput_by_completed_resolution_chart.rb +22 -0
- data/lib/jirametrics/throughput_chart.rb +73 -23
- data/lib/jirametrics/time_based_histogram.rb +139 -0
- data/lib/jirametrics/time_based_scatterplot.rb +107 -0
- data/lib/jirametrics/user.rb +12 -0
- data/lib/jirametrics/value_equality.rb +2 -2
- data/lib/jirametrics/wip_by_column_chart.rb +236 -0
- data/lib/jirametrics.rb +101 -66
- metadata +72 -16
- data/lib/jirametrics/cycletime_config.rb +0 -69
- data/lib/jirametrics/discard_changes_before.rb +0 -37
- data/lib/jirametrics/html/cycletime_histogram.erb +0 -47
- data/lib/jirametrics/html/data_quality_report.erb +0 -126
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a1f64f63f13e8cb59d3b18fb1e1ad90f77ca06d0e2f59a75ff7b7bae4db1870f
|
|
4
|
+
data.tar.gz: 9b7d6b8759102d7590e86c114d2ac8b1b2e7c4cc9f45a168002752196a6bf797
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ec0bee468f8c34c001ea9151b0d78b1018d246cc86f9c2588a70ee55e3940b6263f69032884ec5c2dc596d3182909829f6ee63fe51b6c65444ce667bf70a6ca
|
|
7
|
+
data.tar.gz: 9b9f5337d0fc671639f9f651977cb2ecc60b2d27105cdce1b75a2a4188c093c111ba29ed880adccb515d548b8bd9209b4cf703482a0ede4ccea6b182212a3a57
|
data/bin/jirametrics-mcp
ADDED
|
@@ -41,7 +41,7 @@ class AggregateConfig
|
|
|
41
41
|
def include_issues_from project_name
|
|
42
42
|
project = @project_config.exporter.project_configs.find { |p| p.name == project_name }
|
|
43
43
|
if project.nil?
|
|
44
|
-
|
|
44
|
+
file_system.warning "Aggregated project #{@project_config.name.inspect} is attempting to load " \
|
|
45
45
|
"project #{project_name.inspect} but it can't be found. Is it disabled?"
|
|
46
46
|
return
|
|
47
47
|
end
|
|
@@ -62,7 +62,20 @@ class AggregateConfig
|
|
|
62
62
|
'the first file section'
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
|
+
|
|
66
|
+
if issues.nil?
|
|
67
|
+
file_system.warning "No issues found for #{project_name}"
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
|
|
65
71
|
@project_config.add_issues issues
|
|
72
|
+
|
|
73
|
+
# Bring fix versions over
|
|
74
|
+
project.fix_versions.each do |fix_version|
|
|
75
|
+
unless @project_config.fix_versions.find { |fv| fv.id == fix_version.id }
|
|
76
|
+
@project_config.fix_versions << fix_version
|
|
77
|
+
end
|
|
78
|
+
end
|
|
66
79
|
end
|
|
67
80
|
|
|
68
81
|
def find_time_range projects:
|
|
@@ -81,7 +94,7 @@ class AggregateConfig
|
|
|
81
94
|
|
|
82
95
|
private
|
|
83
96
|
|
|
84
|
-
def
|
|
85
|
-
@project_config.exporter.file_system
|
|
97
|
+
def file_system
|
|
98
|
+
@project_config.exporter.file_system
|
|
86
99
|
end
|
|
87
100
|
end
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'jirametrics/chart_base'
|
|
4
|
+
require 'jirametrics/bar_chart_range'
|
|
4
5
|
|
|
5
6
|
class AgingWorkBarChart < ChartBase
|
|
6
|
-
@@next_id = 0
|
|
7
|
-
|
|
8
7
|
def initialize block
|
|
9
8
|
super()
|
|
10
9
|
|
|
10
|
+
@age_cutoff = nil
|
|
11
11
|
header_text 'Aging Work Bar Chart'
|
|
12
12
|
description_text <<-HTML
|
|
13
13
|
<p>
|
|
@@ -15,16 +15,19 @@ class AgingWorkBarChart < ChartBase
|
|
|
15
15
|
newest at the bottom.
|
|
16
16
|
</p>
|
|
17
17
|
<p>
|
|
18
|
-
There are
|
|
19
|
-
information relevant to that. Hovering over any of the bars will provide more details.
|
|
18
|
+
There are <%= (aggregated_project? || current_board.scrum?) ? 'four' : 'three' %> bars for each issue, and hovering over any of the bars will provide more details.
|
|
20
19
|
<ol>
|
|
21
|
-
<li>The
|
|
20
|
+
<li>Status: The status the issue was in at any time. The colour indicates the
|
|
22
21
|
status category, which will be one of #{color_block '--status-category-todo-color'} To Do,
|
|
23
22
|
#{color_block '--status-category-inprogress-color'} In Progress,
|
|
24
23
|
or #{color_block '--status-category-done-color'} Done</li>
|
|
25
|
-
<li>
|
|
24
|
+
<li>Activity: This bar indicates #{color_block '--blocked-color'} blocked
|
|
26
25
|
or #{color_block '--stalled-color'} stalled.</li>
|
|
27
|
-
<li>
|
|
26
|
+
<li>Priority: This shows the priority over time. If one of these priorities is considered expedited
|
|
27
|
+
then it will be drawn with diagonal lines.</li>
|
|
28
|
+
<% if aggregated_project? || current_board.scrum? %>
|
|
29
|
+
<li>Sprints: The sprints that the issue was in.</li>
|
|
30
|
+
<% end %>
|
|
28
31
|
</ol>
|
|
29
32
|
</p>
|
|
30
33
|
#{describe_non_working_days}
|
|
@@ -38,6 +41,7 @@ class AgingWorkBarChart < ChartBase
|
|
|
38
41
|
|
|
39
42
|
def run
|
|
40
43
|
aging_issues = select_aging_issues issues: @issues
|
|
44
|
+
adjust_time_date_ranges_to_start_from_earliest_issue_start(aging_issues)
|
|
41
45
|
|
|
42
46
|
today = date_range.end
|
|
43
47
|
sort_by_age! issues: aging_issues, today: today
|
|
@@ -53,137 +57,141 @@ class AgingWorkBarChart < ChartBase
|
|
|
53
57
|
percentage_line_x = date_range.end - calculate_percent_line if percentage
|
|
54
58
|
|
|
55
59
|
if aging_issues.empty?
|
|
56
|
-
@description_text =
|
|
57
|
-
return render_top_text(binding)
|
|
60
|
+
@description_text = '<p>There is no aging work</p>'
|
|
61
|
+
return render_top_text(binding)
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
wrap_and_render(binding, __FILE__)
|
|
61
65
|
end
|
|
62
66
|
|
|
67
|
+
def adjust_time_date_ranges_to_start_from_earliest_issue_start aging_issues
|
|
68
|
+
earliest_start_time = aging_issues.collect do |issue|
|
|
69
|
+
issue.started_stopped_times.first
|
|
70
|
+
end.min
|
|
71
|
+
return if earliest_start_time.nil? || earliest_start_time >= @time_range.begin
|
|
72
|
+
|
|
73
|
+
@time_range = earliest_start_time..@time_range.end
|
|
74
|
+
@date_range = @time_range.begin.to_date..@time_range.end.to_date
|
|
75
|
+
end
|
|
76
|
+
|
|
63
77
|
def data_sets_for_one_issue issue:, today:
|
|
64
78
|
cycletime = issue.board.cycletime
|
|
65
|
-
issue_start_time = cycletime.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
[
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
issue_label: issue_label,
|
|
73
|
-
stack: 'blocked',
|
|
74
|
-
issue_start_time: issue_start_time
|
|
75
|
-
),
|
|
76
|
-
data_set_by_block(
|
|
77
|
-
issue: issue,
|
|
78
|
-
issue_label: issue_label,
|
|
79
|
-
title_label: 'Expedited',
|
|
80
|
-
stack: 'expedited',
|
|
81
|
-
color: CssVariable['--expedited-color'],
|
|
82
|
-
start_date: issue_start_date
|
|
83
|
-
) { |day| issue.expedited_on_date?(day) }
|
|
79
|
+
issue_start_time = cycletime.started_stopped_times(issue).first
|
|
80
|
+
end_of_today = Time.parse("#{today}T23:59:59#{@timezone_offset}")
|
|
81
|
+
|
|
82
|
+
bar_data = [
|
|
83
|
+
['status', collect_status_ranges(issue: issue, now: end_of_today)],
|
|
84
|
+
['blocked', collect_blocked_stalled_ranges(issue: issue, issue_start_time: issue_start_time)],
|
|
85
|
+
['priority', collect_priority_ranges(issue: issue)]
|
|
84
86
|
]
|
|
87
|
+
bar_data << ['sprints', collect_sprint_ranges(issue: issue)] if aggregated_project? || current_board.scrum?
|
|
88
|
+
|
|
89
|
+
bar_data.each { |entry| clip_ranges_to_start_time(ranges: entry.last, issue_start_time: issue_start_time) }
|
|
90
|
+
|
|
91
|
+
issue_label = "[#{label_days cycletime.age(issue, today: today)}] #{issue.key}: #{issue.summary}"[0..60]
|
|
92
|
+
bar_data.collect do |stack, ranges|
|
|
93
|
+
bar_chart_range_to_data_set y_value: issue_label, ranges: ranges, stack: stack, issue_start_time: issue_start_time
|
|
94
|
+
end
|
|
85
95
|
end
|
|
86
96
|
|
|
87
97
|
def sort_by_age! issues:, today:
|
|
88
98
|
issues.sort! do |a, b|
|
|
89
|
-
|
|
99
|
+
b.board.cycletime.age(b, today: today) <=> a.board.cycletime.age(a, today: today)
|
|
90
100
|
end
|
|
91
101
|
end
|
|
92
102
|
|
|
93
103
|
def select_aging_issues issues:
|
|
94
104
|
issues.select do |issue|
|
|
95
|
-
|
|
96
|
-
|
|
105
|
+
started_time, stopped_time = issue.started_stopped_times
|
|
106
|
+
next false unless started_time && stopped_time.nil?
|
|
107
|
+
|
|
108
|
+
age = (date_range.end - started_time.to_date).to_i + 1
|
|
109
|
+
!(@age_cutoff && @age_cutoff >= age)
|
|
97
110
|
end
|
|
98
111
|
end
|
|
99
112
|
|
|
100
113
|
def grow_chart_height_if_too_many_issues aging_issue_count:
|
|
101
|
-
px_per_bar =
|
|
114
|
+
px_per_bar = 10
|
|
102
115
|
bars_per_issue = 3
|
|
116
|
+
bars_per_issue += 1 if aggregated_project? || current_board.scrum?
|
|
117
|
+
|
|
103
118
|
preferred_height = aging_issue_count * px_per_bar * bars_per_issue
|
|
104
119
|
@canvas_height = preferred_height if @canvas_height.nil? || @canvas_height < preferred_height
|
|
105
120
|
end
|
|
106
121
|
|
|
107
|
-
def
|
|
108
|
-
|
|
122
|
+
def clip_ranges_to_start_time ranges:, issue_start_time:
|
|
123
|
+
return if issue_start_time.nil?
|
|
109
124
|
|
|
110
|
-
|
|
125
|
+
ranges.each { |range| range.start = issue_start_time if range.start < issue_start_time }
|
|
126
|
+
ranges.reject! { |range| range.start >= range.stop }
|
|
127
|
+
end
|
|
111
128
|
|
|
129
|
+
def collect_status_ranges issue:, now:
|
|
130
|
+
ranges = []
|
|
131
|
+
issue_started_time = issue.started_stopped_times.first
|
|
112
132
|
previous_start = nil
|
|
113
133
|
previous_status = nil
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
unless previous_start.nil? || previous_start < issue_started_time
|
|
122
|
-
hash = {
|
|
123
|
-
type: 'bar',
|
|
124
|
-
data: [{
|
|
125
|
-
x: [chart_format(previous_start), chart_format(change.time)],
|
|
126
|
-
y: label,
|
|
127
|
-
title: "#{issue.type} : #{change.value}"
|
|
128
|
-
}],
|
|
129
|
-
backgroundColor: status_category_color(status),
|
|
130
|
-
borderColor: CssVariable['--aging-work-bar-chart-separator-color'],
|
|
131
|
-
borderWidth: {
|
|
132
|
-
top: 0,
|
|
133
|
-
right: 1,
|
|
134
|
-
bottom: 0,
|
|
135
|
-
left: 0
|
|
136
|
-
},
|
|
137
|
-
stacked: true,
|
|
138
|
-
stack: 'status'
|
|
139
|
-
}
|
|
140
|
-
data_sets << hash if date_range.include?(change.time.to_date)
|
|
134
|
+
issue.status_changes.each do |change|
|
|
135
|
+
new_status = issue.find_or_create_status id: change.value_id, name: change.value
|
|
136
|
+
if previous_start.nil?
|
|
137
|
+
previous_start = change.time
|
|
138
|
+
previous_status = new_status
|
|
139
|
+
next
|
|
141
140
|
end
|
|
142
141
|
|
|
142
|
+
previous_start = issue_started_time if issue_started_time > previous_start
|
|
143
|
+
|
|
144
|
+
ranges << BarChartRange.new(
|
|
145
|
+
start: previous_start,
|
|
146
|
+
stop: change.time,
|
|
147
|
+
color: status_category_color(previous_status),
|
|
148
|
+
title: previous_status.to_s
|
|
149
|
+
)
|
|
143
150
|
previous_start = change.time
|
|
144
|
-
previous_status =
|
|
151
|
+
previous_status = new_status
|
|
145
152
|
end
|
|
146
153
|
|
|
147
|
-
|
|
148
|
-
|
|
154
|
+
ranges << BarChartRange.new(
|
|
155
|
+
start: previous_start,
|
|
156
|
+
stop: now,
|
|
157
|
+
color: status_category_color(previous_status),
|
|
158
|
+
title: previous_status.to_s
|
|
159
|
+
)
|
|
160
|
+
ranges
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def bar_chart_range_to_data_set y_value:, ranges:, stack:, issue_start_time:
|
|
164
|
+
ranges.filter_map do |bar_chart_range|
|
|
165
|
+
next if bar_chart_range.stop < issue_start_time
|
|
166
|
+
|
|
167
|
+
background_color = bar_chart_range.color
|
|
168
|
+
if bar_chart_range.highlight
|
|
169
|
+
background_color = RawJavascript.new("createDiagonalPattern(#{background_color.to_json})")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
{
|
|
149
173
|
type: 'bar',
|
|
150
174
|
data: [{
|
|
151
|
-
x: [chart_format(
|
|
152
|
-
y:
|
|
153
|
-
title:
|
|
175
|
+
x: [chart_format([bar_chart_range.start, issue_start_time].max), chart_format(bar_chart_range.stop)],
|
|
176
|
+
y: y_value,
|
|
177
|
+
title: bar_chart_range.title
|
|
154
178
|
}],
|
|
155
|
-
backgroundColor:
|
|
179
|
+
backgroundColor: background_color,
|
|
180
|
+
borderColor: CssVariable['--aging-work-bar-chart-separator-color'],
|
|
181
|
+
borderWidth: {
|
|
182
|
+
top: 0,
|
|
183
|
+
right: 1,
|
|
184
|
+
bottom: 0,
|
|
185
|
+
left: 0
|
|
186
|
+
},
|
|
156
187
|
stacked: true,
|
|
157
|
-
stack:
|
|
188
|
+
stack: stack
|
|
158
189
|
}
|
|
159
190
|
end
|
|
160
|
-
|
|
161
|
-
data_sets
|
|
162
|
-
end
|
|
163
|
-
|
|
164
|
-
def one_block_change_data_set starting_change:, ending_time:, issue_label:, stack:, issue_start_time:
|
|
165
|
-
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['blocked_color']
|
|
166
|
-
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['stalled_color']
|
|
167
|
-
|
|
168
|
-
color = settings['blocked_color'] || '--blocked-color'
|
|
169
|
-
color = settings['stalled_color'] || '--stalled-color' if starting_change.stalled?
|
|
170
|
-
{
|
|
171
|
-
backgroundColor: CssVariable[color],
|
|
172
|
-
data: [
|
|
173
|
-
{
|
|
174
|
-
title: starting_change.reasons,
|
|
175
|
-
x: [chart_format([issue_start_time, starting_change.time].max), chart_format(ending_time)],
|
|
176
|
-
y: issue_label
|
|
177
|
-
}
|
|
178
|
-
],
|
|
179
|
-
stack: stack,
|
|
180
|
-
stacked: true,
|
|
181
|
-
type: 'bar'
|
|
182
|
-
}
|
|
183
191
|
end
|
|
184
192
|
|
|
185
|
-
def
|
|
186
|
-
|
|
193
|
+
def collect_blocked_stalled_ranges issue:, issue_start_time:
|
|
194
|
+
results = []
|
|
187
195
|
starting_change = nil
|
|
188
196
|
|
|
189
197
|
issue.blocked_stalled_changes(end_time: time_range.end).each do |change|
|
|
@@ -193,58 +201,106 @@ class AgingWorkBarChart < ChartBase
|
|
|
193
201
|
end
|
|
194
202
|
|
|
195
203
|
if change.time >= issue_start_time
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
204
|
+
color = settings['blocked_color'] || '--blocked-color'
|
|
205
|
+
color = settings['stalled_color'] || '--stalled-color' if starting_change.stalled?
|
|
206
|
+
|
|
207
|
+
results << BarChartRange.new(
|
|
208
|
+
start: starting_change.time, stop: change.time, color: CssVariable[color], title: starting_change.reasons
|
|
199
209
|
)
|
|
200
210
|
end
|
|
201
211
|
|
|
202
212
|
starting_change = change
|
|
203
213
|
end
|
|
214
|
+
results
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def collect_priority_ranges issue:
|
|
218
|
+
expedited_priority_names = settings['expedited_priority_names']
|
|
219
|
+
|
|
220
|
+
previous_change = nil
|
|
221
|
+
results = []
|
|
204
222
|
|
|
205
|
-
|
|
223
|
+
issue.changes.each do |change|
|
|
224
|
+
next unless change.priority?
|
|
225
|
+
|
|
226
|
+
if previous_change.nil?
|
|
227
|
+
previous_change = change
|
|
228
|
+
next
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
results << create_range_for_priority(
|
|
232
|
+
previous_change: previous_change, stop_time: change.time,
|
|
233
|
+
expedited_priority_names: expedited_priority_names
|
|
234
|
+
)
|
|
235
|
+
previous_change = change
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
if previous_change
|
|
239
|
+
results << create_range_for_priority(
|
|
240
|
+
previous_change: previous_change, stop_time: time_range.end,
|
|
241
|
+
expedited_priority_names: expedited_priority_names
|
|
242
|
+
)
|
|
243
|
+
end
|
|
244
|
+
results
|
|
206
245
|
end
|
|
207
246
|
|
|
208
|
-
def
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
247
|
+
def collect_sprint_ranges issue:
|
|
248
|
+
results = []
|
|
249
|
+
open_sprints = {}
|
|
250
|
+
|
|
251
|
+
issue.changes.each do |change|
|
|
252
|
+
next unless change.sprint?
|
|
253
|
+
|
|
254
|
+
removed_sprint_ids = change.old_value_id - change.value_id
|
|
255
|
+
added_sprint_ids = change.value_id - change.old_value_id
|
|
256
|
+
|
|
257
|
+
removed_sprint_ids.each do |id|
|
|
258
|
+
data = open_sprints.delete(id)
|
|
259
|
+
next unless data
|
|
260
|
+
|
|
261
|
+
completed = data[:sprint].completed_time
|
|
262
|
+
stop = completed ? [change.time, completed].min : change.time
|
|
263
|
+
results << BarChartRange.new(
|
|
264
|
+
start: data[:start_time], stop: stop,
|
|
265
|
+
color: CssVariable['--sprint-color'], title: data[:sprint].name
|
|
266
|
+
)
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
added_sprint_ids.each do |id|
|
|
270
|
+
sprint = issue.board.sprints.find { |s| s.id == id }
|
|
271
|
+
next unless sprint
|
|
272
|
+
next if sprint.future?
|
|
273
|
+
|
|
274
|
+
start_time = [sprint.start_time, change.time].max
|
|
275
|
+
open_sprints[id] = { start_time: start_time, sprint: sprint }
|
|
228
276
|
end
|
|
229
277
|
end
|
|
230
278
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
279
|
+
open_sprints.each_value do |data|
|
|
280
|
+
next if data[:sprint].future?
|
|
281
|
+
|
|
282
|
+
stop = data[:sprint].completed_time || time_range.end
|
|
283
|
+
results << BarChartRange.new(
|
|
284
|
+
start: data[:start_time], stop: stop,
|
|
285
|
+
color: CssVariable['--sprint-color'], title: data[:sprint].name
|
|
286
|
+
)
|
|
237
287
|
end
|
|
238
288
|
|
|
239
|
-
|
|
289
|
+
results
|
|
290
|
+
end
|
|
240
291
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
292
|
+
def create_range_for_priority previous_change:, stop_time:, expedited_priority_names:
|
|
293
|
+
expedited = expedited_priority_names.include?(previous_change.value)
|
|
294
|
+
title = "Priority: #{previous_change.value}"
|
|
295
|
+
title << ' (expedited)' if expedited
|
|
296
|
+
|
|
297
|
+
BarChartRange.new(
|
|
298
|
+
start: previous_change.time,
|
|
299
|
+
stop: stop_time,
|
|
300
|
+
color: CssVariable["--priority-color-#{previous_change.value.downcase.gsub(/\s/, '')}"],
|
|
301
|
+
title: title,
|
|
302
|
+
highlight: expedited
|
|
303
|
+
)
|
|
248
304
|
end
|
|
249
305
|
|
|
250
306
|
def calculate_percent_line percentage: 85
|
|
@@ -253,4 +309,8 @@ class AgingWorkBarChart < ChartBase
|
|
|
253
309
|
|
|
254
310
|
days[days.length * percentage / 100]
|
|
255
311
|
end
|
|
312
|
+
|
|
313
|
+
def age_cutoff days
|
|
314
|
+
@age_cutoff = days
|
|
315
|
+
end
|
|
256
316
|
end
|