jirametrics 2.0.1 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|