jirametrics 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/jirametrics +4 -0
- data/lib/jirametrics/aggregate_config.rb +89 -0
- data/lib/jirametrics/aging_work_bar_chart.rb +235 -0
- data/lib/jirametrics/aging_work_in_progress_chart.rb +148 -0
- data/lib/jirametrics/aging_work_table.rb +149 -0
- data/lib/jirametrics/anonymizer.rb +186 -0
- data/lib/jirametrics/blocked_stalled_change.rb +43 -0
- data/lib/jirametrics/board.rb +85 -0
- data/lib/jirametrics/board_column.rb +14 -0
- data/lib/jirametrics/board_config.rb +31 -0
- data/lib/jirametrics/change_item.rb +80 -0
- data/lib/jirametrics/chart_base.rb +239 -0
- data/lib/jirametrics/columns_config.rb +42 -0
- data/lib/jirametrics/cycletime_config.rb +69 -0
- data/lib/jirametrics/cycletime_histogram.rb +74 -0
- data/lib/jirametrics/cycletime_scatterplot.rb +128 -0
- data/lib/jirametrics/daily_wip_by_age_chart.rb +88 -0
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +77 -0
- data/lib/jirametrics/daily_wip_chart.rb +123 -0
- data/lib/jirametrics/data_quality_report.rb +278 -0
- data/lib/jirametrics/dependency_chart.rb +217 -0
- data/lib/jirametrics/discard_changes_before.rb +37 -0
- data/lib/jirametrics/download_config.rb +41 -0
- data/lib/jirametrics/downloader.rb +337 -0
- data/lib/jirametrics/examples/aggregated_project.rb +36 -0
- data/lib/jirametrics/examples/standard_project.rb +111 -0
- data/lib/jirametrics/expedited_chart.rb +169 -0
- data/lib/jirametrics/experimental/generator.rb +209 -0
- data/lib/jirametrics/experimental/info.rb +77 -0
- data/lib/jirametrics/exporter.rb +127 -0
- data/lib/jirametrics/file_config.rb +119 -0
- data/lib/jirametrics/fix_version.rb +21 -0
- data/lib/jirametrics/groupable_issue_chart.rb +44 -0
- data/lib/jirametrics/grouping_rules.rb +13 -0
- data/lib/jirametrics/hierarchy_table.rb +31 -0
- data/lib/jirametrics/html/aging_work_bar_chart.erb +72 -0
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +52 -0
- data/lib/jirametrics/html/aging_work_table.erb +60 -0
- data/lib/jirametrics/html/collapsible_issues_panel.erb +32 -0
- data/lib/jirametrics/html/cycletime_histogram.erb +41 -0
- data/lib/jirametrics/html/cycletime_scatterplot.erb +103 -0
- data/lib/jirametrics/html/daily_wip_chart.erb +63 -0
- data/lib/jirametrics/html/data_quality_report.erb +126 -0
- data/lib/jirametrics/html/expedited_chart.erb +67 -0
- data/lib/jirametrics/html/hierarchy_table.erb +29 -0
- data/lib/jirametrics/html/index.erb +66 -0
- data/lib/jirametrics/html/sprint_burndown.erb +116 -0
- data/lib/jirametrics/html/story_point_accuracy_chart.erb +57 -0
- data/lib/jirametrics/html/throughput_chart.erb +65 -0
- data/lib/jirametrics/html_report_config.rb +217 -0
- data/lib/jirametrics/issue.rb +521 -0
- data/lib/jirametrics/issue_link.rb +60 -0
- data/lib/jirametrics/json_file_loader.rb +9 -0
- data/lib/jirametrics/project_config.rb +442 -0
- data/lib/jirametrics/rules.rb +34 -0
- data/lib/jirametrics/self_or_issue_dispatcher.rb +15 -0
- data/lib/jirametrics/sprint.rb +43 -0
- data/lib/jirametrics/sprint_burndown.rb +335 -0
- data/lib/jirametrics/sprint_issue_change_data.rb +31 -0
- data/lib/jirametrics/status.rb +26 -0
- data/lib/jirametrics/status_collection.rb +67 -0
- data/lib/jirametrics/story_point_accuracy_chart.rb +139 -0
- data/lib/jirametrics/throughput_chart.rb +91 -0
- data/lib/jirametrics/tree_organizer.rb +96 -0
- data/lib/jirametrics/trend_line_calculator.rb +74 -0
- data/lib/jirametrics.rb +85 -0
- metadata +167 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
6
|
+
{
|
7
|
+
type: 'bar',
|
8
|
+
data: {
|
9
|
+
datasets: <%= JSON.generate(data_sets) %>
|
10
|
+
},
|
11
|
+
options: {
|
12
|
+
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
13
|
+
indexAxis: 'y', // Make the bars horizontal
|
14
|
+
scales: {
|
15
|
+
x: {
|
16
|
+
type: 'time',
|
17
|
+
min: '<%= @date_range.begin.to_s %>',
|
18
|
+
max: '<%= (@date_range.end ).to_s %>',
|
19
|
+
stacked: false,
|
20
|
+
title: {
|
21
|
+
display: false
|
22
|
+
}
|
23
|
+
},
|
24
|
+
y: {
|
25
|
+
stacked: true,
|
26
|
+
position: 'right',
|
27
|
+
ticks: {
|
28
|
+
display: true
|
29
|
+
}
|
30
|
+
}
|
31
|
+
},
|
32
|
+
plugins: {
|
33
|
+
annotation: {
|
34
|
+
annotations: {
|
35
|
+
<% holidays.each_with_index do |range, index| %>
|
36
|
+
holiday<%= index %>: {
|
37
|
+
drawTime: 'beforeDraw',
|
38
|
+
type: 'box',
|
39
|
+
xMin: '<%= range.begin %>T00:00:00',
|
40
|
+
xMax: '<%= range.end %>T23:59:59',
|
41
|
+
backgroundColor: '#F0F0F0',
|
42
|
+
borderColor: '#F0F0F0'
|
43
|
+
},
|
44
|
+
<% end %>
|
45
|
+
|
46
|
+
<% if percentage_line_x %>
|
47
|
+
line: {
|
48
|
+
type: 'line',
|
49
|
+
xMin: '<%= percentage_line_x %>',
|
50
|
+
xMax: '<%= percentage_line_x %>',
|
51
|
+
borderColor: 'red',
|
52
|
+
borderWidth: 1,
|
53
|
+
drawTime: 'afterDraw'
|
54
|
+
}
|
55
|
+
<% end %>
|
56
|
+
}
|
57
|
+
},
|
58
|
+
legend: {
|
59
|
+
display: false
|
60
|
+
},
|
61
|
+
tooltip: {
|
62
|
+
callbacks: {
|
63
|
+
label: function(context) {
|
64
|
+
return context.dataset.data[context.dataIndex].title
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
});
|
71
|
+
</script>
|
72
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
new Chart(document.getElementById(<%= chart_id.inspect %>).getContext('2d'),
|
6
|
+
{
|
7
|
+
type: 'bar',
|
8
|
+
data: {
|
9
|
+
labels: [<%= column_headings.collect(&:inspect).join(',') %>],
|
10
|
+
datasets: <%= JSON.generate(data_sets) %>
|
11
|
+
},
|
12
|
+
options: {
|
13
|
+
title: {
|
14
|
+
display: true,
|
15
|
+
text: "Aging work in progress"
|
16
|
+
},
|
17
|
+
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
18
|
+
scales: {
|
19
|
+
x: {
|
20
|
+
scaleLabel: {
|
21
|
+
display: true,
|
22
|
+
labelString: 'Date Completed'
|
23
|
+
}
|
24
|
+
},
|
25
|
+
y: {
|
26
|
+
scaleLabel: {
|
27
|
+
display: true,
|
28
|
+
labelString: 'Days'
|
29
|
+
},
|
30
|
+
title: {
|
31
|
+
display: true,
|
32
|
+
text: 'Age in days'
|
33
|
+
},
|
34
|
+
}
|
35
|
+
},
|
36
|
+
plugins: {
|
37
|
+
tooltip: {
|
38
|
+
callbacks: {
|
39
|
+
label: function(context) {
|
40
|
+
if( typeof(context.dataset.data[context.dataIndex]) == "number" ) {
|
41
|
+
return "85% of the issues, leave this column in "+context.dataset.data[context.dataIndex]+" days";
|
42
|
+
}
|
43
|
+
else {
|
44
|
+
return context.dataset.data[context.dataIndex].title
|
45
|
+
}
|
46
|
+
}
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
50
|
+
}
|
51
|
+
});
|
52
|
+
</script>
|
@@ -0,0 +1,60 @@
|
|
1
|
+
<h1>Aging Work Table</h1>
|
2
|
+
<p>
|
3
|
+
This chart shows all active (started but not completed) work, ordered from oldest at the top to
|
4
|
+
newest at the bottom.
|
5
|
+
</p>
|
6
|
+
<p>
|
7
|
+
If there are expedited items that haven't yet started then they're at the bottom of the table. By the
|
8
|
+
very definition of expedited, if we haven't started them already, we'd better get on that.
|
9
|
+
</p>
|
10
|
+
<p>
|
11
|
+
<% if age_cutoff > 0 %>
|
12
|
+
Items less than <%= label_days age_cutoff %> old have been excluded from this chart to provide more
|
13
|
+
focus on the older items. The exception are items that are either expedited or blocked - these are
|
14
|
+
shown no matter how old they are.
|
15
|
+
<% end %>
|
16
|
+
</p>
|
17
|
+
|
18
|
+
<table class='standard'>
|
19
|
+
<thead>
|
20
|
+
<tr>
|
21
|
+
<th>Age (days)</th>
|
22
|
+
<th>E</th>
|
23
|
+
<th>B</th>
|
24
|
+
<th>Issue</th>
|
25
|
+
<th>Status</th>
|
26
|
+
<th>Fix versions</th>
|
27
|
+
<% if any_scrum_boards? %>
|
28
|
+
<th>Sprints</th>
|
29
|
+
<% end %>
|
30
|
+
<th><%= aggregated_project? ? 'Board' : 'Who' %></th>
|
31
|
+
</tr>
|
32
|
+
</thead>
|
33
|
+
<tbody>
|
34
|
+
<% aging_issues.each do |issue| %>
|
35
|
+
<tr>
|
36
|
+
<td style="text-align: right;"><%= issue.board.cycletime.age(issue, today: @today) || 'Not started' %></td>
|
37
|
+
<td><%= expedited_text(issue) %></td>
|
38
|
+
<td><%= blocked_text(issue) %></td>
|
39
|
+
<td>
|
40
|
+
<% parent_hierarchy(issue).each_with_index do |parent, index| %>
|
41
|
+
<% color = (parent == issue ? 'black' : 'gray') %>
|
42
|
+
<div style="padding-left: <%= index %>em; color: <%= color %>">
|
43
|
+
<span style="white-space: nowrap;">
|
44
|
+
<img src="<%= parent.type_icon_url %>" title="<%= parent.type %>"/>
|
45
|
+
<%= link_to_issue parent, style: "color: #{color}" %>
|
46
|
+
</span>
|
47
|
+
<i><%= parent.summary.strip.inspect %></i>
|
48
|
+
</div>
|
49
|
+
<% end %>
|
50
|
+
</td>
|
51
|
+
<td><%= format_status issue.status.name, board: issue.board %><%= unmapped_status_text(issue) unless current_status_visible? issue %></td>
|
52
|
+
<td><%= fix_versions_text(issue) %></td>
|
53
|
+
<% if any_scrum_boards? %>
|
54
|
+
<td><%= sprints_text(issue) %></td>
|
55
|
+
<% end %>
|
56
|
+
<td><%= aggregated_project? ? issue.board.name : issue.assigned_to %></td>
|
57
|
+
</tr>
|
58
|
+
<% end %>
|
59
|
+
</tbody>
|
60
|
+
</table>
|
@@ -0,0 +1,32 @@
|
|
1
|
+
[<a id='<%= link_id %>' href="#" onclick='expand_collapse("<%= link_id %>", "<%= issues_id %>"); return false;'>Show details</a>]
|
2
|
+
<table class='standard' id='<%= issues_id %>' style='display: none;'>
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th>Issue</th>
|
6
|
+
<th>Type</th>
|
7
|
+
<th>Summary</th>
|
8
|
+
<th>Description</th>
|
9
|
+
<% unless args.include? :hide_board_column %>
|
10
|
+
<th>Board</th>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
</tr>
|
14
|
+
</thead>
|
15
|
+
<tbody>
|
16
|
+
<% issue_descriptions.each do |issue, description, problem_key| %>
|
17
|
+
<tr>
|
18
|
+
<td><%= link_to_issue issue %>
|
19
|
+
<% if aggregated_project? %>
|
20
|
+
<br /><span style="font-size: 0.8em; font-style: italic;"><%= issue.board.name %></span>
|
21
|
+
<% end %>
|
22
|
+
</td>
|
23
|
+
<td><img src="<%= issue.type_icon_url %>" /> <%= issue.type %></td>
|
24
|
+
<td><i><%= issue.summary.inspect %></i></td>
|
25
|
+
<td><%= description %></td>
|
26
|
+
<% unless args.include? :hide_board_column %>
|
27
|
+
<td><%= issue.board.name %></td>
|
28
|
+
<% end %>
|
29
|
+
</tr>
|
30
|
+
<% end %>
|
31
|
+
</tbody>
|
32
|
+
</table>
|
@@ -0,0 +1,41 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
6
|
+
{
|
7
|
+
type: 'bar',
|
8
|
+
data: {
|
9
|
+
datasets: <%= JSON.generate(data_sets) %>
|
10
|
+
},
|
11
|
+
options: {
|
12
|
+
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
13
|
+
scales: {
|
14
|
+
x: {
|
15
|
+
type: 'linear',
|
16
|
+
stacked: true,
|
17
|
+
title: {
|
18
|
+
display: true,
|
19
|
+
text: 'Cycletime in days'
|
20
|
+
}
|
21
|
+
},
|
22
|
+
y: {
|
23
|
+
stacked: true,
|
24
|
+
title: {
|
25
|
+
display: true,
|
26
|
+
text: 'Number of items that had that cycletime'
|
27
|
+
}
|
28
|
+
}
|
29
|
+
},
|
30
|
+
plugins: {
|
31
|
+
tooltip: {
|
32
|
+
callbacks: {
|
33
|
+
label: function(context) {
|
34
|
+
return context.dataset.data[context.dataIndex].title
|
35
|
+
}
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
});
|
41
|
+
</script>
|
@@ -0,0 +1,103 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
6
|
+
type: 'scatter',
|
7
|
+
data: {
|
8
|
+
datasets: <%= JSON.generate(data_sets) %>
|
9
|
+
},
|
10
|
+
options: {
|
11
|
+
title: {
|
12
|
+
display: true,
|
13
|
+
text: "Cycletime Scatterplot"
|
14
|
+
},
|
15
|
+
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
16
|
+
scales: {
|
17
|
+
x: {
|
18
|
+
type: "time",
|
19
|
+
scaleLabel: {
|
20
|
+
display: true,
|
21
|
+
labelString: 'Date Completed'
|
22
|
+
},
|
23
|
+
min: "<%= date_range.begin.to_s %>",
|
24
|
+
max: "<%= (date_range.end + 1).to_s %>"
|
25
|
+
},
|
26
|
+
y: {
|
27
|
+
scaleLabel: {
|
28
|
+
display: true,
|
29
|
+
labelString: 'Days',
|
30
|
+
min: 0,
|
31
|
+
max: <%= @highest_cycletime %>
|
32
|
+
},
|
33
|
+
title: {
|
34
|
+
display: true,
|
35
|
+
text: 'Cycle time in days'
|
36
|
+
},
|
37
|
+
}
|
38
|
+
},
|
39
|
+
plugins: {
|
40
|
+
tooltip: {
|
41
|
+
callbacks: {
|
42
|
+
label: function(context) {
|
43
|
+
return context.dataset.data[context.dataIndex].title
|
44
|
+
}
|
45
|
+
}
|
46
|
+
},
|
47
|
+
autocolors: false,
|
48
|
+
annotation: {
|
49
|
+
annotations: {
|
50
|
+
<% holidays.each_with_index do |range, index| %>
|
51
|
+
holiday<%= index %>: {
|
52
|
+
drawTime: 'beforeDraw',
|
53
|
+
type: 'box',
|
54
|
+
xMin: '<%= range.begin %>T00:00:00',
|
55
|
+
xMax: '<%= range.end %>T23:59:59',
|
56
|
+
backgroundColor: '#F0F0F0',
|
57
|
+
borderColor: '#F0F0F0'
|
58
|
+
},
|
59
|
+
<% end %>
|
60
|
+
|
61
|
+
<% @percentage_lines.each_with_index do |args, index| %>
|
62
|
+
<% percent, color = args %>
|
63
|
+
line<%= index %>: {
|
64
|
+
type: 'line',
|
65
|
+
yMin: <%= percent %>,
|
66
|
+
yMax: <%= percent %>,
|
67
|
+
borderColor: '<%= color %>',
|
68
|
+
borderWidth: 1,
|
69
|
+
drawTime: 'beforeDraw'
|
70
|
+
},
|
71
|
+
<% end %>
|
72
|
+
}
|
73
|
+
},
|
74
|
+
legend: {
|
75
|
+
onClick: (evt, legendItem, legend) => {
|
76
|
+
// Find the datasetMeta that corresponds to the item clicked
|
77
|
+
var i = 0
|
78
|
+
while(legendItem.text != legend.chart.getDatasetMeta(i).label) {
|
79
|
+
i++;
|
80
|
+
}
|
81
|
+
nextVisibility = !!legend.chart.getDatasetMeta(i).hidden;
|
82
|
+
|
83
|
+
// Hide/show the 85% line for that dataset
|
84
|
+
legend.chart.options.plugins.annotation.annotations["line"+(i/2)].display = nextVisibility;
|
85
|
+
|
86
|
+
// Hide/show the trendline for this dataset, if they were enabled. The trendline is always
|
87
|
+
// there but not always visible.
|
88
|
+
legend.chart.setDatasetVisibility(i+1, <%= !!@show_trend_lines %> && nextVisibility);
|
89
|
+
|
90
|
+
// Still run the default behaviour
|
91
|
+
Chart.defaults.plugins.legend.onClick(evt, legendItem, legend);
|
92
|
+
},
|
93
|
+
labels: {
|
94
|
+
filter: function(item, chart) {
|
95
|
+
// Logic to remove a particular legend item goes here
|
96
|
+
return !item.text.includes('Trendline');
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
}
|
102
|
+
});
|
103
|
+
</script>
|
@@ -0,0 +1,63 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
6
|
+
{
|
7
|
+
type: 'bar',
|
8
|
+
data: {
|
9
|
+
datasets: <%= JSON.generate(data_sets) %>
|
10
|
+
},
|
11
|
+
options: {
|
12
|
+
title: {
|
13
|
+
display: true,
|
14
|
+
text: "Work that is blocked or stalled"
|
15
|
+
},
|
16
|
+
responsive: true, // If responsive is true then it fills the screen
|
17
|
+
scales: {
|
18
|
+
x: {
|
19
|
+
type: 'time',
|
20
|
+
time: {
|
21
|
+
unit: 'day'
|
22
|
+
},
|
23
|
+
stacked: true
|
24
|
+
},
|
25
|
+
y: {
|
26
|
+
stacked: true,
|
27
|
+
scaleLabel: {
|
28
|
+
display: true,
|
29
|
+
labelString: 'WIP'
|
30
|
+
},
|
31
|
+
title: {
|
32
|
+
display: true,
|
33
|
+
text: 'Count of items'
|
34
|
+
},
|
35
|
+
|
36
|
+
}
|
37
|
+
},
|
38
|
+
plugins: {
|
39
|
+
tooltip: {
|
40
|
+
callbacks: {
|
41
|
+
label: function(context) {
|
42
|
+
return context.dataset.data[context.dataIndex].title
|
43
|
+
}
|
44
|
+
}
|
45
|
+
},
|
46
|
+
annotation: {
|
47
|
+
annotations: {
|
48
|
+
<% holidays.each_with_index do |range, index| %>
|
49
|
+
holiday<%= index %>: {
|
50
|
+
drawTime: 'beforeDraw',
|
51
|
+
type: 'box',
|
52
|
+
xMin: '<%= range.begin %>T00:00:00',
|
53
|
+
xMax: '<%= range.end %>T23:59:59',
|
54
|
+
backgroundColor: '#F0F0F0',
|
55
|
+
borderColor: '#F0F0F0'
|
56
|
+
},
|
57
|
+
<% end %>
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
}
|
62
|
+
});
|
63
|
+
</script>
|
@@ -0,0 +1,126 @@
|
|
1
|
+
<%
|
2
|
+
problems = problems_for :discarded_changes
|
3
|
+
unless problems.empty?
|
4
|
+
%>
|
5
|
+
<p>
|
6
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> have had information discarded. This configuration is set
|
7
|
+
to "reset the clock" if an item is moved back to the backlog after it's been started. This hides important
|
8
|
+
information and makes the data less accurate. <b>Moving items back to the backlog is strongly discouraged.</b>
|
9
|
+
<%= collapsible_issues_panel problems %>
|
10
|
+
</p>
|
11
|
+
<%
|
12
|
+
end
|
13
|
+
%>
|
14
|
+
|
15
|
+
<%
|
16
|
+
problems = problems_for :completed_but_not_started
|
17
|
+
unless problems.empty?
|
18
|
+
percentage_work_included = ((issues.size - problems.size).to_f / issues.size * 100).to_i
|
19
|
+
%>
|
20
|
+
<p>
|
21
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> were discarded from all charts using cycletime (scatterplot, histogram, etc) as we couldn't determine when they started.
|
22
|
+
<% if percentage_work_included < 85 %>
|
23
|
+
Consider whether looking at only <%= percentage_work_included %>% of the total data points is enough to come to any reasonable conclusions. See <a href="https://en.wikipedia.org/wiki/Survivorship_bias">Survivorship Bias</a>.
|
24
|
+
<% end %>
|
25
|
+
<%= collapsible_issues_panel problems %>
|
26
|
+
</p>
|
27
|
+
<%
|
28
|
+
end
|
29
|
+
%>
|
30
|
+
|
31
|
+
<%
|
32
|
+
problems = problems_for :status_changes_after_done
|
33
|
+
unless problems.empty?
|
34
|
+
%>
|
35
|
+
<p>
|
36
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> had a status change after being identified as done. We should question whether they were really done at that point or if we stopped the clock too early.
|
37
|
+
<%= collapsible_issues_panel problems %>
|
38
|
+
</p>
|
39
|
+
<%
|
40
|
+
end
|
41
|
+
%>
|
42
|
+
|
43
|
+
<%
|
44
|
+
problems = problems_for :backwards_through_status_categories
|
45
|
+
unless problems.empty?
|
46
|
+
%>
|
47
|
+
<p>
|
48
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> moved backwards across the board, <b>crossing status categories</b>. This will almost certainly have impacted timings as the end times are often taken at status category boundaries. You should assume that any timing measurements for this item are wrong.
|
49
|
+
<%= collapsible_issues_panel problems %>
|
50
|
+
</p>
|
51
|
+
<%
|
52
|
+
end
|
53
|
+
%>
|
54
|
+
|
55
|
+
<%
|
56
|
+
problems = problems_for :backwords_through_statuses
|
57
|
+
unless problems.empty?
|
58
|
+
%>
|
59
|
+
<p>
|
60
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> moved backwards across the board. Depending where we have set the start and end points, this may give us incorrect timing data. Note that these items did not cross a status category and may not have affected metrics.
|
61
|
+
<%= collapsible_issues_panel problems %>
|
62
|
+
</p>
|
63
|
+
<%
|
64
|
+
end
|
65
|
+
%>
|
66
|
+
|
67
|
+
<%
|
68
|
+
problems = problems_for :status_not_on_board
|
69
|
+
unless problems.empty?
|
70
|
+
%>
|
71
|
+
<p>
|
72
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> were not visible on the board for some period of time. This may impact timings as the work was likely to have been forgotten if it wasn't visible.
|
73
|
+
<%= collapsible_issues_panel problems %>
|
74
|
+
</p>
|
75
|
+
<%
|
76
|
+
end
|
77
|
+
%>
|
78
|
+
|
79
|
+
<%
|
80
|
+
problems = problems_for :created_in_wrong_status
|
81
|
+
unless problems.empty?
|
82
|
+
%>
|
83
|
+
<p>
|
84
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> were created in a status not designated as Backlog. This will impact the measurement of start times and will therefore impact whether it's shown as in progress or not.
|
85
|
+
<%= collapsible_issues_panel problems %>
|
86
|
+
</p>
|
87
|
+
<%
|
88
|
+
end
|
89
|
+
%>
|
90
|
+
|
91
|
+
<%
|
92
|
+
problems = problems_for :stopped_before_started
|
93
|
+
unless problems.empty?
|
94
|
+
%>
|
95
|
+
<p>
|
96
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> were stopped before they were started and this will play havoc with any cycletime or WIP calculations. The most common case for this is when an item gets closed and then moved back into an in-progress status.
|
97
|
+
<%= collapsible_issues_panel problems %>
|
98
|
+
</p>
|
99
|
+
<%
|
100
|
+
end
|
101
|
+
%>
|
102
|
+
|
103
|
+
<%
|
104
|
+
problems = problems_for :issue_not_started_but_subtasks_have
|
105
|
+
unless problems.empty?
|
106
|
+
%>
|
107
|
+
<p>
|
108
|
+
<span class="quality_note_bullet">⮕</span> <%= label_issues problems.size %> still showing 'not started' while sub-tasks underneath them have started. This is almost always a mistake; if we're working on subtasks, the top level
|
109
|
+
item should also have started.
|
110
|
+
<%= collapsible_issues_panel problems %>
|
111
|
+
</p>
|
112
|
+
<%
|
113
|
+
end
|
114
|
+
%>
|
115
|
+
|
116
|
+
<%
|
117
|
+
problems = problems_for :issue_on_multiple_boards
|
118
|
+
unless problems.empty?
|
119
|
+
%>
|
120
|
+
<p>
|
121
|
+
<span class="quality_note_bullet">⮕</span> For <%= label_issues problems.size %>, we have an issue that shows up on more than one board. This could result in more data points showing up on a chart then there really should be.
|
122
|
+
<%= collapsible_issues_panel problems, :hide_board_column %>
|
123
|
+
</p>
|
124
|
+
<%
|
125
|
+
end
|
126
|
+
%>
|
@@ -0,0 +1,67 @@
|
|
1
|
+
<div>
|
2
|
+
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
3
|
+
</div>
|
4
|
+
<script>
|
5
|
+
const expedited = (ctx, value) => ctx.p0.raw.expedited == 1 ? value : undefined;
|
6
|
+
const notExpedited = (ctx, value) => ctx.p0.raw.expedited == 0 ? value : undefined;
|
7
|
+
|
8
|
+
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
9
|
+
type: 'scatter',
|
10
|
+
data: {
|
11
|
+
datasets: <%= JSON.generate(data_sets) %>
|
12
|
+
},
|
13
|
+
options: {
|
14
|
+
title: {
|
15
|
+
display: true,
|
16
|
+
text: "Cycletime Scatterplot"
|
17
|
+
},
|
18
|
+
responsive: <%= canvas_responsive? %>, // If responsive is true then it fills the screen
|
19
|
+
scales: {
|
20
|
+
x: {
|
21
|
+
type: "time",
|
22
|
+
scaleLabel: {
|
23
|
+
display: true,
|
24
|
+
labelString: 'Date Completed'
|
25
|
+
},
|
26
|
+
min: "<%= date_range.begin.to_s %>",
|
27
|
+
max: "<%= date_range.end.to_s %>"
|
28
|
+
},
|
29
|
+
y: {
|
30
|
+
scaleLabel: {
|
31
|
+
display: true,
|
32
|
+
labelString: 'Days'
|
33
|
+
},
|
34
|
+
title: {
|
35
|
+
display: true,
|
36
|
+
text: 'Age in days'
|
37
|
+
},
|
38
|
+
min: 0
|
39
|
+
}
|
40
|
+
},
|
41
|
+
plugins: {
|
42
|
+
tooltip: {
|
43
|
+
callbacks: {
|
44
|
+
label: function(context) {
|
45
|
+
return context.dataset.data[context.dataIndex].title
|
46
|
+
}
|
47
|
+
}
|
48
|
+
},
|
49
|
+
autocolors: false,
|
50
|
+
annotation: {
|
51
|
+
annotations: {
|
52
|
+
<% holidays.each_with_index do |range, index| %>
|
53
|
+
holiday<%= index %>: {
|
54
|
+
drawTime: 'beforeDraw',
|
55
|
+
type: 'box',
|
56
|
+
xMin: '<%= range.begin %>T00:00:00',
|
57
|
+
xMax: '<%= range.end %>T23:59:59',
|
58
|
+
backgroundColor: '#F0F0F0',
|
59
|
+
borderColor: '#F0F0F0'
|
60
|
+
},
|
61
|
+
<% end %>
|
62
|
+
}
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
});
|
67
|
+
</script>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
<table class='standard'>
|
3
|
+
<thead>
|
4
|
+
<tr>
|
5
|
+
<th>Age (days)</th>
|
6
|
+
<th>Issue</th>
|
7
|
+
<th>Summary</th>
|
8
|
+
<th>Status</th>
|
9
|
+
</tr>
|
10
|
+
</thead>
|
11
|
+
<tbody>
|
12
|
+
<% tree_organizer.flattened_nodes.each do |node, depth| %>
|
13
|
+
<% issue = node.issue %>
|
14
|
+
<tr>
|
15
|
+
<td style="text-align: right;"><%= issue.board.cycletime.age(issue, today: @today) || 'Not started' %></td>
|
16
|
+
<td>
|
17
|
+
<% color = (node.children? ? 'gray' : 'black') %>
|
18
|
+
<span style="padding-left: <%= depth - 1 %>em;" />
|
19
|
+
<span style="white-space: nowrap;">
|
20
|
+
<img src="<%= issue.type_icon_url %>" title="<%= issue.type %>"/>
|
21
|
+
<%= link_to_issue issue, style: "color: #{color}" %>
|
22
|
+
</span>
|
23
|
+
</td>
|
24
|
+
<td><span style="color: <%= color %>; font-style: italic;"><%= issue.summary[0..80] %></span></td>
|
25
|
+
<td><%= format_status issue.status.name, board: issue.board %></td>
|
26
|
+
</tr>
|
27
|
+
<% end %>
|
28
|
+
</tbody>
|
29
|
+
</table>
|