jirametrics 2.13 → 2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/jirametrics-mcp +5 -0
- data/lib/jirametrics/aggregate_config.rb +10 -2
- data/lib/jirametrics/aging_work_bar_chart.rb +191 -133
- data/lib/jirametrics/aging_work_in_progress_chart.rb +43 -11
- data/lib/jirametrics/aging_work_table.rb +9 -7
- data/lib/jirametrics/anonymizer.rb +81 -6
- data/lib/jirametrics/atlassian_document_format.rb +101 -97
- data/lib/jirametrics/bar_chart_range.rb +17 -0
- data/lib/jirametrics/blocked_stalled_change.rb +5 -3
- data/lib/jirametrics/board.rb +32 -8
- data/lib/jirametrics/board_config.rb +4 -1
- data/lib/jirametrics/board_feature.rb +14 -0
- data/lib/jirametrics/board_movement_calculator.rb +2 -2
- data/lib/jirametrics/cfd_data_builder.rb +108 -0
- data/lib/jirametrics/change_item.rb +14 -6
- data/lib/jirametrics/chart_base.rb +141 -3
- data/lib/jirametrics/css_variable.rb +1 -1
- data/lib/jirametrics/cumulative_flow_diagram.rb +208 -0
- data/lib/jirametrics/{cycletime_config.rb → cycle_time_config.rb} +21 -4
- data/lib/jirametrics/cycletime_histogram.rb +15 -101
- data/lib/jirametrics/cycletime_scatterplot.rb +17 -83
- data/lib/jirametrics/daily_view.rb +85 -53
- data/lib/jirametrics/daily_wip_by_age_chart.rb +4 -5
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +14 -4
- data/lib/jirametrics/daily_wip_by_parent_chart.rb +4 -2
- data/lib/jirametrics/daily_wip_chart.rb +30 -8
- data/lib/jirametrics/data_quality_report.rb +43 -12
- data/lib/jirametrics/dependency_chart.rb +6 -3
- data/lib/jirametrics/download_config.rb +15 -0
- data/lib/jirametrics/downloader.rb +117 -100
- data/lib/jirametrics/downloader_for_cloud.rb +287 -0
- data/lib/jirametrics/downloader_for_data_center.rb +95 -0
- data/lib/jirametrics/estimate_accuracy_chart.rb +42 -4
- data/lib/jirametrics/examples/aggregated_project.rb +2 -2
- data/lib/jirametrics/examples/standard_project.rb +41 -28
- data/lib/jirametrics/expedited_chart.rb +3 -1
- data/lib/jirametrics/exporter.rb +26 -6
- data/lib/jirametrics/file_config.rb +9 -11
- data/lib/jirametrics/file_system.rb +59 -3
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +5 -1
- data/lib/jirametrics/github_gateway.rb +115 -0
- data/lib/jirametrics/groupable_issue_chart.rb +11 -1
- data/lib/jirametrics/grouping_rules.rb +26 -4
- data/lib/jirametrics/html/aging_work_bar_chart.erb +5 -5
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +3 -1
- data/lib/jirametrics/html/aging_work_table.erb +5 -0
- data/lib/jirametrics/html/collapsible_issues_panel.erb +2 -2
- data/lib/jirametrics/html/cumulative_flow_diagram.erb +503 -0
- data/lib/jirametrics/html/daily_wip_chart.erb +40 -5
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +4 -12
- data/lib/jirametrics/html/expedited_chart.erb +6 -14
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +4 -8
- data/lib/jirametrics/html/index.css +249 -69
- data/lib/jirametrics/html/index.erb +9 -35
- data/lib/jirametrics/html/index.js +164 -0
- data/lib/jirametrics/html/legacy_colors.css +174 -0
- data/lib/jirametrics/html/sprint_burndown.erb +17 -15
- data/lib/jirametrics/html/throughput_chart.erb +42 -11
- data/lib/jirametrics/html/{cycletime_histogram.erb → time_based_histogram.erb} +61 -59
- data/lib/jirametrics/html/{cycletime_scatterplot.erb → time_based_scatterplot.erb} +15 -11
- data/lib/jirametrics/html/wip_by_column_chart.erb +250 -0
- data/lib/jirametrics/html_generator.rb +32 -0
- data/lib/jirametrics/html_report_config.rb +52 -57
- data/lib/jirametrics/issue.rb +304 -101
- data/lib/jirametrics/issue_printer.rb +97 -0
- data/lib/jirametrics/jira_gateway.rb +77 -17
- data/lib/jirametrics/mcp_server.rb +531 -0
- data/lib/jirametrics/project_config.rb +128 -12
- data/lib/jirametrics/pull_request.rb +30 -0
- data/lib/jirametrics/pull_request_cycle_time_histogram.rb +77 -0
- data/lib/jirametrics/pull_request_cycle_time_scatterplot.rb +88 -0
- data/lib/jirametrics/pull_request_review.rb +13 -0
- data/lib/jirametrics/raw_javascript.rb +17 -0
- data/lib/jirametrics/settings.json +5 -1
- data/lib/jirametrics/sprint.rb +12 -0
- data/lib/jirametrics/sprint_burndown.rb +10 -4
- data/lib/jirametrics/status.rb +1 -1
- data/lib/jirametrics/stitcher.rb +81 -0
- data/lib/jirametrics/throughput_by_completed_resolution_chart.rb +22 -0
- data/lib/jirametrics/throughput_chart.rb +73 -23
- data/lib/jirametrics/time_based_histogram.rb +139 -0
- data/lib/jirametrics/time_based_scatterplot.rb +107 -0
- data/lib/jirametrics/wip_by_column_chart.rb +236 -0
- data/lib/jirametrics.rb +83 -69
- metadata +60 -6
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/* Legacy color scheme for jirametrics
|
|
2
|
+
*
|
|
3
|
+
* The default colors were updated to improve accessibility for people with
|
|
4
|
+
* color vision deficiencies. If you prefer the original colors, add this to
|
|
5
|
+
* your project config:
|
|
6
|
+
*
|
|
7
|
+
* setting['include_css'] = './legacy_colors.css'
|
|
8
|
+
*
|
|
9
|
+
* and copy this file to the same directory as your config file.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* Light mode */
|
|
13
|
+
:root,
|
|
14
|
+
html[data-theme="light"] {
|
|
15
|
+
--body-background: white;
|
|
16
|
+
--default-text-color: black;
|
|
17
|
+
--grid-line-color: lightgray;
|
|
18
|
+
--warning-banner: yellow;
|
|
19
|
+
|
|
20
|
+
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
21
|
+
|
|
22
|
+
--non-working-days-color: #F0F0F0;
|
|
23
|
+
--expedited-color: red;
|
|
24
|
+
--blocked-color: #FF7400;
|
|
25
|
+
--stalled-color: orange;
|
|
26
|
+
--dead-color: black;
|
|
27
|
+
|
|
28
|
+
--type-story-color: #4bc14b;
|
|
29
|
+
--type-task-color: blue;
|
|
30
|
+
--type-bug-color: orange;
|
|
31
|
+
--type-spike-color: #9400D3;
|
|
32
|
+
|
|
33
|
+
--status-category-todo-color: gray;
|
|
34
|
+
--status-category-inprogress-color: #2663ff;
|
|
35
|
+
--status-category-done-color: #00ff00;
|
|
36
|
+
--status-category-unknown-color: black;
|
|
37
|
+
|
|
38
|
+
--aging-work-bar-chart-percentage-line-color: red;
|
|
39
|
+
--aging-work-bar-chart-separator-color: white;
|
|
40
|
+
|
|
41
|
+
--throughput_chart_total_line_color: gray;
|
|
42
|
+
|
|
43
|
+
--aging-work-in-progress-chart-shading-color: lightgray;
|
|
44
|
+
--aging-work-in-progress-chart-shading-50-color: #2E8BC0;
|
|
45
|
+
--aging-work-in-progress-chart-shading-85-color: #ADD8E6;
|
|
46
|
+
--aging-work-in-progress-chart-shading-98-color: #FF8A8A;
|
|
47
|
+
--aging-work-in-progress-chart-shading-100-color: #FF2E2E;
|
|
48
|
+
|
|
49
|
+
--aging-work-in-progress-by-age-trend-line-color: gray;
|
|
50
|
+
|
|
51
|
+
--aging-work-table-date-in-jeopardy: yellow;
|
|
52
|
+
--aging-work-table-date-overdue: red;
|
|
53
|
+
|
|
54
|
+
--hierarchy-table-inactive-item-text-color: gray;
|
|
55
|
+
|
|
56
|
+
--wip-chart-completed-color: #00ff00;
|
|
57
|
+
--wip-chart-completed-but-not-started-color: #99FF99;
|
|
58
|
+
--wip-chart-duration-less-than-day-color: #ffef41;
|
|
59
|
+
--wip-chart-duration-week-or-less-color: #dcc900;
|
|
60
|
+
--wip-chart-duration-two-weeks-or-less-color: #dfa000;
|
|
61
|
+
--wip-chart-duration-four-weeks-or-less-color: #eb7200;
|
|
62
|
+
--wip-chart-duration-more-than-four-weeks-color: #e70000;
|
|
63
|
+
--wip-chart-active-color: #326cff;
|
|
64
|
+
--wip-chart-border-color: gray;
|
|
65
|
+
|
|
66
|
+
--wip-by-column-chart-bar-fill-color: #0072B2;
|
|
67
|
+
--wip-by-column-chart-bar-text-color: #ffffff;
|
|
68
|
+
--wip-by-column-chart-limit-line-color: #D55E00;
|
|
69
|
+
--wip-by-column-chart-recommendation-color: #009E73;
|
|
70
|
+
|
|
71
|
+
--estimate-accuracy-chart-completed-fill-color: #00ff00;
|
|
72
|
+
--estimate-accuracy-chart-completed-border-color: green;
|
|
73
|
+
--estimate-accuracy-chart-active-fill-color: #FFCCCB;
|
|
74
|
+
--estimate-accuracy-chart-active-border-color: red;
|
|
75
|
+
|
|
76
|
+
--expedited-chart-no-longer-expedited: gray;
|
|
77
|
+
--expedited-chart-dot-issue-started-color: orange;
|
|
78
|
+
--expedited-chart-dot-issue-stopped-color: green;
|
|
79
|
+
--expedited-chart-dot-expedite-started-color: red;
|
|
80
|
+
--expedited-chart-dot-expedite-stopped-color: green;
|
|
81
|
+
|
|
82
|
+
--sprint-burndown-sprint-color-1: blue;
|
|
83
|
+
--sprint-burndown-sprint-color-2: orange;
|
|
84
|
+
--sprint-burndown-sprint-color-3: green;
|
|
85
|
+
--sprint-burndown-sprint-color-4: red;
|
|
86
|
+
--sprint-burndown-sprint-color-5: brown;
|
|
87
|
+
--sprint-burndown-sprint-color-6: blue; /* wraps back to color-1 (legacy had only 5) */
|
|
88
|
+
--sprint-burndown-sprint-color-7: orange; /* wraps back to color-2 (legacy had only 5) */
|
|
89
|
+
|
|
90
|
+
--sprint-color: lightblue;
|
|
91
|
+
|
|
92
|
+
--daily-view-selected-issue-background: lightgray;
|
|
93
|
+
--daily-view-issue-border: green;
|
|
94
|
+
--daily-view-selected-issue-border: red;
|
|
95
|
+
|
|
96
|
+
--priority-color-highest: #dc2626;
|
|
97
|
+
--priority-color-high: #ea580c;
|
|
98
|
+
--priority-color-medium: #9ca3af;
|
|
99
|
+
--priority-color-low: #0891b2;
|
|
100
|
+
--priority-color-lowest: #64748b;
|
|
101
|
+
--priority-color-notset: gray;
|
|
102
|
+
--priority-color-critical: red;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Dark mode */
|
|
106
|
+
html[data-theme="dark"] {
|
|
107
|
+
--warning-banner: #9F2B00;
|
|
108
|
+
--non-working-days-color: #2f2f2f;
|
|
109
|
+
--type-story-color: #6fb86f;
|
|
110
|
+
--type-task-color: #0021b3;
|
|
111
|
+
--type-bug-color: #bb5603;
|
|
112
|
+
--body-background: #343434;
|
|
113
|
+
--default-text-color: #aaa;
|
|
114
|
+
--grid-line-color: #424242;
|
|
115
|
+
--expedited-color: #b90000;
|
|
116
|
+
--blocked-color: #c75b02;
|
|
117
|
+
--stalled-color: #ae7202;
|
|
118
|
+
--wip-chart-active-color: #2551c1;
|
|
119
|
+
--status-category-inprogress-color: #1c49bb;
|
|
120
|
+
--hierarchy-table-inactive-item-text-color: #939393;
|
|
121
|
+
--wip-by-column-chart-bar-fill-color: #56B4E9;
|
|
122
|
+
--wip-by-column-chart-bar-text-color: #000000;
|
|
123
|
+
--wip-by-column-chart-limit-line-color: #E69F00;
|
|
124
|
+
--wip-by-column-chart-recommendation-color: #2DCB9A;
|
|
125
|
+
--wip-chart-completed-color: #03cb03;
|
|
126
|
+
--wip-chart-duration-less-than-day-color: #d2d988;
|
|
127
|
+
--wip-chart-duration-week-or-less-color: #dfcd00;
|
|
128
|
+
--wip-chart-duration-two-weeks-or-less-color: #cf9400;
|
|
129
|
+
--wip-chart-duration-four-weeks-or-less-color: #c25e00;
|
|
130
|
+
--wip-chart-duration-more-than-four-weeks-color: #8e0000;
|
|
131
|
+
--daily-view-selected-issue-background: #474747;
|
|
132
|
+
--priority-color-highest: #ef4444;
|
|
133
|
+
--priority-color-high: #f97316;
|
|
134
|
+
--priority-color-low: #06b6d4;
|
|
135
|
+
--priority-color-lowest: #94a3b8;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
@media screen and (prefers-color-scheme: dark) {
|
|
139
|
+
:root {
|
|
140
|
+
--warning-banner: #9F2B00;
|
|
141
|
+
--non-working-days-color: #2f2f2f;
|
|
142
|
+
--type-story-color: #6fb86f;
|
|
143
|
+
--type-task-color: #0021b3;
|
|
144
|
+
--type-bug-color: #bb5603;
|
|
145
|
+
--body-background: #343434;
|
|
146
|
+
--default-text-color: #aaa;
|
|
147
|
+
--grid-line-color: #424242;
|
|
148
|
+
--expedited-color: #b90000;
|
|
149
|
+
--blocked-color: #c75b02;
|
|
150
|
+
--stalled-color: #ae7202;
|
|
151
|
+
--dead-color: black;
|
|
152
|
+
--wip-chart-active-color: #2551c1;
|
|
153
|
+
--status-category-inprogress-color: #1c49bb;
|
|
154
|
+
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
155
|
+
--hierarchy-table-inactive-item-text-color: #939393;
|
|
156
|
+
--wip-by-column-chart-bar-fill-color: #56B4E9;
|
|
157
|
+
--wip-by-column-chart-bar-text-color: #000000;
|
|
158
|
+
--wip-by-column-chart-limit-line-color: #E69F00;
|
|
159
|
+
--wip-by-column-chart-recommendation-color: #2DCB9A;
|
|
160
|
+
--wip-chart-completed-color: #03cb03;
|
|
161
|
+
--wip-chart-completed-but-not-started-color: #99FF99;
|
|
162
|
+
--wip-chart-duration-less-than-day-color: #d2d988;
|
|
163
|
+
--wip-chart-duration-week-or-less-color: #dfcd00;
|
|
164
|
+
--wip-chart-duration-two-weeks-or-less-color: #cf9400;
|
|
165
|
+
--wip-chart-duration-four-weeks-or-less-color: #c25e00;
|
|
166
|
+
--wip-chart-duration-more-than-four-weeks-color: #8e0000;
|
|
167
|
+
--daily-view-selected-issue-background: #474747;
|
|
168
|
+
--priority-color-highest: #ef4444;
|
|
169
|
+
--priority-color-high: #f97316;
|
|
170
|
+
--priority-color-medium: #9ca3af;
|
|
171
|
+
--priority-color-low: #06b6d4;
|
|
172
|
+
--priority-color-lowest: #94a3b8;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<h2>Burndown by <%= y_axis_title %></h2>
|
|
2
2
|
|
|
3
|
+
<%= seam_start %>
|
|
3
4
|
<div class="chart">
|
|
4
5
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
5
6
|
</div>
|
|
@@ -21,10 +22,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
21
22
|
time: {
|
|
22
23
|
format: 'YYYY-MM-DD'
|
|
23
24
|
},
|
|
24
|
-
|
|
25
|
-
display: true,
|
|
26
|
-
labelString: 'Date'
|
|
27
|
-
},
|
|
25
|
+
<%= render_axis_title :x %>
|
|
28
26
|
min: "<%= date_range.begin.to_s %>",
|
|
29
27
|
max: "<%= (date_range.end + 1).to_s %>",
|
|
30
28
|
grid: {
|
|
@@ -32,14 +30,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
32
30
|
},
|
|
33
31
|
},
|
|
34
32
|
y: {
|
|
35
|
-
|
|
36
|
-
display: true,
|
|
37
|
-
labelString: 'Items remaining'
|
|
38
|
-
},
|
|
39
|
-
title: {
|
|
40
|
-
display: true,
|
|
41
|
-
text: "<%= y_axis_title %>"
|
|
42
|
-
},
|
|
33
|
+
<%= render_axis_title :y %>
|
|
43
34
|
min: 0.0,
|
|
44
35
|
grid: {
|
|
45
36
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -63,16 +54,22 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
63
54
|
}
|
|
64
55
|
});
|
|
65
56
|
</script>
|
|
57
|
+
<%= seam_end %>
|
|
66
58
|
|
|
67
59
|
<%
|
|
68
60
|
link_id = next_id
|
|
69
61
|
issues_id = next_id
|
|
70
62
|
%>
|
|
71
|
-
|
|
72
|
-
<div
|
|
63
|
+
<section>
|
|
64
|
+
<div class='foldable startFolded'>Show statistics</div>
|
|
65
|
+
<div id="<%= issues_id %>">
|
|
66
|
+
<%= seam_start 'stats_table' %>
|
|
73
67
|
<table class='standard' style="margin-left: 1em;">
|
|
74
68
|
<thead>
|
|
75
69
|
<th>Sprint</th>
|
|
70
|
+
<th>Started</th>
|
|
71
|
+
<th>Completed</th>
|
|
72
|
+
<th>Days</th>
|
|
76
73
|
<th>State</th>
|
|
77
74
|
<th>Started</th>
|
|
78
75
|
<th>Completed</th>
|
|
@@ -85,6 +82,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
85
82
|
<% @summary_stats.keys.sort_by(&:start_time).each do |sprint| %>
|
|
86
83
|
<tr>
|
|
87
84
|
<td><%= sprint.name %></td>
|
|
85
|
+
<td><%= sprint.start_time.to_date %></td>
|
|
86
|
+
<td><%= sprint.completed_time&.to_date %></td>
|
|
87
|
+
<td><%= sprint.day_count %></td>
|
|
88
88
|
<td><%= sprint.raw['state'] %></td>
|
|
89
89
|
<% stats = @summary_stats[sprint] %>
|
|
90
90
|
<td><%= stats.started %></td>
|
|
@@ -101,6 +101,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
101
101
|
<% end %>
|
|
102
102
|
</tbody>
|
|
103
103
|
</table>
|
|
104
|
+
<%= seam_end 'stats_table' %>
|
|
104
105
|
|
|
105
106
|
<p>Legend:
|
|
106
107
|
<ul>
|
|
@@ -109,4 +110,5 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
109
110
|
<% end %>
|
|
110
111
|
</ul>
|
|
111
112
|
</p>
|
|
112
|
-
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</section>
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
<%= seam_start %>
|
|
2
2
|
<div class="chart">
|
|
3
3
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
4
4
|
</div>
|
|
5
5
|
<script>
|
|
6
|
+
if (!Chart.Tooltip.positioners.legendItem) {
|
|
7
|
+
Chart.Tooltip.positioners.legendItem = function(items) {
|
|
8
|
+
return this.chart._legendHoverPosition || Chart.Tooltip.positioners.average.call(this, items);
|
|
9
|
+
};
|
|
10
|
+
}
|
|
6
11
|
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
7
12
|
type: 'scatter',
|
|
8
13
|
data: {
|
|
@@ -20,10 +25,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
20
25
|
time: {
|
|
21
26
|
format: 'YYYY-MM-DD'
|
|
22
27
|
},
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
28
|
+
min: "<%= date_range.begin.to_s %>",
|
|
29
|
+
max: "<%= (date_range.end + 1).to_s %>",
|
|
30
|
+
<%= render_axis_title :x %>
|
|
27
31
|
grid: {
|
|
28
32
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
29
33
|
},
|
|
@@ -32,10 +36,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
32
36
|
scaleLabel: {
|
|
33
37
|
display: true,
|
|
34
38
|
},
|
|
35
|
-
|
|
36
|
-
display: true,
|
|
37
|
-
text: 'Count of items'
|
|
38
|
-
},
|
|
39
|
+
<%= render_axis_title :y %>
|
|
39
40
|
min: 0,
|
|
40
41
|
grid: {
|
|
41
42
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -44,19 +45,49 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
44
45
|
},
|
|
45
46
|
plugins: {
|
|
46
47
|
tooltip: {
|
|
48
|
+
position: 'legendItem',
|
|
47
49
|
callbacks: {
|
|
50
|
+
title: function(contexts) {
|
|
51
|
+
if (contexts[0]?.chart._legendHoverIndex != null) return '';
|
|
52
|
+
},
|
|
48
53
|
label: function(context) {
|
|
49
|
-
|
|
54
|
+
if (context.chart._legendHoverIndex != null) {
|
|
55
|
+
return context.dataset.label_hint || '';
|
|
56
|
+
}
|
|
57
|
+
return context.dataset.data[context.dataIndex].title;
|
|
50
58
|
}
|
|
51
59
|
}
|
|
52
60
|
},
|
|
53
61
|
annotation: {
|
|
54
62
|
annotations: {
|
|
55
63
|
<%= working_days_annotation %>
|
|
64
|
+
<%= date_annotation %>
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
legend: {
|
|
68
|
+
onHover: function(event, legendItem, legend) {
|
|
69
|
+
const chart = legend.chart;
|
|
70
|
+
const dataset = chart.data.datasets[legendItem.datasetIndex];
|
|
71
|
+
if (!dataset?.label_hint) return;
|
|
72
|
+
chart._legendHoverIndex = legendItem.datasetIndex;
|
|
73
|
+
chart._legendHoverPosition = { x: event.x, y: event.y };
|
|
74
|
+
const firstNonZero = dataset.data.findIndex(d => d?.y !== 0);
|
|
75
|
+
if (firstNonZero === -1) return;
|
|
76
|
+
chart.tooltip.setActiveElements(
|
|
77
|
+
[{ datasetIndex: legendItem.datasetIndex, index: firstNonZero }],
|
|
78
|
+
{ x: event.x, y: event.y }
|
|
79
|
+
);
|
|
80
|
+
chart.update();
|
|
81
|
+
},
|
|
82
|
+
onLeave: function(event, legendItem, legend) {
|
|
83
|
+
legend.chart._legendHoverIndex = null;
|
|
84
|
+
legend.chart._legendHoverPosition = null;
|
|
85
|
+
legend.chart.tooltip.setActiveElements([], { x: 0, y: 0 });
|
|
86
|
+
legend.chart.update();
|
|
56
87
|
}
|
|
57
88
|
}
|
|
58
89
|
}
|
|
59
90
|
}
|
|
60
91
|
});
|
|
61
92
|
</script>
|
|
62
|
-
|
|
93
|
+
<%= seam_end %>
|
|
@@ -1,57 +1,8 @@
|
|
|
1
|
+
<div>
|
|
2
|
+
<%= seam_start %>
|
|
1
3
|
<div class="chart">
|
|
2
4
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
3
5
|
</div>
|
|
4
|
-
<%
|
|
5
|
-
if show_stats
|
|
6
|
-
link_id = next_id
|
|
7
|
-
issues_id = next_id
|
|
8
|
-
%>
|
|
9
|
-
[<a id='<%= link_id %>' href="#" onclick='expand_collapse("<%= link_id %>", "<%= issues_id %>"); return false;'>Show details</a>]
|
|
10
|
-
<div id="<%= issues_id %>" style="display: none;">
|
|
11
|
-
<div>
|
|
12
|
-
<table class="standard">
|
|
13
|
-
<tr>
|
|
14
|
-
<th>Issue Type</th>
|
|
15
|
-
<th>Min</th>
|
|
16
|
-
<th>Max</th>
|
|
17
|
-
<th>Avg</th>
|
|
18
|
-
<th>Mode</th>
|
|
19
|
-
<% percentiles.each do |p| %>
|
|
20
|
-
<th><%= p %>th</th>
|
|
21
|
-
<% end %>
|
|
22
|
-
</tr>
|
|
23
|
-
<% the_stats.each do |k, v| %>
|
|
24
|
-
<tr>
|
|
25
|
-
<td><%= k %></td>
|
|
26
|
-
<td style="text-align: right;"><%= v[:min] %></td>
|
|
27
|
-
<td style="text-align: right;"><%= v[:max] %></td>
|
|
28
|
-
<td style="text-align: right;"><%= sprintf('%.2f', v[:average]) %></td>
|
|
29
|
-
<td><%= v[:mode].join(', ') %></td>
|
|
30
|
-
<% percentiles.each do |p| %>
|
|
31
|
-
<td style="text-align: right;"><%= v[:percentiles][p] %></td>
|
|
32
|
-
<% end %>
|
|
33
|
-
</tr>
|
|
34
|
-
<% end %>
|
|
35
|
-
</table>
|
|
36
|
-
</div>
|
|
37
|
-
<div>
|
|
38
|
-
<p>These statistics help understand the <i>"shape"</i> of the cycletime histogram distribution, to help us with predictions.</p>
|
|
39
|
-
<ul>
|
|
40
|
-
<li><b>Min & Max:</b> the observed spread for the data set. Useful to judge how wide the variation is. </li>
|
|
41
|
-
<li><b>Average:</b> the arithmetic mean of the data set. Useful as a <i>"typical representative"</i> of the complete set.</li>
|
|
42
|
-
<li><b>Mode:</b> the most repeated value(s) in the data set. This is the value we're most likely to remember. </li>
|
|
43
|
-
<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>
|
|
44
|
-
<ul>
|
|
45
|
-
<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>
|
|
46
|
-
<li><b>85%</b>: useful to establish service level expectations, accounting for rare events..</li>
|
|
47
|
-
<li><b>98% (or higher)</b>: useful to gauge worst case expectations..</li>
|
|
48
|
-
</ul>
|
|
49
|
-
</ul>
|
|
50
|
-
</div>
|
|
51
|
-
</div>
|
|
52
|
-
<%
|
|
53
|
-
end
|
|
54
|
-
%>
|
|
55
6
|
<script>
|
|
56
7
|
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
57
8
|
{
|
|
@@ -65,22 +16,17 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
65
16
|
x: {
|
|
66
17
|
type: 'linear',
|
|
67
18
|
stacked: true,
|
|
68
|
-
|
|
69
|
-
display: true,
|
|
70
|
-
text: 'Cycletime in days'
|
|
71
|
-
},
|
|
19
|
+
<%= render_axis_title :x %>
|
|
72
20
|
grid: {
|
|
73
21
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
74
22
|
},
|
|
75
23
|
min: 0,
|
|
24
|
+
<%= @max_x_value.nil? ? '' : "max: #{@max_x_value}," %>
|
|
76
25
|
offset: false, // Gets rid of the ugly padding on left.
|
|
77
26
|
},
|
|
78
27
|
y: {
|
|
79
28
|
stacked: true,
|
|
80
|
-
|
|
81
|
-
display: true,
|
|
82
|
-
text: 'Number of items that had that cycletime'
|
|
83
|
-
},
|
|
29
|
+
<%= render_axis_title :y %>
|
|
84
30
|
grid: {
|
|
85
31
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
86
32
|
},
|
|
@@ -119,3 +65,59 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
119
65
|
}
|
|
120
66
|
});
|
|
121
67
|
</script>
|
|
68
|
+
<%= seam_end %>
|
|
69
|
+
|
|
70
|
+
<%= seam_start 'stats_table' %>
|
|
71
|
+
<%
|
|
72
|
+
if show_stats
|
|
73
|
+
link_id = next_id
|
|
74
|
+
issues_id = next_id
|
|
75
|
+
%>
|
|
76
|
+
<div class='foldable' style="padding-left: 1em;">Statistics</div>
|
|
77
|
+
<div id="<%= issues_id %>" style="padding-left: 1em;">
|
|
78
|
+
<div>
|
|
79
|
+
<table class="standard">
|
|
80
|
+
<tr>
|
|
81
|
+
<th>Type</th>
|
|
82
|
+
<th>Min</th>
|
|
83
|
+
<th>Max</th>
|
|
84
|
+
<th>Avg</th>
|
|
85
|
+
<th>Mode</th>
|
|
86
|
+
<% percentiles.each do |p| %>
|
|
87
|
+
<th><%= p %>th</th>
|
|
88
|
+
<% end %>
|
|
89
|
+
</tr>
|
|
90
|
+
<% the_stats.each do |k, v| %>
|
|
91
|
+
<tr>
|
|
92
|
+
<td><%= k %></td>
|
|
93
|
+
<td style="text-align: right;"><%= v[:min] %></td>
|
|
94
|
+
<td style="text-align: right;"><%= v[:max] %></td>
|
|
95
|
+
<td style="text-align: right;"><%= sprintf('%.2f', v[:average]) %></td>
|
|
96
|
+
<td><%= v[:mode].join(', ') %></td>
|
|
97
|
+
<% percentiles.each do |p| %>
|
|
98
|
+
<td style="text-align: right;"><%= v[:percentiles][p] %></td>
|
|
99
|
+
<% end %>
|
|
100
|
+
</tr>
|
|
101
|
+
<% end %>
|
|
102
|
+
</table>
|
|
103
|
+
</div>
|
|
104
|
+
<div>
|
|
105
|
+
<p>These statistics help understand the <i>"shape"</i> of the histogram distribution, to help us with predictions.</p>
|
|
106
|
+
<ul>
|
|
107
|
+
<li><b>Min & Max:</b> the observed spread for the data set. Useful to judge how wide the variation is. </li>
|
|
108
|
+
<li><b>Average:</b> the arithmetic mean of the data set. Useful as a <i>"typical representative"</i> of the complete set.</li>
|
|
109
|
+
<li><b>Mode:</b> the most repeated value(s) in the data set. This is the value we're most likely to remember. </li>
|
|
110
|
+
<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>
|
|
111
|
+
<ul>
|
|
112
|
+
<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>
|
|
113
|
+
<li><b>85%</b>: useful to establish service level expectations, accounting for rare events..</li>
|
|
114
|
+
<li><b>98% (or higher)</b>: useful to gauge worst case expectations..</li>
|
|
115
|
+
</ul>
|
|
116
|
+
</ul>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
<%
|
|
120
|
+
end
|
|
121
|
+
%>
|
|
122
|
+
<%= seam_end 'stats_table' %>
|
|
123
|
+
</div>
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<%= seam_start %>
|
|
1
2
|
<div class="chart">
|
|
2
3
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
3
4
|
</div>
|
|
@@ -10,36 +11,37 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
10
11
|
options: {
|
|
11
12
|
title: {
|
|
12
13
|
display: true,
|
|
13
|
-
text: "
|
|
14
|
+
text: "<%= @header_text %>"
|
|
14
15
|
},
|
|
15
16
|
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
|
16
17
|
scales: {
|
|
17
18
|
x: {
|
|
18
19
|
type: "time",
|
|
19
20
|
scaleLabel: {
|
|
20
|
-
display: true
|
|
21
|
-
labelString: 'Date Completed'
|
|
21
|
+
display: true
|
|
22
22
|
},
|
|
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
|
},
|
|
29
30
|
y: {
|
|
31
|
+
min: 0,
|
|
32
|
+
max: <%= (@highest_y_value * 1.1).ceil %>,
|
|
30
33
|
scaleLabel: {
|
|
31
|
-
display: true
|
|
32
|
-
labelString: 'Days',
|
|
33
|
-
min: 0,
|
|
34
|
-
max: <%= @highest_cycletime %>
|
|
35
|
-
},
|
|
36
|
-
title: {
|
|
37
|
-
display: true,
|
|
38
|
-
text: 'Cycle time in days'
|
|
34
|
+
display: true
|
|
39
35
|
},
|
|
36
|
+
<%= render_axis_title :y %>
|
|
40
37
|
grid: {
|
|
41
38
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
42
39
|
},
|
|
40
|
+
ticks: {
|
|
41
|
+
callback: function(value, index, ticks) {
|
|
42
|
+
return index === ticks.length - 1 ? null : value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
43
45
|
}
|
|
44
46
|
},
|
|
45
47
|
plugins: {
|
|
@@ -54,6 +56,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
54
56
|
annotation: {
|
|
55
57
|
annotations: {
|
|
56
58
|
<%= working_days_annotation %>
|
|
59
|
+
<%= date_annotation %>
|
|
57
60
|
|
|
58
61
|
<% @percentage_lines.each_with_index do |args, index| %>
|
|
59
62
|
<% percent, color = args %>
|
|
@@ -98,3 +101,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
98
101
|
}
|
|
99
102
|
});
|
|
100
103
|
</script>
|
|
104
|
+
<%= seam_end %>
|