jirametrics 2.0.1 → 2.2.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 +1 -1
- data/lib/jirametrics/aging_work_bar_chart.rb +18 -13
- data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
- data/lib/jirametrics/aging_work_table.rb +21 -16
- data/lib/jirametrics/anonymizer.rb +6 -5
- data/lib/jirametrics/chart_base.rb +35 -19
- data/lib/jirametrics/css_variable.rb +33 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +8 -9
- data/lib/jirametrics/daily_wip_by_age_chart.rb +43 -17
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +32 -17
- data/lib/jirametrics/daily_wip_by_parent_chart.rb +42 -0
- data/lib/jirametrics/daily_wip_chart.rb +67 -6
- data/lib/jirametrics/data_quality_report.rb +1 -1
- data/lib/jirametrics/dependency_chart.rb +18 -14
- data/lib/jirametrics/downloader.rb +13 -11
- data/lib/jirametrics/examples/aggregated_project.rb +17 -20
- data/lib/jirametrics/examples/standard_project.rb +10 -18
- data/lib/jirametrics/expedited_chart.rb +17 -15
- data/lib/jirametrics/exporter.rb +26 -20
- data/lib/jirametrics/grouping_rules.rb +7 -1
- data/lib/jirametrics/html/aging_work_bar_chart.erb +12 -6
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +8 -2
- data/lib/jirametrics/html/aging_work_table.erb +11 -19
- data/lib/jirametrics/html/cycletime_histogram.erb +9 -3
- data/lib/jirametrics/html/cycletime_scatterplot.erb +10 -4
- data/lib/jirametrics/html/daily_wip_chart.erb +18 -5
- data/lib/jirametrics/html/expedited_chart.erb +11 -5
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/index.css +186 -0
- data/lib/jirametrics/html/index.erb +8 -36
- data/lib/jirametrics/html/sprint_burndown.erb +11 -6
- data/lib/jirametrics/html/story_point_accuracy_chart.erb +9 -4
- data/lib/jirametrics/html/throughput_chart.erb +11 -5
- data/lib/jirametrics/html_report_config.rb +28 -3
- data/lib/jirametrics/issue.rb +5 -3
- data/lib/jirametrics/jira_gateway.rb +5 -2
- data/lib/jirametrics/project_config.rb +14 -19
- data/lib/jirametrics/settings.json +7 -0
- data/lib/jirametrics/sprint_burndown.rb +10 -4
- data/lib/jirametrics/status_collection.rb +1 -1
- data/lib/jirametrics/story_point_accuracy_chart.rb +20 -10
- data/lib/jirametrics/throughput_chart.rb +10 -2
- data/lib/jirametrics.rb +2 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0f5551ca8d38a0b12579429080055e21bb161066e7bb2237ada5d47628a212de
|
4
|
+
data.tar.gz: 7bd667226812044854dde31dfac4f347380b077369ec711bec5d6cb98a0082e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 397a3c4eb84497df209c702858a77def1d9606eecf84d08a8ab7b4f0a6decd968f77ed617f8189acb4916e372742bfc8352bb0fcd2b5ad74e04880c03680fece
|
7
|
+
data.tar.gz: 8aa2cbc8d7e60ed80749bf58c48d78689adcb65892ad133a378956b308fdb3d326a665eb3a884bd9d29783142639813f696c77f393697e285a0b7769fbe0ad18
|
@@ -17,15 +17,17 @@ class AgingWorkBarChart < ChartBase
|
|
17
17
|
<p>
|
18
18
|
There are potentially three bars for each issue, although a bar may be missing if the issue has no
|
19
19
|
information relevant to that. Hovering over any of the bars will provide more details.
|
20
|
-
<ol
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
20
|
+
<ol>
|
21
|
+
<li>The top bar tells you what status the issue is in at any time. The colour indicates the
|
22
|
+
status category, which will be one of #{color_block '--status-category-todo-color'} To Do,
|
23
|
+
#{color_block '--status-category-inprogress-color'} In Progress,
|
24
|
+
or #{color_block '--status-category-done-color'} Done</li>
|
25
|
+
<li>The middle bar indicates #{color_block '--blocked-color'} blocked
|
26
|
+
or #{color_block '--stalled-color'} stalled.</li>
|
27
|
+
<li>The bottom bar indicated #{color_block '--expedited-color'} expedited.</li>
|
28
|
+
</ol>
|
28
29
|
</p>
|
30
|
+
#{ describe_non_working_days }
|
29
31
|
HTML
|
30
32
|
|
31
33
|
# Because this one will size itself as needed, we start with a smaller default size
|
@@ -65,7 +67,7 @@ class AgingWorkBarChart < ChartBase
|
|
65
67
|
issue_label: issue_label,
|
66
68
|
title_label: 'Expedited',
|
67
69
|
stack: 'expedited',
|
68
|
-
color: '
|
70
|
+
color: CssVariable['--expedited-color'],
|
69
71
|
start_date: issue_start_date
|
70
72
|
) { |day| issue.expedited_on_date?(day) }
|
71
73
|
].compact.flatten.each do |data|
|
@@ -109,7 +111,7 @@ class AgingWorkBarChart < ChartBase
|
|
109
111
|
title: "#{issue.type} : #{change.value}"
|
110
112
|
}],
|
111
113
|
backgroundColor: status_category_color(status),
|
112
|
-
borderColor: '
|
114
|
+
borderColor: CssVariable['--aging-work-bar-chart-separator-color'],
|
113
115
|
borderWidth: {
|
114
116
|
top: 0,
|
115
117
|
right: 1,
|
@@ -144,10 +146,13 @@ class AgingWorkBarChart < ChartBase
|
|
144
146
|
end
|
145
147
|
|
146
148
|
def one_block_change_data_set starting_change:, ending_time:, issue_label:, stack:, issue_start_time:
|
147
|
-
color
|
148
|
-
color
|
149
|
+
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['blocked_color']
|
150
|
+
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['stalled_color']
|
151
|
+
|
152
|
+
color = settings['blocked_color'] || '--blocked-color'
|
153
|
+
color = settings['stalled_color'] || '--stalled-color' if starting_change.stalled?
|
149
154
|
{
|
150
|
-
backgroundColor: color,
|
155
|
+
backgroundColor: CssVariable[color],
|
151
156
|
data: [
|
152
157
|
{
|
153
158
|
title: starting_change.reasons,
|
@@ -14,13 +14,14 @@ class AgingWorkInProgressChart < ChartBase
|
|
14
14
|
description_text <<-HTML
|
15
15
|
<p>
|
16
16
|
This chart shows only work items that have started but not completed, grouped by the column
|
17
|
-
|
18
|
-
</p>
|
19
|
-
<p>
|
20
|
-
The gray area indicates the 85% mark for work items that have passed through here - 85% of
|
21
|
-
previous work items left this column while still inside the gray area. Any work items above
|
22
|
-
the gray area are outliers and they are the items that you should pay special attention to.
|
17
|
+
they're currently in. Hovering over a dot will show you the ID of that work item.
|
23
18
|
</p>
|
19
|
+
<div>
|
20
|
+
The #{color_block '--non-working-days-color'} shaded area indicates the 85%
|
21
|
+
mark for work items that have passed through here; 85% of
|
22
|
+
previous work items left this column while still inside the shaded area. Any work items above
|
23
|
+
the shading are outliers and they are the items that you should pay special attention to.
|
24
|
+
</div>
|
24
25
|
HTML
|
25
26
|
init_configuration_block(block) do
|
26
27
|
grouping_rules do |issue, rule|
|
@@ -87,6 +88,7 @@ class AgingWorkInProgressChart < ChartBase
|
|
87
88
|
'label' => "#{percentage}%",
|
88
89
|
'barPercentage' => 1.0,
|
89
90
|
'categoryPercentage' => 1.0,
|
91
|
+
'backgroundColor' => CssVariable['--aging-work-in-progress-chart-shading-color'],
|
90
92
|
'data' => days_at_percentage_threshold_for_all_columns(percentage: percentage, issues: @issues).drop(1)
|
91
93
|
}
|
92
94
|
end
|
@@ -7,14 +7,22 @@ class AgingWorkTable < ChartBase
|
|
7
7
|
|
8
8
|
def initialize block
|
9
9
|
super()
|
10
|
-
@blocked_icon = '🛑'
|
11
|
-
@expedited_icon = '🔥'
|
12
|
-
@stalled_icon = '🟧'
|
13
10
|
@stalled_threshold = 5
|
14
|
-
@dead_icon = '⬛'
|
15
11
|
@dead_threshold = 45
|
16
12
|
@age_cutoff = 0
|
17
13
|
|
14
|
+
header_text 'Aging Work Table'
|
15
|
+
description_text <<-TEXT
|
16
|
+
<p>
|
17
|
+
This chart shows all active (started but not completed) work, ordered from oldest at the top to
|
18
|
+
newest at the bottom.
|
19
|
+
</p>
|
20
|
+
<p>
|
21
|
+
If there are expedited items that haven't yet started then they're at the bottom of the table.
|
22
|
+
By the very definition of expedited, if we haven't started them already, we'd better get on that.
|
23
|
+
</p>
|
24
|
+
TEXT
|
25
|
+
|
18
26
|
instance_eval(&block) if block
|
19
27
|
end
|
20
28
|
|
@@ -28,7 +36,7 @@ class AgingWorkTable < ChartBase
|
|
28
36
|
end
|
29
37
|
aging_issues += expedited_but_not_started.sort_by(&:created)
|
30
38
|
|
31
|
-
|
39
|
+
wrap_and_render(binding, __FILE__)
|
32
40
|
end
|
33
41
|
|
34
42
|
def select_aging_issues
|
@@ -54,7 +62,7 @@ class AgingWorkTable < ChartBase
|
|
54
62
|
return unless issue.expedited?
|
55
63
|
|
56
64
|
name = issue.raw['fields']['priority']['name']
|
57
|
-
|
65
|
+
color_block '--expedited-color', title: "Expedited: Has a priority of "#{name}""
|
58
66
|
end
|
59
67
|
|
60
68
|
def blocked_text issue
|
@@ -63,27 +71,24 @@ class AgingWorkTable < ChartBase
|
|
63
71
|
|
64
72
|
current = issue.blocked_stalled_changes(end_time: time_range.end)[-1]
|
65
73
|
if current.blocked?
|
66
|
-
|
74
|
+
color_block '--blocked-color', title: current.reasons
|
67
75
|
elsif current.stalled?
|
68
76
|
if current.stalled_days && current.stalled_days > @dead_threshold
|
69
|
-
|
77
|
+
color_block(
|
78
|
+
'--dead-color',
|
70
79
|
title: "Dead? Hasn't had any activity in #{label_days current.stalled_days}. " \
|
71
|
-
'Does anyone still care about this?'
|
72
|
-
icon: @dead_icon
|
80
|
+
'Does anyone still care about this?'
|
73
81
|
)
|
74
82
|
else
|
75
|
-
|
76
|
-
title: current.reasons,
|
77
|
-
icon: @stalled_icon
|
78
|
-
)
|
83
|
+
color_block '--stalled-color', title: current.reasons
|
79
84
|
end
|
80
85
|
end
|
81
86
|
end
|
82
87
|
|
83
88
|
def unmapped_status_text issue
|
84
89
|
icon_span(
|
85
|
-
title: "The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
|
86
|
-
icon: '
|
90
|
+
title: "Not visible: The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
|
91
|
+
icon: ' 👀'
|
87
92
|
)
|
88
93
|
end
|
89
94
|
|
@@ -12,6 +12,7 @@ class Anonymizer
|
|
12
12
|
@all_boards = @project_config.all_boards
|
13
13
|
@possible_statuses = @project_config.possible_statuses
|
14
14
|
@date_adjustment = date_adjustment
|
15
|
+
@file_system = project_config.exporter.file_system
|
15
16
|
end
|
16
17
|
|
17
18
|
def run
|
@@ -20,7 +21,7 @@ class Anonymizer
|
|
20
21
|
# anonymize_issue_statuses
|
21
22
|
anonymize_board_names
|
22
23
|
shift_all_dates unless @date_adjustment.zero?
|
23
|
-
|
24
|
+
@file_system.log 'Anonymize done'
|
24
25
|
end
|
25
26
|
|
26
27
|
def random_phrase
|
@@ -30,7 +31,7 @@ class Anonymizer
|
|
30
31
|
5.times do |i|
|
31
32
|
return RandomWord.phrases.next.tr('_', ' ')
|
32
33
|
rescue # rubocop:disable Style/RescueStandardError We don't care what exception was thrown.
|
33
|
-
|
34
|
+
@file_system.log "Random word blew up on attempt #{i + 1}"
|
34
35
|
end
|
35
36
|
end
|
36
37
|
|
@@ -55,7 +56,7 @@ class Anonymizer
|
|
55
56
|
|
56
57
|
def anonymize_column_names
|
57
58
|
@all_boards.each_key do |board_id|
|
58
|
-
|
59
|
+
@file_system.log "Anonymizing column names for board #{board_id}"
|
59
60
|
|
60
61
|
column_name = 'Column-A'
|
61
62
|
@all_boards[board_id].visible_columns.each do |column|
|
@@ -93,7 +94,7 @@ class Anonymizer
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def anonymize_issue_statuses
|
96
|
-
|
97
|
+
@file_system.log 'Anonymizing issue statuses and status categories'
|
97
98
|
status_name_hash = build_status_name_hash
|
98
99
|
|
99
100
|
@issues.each do |issue|
|
@@ -130,7 +131,7 @@ class Anonymizer
|
|
130
131
|
end
|
131
132
|
|
132
133
|
def shift_all_dates
|
133
|
-
|
134
|
+
@file_system.log "Shifting all dates by #{@date_adjustment} days"
|
134
135
|
@issues.each do |issue|
|
135
136
|
issue.changes.each do |change|
|
136
137
|
change.time = change.time + @date_adjustment
|
@@ -10,17 +10,11 @@ class ChartBase
|
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@chart_colors = {
|
13
|
-
'
|
14
|
-
'
|
15
|
-
'
|
16
|
-
'
|
17
|
-
'
|
18
|
-
'light:Story' => '#90EE90',
|
19
|
-
'light:Task' => '#87CEFA',
|
20
|
-
'light:Bug' => '#ffdab9',
|
21
|
-
'light:Defect' => 'orange',
|
22
|
-
'light:Epic' => '#fafad2',
|
23
|
-
'light:Spike' => '#DDA0DD' # light purple
|
13
|
+
'Story' => CssVariable['--type-story-color'],
|
14
|
+
'Task' => CssVariable['--type-task-color'],
|
15
|
+
'Bug' => CssVariable['--type-bug-color'],
|
16
|
+
'Defect' => CssVariable['--type-bug-color'],
|
17
|
+
'Spike' => CssVariable['--type-spike-color']
|
24
18
|
}
|
25
19
|
@canvas_width = 800
|
26
20
|
@canvas_height = 200
|
@@ -44,11 +38,16 @@ class ChartBase
|
|
44
38
|
erb.result(caller_binding)
|
45
39
|
end
|
46
40
|
|
47
|
-
|
48
|
-
def wrap_and_render caller_binding, file
|
41
|
+
def render_top_text caller_binding
|
49
42
|
result = +''
|
50
43
|
result << "<h1>#{@header_text}</h1>" if @header_text
|
51
44
|
result << ERB.new(@description_text).result(caller_binding) if @description_text
|
45
|
+
end
|
46
|
+
|
47
|
+
# Render the file and then wrap it with standard headers and quality checks.
|
48
|
+
def wrap_and_render caller_binding, file
|
49
|
+
result = +''
|
50
|
+
result << render_top_text(caller_binding)
|
52
51
|
result << render(caller_binding, file)
|
53
52
|
result
|
54
53
|
end
|
@@ -57,8 +56,8 @@ class ChartBase
|
|
57
56
|
@@chart_counter += 1
|
58
57
|
end
|
59
58
|
|
60
|
-
def color_for type
|
61
|
-
@chart_colors[
|
59
|
+
def color_for type:
|
60
|
+
@chart_colors[type] ||= random_color
|
62
61
|
end
|
63
62
|
|
64
63
|
def label_days days
|
@@ -199,15 +198,15 @@ class ChartBase
|
|
199
198
|
color = status_category_color status
|
200
199
|
|
201
200
|
text = is_category ? status.category_name : status.name
|
202
|
-
"<span
|
201
|
+
"<span title='Category: #{status.category_name}'>#{color_block color.name} #{text}</span>"
|
203
202
|
end
|
204
203
|
|
205
204
|
def status_category_color status
|
206
205
|
case status.category_name
|
207
206
|
when nil then 'black'
|
208
|
-
when 'To Do' then '
|
209
|
-
when 'In Progress' then '
|
210
|
-
when 'Done' then '
|
207
|
+
when 'To Do' then CssVariable['--status-category-todo-color']
|
208
|
+
when 'In Progress' then CssVariable['--status-category-inprogress-color']
|
209
|
+
when 'Done' then CssVariable['--status-category-done-color']
|
211
210
|
end
|
212
211
|
end
|
213
212
|
|
@@ -236,4 +235,21 @@ class ChartBase
|
|
236
235
|
@issues = issues.filter_map { |i| @filter_issues_block.call(i) }.uniq
|
237
236
|
puts @issues.collect(&:key).join(', ')
|
238
237
|
end
|
238
|
+
|
239
|
+
def color_block color, title: nil
|
240
|
+
result = +''
|
241
|
+
result << "<div class='color_block' style='background: var(#{color});'"
|
242
|
+
result << " title=#{title.inspect}" if title
|
243
|
+
result << '></div>'
|
244
|
+
result
|
245
|
+
end
|
246
|
+
|
247
|
+
def describe_non_working_days
|
248
|
+
<<-TEXT
|
249
|
+
<div class='p'>
|
250
|
+
The #{color_block '--non-working-days-color'} vertical bars indicate non-working days; weekends
|
251
|
+
and any other holidays mentioned in the configuration.
|
252
|
+
</div>
|
253
|
+
TEXT
|
254
|
+
end
|
239
255
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CssVariable
|
4
|
+
attr_reader :name
|
5
|
+
|
6
|
+
def self.[](name)
|
7
|
+
if name.start_with? '--'
|
8
|
+
CssVariable.new name
|
9
|
+
else
|
10
|
+
name
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize name
|
15
|
+
@name = name
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_json(*_args)
|
19
|
+
"getComputedStyle(document.body).getPropertyValue('#{@name}')"
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_s
|
23
|
+
"var(#{@name})"
|
24
|
+
end
|
25
|
+
|
26
|
+
def inspect
|
27
|
+
"CssVariable['#{@name}']"
|
28
|
+
end
|
29
|
+
|
30
|
+
def == other
|
31
|
+
self.class == other.class && @name == other.name
|
32
|
+
end
|
33
|
+
end
|
@@ -12,20 +12,19 @@ class CycletimeScatterplot < ChartBase
|
|
12
12
|
|
13
13
|
header_text 'Cycletime Scatterplot'
|
14
14
|
description_text <<-HTML
|
15
|
-
<p>
|
15
|
+
<div class="p">
|
16
16
|
This chart shows only completed work and indicates both what day it completed as well as
|
17
17
|
how many days it took to get done. Hovering over a dot will show you the ID of the work item.
|
18
|
-
</
|
19
|
-
<p>
|
20
|
-
The
|
18
|
+
</div>
|
19
|
+
<div class="p">
|
20
|
+
The #{color_block '--cycletime-scatterplot-overall-trendline-color'} line indicates the 85th
|
21
|
+
percentile (<%= overall_percent_line %> days). 85% of all
|
21
22
|
items on this chart fall on or below the line and the remaining 15% are above the line. 85%
|
22
23
|
is a reasonable proxy for "most" so that we can say that based on this data set, we can
|
23
24
|
predict that most work of this type will complete in <%= overall_percent_line %> days or
|
24
25
|
less. The other lines reflect the 85% line for that respective type of work.
|
25
|
-
</
|
26
|
-
|
27
|
-
The gray vertical bars indicate weekends, when theoretically we aren't working.
|
28
|
-
</p>
|
26
|
+
</div>
|
27
|
+
#{ describe_non_working_days }
|
29
28
|
HTML
|
30
29
|
|
31
30
|
init_configuration_block block do
|
@@ -44,7 +43,7 @@ class CycletimeScatterplot < ChartBase
|
|
44
43
|
|
45
44
|
data_sets = create_datasets completed_issues
|
46
45
|
overall_percent_line = calculate_percent_line(completed_issues)
|
47
|
-
@percentage_lines << [overall_percent_line, '
|
46
|
+
@percentage_lines << [overall_percent_line, CssVariable['--cycletime-scatterplot-overall-trendline-color']]
|
48
47
|
|
49
48
|
return "<h1>#{@header_text}</h1>No data matched the selected criteria. Nothing to show." if data_sets.empty?
|
50
49
|
|
@@ -3,23 +3,48 @@
|
|
3
3
|
require 'jirametrics/daily_wip_chart'
|
4
4
|
|
5
5
|
class DailyWipByAgeChart < DailyWipChart
|
6
|
+
def initialize block = nil
|
7
|
+
super(block)
|
8
|
+
|
9
|
+
add_trend_line line_color: '--aging-work-in-progress-by-age-trend-line-color', group_labels: [
|
10
|
+
'Less than a day',
|
11
|
+
'A week or less',
|
12
|
+
'Two weeks or less',
|
13
|
+
'Four weeks or less',
|
14
|
+
'More than four weeks'
|
15
|
+
]
|
16
|
+
end
|
17
|
+
|
6
18
|
def default_header_text
|
7
19
|
'Daily WIP grouped by Age'
|
8
20
|
end
|
9
21
|
|
10
22
|
def default_description_text
|
11
23
|
<<-HTML
|
12
|
-
<p>
|
24
|
+
<div class="p">
|
13
25
|
This chart shows the highest WIP on each given day. The WIP is color coded so you can see
|
14
26
|
how old it is and hovering over the bar will show you exactly which work items it relates
|
15
|
-
to. The
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
27
|
+
to. The #{color_block '--wip-chart-completed-color'}
|
28
|
+
#{color_block '--wip-chart-completed-but-not-started-color'}
|
29
|
+
bars underneath, show how many items completed on that day.
|
30
|
+
</div>
|
31
|
+
<% if @has_completed_but_not_started %>
|
32
|
+
<div class="p">
|
33
|
+
#{color_block '--wip-chart-completed-but-not-started-color'} "Completed but not started"
|
34
|
+
reflects the fact that while we know that it completed that day, we were unable to determine when
|
35
|
+
it had started; it had moved directly from a To Do status to a Done status.
|
36
|
+
The #{color_block '--body-background'} shading at the top shows when they might
|
37
|
+
have been active. Note that the this grouping is approximate as we just don't know for sure.
|
38
|
+
</div>
|
39
|
+
<% end %>
|
40
|
+
#{describe_non_working_days}
|
41
|
+
<div class="p">
|
42
|
+
The #{color_block '--aging-work-in-progress-by-age-trend-line-color'} dashed line is a general trend line.
|
43
|
+
<% if @has_completed_but_not_started %>
|
44
|
+
Note that this trend line only includes items where we know both the start and end times of
|
45
|
+
the work so it may not be as accurate as we hope.
|
46
|
+
<% end %>
|
47
|
+
</div>
|
23
48
|
HTML
|
24
49
|
end
|
25
50
|
|
@@ -31,6 +56,7 @@ class DailyWipByAgeChart < DailyWipChart
|
|
31
56
|
rules.issue_hint = "(age: #{label_days (rules.current_date - started + 1).to_i})" if started
|
32
57
|
|
33
58
|
if stopped && started.nil? # We can't tell when it started
|
59
|
+
@has_completed_but_not_started = true
|
34
60
|
not_started stopped: stopped, rules: rules, created: issue.created.to_date
|
35
61
|
elsif stopped == rules.current_date
|
36
62
|
stopped_today rules: rules
|
@@ -42,11 +68,11 @@ class DailyWipByAgeChart < DailyWipChart
|
|
42
68
|
def not_started stopped:, rules:, created:
|
43
69
|
if stopped == rules.current_date
|
44
70
|
rules.label = 'Completed but not started'
|
45
|
-
rules.color = '
|
71
|
+
rules.color = '--wip-chart-completed-but-not-started-color'
|
46
72
|
rules.group_priority = -1
|
47
73
|
else
|
48
74
|
rules.label = 'Start date unknown'
|
49
|
-
rules.color = '
|
75
|
+
rules.color = '--body-background'
|
50
76
|
rules.group_priority = 11
|
51
77
|
created_days = rules.current_date - created + 1
|
52
78
|
rules.issue_hint = "(created: #{label_days created_days.to_i} earlier, stopped on #{stopped})"
|
@@ -55,7 +81,7 @@ class DailyWipByAgeChart < DailyWipChart
|
|
55
81
|
|
56
82
|
def stopped_today rules:
|
57
83
|
rules.label = 'Completed'
|
58
|
-
rules.color = '
|
84
|
+
rules.color = '--wip-chart-completed-color'
|
59
85
|
rules.group_priority = -2
|
60
86
|
end
|
61
87
|
|
@@ -65,23 +91,23 @@ class DailyWipByAgeChart < DailyWipChart
|
|
65
91
|
case age
|
66
92
|
when 1
|
67
93
|
rules.label = 'Less than a day'
|
68
|
-
rules.color = '
|
94
|
+
rules.color = '--wip-chart-duration-less-than-day-color'
|
69
95
|
rules.group_priority = 10 # Highest is top
|
70
96
|
when 2..7
|
71
97
|
rules.label = 'A week or less'
|
72
|
-
rules.color = '
|
98
|
+
rules.color = '--wip-chart-duration-week-or-less-color'
|
73
99
|
rules.group_priority = 9
|
74
100
|
when 8..14
|
75
101
|
rules.label = 'Two weeks or less'
|
76
|
-
rules.color = '
|
102
|
+
rules.color = '--wip-chart-duration-two-weeks-or-less-color'
|
77
103
|
rules.group_priority = 8
|
78
104
|
when 15..28
|
79
105
|
rules.label = 'Four weeks or less'
|
80
|
-
rules.color = '
|
106
|
+
rules.color = '--wip-chart-duration-four-weeks-or-less-color'
|
81
107
|
rules.group_priority = 7
|
82
108
|
else
|
83
109
|
rules.label = 'More than four weeks'
|
84
|
-
rules.color = '
|
110
|
+
rules.color = '--wip-chart-duration-more-than-four-weeks-color'
|
85
111
|
rules.group_priority = 6
|
86
112
|
end
|
87
113
|
end
|
@@ -9,18 +9,32 @@ class DailyWipByBlockedStalledChart < DailyWipChart
|
|
9
9
|
|
10
10
|
def default_description_text
|
11
11
|
<<-HTML
|
12
|
-
<p>
|
13
|
-
This chart highlights work that is blocked
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
12
|
+
<div class="p">
|
13
|
+
This chart highlights work that is #{color_block '--blocked-color'} blocked,
|
14
|
+
#{color_block '--stalled-color'} stalled, or
|
15
|
+
#{color_block '--wip-chart-active-color'} active on each given day.
|
16
|
+
<ul>
|
17
|
+
<li>#{color_block '--blocked-color'} Blocked could mean that the item has been flagged or it's
|
18
|
+
in a status that is configured as blocked, or it could have a link showing that it is blocked
|
19
|
+
by another item. It all depends how the report has been configured.</li>
|
20
|
+
<li>#{color_block '--stalled-color'} Stalled indicates that there has been no activity on this
|
21
|
+
item in five days.</li>
|
22
|
+
</ul>
|
23
|
+
</div>
|
24
|
+
<div class="p">
|
25
|
+
Note that if an item tracks as both blocked and stalled, it will only show up in the blocked totals.
|
18
26
|
It will not be double counted.
|
19
|
-
</
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
27
|
+
</div>
|
28
|
+
<% if @has_completed_but_not_started %>
|
29
|
+
<div class="p">
|
30
|
+
#{color_block '--wip-chart-completed-but-not-started-color'} "Completed but not started"
|
31
|
+
reflects the fact that while we know that it completed that day, we were unable to determine when
|
32
|
+
it had started; it had moved directly from a To Do status to a Done status.
|
33
|
+
The #{color_block '--body-background'} shading at the top shows when they might
|
34
|
+
have been active. Note that the this grouping is approximate as we just don't know for sure.
|
35
|
+
</div>
|
36
|
+
<% end %>
|
37
|
+
#{describe_non_working_days}
|
24
38
|
HTML
|
25
39
|
end
|
26
40
|
|
@@ -47,30 +61,31 @@ class DailyWipByBlockedStalledChart < DailyWipChart
|
|
47
61
|
stopped_today = stopped_date == rules.current_date
|
48
62
|
|
49
63
|
if stopped_today && started.nil?
|
64
|
+
@has_completed_but_not_started = true
|
50
65
|
rules.label = 'Completed but not started'
|
51
|
-
rules.color = '
|
66
|
+
rules.color = '--wip-chart-completed-but-not-started-color'
|
52
67
|
rules.group_priority = -1
|
53
68
|
elsif stopped_today
|
54
69
|
rules.label = 'Completed'
|
55
|
-
rules.color = '
|
70
|
+
rules.color = '--wip-chart-completed-color'
|
56
71
|
rules.group_priority = -2
|
57
72
|
elsif started.nil?
|
58
73
|
rules.label = 'Start date unknown'
|
59
|
-
rules.color = '
|
74
|
+
rules.color = '--body-background'
|
60
75
|
rules.group_priority = 4
|
61
76
|
elsif change&.blocked?
|
62
77
|
rules.label = 'Blocked'
|
63
|
-
rules.color = '
|
78
|
+
rules.color = '--blocked-color'
|
64
79
|
rules.group_priority = 1
|
65
80
|
rules.issue_hint = "(#{change.reasons})"
|
66
81
|
elsif change&.stalled?
|
67
82
|
rules.label = 'Stalled'
|
68
|
-
rules.color = '
|
83
|
+
rules.color = '--stalled-color'
|
69
84
|
rules.group_priority = 2
|
70
85
|
rules.issue_hint = "(#{change.reasons})"
|
71
86
|
else
|
72
87
|
rules.label = 'Active'
|
73
|
-
rules.color = '
|
88
|
+
rules.color = '--wip-chart-active-color'
|
74
89
|
rules.group_priority = 3
|
75
90
|
end
|
76
91
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'jirametrics/daily_wip_chart'
|
4
|
+
|
5
|
+
class DailyWipByParentChart < DailyWipChart
|
6
|
+
def initialize block
|
7
|
+
super(block)
|
8
|
+
end
|
9
|
+
|
10
|
+
def default_header_text
|
11
|
+
'Daily WIP, grouped by the parent ticket (Epic, Feature, etc)'
|
12
|
+
end
|
13
|
+
|
14
|
+
def default_description_text
|
15
|
+
<<-HTML
|
16
|
+
<div class="p">
|
17
|
+
How much work is in progress, grouped by the parent of the issue. This will give us an
|
18
|
+
indication of how focused we are on higher level objectives. If there are many parent
|
19
|
+
tickets in progress at the same time, either this team has their focus scattered or we
|
20
|
+
aren't doing a good job of
|
21
|
+
<a href="https://improvingflow.com/2024/02/21/slicing-epics.html">splitting those parent
|
22
|
+
tickets</a>. Neither of those is desirable.
|
23
|
+
</div>
|
24
|
+
<div class="p">
|
25
|
+
The #{color_block '--body-background'} shading at the top shows items that don't have a parent
|
26
|
+
at all.
|
27
|
+
</div>
|
28
|
+
#{describe_non_working_days}
|
29
|
+
HTML
|
30
|
+
end
|
31
|
+
|
32
|
+
def default_grouping_rules issue:, rules:
|
33
|
+
parent = issue.parent&.key
|
34
|
+
if parent
|
35
|
+
rules.label = parent
|
36
|
+
else
|
37
|
+
rules.label = 'No parent'
|
38
|
+
rules.group_priority = 1000
|
39
|
+
rules.color = '--body-background'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|