jirametrics 2.0.1 → 2.1.1
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/aging_work_bar_chart.rb +17 -9
- data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
- data/lib/jirametrics/aging_work_table.rb +8 -15
- data/lib/jirametrics/anonymizer.rb +6 -5
- data/lib/jirametrics/chart_base.rb +19 -17
- data/lib/jirametrics/css_variable.rb +33 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +9 -7
- data/lib/jirametrics/daily_wip_by_age_chart.rb +9 -9
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +22 -14
- data/lib/jirametrics/daily_wip_chart.rb +2 -2
- data/lib/jirametrics/dependency_chart.rb +18 -14
- data/lib/jirametrics/downloader.rb +6 -7
- data/lib/jirametrics/examples/aggregated_project.rb +3 -1
- data/lib/jirametrics/examples/standard_project.rb +10 -4
- data/lib/jirametrics/expedited_chart.rb +5 -2
- 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 +1 -1
- 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 +10 -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 +174 -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 +17 -2
- data/lib/jirametrics/issue.rb +3 -2
- data/lib/jirametrics/jira_gateway.rb +1 -1
- data/lib/jirametrics/project_config.rb +13 -18
- data/lib/jirametrics/settings.json +7 -0
- data/lib/jirametrics/sprint_burndown.rb +2 -2
- data/lib/jirametrics/status_collection.rb +1 -1
- data/lib/jirametrics/story_point_accuracy_chart.rb +8 -4
- data/lib/jirametrics/throughput_chart.rb +4 -1
- data/lib/jirametrics.rb +1 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b424b3a1faea57826e0afa0d607b5edea1b824a14af63d2f4fc6b00e4f0c6190
|
4
|
+
data.tar.gz: c4fa2e71b82a118140cbcfaeb8000133917189c5449d7aa03a93d601b2df410b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b361d8f4ab3952de3fbc3b39f70704e6817a157d9166218ad436f80f13ca80d83b26a10a002c08528da298a9e8a961ed699dbb121fc6a7edda75572209c0501f
|
7
|
+
data.tar.gz: 86ce2be29726bf2870d8a8bda0e6fd12184f09946e1cbb04fd66b8666b2ae878ec405e20f2b1658f42ccc6f6b783222853d95cb1603b34158a0ada3e4e37c897
|
@@ -17,10 +17,15 @@ 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
|
-
|
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>
|
24
29
|
</p>
|
25
30
|
<p>
|
26
31
|
The gray backgrounds indicate weekends and the red vertical line indicates the 85% point for all
|
@@ -65,7 +70,7 @@ class AgingWorkBarChart < ChartBase
|
|
65
70
|
issue_label: issue_label,
|
66
71
|
title_label: 'Expedited',
|
67
72
|
stack: 'expedited',
|
68
|
-
color: '
|
73
|
+
color: CssVariable['--expedited-color'],
|
69
74
|
start_date: issue_start_date
|
70
75
|
) { |day| issue.expedited_on_date?(day) }
|
71
76
|
].compact.flatten.each do |data|
|
@@ -109,7 +114,7 @@ class AgingWorkBarChart < ChartBase
|
|
109
114
|
title: "#{issue.type} : #{change.value}"
|
110
115
|
}],
|
111
116
|
backgroundColor: status_category_color(status),
|
112
|
-
borderColor: '
|
117
|
+
borderColor: CssVariable['--aging-work-bar-chart-separator-color'],
|
113
118
|
borderWidth: {
|
114
119
|
top: 0,
|
115
120
|
right: 1,
|
@@ -144,10 +149,13 @@ class AgingWorkBarChart < ChartBase
|
|
144
149
|
end
|
145
150
|
|
146
151
|
def one_block_change_data_set starting_change:, ending_time:, issue_label:, stack:, issue_start_time:
|
147
|
-
color
|
148
|
-
color
|
152
|
+
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['blocked_color']
|
153
|
+
deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['stalled_color']
|
154
|
+
|
155
|
+
color = settings['blocked_color'] || '--blocked-color'
|
156
|
+
color = settings['stalled_color'] || '--stalled-color' if starting_change.stalled?
|
149
157
|
{
|
150
|
-
backgroundColor: color,
|
158
|
+
backgroundColor: CssVariable[color],
|
151
159
|
data: [
|
152
160
|
{
|
153
161
|
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,11 +7,7 @@ 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
|
|
@@ -54,7 +50,7 @@ class AgingWorkTable < ChartBase
|
|
54
50
|
return unless issue.expedited?
|
55
51
|
|
56
52
|
name = issue.raw['fields']['priority']['name']
|
57
|
-
|
53
|
+
color_block '--expedited-color', title: "Expedited: Has a priority of "#{name}""
|
58
54
|
end
|
59
55
|
|
60
56
|
def blocked_text issue
|
@@ -63,27 +59,24 @@ class AgingWorkTable < ChartBase
|
|
63
59
|
|
64
60
|
current = issue.blocked_stalled_changes(end_time: time_range.end)[-1]
|
65
61
|
if current.blocked?
|
66
|
-
|
62
|
+
color_block '--blocked-color', title: current.reasons
|
67
63
|
elsif current.stalled?
|
68
64
|
if current.stalled_days && current.stalled_days > @dead_threshold
|
69
|
-
|
65
|
+
color_block(
|
66
|
+
'--dead-color',
|
70
67
|
title: "Dead? Hasn't had any activity in #{label_days current.stalled_days}. " \
|
71
|
-
'Does anyone still care about this?'
|
72
|
-
icon: @dead_icon
|
68
|
+
'Does anyone still care about this?'
|
73
69
|
)
|
74
70
|
else
|
75
|
-
|
76
|
-
title: current.reasons,
|
77
|
-
icon: @stalled_icon
|
78
|
-
)
|
71
|
+
color_block '--stalled-color', title: current.reasons
|
79
72
|
end
|
80
73
|
end
|
81
74
|
end
|
82
75
|
|
83
76
|
def unmapped_status_text issue
|
84
77
|
icon_span(
|
85
|
-
title: "The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
|
86
|
-
icon: '
|
78
|
+
title: "Not visible: The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
|
79
|
+
icon: ' 👀'
|
87
80
|
)
|
88
81
|
end
|
89
82
|
|
@@ -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
|
@@ -57,8 +51,8 @@ class ChartBase
|
|
57
51
|
@@chart_counter += 1
|
58
52
|
end
|
59
53
|
|
60
|
-
def color_for type
|
61
|
-
@chart_colors[
|
54
|
+
def color_for type:
|
55
|
+
@chart_colors[type] ||= random_color
|
62
56
|
end
|
63
57
|
|
64
58
|
def label_days days
|
@@ -199,15 +193,15 @@ class ChartBase
|
|
199
193
|
color = status_category_color status
|
200
194
|
|
201
195
|
text = is_category ? status.category_name : status.name
|
202
|
-
"<span
|
196
|
+
"<span title='Category: #{status.category_name}'>#{color_block color.name} #{text}</span>"
|
203
197
|
end
|
204
198
|
|
205
199
|
def status_category_color status
|
206
200
|
case status.category_name
|
207
201
|
when nil then 'black'
|
208
|
-
when 'To Do' then '
|
209
|
-
when 'In Progress' then '
|
210
|
-
when 'Done' then '
|
202
|
+
when 'To Do' then CssVariable['--status-category-todo-color']
|
203
|
+
when 'In Progress' then CssVariable['--status-category-inprogress-color']
|
204
|
+
when 'Done' then CssVariable['--status-category-done-color']
|
211
205
|
end
|
212
206
|
end
|
213
207
|
|
@@ -236,4 +230,12 @@ class ChartBase
|
|
236
230
|
@issues = issues.filter_map { |i| @filter_issues_block.call(i) }.uniq
|
237
231
|
puts @issues.collect(&:key).join(', ')
|
238
232
|
end
|
233
|
+
|
234
|
+
def color_block color, title: nil
|
235
|
+
result = +''
|
236
|
+
result << "<div class='color_block' style='background: var(#{color});'"
|
237
|
+
result << " title=#{title.inspect}" if title
|
238
|
+
result << '></div>'
|
239
|
+
result
|
240
|
+
end
|
239
241
|
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
|
@@ -16,16 +16,18 @@ class CycletimeScatterplot < ChartBase
|
|
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
18
|
</p>
|
19
|
-
<
|
20
|
-
The
|
19
|
+
<div style="padding-bottom: 0.7em">
|
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
|
28
|
-
|
26
|
+
</div>
|
27
|
+
<div>
|
28
|
+
The #{color_block '--non-working-days-color'} vertical bars indicate weekends, when theoretically
|
29
|
+
we aren't working.
|
30
|
+
</div>
|
29
31
|
HTML
|
30
32
|
|
31
33
|
init_configuration_block block do
|
@@ -44,7 +46,7 @@ class CycletimeScatterplot < ChartBase
|
|
44
46
|
|
45
47
|
data_sets = create_datasets completed_issues
|
46
48
|
overall_percent_line = calculate_percent_line(completed_issues)
|
47
|
-
@percentage_lines << [overall_percent_line, '
|
49
|
+
@percentage_lines << [overall_percent_line, CssVariable['--cycletime-scatterplot-overall-trendline-color']]
|
48
50
|
|
49
51
|
return "<h1>#{@header_text}</h1>No data matched the selected criteria. Nothing to show." if data_sets.empty?
|
50
52
|
|
@@ -17,7 +17,7 @@ class DailyWipByAgeChart < DailyWipChart
|
|
17
17
|
<p>
|
18
18
|
"Completed without being started" reflects the fact that while we know that it completed
|
19
19
|
that day, we were unable to determine when it had started. These items will show up in
|
20
|
-
white at the top. Note that the
|
20
|
+
white at the top. Note that the this grouping is approximate because we don't know exactly when
|
21
21
|
it started so we're guessing.
|
22
22
|
</p>
|
23
23
|
HTML
|
@@ -42,11 +42,11 @@ class DailyWipByAgeChart < DailyWipChart
|
|
42
42
|
def not_started stopped:, rules:, created:
|
43
43
|
if stopped == rules.current_date
|
44
44
|
rules.label = 'Completed but not started'
|
45
|
-
rules.color = '
|
45
|
+
rules.color = '--wip-chart-completed-but-not-started-color'
|
46
46
|
rules.group_priority = -1
|
47
47
|
else
|
48
48
|
rules.label = 'Start date unknown'
|
49
|
-
rules.color = '
|
49
|
+
rules.color = '--body-background'
|
50
50
|
rules.group_priority = 11
|
51
51
|
created_days = rules.current_date - created + 1
|
52
52
|
rules.issue_hint = "(created: #{label_days created_days.to_i} earlier, stopped on #{stopped})"
|
@@ -55,7 +55,7 @@ class DailyWipByAgeChart < DailyWipChart
|
|
55
55
|
|
56
56
|
def stopped_today rules:
|
57
57
|
rules.label = 'Completed'
|
58
|
-
rules.color = '
|
58
|
+
rules.color = '--wip-chart-completed-color'
|
59
59
|
rules.group_priority = -2
|
60
60
|
end
|
61
61
|
|
@@ -65,23 +65,23 @@ class DailyWipByAgeChart < DailyWipChart
|
|
65
65
|
case age
|
66
66
|
when 1
|
67
67
|
rules.label = 'Less than a day'
|
68
|
-
rules.color = '
|
68
|
+
rules.color = '--wip-chart-duration-less-than-day-color'
|
69
69
|
rules.group_priority = 10 # Highest is top
|
70
70
|
when 2..7
|
71
71
|
rules.label = 'A week or less'
|
72
|
-
rules.color = '
|
72
|
+
rules.color = '--wip-chart-duration-week-or-less-color'
|
73
73
|
rules.group_priority = 9
|
74
74
|
when 8..14
|
75
75
|
rules.label = 'Two weeks or less'
|
76
|
-
rules.color = '
|
76
|
+
rules.color = '--wip-chart-duration-two-weeks-or-less-color'
|
77
77
|
rules.group_priority = 8
|
78
78
|
when 15..28
|
79
79
|
rules.label = 'Four weeks or less'
|
80
|
-
rules.color = '
|
80
|
+
rules.color = '--wip-chart-duration-four-weeks-or-less-color'
|
81
81
|
rules.group_priority = 7
|
82
82
|
else
|
83
83
|
rules.label = 'More than four weeks'
|
84
|
-
rules.color = '
|
84
|
+
rules.color = '--wip-chart-duration-more-than-four-weeks-color'
|
85
85
|
rules.group_priority = 6
|
86
86
|
end
|
87
87
|
end
|
@@ -9,18 +9,26 @@ class DailyWipByBlockedStalledChart < DailyWipChart
|
|
9
9
|
|
10
10
|
def default_description_text
|
11
11
|
<<-HTML
|
12
|
+
<div>
|
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>
|
12
24
|
<p>
|
13
|
-
|
14
|
-
means that the issue has been "flagged". Stalled indicates that the item hasn't had any updates in 5 days.
|
15
|
-
</p>
|
16
|
-
<p>
|
17
|
-
Note that if an item tracks as both blocked and stalled, it will only show up in the flagged totals.
|
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
27
|
</p>
|
20
|
-
<
|
21
|
-
The
|
28
|
+
<div>
|
29
|
+
The #{color_block '--body-background'} shaded section reflects items that have stopped but for which we can't identify the start date. As
|
22
30
|
a result, we are unable to properly show the WIP for these items.
|
23
|
-
</
|
31
|
+
</div>
|
24
32
|
HTML
|
25
33
|
end
|
26
34
|
|
@@ -48,29 +56,29 @@ class DailyWipByBlockedStalledChart < DailyWipChart
|
|
48
56
|
|
49
57
|
if stopped_today && started.nil?
|
50
58
|
rules.label = 'Completed but not started'
|
51
|
-
rules.color = '
|
59
|
+
rules.color = '--wip-chart-completed-but-not-started-color'
|
52
60
|
rules.group_priority = -1
|
53
61
|
elsif stopped_today
|
54
62
|
rules.label = 'Completed'
|
55
|
-
rules.color = '
|
63
|
+
rules.color = '--wip-chart-completed-color'
|
56
64
|
rules.group_priority = -2
|
57
65
|
elsif started.nil?
|
58
66
|
rules.label = 'Start date unknown'
|
59
|
-
rules.color = '
|
67
|
+
rules.color = '--body-background'
|
60
68
|
rules.group_priority = 4
|
61
69
|
elsif change&.blocked?
|
62
70
|
rules.label = 'Blocked'
|
63
|
-
rules.color = '
|
71
|
+
rules.color = '--blocked-color'
|
64
72
|
rules.group_priority = 1
|
65
73
|
rules.issue_hint = "(#{change.reasons})"
|
66
74
|
elsif change&.stalled?
|
67
75
|
rules.label = 'Stalled'
|
68
|
-
rules.color = '
|
76
|
+
rules.color = '--stalled-color'
|
69
77
|
rules.group_priority = 2
|
70
78
|
rules.issue_hint = "(#{change.reasons})"
|
71
79
|
else
|
72
80
|
rules.label = 'Active'
|
73
|
-
rules.color = '
|
81
|
+
rules.color = '--wip-chart-active-color'
|
74
82
|
rules.group_priority = 3
|
75
83
|
end
|
76
84
|
end
|
@@ -102,8 +102,8 @@ class DailyWipChart < ChartBase
|
|
102
102
|
label: grouping_rule.label,
|
103
103
|
data: data,
|
104
104
|
backgroundColor: grouping_rule.color || random_color,
|
105
|
-
borderColor: '
|
106
|
-
borderWidth: grouping_rule.color == '
|
105
|
+
borderColor: CssVariable['--wip-chart-border-color'],
|
106
|
+
borderWidth: grouping_rule.color.to_s == 'var(--body-background)' ? 1 : 0,
|
107
107
|
borderRadius: positive ? 0 : 5
|
108
108
|
}
|
109
109
|
end
|
@@ -84,7 +84,8 @@ class DependencyChart < ChartBase
|
|
84
84
|
result << issue_link.other_issue.key.inspect
|
85
85
|
result << '['
|
86
86
|
result << 'label=' << (link_rules.label || issue_link.label).inspect
|
87
|
-
result << ',color=' << (link_rules.line_color || '
|
87
|
+
result << ',color=' << (link_rules.line_color || 'gray').inspect
|
88
|
+
result << ',fontcolor=' << (link_rules.line_color || 'gray').inspect
|
88
89
|
result << ',dir=both' if link_rules.bidirectional_arrows?
|
89
90
|
result << '];'
|
90
91
|
result
|
@@ -101,12 +102,26 @@ class DependencyChart < ChartBase
|
|
101
102
|
tooltip = "#{issue.key}: #{issue.summary}"
|
102
103
|
result << ",tooltip=#{tooltip[0..80].inspect}"
|
103
104
|
unless issue_rules.color == :none
|
104
|
-
result << %(,style=filled,fillcolor="#{issue_rules.color || color_for(type: issue.type
|
105
|
+
result << %(,style=filled,fillcolor="#{issue_rules.color || color_for(type: issue.type)}")
|
105
106
|
end
|
106
107
|
result << ']'
|
107
108
|
result
|
108
109
|
end
|
109
110
|
|
111
|
+
# This used to pull colours from chart_base but the migration to CSS colours kept breaking
|
112
|
+
# this chart so we moved it here, until we're finished with the rest. TODO: Revisit whether
|
113
|
+
# this can also use customizable CSS colours
|
114
|
+
def color_for type:
|
115
|
+
@chart_colors = {
|
116
|
+
'Story' => '#90EE90',
|
117
|
+
'Task' => '#87CEFA',
|
118
|
+
'Bug' => '#ffdab9',
|
119
|
+
'Defect' => '#ffdab9',
|
120
|
+
'Epic' => '#fafad2',
|
121
|
+
'Spike' => '#DDA0DD' # light purple
|
122
|
+
}[type] ||= random_color
|
123
|
+
end
|
124
|
+
|
110
125
|
def build_dot_graph
|
111
126
|
issue_links = find_links
|
112
127
|
|
@@ -148,6 +163,7 @@ class DependencyChart < ChartBase
|
|
148
163
|
dot_graph = []
|
149
164
|
dot_graph << 'digraph mygraph {'
|
150
165
|
dot_graph << 'rankdir=LR'
|
166
|
+
dot_graph << 'bgcolor="transparent"'
|
151
167
|
|
152
168
|
# Sort the keys so they are proccessed in a deterministic order.
|
153
169
|
visible_issues.values.sort_by(&:key_as_i).each do |issue|
|
@@ -177,18 +193,6 @@ class DependencyChart < ChartBase
|
|
177
193
|
message
|
178
194
|
end
|
179
195
|
|
180
|
-
def default_color_for_issue issue
|
181
|
-
{
|
182
|
-
'Story' => '#90EE90',
|
183
|
-
'Task' => '#87CEFA',
|
184
|
-
'Bug' => '#f08080',
|
185
|
-
'Defect' => '#f08080',
|
186
|
-
'Epic' => '#fafad2',
|
187
|
-
'Spike' => '#7fffd4',
|
188
|
-
'Sub-task' => '#dcdcdc'
|
189
|
-
}[issue.type]
|
190
|
-
end
|
191
|
-
|
192
196
|
def shrink_svg svg
|
193
197
|
scale = 0.8
|
194
198
|
svg.sub(/width="([\d.]+)pt" height="([\d.]+)pt"/) do
|
@@ -10,7 +10,7 @@ class Downloader
|
|
10
10
|
attr_reader :file_system
|
11
11
|
|
12
12
|
# For testing only
|
13
|
-
attr_reader :start_date_in_query
|
13
|
+
attr_reader :start_date_in_query, :board_id_to_filter_id
|
14
14
|
|
15
15
|
def initialize download_config:, file_system:, jira_gateway:
|
16
16
|
@metadata = {}
|
@@ -55,7 +55,7 @@ class Downloader
|
|
55
55
|
|
56
56
|
def log text, both: false
|
57
57
|
@file_system.log text
|
58
|
-
puts text if both
|
58
|
+
puts text if both && !@quiet_mode
|
59
59
|
end
|
60
60
|
|
61
61
|
def find_board_ids
|
@@ -93,7 +93,7 @@ class Downloader
|
|
93
93
|
intercept_jql = @download_config.project_config.settings['intercept_jql']
|
94
94
|
jql = intercept_jql.call jql if intercept_jql
|
95
95
|
|
96
|
-
log " #{jql}"
|
96
|
+
log " JQL: #{jql}"
|
97
97
|
escaped_jql = CGI.escape jql
|
98
98
|
|
99
99
|
max_results = 100
|
@@ -157,10 +157,10 @@ class Downloader
|
|
157
157
|
|
158
158
|
def download_statuses
|
159
159
|
log ' Downloading all statuses', both: true
|
160
|
-
json = @jira_gateway.call_url relative_url:
|
160
|
+
json = @jira_gateway.call_url relative_url: '/rest/api/2/status'
|
161
161
|
|
162
162
|
@file_system.save_json(
|
163
|
-
json: json,
|
163
|
+
json: json,
|
164
164
|
filename: "#{@target_path}#{@download_config.project_config.file_prefix}_statuses.json"
|
165
165
|
)
|
166
166
|
end
|
@@ -282,9 +282,8 @@ class Downloader
|
|
282
282
|
|
283
283
|
# Pick up any issues that had a status change in the range
|
284
284
|
start_date_text = @start_date_in_query.strftime '%Y-%m-%d'
|
285
|
-
end_date_text = today.strftime '%Y-%m-%d'
|
286
285
|
# find_in_range = %((status changed DURING ("#{start_date_text} 00:00","#{end_date_text} 23:59")))
|
287
|
-
find_in_range = %(
|
286
|
+
find_in_range = %(updated >= "#{start_date_text} 00:00")
|
288
287
|
|
289
288
|
segments << "(#{find_in_range} OR #{catch_all})"
|
290
289
|
end
|
@@ -10,9 +10,11 @@
|
|
10
10
|
# single team. For that reason, we look at slightly different things that we would on a single team board.
|
11
11
|
|
12
12
|
class Exporter
|
13
|
-
def aggregated_project name:, project_names:
|
13
|
+
def aggregated_project name:, project_names:, settings: {}
|
14
14
|
project name: name do
|
15
15
|
puts name
|
16
|
+
self.settings.merge! settings
|
17
|
+
|
16
18
|
aggregate do
|
17
19
|
project_names.each do |project_name|
|
18
20
|
include_issues_from project_name
|
@@ -6,13 +6,19 @@
|
|
6
6
|
# See https://github.com/mikebowler/jirametrics/wiki/Examples-folder for more
|
7
7
|
class Exporter
|
8
8
|
def standard_project name:, file_prefix:, ignore_issues: nil, starting_status: nil, boards: {},
|
9
|
-
default_board: nil, anonymize: false
|
9
|
+
default_board: nil, anonymize: false, settings: {}, status_category_mappings: {}
|
10
10
|
|
11
11
|
project name: name do
|
12
12
|
puts name
|
13
13
|
self.anonymize if anonymize
|
14
14
|
|
15
15
|
settings['blocked_link_text'] = ['is blocked by']
|
16
|
+
self.settings.merge! settings
|
17
|
+
|
18
|
+
status_category_mappings.each do |status, category|
|
19
|
+
status_category_mapping status: status, category: category
|
20
|
+
end
|
21
|
+
|
16
22
|
file_prefix file_prefix
|
17
23
|
download do
|
18
24
|
rolling_date_count 90
|
@@ -50,7 +56,7 @@ class Exporter
|
|
50
56
|
type: :header
|
51
57
|
end
|
52
58
|
|
53
|
-
discard_changes_before status_becomes: (starting_status || :backlog)
|
59
|
+
discard_changes_before status_becomes: (starting_status || :backlog) # rubocop:disable Style/RedundantParentheses
|
54
60
|
|
55
61
|
cycletime_scatterplot do
|
56
62
|
show_trend_lines
|
@@ -89,7 +95,7 @@ class Exporter
|
|
89
95
|
TEXT
|
90
96
|
grouping_rules do |issue, rules|
|
91
97
|
rules.label = issue.parent&.key || 'No parent'
|
92
|
-
rules.color = '
|
98
|
+
rules.color = '--body-background' if rules.label == 'No parent'
|
93
99
|
end
|
94
100
|
end
|
95
101
|
expedited_chart
|
@@ -107,7 +113,7 @@ class Exporter
|
|
107
113
|
when 'Sync'
|
108
114
|
rules.use_bidirectional_arrows
|
109
115
|
else
|
110
|
-
# This is a link type that we don't
|
116
|
+
# This is a link type that we don't recognize. Dump it to standard out to draw attention
|
111
117
|
# to it.
|
112
118
|
puts "name=#{link.name}, label=#{link.label}"
|
113
119
|
end
|