jirametrics 2.22 → 2.24
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 +10 -2
- data/lib/jirametrics/aging_work_bar_chart.rb +15 -1
- data/lib/jirametrics/aging_work_table.rb +1 -1
- data/lib/jirametrics/anonymizer.rb +74 -1
- data/lib/jirametrics/atlassian_document_format.rb +104 -93
- data/lib/jirametrics/blocked_stalled_change.rb +5 -3
- data/lib/jirametrics/board.rb +20 -8
- data/lib/jirametrics/board_feature.rb +14 -0
- data/lib/jirametrics/change_item.rb +4 -3
- data/lib/jirametrics/chart_base.rb +87 -1
- data/lib/jirametrics/css_variable.rb +1 -1
- data/lib/jirametrics/{cycletime_config.rb → cycle_time_config.rb} +1 -2
- data/lib/jirametrics/cycletime_histogram.rb +15 -103
- data/lib/jirametrics/cycletime_scatterplot.rb +8 -97
- data/lib/jirametrics/daily_view.rb +32 -9
- data/lib/jirametrics/daily_wip_chart.rb +27 -7
- data/lib/jirametrics/data_quality_report.rb +31 -7
- data/lib/jirametrics/download_config.rb +15 -0
- data/lib/jirametrics/downloader.rb +76 -5
- data/lib/jirametrics/downloader_for_cloud.rb +39 -0
- data/lib/jirametrics/downloader_for_data_center.rb +2 -1
- data/lib/jirametrics/estimate_accuracy_chart.rb +42 -4
- data/lib/jirametrics/examples/aggregated_project.rb +1 -1
- data/lib/jirametrics/examples/standard_project.rb +20 -9
- data/lib/jirametrics/expedited_chart.rb +2 -0
- data/lib/jirametrics/exporter.rb +3 -1
- data/lib/jirametrics/file_system.rb +4 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +2 -0
- data/lib/jirametrics/github_gateway.rb +106 -0
- data/lib/jirametrics/groupable_issue_chart.rb +2 -0
- data/lib/jirametrics/grouping_rules.rb +21 -3
- data/lib/jirametrics/html/aging_work_bar_chart.erb +3 -4
- data/lib/jirametrics/html/aging_work_table.erb +3 -0
- data/lib/jirametrics/html/daily_wip_chart.erb +5 -4
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +2 -12
- data/lib/jirametrics/html/expedited_chart.erb +3 -13
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +2 -8
- data/lib/jirametrics/html/index.css +114 -0
- data/lib/jirametrics/html/index.erb +5 -0
- data/lib/jirametrics/html/index.js +52 -2
- data/lib/jirametrics/html/sprint_burndown.erb +7 -13
- data/lib/jirametrics/html/throughput_chart.erb +5 -8
- data/lib/jirametrics/html/{cycletime_histogram.erb → time_based_histogram.erb} +57 -59
- data/lib/jirametrics/html/{cycletime_scatterplot.erb → time_based_scatterplot.erb} +3 -4
- data/lib/jirametrics/html_report_config.rb +2 -0
- data/lib/jirametrics/issue.rb +84 -95
- data/lib/jirametrics/issue_printer.rb +97 -0
- data/lib/jirametrics/jira_gateway.rb +6 -3
- data/lib/jirametrics/project_config.rb +66 -6
- data/lib/jirametrics/pull_request.rb +30 -0
- data/lib/jirametrics/pull_request_review.rb +13 -0
- data/lib/jirametrics/raw_javascript.rb +4 -0
- data/lib/jirametrics/settings.json +3 -1
- data/lib/jirametrics/sprint_burndown.rb +2 -0
- data/lib/jirametrics/stitcher.rb +2 -1
- data/lib/jirametrics/throughput_chart.rb +7 -1
- data/lib/jirametrics/time_based_histogram.rb +139 -0
- data/lib/jirametrics/time_based_scatterplot.rb +100 -0
- metadata +12 -5
|
@@ -9,10 +9,6 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
9
9
|
datasets: <%= JSON.generate(data_sets) %>
|
|
10
10
|
},
|
|
11
11
|
options: {
|
|
12
|
-
title: {
|
|
13
|
-
display: true,
|
|
14
|
-
text: "Sprint Burndown"
|
|
15
|
-
},
|
|
16
12
|
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
|
17
13
|
scales: {
|
|
18
14
|
x: {
|
|
@@ -21,10 +17,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
21
17
|
display: true,
|
|
22
18
|
labelString: 'Date'
|
|
23
19
|
},
|
|
24
|
-
|
|
25
|
-
display: true,
|
|
26
|
-
text: "Cycletime (days)"
|
|
27
|
-
},
|
|
20
|
+
<%= render_axis_title :x %>
|
|
28
21
|
min: 0,
|
|
29
22
|
grid: {
|
|
30
23
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -39,10 +32,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
39
32
|
display: true,
|
|
40
33
|
labelString: 'Items remaining'
|
|
41
34
|
},
|
|
42
|
-
|
|
43
|
-
display: true,
|
|
44
|
-
text: "<%= @y_axis_label %>"
|
|
45
|
-
},
|
|
35
|
+
<%= render_axis_title :y %>
|
|
46
36
|
min: 0.0,
|
|
47
37
|
grid: {
|
|
48
38
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -20,25 +20,15 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
20
20
|
scales: {
|
|
21
21
|
x: {
|
|
22
22
|
type: "time",
|
|
23
|
-
|
|
24
|
-
display: true,
|
|
25
|
-
labelString: 'Date Completed'
|
|
26
|
-
},
|
|
23
|
+
<%= render_axis_title :x %>
|
|
27
24
|
min: "<%= date_range.begin.to_s %>",
|
|
28
|
-
max: "<%= date_range.end.to_s %>",
|
|
25
|
+
max: "<%= (date_range.end + 1).to_s %>",
|
|
29
26
|
grid: {
|
|
30
27
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
31
28
|
},
|
|
32
29
|
},
|
|
33
30
|
y: {
|
|
34
|
-
|
|
35
|
-
display: true,
|
|
36
|
-
labelString: 'Days'
|
|
37
|
-
},
|
|
38
|
-
title: {
|
|
39
|
-
display: true,
|
|
40
|
-
text: 'Age in days'
|
|
41
|
-
},
|
|
31
|
+
<%= render_axis_title :y %>
|
|
42
32
|
min: 0,
|
|
43
33
|
grid: {
|
|
44
34
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -20,10 +20,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
20
20
|
display: true,
|
|
21
21
|
labelString: 'Days'
|
|
22
22
|
},
|
|
23
|
-
|
|
24
|
-
display: true,
|
|
25
|
-
text: 'Total time (days)'
|
|
26
|
-
},
|
|
23
|
+
<%= render_axis_title :x %>
|
|
27
24
|
grid: {
|
|
28
25
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
29
26
|
},
|
|
@@ -36,10 +33,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
36
33
|
min: 0,
|
|
37
34
|
max: <%= @highest_cycletime %>
|
|
38
35
|
},
|
|
39
|
-
|
|
40
|
-
display: true,
|
|
41
|
-
text: 'Time adding value (days)'
|
|
42
|
-
},
|
|
36
|
+
<%= render_axis_title :y %>
|
|
43
37
|
grid: {
|
|
44
38
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
45
39
|
},
|
|
@@ -214,6 +214,120 @@ div.child_issue {
|
|
|
214
214
|
padding: 0.5em;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
+
/* Dark CSS variables — shared by the media query and the forced dark theme */
|
|
218
|
+
html[data-theme="dark"] {
|
|
219
|
+
--warning-banner: #9F2B00;
|
|
220
|
+
--non-working-days-color: #2f2f2f;
|
|
221
|
+
--type-story-color: #6fb86f;
|
|
222
|
+
--type-task-color: #0021b3;
|
|
223
|
+
--type-bug-color: #bb5603;
|
|
224
|
+
--body-background: #343434;
|
|
225
|
+
--default-text-color: #aaa;
|
|
226
|
+
--grid-line-color: #424242;
|
|
227
|
+
--expedited-color: #b90000;
|
|
228
|
+
--blocked-color: #c75b02;
|
|
229
|
+
--stalled-color: #ae7202;
|
|
230
|
+
--wip-chart-active-color: #2551c1;
|
|
231
|
+
--status-category-inprogress-color: #1c49bb;
|
|
232
|
+
--hierarchy-table-inactive-item-text-color: #939393;
|
|
233
|
+
--wip-chart-completed-color: #03cb03;
|
|
234
|
+
--wip-chart-duration-less-than-day-color: #d2d988;
|
|
235
|
+
--wip-chart-duration-week-or-less-color: #dfcd00;
|
|
236
|
+
--wip-chart-duration-two-weeks-or-less-color: #cf9400;
|
|
237
|
+
--wip-chart-duration-four-weeks-or-less-color: #c25e00;
|
|
238
|
+
--wip-chart-duration-more-than-four-weeks-color: #8e0000;
|
|
239
|
+
--daily-view-selected-issue-background: #474747;
|
|
240
|
+
--priority-color-highest: #ef4444;
|
|
241
|
+
--priority-color-high: #f97316;
|
|
242
|
+
--priority-color-low: #06b6d4;
|
|
243
|
+
--priority-color-lowest: #94a3b8;
|
|
244
|
+
|
|
245
|
+
a[href] { color: #1e8ad6; }
|
|
246
|
+
a[href]:hover { color: #3ba0e6; }
|
|
247
|
+
.chart { background: var(--body-background); }
|
|
248
|
+
table.standard {
|
|
249
|
+
th { background: var(--body-background); }
|
|
250
|
+
tr:nth-child(odd) { background-color: #656565; }
|
|
251
|
+
}
|
|
252
|
+
div.color_block { border: 1px solid lightgray; }
|
|
253
|
+
div.daily_issue {
|
|
254
|
+
.field { color: var(--default-text-color); }
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* Force light mode — overrides the dark media query when user explicitly picks light */
|
|
259
|
+
html[data-theme="light"] {
|
|
260
|
+
--warning-banner: yellow;
|
|
261
|
+
--non-working-days-color: #F0F0F0;
|
|
262
|
+
--type-story-color: #4bc14b;
|
|
263
|
+
--type-task-color: blue;
|
|
264
|
+
--type-bug-color: orange;
|
|
265
|
+
--body-background: white;
|
|
266
|
+
--default-text-color: black;
|
|
267
|
+
--grid-line-color: lightgray;
|
|
268
|
+
--expedited-color: red;
|
|
269
|
+
--blocked-color: #FF7400;
|
|
270
|
+
--stalled-color: orange;
|
|
271
|
+
--wip-chart-active-color: #326cff;
|
|
272
|
+
--status-category-inprogress-color: #2663ff;
|
|
273
|
+
--hierarchy-table-inactive-item-text-color: gray;
|
|
274
|
+
--wip-chart-completed-color: #00ff00;
|
|
275
|
+
--wip-chart-duration-less-than-day-color: #ffef41;
|
|
276
|
+
--wip-chart-duration-week-or-less-color: #dcc900;
|
|
277
|
+
--wip-chart-duration-two-weeks-or-less-color: #dfa000;
|
|
278
|
+
--wip-chart-duration-four-weeks-or-less-color: #eb7200;
|
|
279
|
+
--wip-chart-duration-more-than-four-weeks-color: #e70000;
|
|
280
|
+
--daily-view-selected-issue-background: lightgray;
|
|
281
|
+
--priority-color-highest: #dc2626;
|
|
282
|
+
--priority-color-high: #ea580c;
|
|
283
|
+
--priority-color-low: #0891b2;
|
|
284
|
+
--priority-color-lowest: #64748b;
|
|
285
|
+
|
|
286
|
+
a[href] { color: revert; }
|
|
287
|
+
a[href]:hover { color: revert; }
|
|
288
|
+
.chart { background: white; }
|
|
289
|
+
table.standard {
|
|
290
|
+
th { background: white; }
|
|
291
|
+
tr:nth-child(odd) { background-color: #eee; }
|
|
292
|
+
}
|
|
293
|
+
div.color_block { border: 1px solid black; }
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/* Theme toggle widget */
|
|
297
|
+
#theme-toggle {
|
|
298
|
+
position: fixed;
|
|
299
|
+
top: 0.5rem;
|
|
300
|
+
right: 0.5rem;
|
|
301
|
+
display: flex;
|
|
302
|
+
gap: 2px;
|
|
303
|
+
background: var(--body-background);
|
|
304
|
+
border: 1px solid var(--grid-line-color);
|
|
305
|
+
border-radius: 6px;
|
|
306
|
+
padding: 3px;
|
|
307
|
+
z-index: 1000;
|
|
308
|
+
|
|
309
|
+
button {
|
|
310
|
+
background: none;
|
|
311
|
+
border: none;
|
|
312
|
+
cursor: pointer;
|
|
313
|
+
padding: 2px 6px;
|
|
314
|
+
border-radius: 4px;
|
|
315
|
+
font-size: 1rem;
|
|
316
|
+
color: var(--default-text-color);
|
|
317
|
+
opacity: 0.5;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
button:hover {
|
|
321
|
+
opacity: 1;
|
|
322
|
+
background: var(--grid-line-color);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
button.active {
|
|
326
|
+
opacity: 1;
|
|
327
|
+
background: var(--grid-line-color);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
217
331
|
@media screen and (prefers-color-scheme: dark) {
|
|
218
332
|
:root {
|
|
219
333
|
--warning-banner: #9F2B00;
|
|
@@ -17,6 +17,11 @@
|
|
|
17
17
|
</script>
|
|
18
18
|
</head>
|
|
19
19
|
<body>
|
|
20
|
+
<div id="theme-toggle">
|
|
21
|
+
<button id="theme-btn-system" title="Use system preference">⊙</button>
|
|
22
|
+
<button id="theme-btn-light" title="Light mode">☀</button>
|
|
23
|
+
<button id="theme-btn-dark" title="Dark mode">☾</button>
|
|
24
|
+
</div>
|
|
20
25
|
<noscript>
|
|
21
26
|
<div style="padding: 1em; background: red; color: white; font-size: 2em;">
|
|
22
27
|
Javascript is currently disabled and that means that almost all of the charts in this report won't render. If you've loaded this from a folder on SharePoint then save it locally and load it again.
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
// Apply saved theme immediately (before Chart.js reads CSS variables) so charts
|
|
2
|
+
// initialize with the correct colour scheme.
|
|
3
|
+
(function () {
|
|
4
|
+
const saved = localStorage.getItem('jirametrics:theme');
|
|
5
|
+
if (saved) {
|
|
6
|
+
document.documentElement.setAttribute('data-theme', saved);
|
|
7
|
+
}
|
|
8
|
+
}());
|
|
9
|
+
|
|
1
10
|
function makeFoldable() {
|
|
2
11
|
// Get all elements with the "foldable" class
|
|
3
12
|
const foldableElements = document.querySelectorAll('.foldable');
|
|
@@ -77,16 +86,57 @@ function makeFoldable() {
|
|
|
77
86
|
});
|
|
78
87
|
}
|
|
79
88
|
|
|
89
|
+
function initThemeToggle() {
|
|
90
|
+
const html = document.documentElement;
|
|
91
|
+
const saved = localStorage.getItem('jirametrics:theme');
|
|
92
|
+
if (saved) {
|
|
93
|
+
html.setAttribute('data-theme', saved);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function updateActiveButton(theme) {
|
|
97
|
+
['system', 'light', 'dark'].forEach(t => {
|
|
98
|
+
const btn = document.getElementById(`theme-btn-${t}`);
|
|
99
|
+
if (btn) {
|
|
100
|
+
btn.classList.toggle('active', t === theme);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function setTheme(theme) {
|
|
106
|
+
if (theme === 'system') {
|
|
107
|
+
html.removeAttribute('data-theme');
|
|
108
|
+
localStorage.removeItem('jirametrics:theme');
|
|
109
|
+
} else {
|
|
110
|
+
html.setAttribute('data-theme', theme);
|
|
111
|
+
localStorage.setItem('jirametrics:theme', theme);
|
|
112
|
+
}
|
|
113
|
+
updateActiveButton(theme);
|
|
114
|
+
location.reload();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
updateActiveButton(saved || 'system');
|
|
118
|
+
|
|
119
|
+
['system', 'light', 'dark'].forEach(theme => {
|
|
120
|
+
const btn = document.getElementById(`theme-btn-${theme}`);
|
|
121
|
+
if (btn) {
|
|
122
|
+
btn.addEventListener('click', () => setTheme(theme));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
80
127
|
// Auto-initialize when DOM is loaded
|
|
81
128
|
document.addEventListener('DOMContentLoaded', function() {
|
|
82
129
|
makeFoldable();
|
|
130
|
+
initThemeToggle();
|
|
83
131
|
});
|
|
84
132
|
|
|
85
133
|
|
|
86
134
|
// If we switch between light/dark mode then force a refresh so all charts will redraw correctly
|
|
87
|
-
// in the other colour scheme.
|
|
135
|
+
// in the other colour scheme. Skip reload if a manual theme override is set.
|
|
88
136
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
89
|
-
|
|
137
|
+
if (!document.documentElement.hasAttribute('data-theme')) {
|
|
138
|
+
location.reload();
|
|
139
|
+
}
|
|
90
140
|
})
|
|
91
141
|
|
|
92
142
|
// Draw a diagonal pattern to highlight sections of a bar chart. Based on code found at:
|
|
@@ -22,10 +22,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
22
22
|
time: {
|
|
23
23
|
format: 'YYYY-MM-DD'
|
|
24
24
|
},
|
|
25
|
-
|
|
26
|
-
display: true,
|
|
27
|
-
labelString: 'Date'
|
|
28
|
-
},
|
|
25
|
+
<%= render_axis_title :x %>
|
|
29
26
|
min: "<%= date_range.begin.to_s %>",
|
|
30
27
|
max: "<%= (date_range.end + 1).to_s %>",
|
|
31
28
|
grid: {
|
|
@@ -33,14 +30,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
33
30
|
},
|
|
34
31
|
},
|
|
35
32
|
y: {
|
|
36
|
-
|
|
37
|
-
display: true,
|
|
38
|
-
labelString: 'Items remaining'
|
|
39
|
-
},
|
|
40
|
-
title: {
|
|
41
|
-
display: true,
|
|
42
|
-
text: "<%= y_axis_title %>"
|
|
43
|
-
},
|
|
33
|
+
<%= render_axis_title :y %>
|
|
44
34
|
min: 0.0,
|
|
45
35
|
grid: {
|
|
46
36
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -77,7 +67,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
77
67
|
<table class='standard' style="margin-left: 1em;">
|
|
78
68
|
<thead>
|
|
79
69
|
<th>Sprint</th>
|
|
80
|
-
<th>
|
|
70
|
+
<th>Started</th>
|
|
71
|
+
<th>Completed</th>
|
|
72
|
+
<th>Days</th>
|
|
81
73
|
<th>State</th>
|
|
82
74
|
<th>Started</th>
|
|
83
75
|
<th>Completed</th>
|
|
@@ -90,6 +82,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
90
82
|
<% @summary_stats.keys.sort_by(&:start_time).each do |sprint| %>
|
|
91
83
|
<tr>
|
|
92
84
|
<td><%= sprint.name %></td>
|
|
85
|
+
<td><%= sprint.start_time.to_date %></td>
|
|
86
|
+
<td><%= sprint.completed_time&.to_date %></td>
|
|
93
87
|
<td><%= sprint.day_count %></td>
|
|
94
88
|
<td><%= sprint.raw['state'] %></td>
|
|
95
89
|
<% stats = @summary_stats[sprint] %>
|
|
@@ -20,10 +20,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
20
20
|
time: {
|
|
21
21
|
format: 'YYYY-MM-DD'
|
|
22
22
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
23
|
+
min: "<%= date_range.begin.to_s %>",
|
|
24
|
+
max: "<%= (date_range.end + 1).to_s %>",
|
|
25
|
+
<%= render_axis_title :x %>
|
|
27
26
|
grid: {
|
|
28
27
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
29
28
|
},
|
|
@@ -32,10 +31,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
32
31
|
scaleLabel: {
|
|
33
32
|
display: true,
|
|
34
33
|
},
|
|
35
|
-
|
|
36
|
-
display: true,
|
|
37
|
-
text: 'Count of items'
|
|
38
|
-
},
|
|
34
|
+
<%= render_axis_title :y %>
|
|
39
35
|
min: 0,
|
|
40
36
|
grid: {
|
|
41
37
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -53,6 +49,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
53
49
|
annotation: {
|
|
54
50
|
annotations: {
|
|
55
51
|
<%= working_days_annotation %>
|
|
52
|
+
<%= date_annotation %>
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
}
|
|
@@ -2,57 +2,6 @@
|
|
|
2
2
|
<div class="chart">
|
|
3
3
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
4
4
|
</div>
|
|
5
|
-
<%
|
|
6
|
-
if show_stats
|
|
7
|
-
link_id = next_id
|
|
8
|
-
issues_id = next_id
|
|
9
|
-
%>
|
|
10
|
-
<div class='foldable' style="padding-left: 1em;">Statistics</div>
|
|
11
|
-
<div id="<%= issues_id %>" style="padding-left: 1em;">
|
|
12
|
-
<div>
|
|
13
|
-
<table class="standard">
|
|
14
|
-
<tr>
|
|
15
|
-
<th>Issue Type</th>
|
|
16
|
-
<th>Min</th>
|
|
17
|
-
<th>Max</th>
|
|
18
|
-
<th>Avg</th>
|
|
19
|
-
<th>Mode</th>
|
|
20
|
-
<% percentiles.each do |p| %>
|
|
21
|
-
<th><%= p %>th</th>
|
|
22
|
-
<% end %>
|
|
23
|
-
</tr>
|
|
24
|
-
<% the_stats.each do |k, v| %>
|
|
25
|
-
<tr>
|
|
26
|
-
<td><%= k %></td>
|
|
27
|
-
<td style="text-align: right;"><%= v[:min] %></td>
|
|
28
|
-
<td style="text-align: right;"><%= v[:max] %></td>
|
|
29
|
-
<td style="text-align: right;"><%= sprintf('%.2f', v[:average]) %></td>
|
|
30
|
-
<td><%= v[:mode].join(', ') %></td>
|
|
31
|
-
<% percentiles.each do |p| %>
|
|
32
|
-
<td style="text-align: right;"><%= v[:percentiles][p] %></td>
|
|
33
|
-
<% end %>
|
|
34
|
-
</tr>
|
|
35
|
-
<% end %>
|
|
36
|
-
</table>
|
|
37
|
-
</div>
|
|
38
|
-
<div>
|
|
39
|
-
<p>These statistics help understand the <i>"shape"</i> of the cycletime histogram distribution, to help us with predictions.</p>
|
|
40
|
-
<ul>
|
|
41
|
-
<li><b>Min & Max:</b> the observed spread for the data set. Useful to judge how wide the variation is. </li>
|
|
42
|
-
<li><b>Average:</b> the arithmetic mean of the data set. Useful as a <i>"typical representative"</i> of the complete set.</li>
|
|
43
|
-
<li><b>Mode:</b> the most repeated value(s) in the data set. This is the value we're most likely to remember. </li>
|
|
44
|
-
<li><b>Percentiles:</b> they partition the data set. If X is the Nth percentile, it means that N% of cycletime values are X or less. Typical percentiles of interest are:</li>
|
|
45
|
-
<ul>
|
|
46
|
-
<li><b>50%</b>: also known as the <b>Median</b>. Useful to establish short feedback loops, to monitor that it's not drifting to the right.</li>
|
|
47
|
-
<li><b>85%</b>: useful to establish service level expectations, accounting for rare events..</li>
|
|
48
|
-
<li><b>98% (or higher)</b>: useful to gauge worst case expectations..</li>
|
|
49
|
-
</ul>
|
|
50
|
-
</ul>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
<%
|
|
54
|
-
end
|
|
55
|
-
%>
|
|
56
5
|
<script>
|
|
57
6
|
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
58
7
|
{
|
|
@@ -66,22 +15,17 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
66
15
|
x: {
|
|
67
16
|
type: 'linear',
|
|
68
17
|
stacked: true,
|
|
69
|
-
|
|
70
|
-
display: true,
|
|
71
|
-
text: 'Cycletime in days'
|
|
72
|
-
},
|
|
18
|
+
<%= render_axis_title :x %>
|
|
73
19
|
grid: {
|
|
74
20
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
75
21
|
},
|
|
76
22
|
min: 0,
|
|
23
|
+
<%= @max_x_value.nil? ? '' : "max: #{@max_x_value}," %>
|
|
77
24
|
offset: false, // Gets rid of the ugly padding on left.
|
|
78
25
|
},
|
|
79
26
|
y: {
|
|
80
27
|
stacked: true,
|
|
81
|
-
|
|
82
|
-
display: true,
|
|
83
|
-
text: 'Number of items that had that cycletime'
|
|
84
|
-
},
|
|
28
|
+
<%= render_axis_title :y %>
|
|
85
29
|
grid: {
|
|
86
30
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
87
31
|
},
|
|
@@ -121,3 +65,57 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
121
65
|
});
|
|
122
66
|
</script>
|
|
123
67
|
<%= seam_end %>
|
|
68
|
+
|
|
69
|
+
<%= seam_start 'stats_table' %>
|
|
70
|
+
<%
|
|
71
|
+
if show_stats
|
|
72
|
+
link_id = next_id
|
|
73
|
+
issues_id = next_id
|
|
74
|
+
%>
|
|
75
|
+
<div class='foldable' style="padding-left: 1em;">Statistics</div>
|
|
76
|
+
<div id="<%= issues_id %>" style="padding-left: 1em;">
|
|
77
|
+
<div>
|
|
78
|
+
<table class="standard">
|
|
79
|
+
<tr>
|
|
80
|
+
<th>Type</th>
|
|
81
|
+
<th>Min</th>
|
|
82
|
+
<th>Max</th>
|
|
83
|
+
<th>Avg</th>
|
|
84
|
+
<th>Mode</th>
|
|
85
|
+
<% percentiles.each do |p| %>
|
|
86
|
+
<th><%= p %>th</th>
|
|
87
|
+
<% end %>
|
|
88
|
+
</tr>
|
|
89
|
+
<% the_stats.each do |k, v| %>
|
|
90
|
+
<tr>
|
|
91
|
+
<td><%= k %></td>
|
|
92
|
+
<td style="text-align: right;"><%= v[:min] %></td>
|
|
93
|
+
<td style="text-align: right;"><%= v[:max] %></td>
|
|
94
|
+
<td style="text-align: right;"><%= sprintf('%.2f', v[:average]) %></td>
|
|
95
|
+
<td><%= v[:mode].join(', ') %></td>
|
|
96
|
+
<% percentiles.each do |p| %>
|
|
97
|
+
<td style="text-align: right;"><%= v[:percentiles][p] %></td>
|
|
98
|
+
<% end %>
|
|
99
|
+
</tr>
|
|
100
|
+
<% end %>
|
|
101
|
+
</table>
|
|
102
|
+
</div>
|
|
103
|
+
<div>
|
|
104
|
+
<p>These statistics help understand the <i>"shape"</i> of the histogram distribution, to help us with predictions.</p>
|
|
105
|
+
<ul>
|
|
106
|
+
<li><b>Min & Max:</b> the observed spread for the data set. Useful to judge how wide the variation is. </li>
|
|
107
|
+
<li><b>Average:</b> the arithmetic mean of the data set. Useful as a <i>"typical representative"</i> of the complete set.</li>
|
|
108
|
+
<li><b>Mode:</b> the most repeated value(s) in the data set. This is the value we're most likely to remember. </li>
|
|
109
|
+
<li><b>Percentiles:</b> they partition the data set. If X is the Nth percentile, it means that N% of values are X or less. Typical percentiles of interest are:</li>
|
|
110
|
+
<ul>
|
|
111
|
+
<li><b>50%</b>: also known as the <b>Median</b>. Useful to establish short feedback loops, to monitor that it's not drifting to the right.</li>
|
|
112
|
+
<li><b>85%</b>: useful to establish service level expectations, accounting for rare events..</li>
|
|
113
|
+
<li><b>98% (or higher)</b>: useful to gauge worst case expectations..</li>
|
|
114
|
+
</ul>
|
|
115
|
+
</ul>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
<%
|
|
119
|
+
end
|
|
120
|
+
%>
|
|
121
|
+
<%= seam_end 'stats_table' %>
|
|
@@ -23,6 +23,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
23
23
|
grid: {
|
|
24
24
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
25
25
|
},
|
|
26
|
+
<%= render_axis_title :x %>
|
|
26
27
|
min: "<%= date_range.begin.to_s %>",
|
|
27
28
|
max: "<%= (date_range.end + 1).to_s %>"
|
|
28
29
|
},
|
|
@@ -32,10 +33,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
32
33
|
min: 0,
|
|
33
34
|
max: <%= @highest_y_value %>
|
|
34
35
|
},
|
|
35
|
-
|
|
36
|
-
display: true,
|
|
37
|
-
text: '<%= y_axis_heading %>'
|
|
38
|
-
},
|
|
36
|
+
<%= render_axis_title :y %>
|
|
39
37
|
grid: {
|
|
40
38
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
41
39
|
},
|
|
@@ -53,6 +51,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
53
51
|
annotation: {
|
|
54
52
|
annotations: {
|
|
55
53
|
<%= working_days_annotation %>
|
|
54
|
+
<%= date_annotation %>
|
|
56
55
|
|
|
57
56
|
<% @percentage_lines.each_with_index do |args, index| %>
|
|
58
57
|
<% percent, color = args %>
|
|
@@ -41,6 +41,7 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
41
41
|
deprecated_warning: 'Renamed to estimate_accuracy_chart. Please use that one', deprecated_date: '2024-05-23'
|
|
42
42
|
|
|
43
43
|
def initialize file_config:, block:
|
|
44
|
+
super()
|
|
44
45
|
@file_config = file_config
|
|
45
46
|
@block = block
|
|
46
47
|
@sections = [] # Where we store the chunks of text that will be assembled into the HTML
|
|
@@ -147,6 +148,7 @@ class HtmlReportConfig < HtmlGenerator
|
|
|
147
148
|
chart.all_boards = project_config.all_boards
|
|
148
149
|
chart.board_id = find_board_id
|
|
149
150
|
chart.holiday_dates = project_config.exporter.holiday_dates
|
|
151
|
+
chart.fix_versions = project_config.fix_versions
|
|
150
152
|
|
|
151
153
|
time_range = @file_config.project_config.time_range
|
|
152
154
|
chart.date_range = time_range.begin.to_date..time_range.end.to_date
|