jirametrics 2.12.1 → 2.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/jirametrics/aging_work_bar_chart.rb +176 -134
- data/lib/jirametrics/anonymizer.rb +8 -6
- data/lib/jirametrics/atlassian_document_format.rb +160 -0
- data/lib/jirametrics/bar_chart_range.rb +17 -0
- data/lib/jirametrics/board.rb +4 -0
- data/lib/jirametrics/board_config.rb +4 -1
- data/lib/jirametrics/change_item.rb +12 -4
- data/lib/jirametrics/chart_base.rb +36 -2
- data/lib/jirametrics/cycletime_config.rb +22 -4
- data/lib/jirametrics/cycletime_histogram.rb +3 -1
- data/lib/jirametrics/cycletime_scatterplot.rb +36 -17
- data/lib/jirametrics/daily_view.rb +57 -53
- data/lib/jirametrics/daily_wip_by_age_chart.rb +3 -4
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +13 -3
- data/lib/jirametrics/daily_wip_chart.rb +1 -1
- data/lib/jirametrics/data_quality_report.rb +8 -3
- data/lib/jirametrics/dependency_chart.rb +4 -1
- data/lib/jirametrics/downloader.rb +34 -70
- data/lib/jirametrics/downloader_for_cloud.rb +202 -0
- data/lib/jirametrics/downloader_for_data_center.rb +94 -0
- data/lib/jirametrics/examples/standard_project.rb +9 -9
- data/lib/jirametrics/expedited_chart.rb +1 -1
- data/lib/jirametrics/exporter.rb +12 -5
- data/lib/jirametrics/file_system.rb +24 -1
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +1 -1
- data/lib/jirametrics/groupable_issue_chart.rb +7 -1
- data/lib/jirametrics/html/aging_work_bar_chart.erb +2 -1
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +3 -1
- data/lib/jirametrics/html/aging_work_table.erb +2 -0
- data/lib/jirametrics/html/collapsible_issues_panel.erb +2 -2
- data/lib/jirametrics/html/cycletime_histogram.erb +4 -2
- data/lib/jirametrics/html/cycletime_scatterplot.erb +6 -6
- data/lib/jirametrics/html/daily_wip_chart.erb +2 -0
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +2 -0
- data/lib/jirametrics/html/expedited_chart.erb +3 -1
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +2 -0
- data/lib/jirametrics/html/index.css +21 -9
- data/lib/jirametrics/html/index.erb +5 -37
- data/lib/jirametrics/html/index.js +114 -0
- data/lib/jirametrics/html/sprint_burndown.erb +11 -3
- data/lib/jirametrics/html/throughput_chart.erb +2 -2
- data/lib/jirametrics/html_generator.rb +31 -0
- data/lib/jirametrics/html_report_config.rb +8 -25
- data/lib/jirametrics/issue.rb +128 -23
- data/lib/jirametrics/jira_gateway.rb +59 -17
- data/lib/jirametrics/project_config.rb +42 -5
- data/lib/jirametrics/raw_javascript.rb +13 -0
- data/lib/jirametrics/settings.json +3 -1
- data/lib/jirametrics/sprint.rb +12 -0
- data/lib/jirametrics/sprint_burndown.rb +6 -2
- data/lib/jirametrics/status_collection.rb +1 -0
- data/lib/jirametrics/stitcher.rb +75 -0
- data/lib/jirametrics.rb +26 -69
- metadata +11 -3
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
<table class='standard' id='<%= issues_id %>'
|
|
1
|
+
<div class='foldable startFolded'>Show details</div>
|
|
2
|
+
<table class='standard' id='<%= issues_id %>'>
|
|
3
3
|
<thead>
|
|
4
4
|
<tr>
|
|
5
5
|
<th>Issue</th>
|
|
@@ -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>
|
|
@@ -6,8 +7,8 @@ if show_stats
|
|
|
6
7
|
link_id = next_id
|
|
7
8
|
issues_id = next_id
|
|
8
9
|
%>
|
|
9
|
-
|
|
10
|
-
<div id="<%= issues_id %>" style="
|
|
10
|
+
<div class='foldable' style="padding-left: 1em;">Statistics</div>
|
|
11
|
+
<div id="<%= issues_id %>" style="padding-left: 1em;">
|
|
11
12
|
<div>
|
|
12
13
|
<table class="standard">
|
|
13
14
|
<tr>
|
|
@@ -119,3 +120,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
119
120
|
}
|
|
120
121
|
});
|
|
121
122
|
</script>
|
|
123
|
+
<%= seam_end %>
|
|
@@ -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,15 +11,14 @@ 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 %>
|
|
@@ -29,13 +29,12 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
29
29
|
y: {
|
|
30
30
|
scaleLabel: {
|
|
31
31
|
display: true,
|
|
32
|
-
labelString: 'Days',
|
|
33
32
|
min: 0,
|
|
34
|
-
max: <%= @
|
|
33
|
+
max: <%= @highest_y_value %>
|
|
35
34
|
},
|
|
36
35
|
title: {
|
|
37
36
|
display: true,
|
|
38
|
-
text: '
|
|
37
|
+
text: '<%= y_axis_heading %>'
|
|
39
38
|
},
|
|
40
39
|
grid: {
|
|
41
40
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
@@ -98,3 +97,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
98
97
|
}
|
|
99
98
|
});
|
|
100
99
|
</script>
|
|
100
|
+
<%= seam_end %>
|
|
@@ -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>
|
|
@@ -65,3 +66,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
68
|
</script>
|
|
69
|
+
<%= seam_end %>
|
|
@@ -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>
|
|
@@ -60,3 +61,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
60
61
|
}
|
|
61
62
|
});
|
|
62
63
|
</script>
|
|
64
|
+
<%= seam_end %>
|
|
@@ -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>
|
|
@@ -61,4 +62,5 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
61
62
|
}
|
|
62
63
|
}
|
|
63
64
|
});
|
|
64
|
-
</script>
|
|
65
|
+
</script>
|
|
66
|
+
<%= seam_end %>
|
|
@@ -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>
|
|
@@ -83,3 +84,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
83
84
|
}
|
|
84
85
|
});
|
|
85
86
|
</script>
|
|
87
|
+
<%= seam_start %>
|
|
@@ -67,10 +67,21 @@
|
|
|
67
67
|
--sprint-burndown-sprint-color-4: red;
|
|
68
68
|
--sprint-burndown-sprint-color-5: brown;
|
|
69
69
|
|
|
70
|
+
--sprint-color: lightblue;
|
|
71
|
+
|
|
70
72
|
--daily-view-selected-issue-background: lightgray;
|
|
71
73
|
--daily-view-issue-border: green;
|
|
72
74
|
--daily-view-selected-issue-border: red;
|
|
73
75
|
|
|
76
|
+
/* The first five are the standard priorities that Jira creates by default. */
|
|
77
|
+
--priority-color-highest: #dc2626; /* red-600 - urgent red */
|
|
78
|
+
--priority-color-high: #ea580c; /* orange-600 - warning orange */
|
|
79
|
+
--priority-color-medium: #9ca3af; /* gray-400 - neutral light gray */
|
|
80
|
+
--priority-color-low: #0891b2; /* cyan-600 - calm blue */
|
|
81
|
+
--priority-color-lowest: #64748b; /* slate-500 - muted slate */
|
|
82
|
+
/* Then here are some values we've seen in multiple instances. */
|
|
83
|
+
--priority-color-notset: gray;
|
|
84
|
+
--priority-color-critical: red;
|
|
74
85
|
}
|
|
75
86
|
|
|
76
87
|
body {
|
|
@@ -78,11 +89,6 @@ body {
|
|
|
78
89
|
color: var(--default-text-color);
|
|
79
90
|
}
|
|
80
91
|
|
|
81
|
-
h1 {
|
|
82
|
-
border: 1px solid black;
|
|
83
|
-
background: lightgray;
|
|
84
|
-
padding-left: 0.2em;
|
|
85
|
-
}
|
|
86
92
|
dl, dd, dt {
|
|
87
93
|
padding: 0;
|
|
88
94
|
margin: 0;
|
|
@@ -191,6 +197,11 @@ div.daily_issue {
|
|
|
191
197
|
padding-right: 0.2em;
|
|
192
198
|
border-radius: 0.2em;
|
|
193
199
|
}
|
|
200
|
+
h1 {
|
|
201
|
+
border: none;
|
|
202
|
+
background: none;
|
|
203
|
+
padding-left: 0;
|
|
204
|
+
}
|
|
194
205
|
margin-bottom: 0.5em;
|
|
195
206
|
}
|
|
196
207
|
div.child_issue:hover {
|
|
@@ -237,11 +248,12 @@ div.child_issue {
|
|
|
237
248
|
--wip-chart-duration-more-than-four-weeks-color: #8e0000;
|
|
238
249
|
|
|
239
250
|
--daily-view-selected-issue-background: #474747;
|
|
240
|
-
}
|
|
241
251
|
|
|
242
|
-
|
|
243
|
-
color: #
|
|
244
|
-
|
|
252
|
+
--priority-color-highest: #ef4444; /* red-500 - bright urgent red */
|
|
253
|
+
--priority-color-high: #f97316; /* orange-500 - bright orange */
|
|
254
|
+
--priority-color-medium: #9ca3af; /* gray-400 - neutral light gray */
|
|
255
|
+
--priority-color-low: #06b6d4; /* cyan-500 - bright calm blue */
|
|
256
|
+
--priority-color-lowest: #94a3b8; /* slate-400 - muted light slate */
|
|
245
257
|
}
|
|
246
258
|
|
|
247
259
|
a[href] {
|
|
@@ -5,41 +5,9 @@
|
|
|
5
5
|
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.js"></script>
|
|
6
6
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
|
7
7
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
|
|
8
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/1.
|
|
8
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-annotation/3.1.0/chartjs-plugin-annotation.min.js"></script>
|
|
9
9
|
<script type="text/javascript">
|
|
10
|
-
|
|
11
|
-
link_text = document.getElementById(link_id).textContent
|
|
12
|
-
if( link_text == 'Show details') {
|
|
13
|
-
document.getElementById(link_id).textContent = 'Hide details'
|
|
14
|
-
document.getElementById(issues_id).style.display = 'block'
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
document.getElementById(link_id).textContent = 'Show details'
|
|
18
|
-
document.getElementById(issues_id).style.display = 'none'
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function toggle_visibility(open_link_id, close_link_id, toggleable_id) {
|
|
23
|
-
let open_link = document.getElementById(open_link_id)
|
|
24
|
-
let close_link = document.getElementById(close_link_id)
|
|
25
|
-
let toggleable_element = document.getElementById(toggleable_id)
|
|
26
|
-
|
|
27
|
-
if(open_link.style.display == 'none') {
|
|
28
|
-
open_link.style.display = 'block'
|
|
29
|
-
close_link.style.display = 'none'
|
|
30
|
-
toggleable_element.style.display = 'none'
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
open_link.style.display = 'none'
|
|
34
|
-
close_link.style.display = 'block'
|
|
35
|
-
toggleable_element.style.display = 'block'
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
// If we switch between light/dark mode then force a refresh so all charts will redraw correctly
|
|
39
|
-
// in the other colour scheme.
|
|
40
|
-
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
41
|
-
location.reload()
|
|
42
|
-
})
|
|
10
|
+
<%= javascript %>
|
|
43
11
|
</script>
|
|
44
12
|
<style>
|
|
45
13
|
<%= css %>
|
|
@@ -50,11 +18,11 @@
|
|
|
50
18
|
</head>
|
|
51
19
|
<body>
|
|
52
20
|
<noscript>
|
|
53
|
-
<div style="padding: 1em; background:
|
|
54
|
-
Javascript is currently disabled and that means that almost all of the charts in this report won't render. If you'
|
|
21
|
+
<div style="padding: 1em; background: red; color: white; font-size: 2em;">
|
|
22
|
+
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.
|
|
55
23
|
</div>
|
|
56
24
|
</noscript>
|
|
57
25
|
<%= "\n" + @sections.collect { |text, type| text if type == :header }.compact.join("\n\n") %>
|
|
58
|
-
<%= "\n" + @sections.collect { |text, type| text if type
|
|
26
|
+
<%= "\n" + @sections.collect { |text, type| text if type != :header }.compact.join("\n\n") %>
|
|
59
27
|
</body>
|
|
60
28
|
</html>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
function makeFoldable() {
|
|
2
|
+
// Get all elements with the "foldable" class
|
|
3
|
+
const foldableElements = document.querySelectorAll('.foldable');
|
|
4
|
+
|
|
5
|
+
if (foldableElements.length === 0) {
|
|
6
|
+
return; // No foldable elements found
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Process each foldable element
|
|
10
|
+
foldableElements.forEach((element, index) => {
|
|
11
|
+
// Skip if this is the footer element
|
|
12
|
+
if (element.id === 'footer') {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// Create a unique ID for this section
|
|
17
|
+
const sectionId = `foldable-section-${index}`;
|
|
18
|
+
const toggleId = `foldable-toggle-${index}`;
|
|
19
|
+
|
|
20
|
+
// Create a container div for the foldable element and its content
|
|
21
|
+
const container = document.createElement('div');
|
|
22
|
+
container.className = 'foldable-section';
|
|
23
|
+
container.id = sectionId;
|
|
24
|
+
|
|
25
|
+
// Create a toggle button
|
|
26
|
+
const toggleButton = document.createElement(element.tagName); //'button');
|
|
27
|
+
toggleButton.id = toggleId;
|
|
28
|
+
toggleButton.className = 'foldable-toggle-btn';
|
|
29
|
+
toggleButton.innerHTML = '▼ ' + element.textContent;
|
|
30
|
+
|
|
31
|
+
// Create a content container
|
|
32
|
+
const contentContainer = document.createElement('div');
|
|
33
|
+
contentContainer.className = 'foldable-content';
|
|
34
|
+
contentContainer.style.cssText = `
|
|
35
|
+
border-left: 2px solid #ccc;
|
|
36
|
+
padding-left: 15px;
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
// Move the foldable element into the container and replace it with the toggle button
|
|
40
|
+
element.parentNode.insertBefore(container, element);
|
|
41
|
+
container.appendChild(toggleButton);
|
|
42
|
+
container.appendChild(contentContainer);
|
|
43
|
+
|
|
44
|
+
// Move all elements between this foldable element and the next foldable element (or end of document) into the content container
|
|
45
|
+
let nextElement = element.nextElementSibling;
|
|
46
|
+
while (nextElement && !nextElement.classList.contains('foldable')) {
|
|
47
|
+
// Skip the footer element
|
|
48
|
+
if (nextElement.id === 'footer') {
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const temp = nextElement.nextElementSibling;
|
|
53
|
+
contentContainer.appendChild(nextElement);
|
|
54
|
+
nextElement = temp;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Remove the original foldable element
|
|
58
|
+
element.remove();
|
|
59
|
+
|
|
60
|
+
// Add click event to toggle visibility
|
|
61
|
+
toggleButton.addEventListener('click', function() {
|
|
62
|
+
const content = this.nextElementSibling;
|
|
63
|
+
if (content.style.display === 'none') {
|
|
64
|
+
content.style.display = 'block';
|
|
65
|
+
this.innerHTML = '▼ ' + this.innerHTML.substring(2);
|
|
66
|
+
} else {
|
|
67
|
+
content.style.display = 'none';
|
|
68
|
+
this.innerHTML = '▶ ' + this.innerHTML.substring(2);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Initially show the content (you can change this to 'none' if you want sections collapsed by default)
|
|
73
|
+
contentContainer.style.display = 'block';
|
|
74
|
+
if(element.classList.contains('startFolded')) {
|
|
75
|
+
toggleButton.click();
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Auto-initialize when DOM is loaded
|
|
81
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
82
|
+
makeFoldable();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
// If we switch between light/dark mode then force a refresh so all charts will redraw correctly
|
|
87
|
+
// in the other colour scheme.
|
|
88
|
+
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
89
|
+
location.reload()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
// Draw a diagonal pattern to highlight sections of a bar chart. Based on code found at:
|
|
93
|
+
// https://stackoverflow.com/questions/28569667/fill-chart-js-bar-chart-with-diagonal-stripes-or-other-patterns
|
|
94
|
+
function createDiagonalPattern(color = 'black') {
|
|
95
|
+
// create a 5x5 px canvas for the pattern's base shape
|
|
96
|
+
let shape = document.createElement('canvas')
|
|
97
|
+
shape.width = 5
|
|
98
|
+
shape.height = 5
|
|
99
|
+
// get the context for drawing
|
|
100
|
+
let c = shape.getContext('2d')
|
|
101
|
+
// draw 1st line of the shape
|
|
102
|
+
c.strokeStyle = color
|
|
103
|
+
c.beginPath()
|
|
104
|
+
c.moveTo(1, 0)
|
|
105
|
+
c.lineTo(5, 4)
|
|
106
|
+
c.stroke()
|
|
107
|
+
// draw 2nd line of the shape
|
|
108
|
+
c.beginPath()
|
|
109
|
+
c.moveTo(0, 4)
|
|
110
|
+
c.lineTo(1, 5)
|
|
111
|
+
c.stroke()
|
|
112
|
+
// create the pattern from the shape
|
|
113
|
+
return c.createPattern(shape, 'repeat')
|
|
114
|
+
}
|
|
@@ -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>
|
|
@@ -63,16 +64,20 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
63
64
|
}
|
|
64
65
|
});
|
|
65
66
|
</script>
|
|
67
|
+
<%= seam_end %>
|
|
66
68
|
|
|
67
69
|
<%
|
|
68
70
|
link_id = next_id
|
|
69
71
|
issues_id = next_id
|
|
70
72
|
%>
|
|
71
|
-
|
|
72
|
-
<div
|
|
73
|
+
<section>
|
|
74
|
+
<div class='foldable startFolded'>Show statistics</div>
|
|
75
|
+
<div id="<%= issues_id %>">
|
|
76
|
+
<%= seam_start 'stats_table' %>
|
|
73
77
|
<table class='standard' style="margin-left: 1em;">
|
|
74
78
|
<thead>
|
|
75
79
|
<th>Sprint</th>
|
|
80
|
+
<th>Length</th>
|
|
76
81
|
<th>State</th>
|
|
77
82
|
<th>Started</th>
|
|
78
83
|
<th>Completed</th>
|
|
@@ -85,6 +90,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
85
90
|
<% @summary_stats.keys.sort_by(&:start_time).each do |sprint| %>
|
|
86
91
|
<tr>
|
|
87
92
|
<td><%= sprint.name %></td>
|
|
93
|
+
<td><%= sprint.day_count %></td>
|
|
88
94
|
<td><%= sprint.raw['state'] %></td>
|
|
89
95
|
<% stats = @summary_stats[sprint] %>
|
|
90
96
|
<td><%= stats.started %></td>
|
|
@@ -101,6 +107,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
101
107
|
<% end %>
|
|
102
108
|
</tbody>
|
|
103
109
|
</table>
|
|
110
|
+
<%= seam_end 'stats_table' %>
|
|
104
111
|
|
|
105
112
|
<p>Legend:
|
|
106
113
|
<ul>
|
|
@@ -109,4 +116,5 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
109
116
|
<% end %>
|
|
110
117
|
</ul>
|
|
111
118
|
</p>
|
|
112
|
-
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</section>
|
|
@@ -1,4 +1,4 @@
|
|
|
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>
|
|
@@ -59,4 +59,4 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
59
59
|
}
|
|
60
60
|
});
|
|
61
61
|
</script>
|
|
62
|
-
|
|
62
|
+
<%= seam_end %>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class HtmlGenerator
|
|
4
|
+
attr_accessor :file_system, :settings
|
|
5
|
+
|
|
6
|
+
def create_html output_filename:, settings:
|
|
7
|
+
@settings = settings
|
|
8
|
+
html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
|
|
9
|
+
css = load_css html_directory: html_directory
|
|
10
|
+
javascript = file_system.load(File.join(html_directory, 'index.js'))
|
|
11
|
+
erb = ERB.new file_system.load(File.join(html_directory, 'index.erb'))
|
|
12
|
+
file_system.save_file content: erb.result(binding), filename: output_filename
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def load_css html_directory:
|
|
16
|
+
base_css_filename = File.join(html_directory, 'index.css')
|
|
17
|
+
base_css = file_system.load(base_css_filename)
|
|
18
|
+
|
|
19
|
+
extra_css_filename = settings['include_css']
|
|
20
|
+
if extra_css_filename
|
|
21
|
+
if File.exist?(extra_css_filename)
|
|
22
|
+
base_css << "\n\n" << file_system.load(extra_css_filename)
|
|
23
|
+
log("Loaded CSS: #{extra_css_filename}")
|
|
24
|
+
else
|
|
25
|
+
log("Unable to find specified CSS file: #{extra_css_filename}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
base_css
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'erb'
|
|
4
4
|
require 'jirametrics/self_or_issue_dispatcher'
|
|
5
5
|
|
|
6
|
-
class HtmlReportConfig
|
|
6
|
+
class HtmlReportConfig < HtmlGenerator
|
|
7
7
|
include SelfOrIssueDispatcher
|
|
8
8
|
|
|
9
9
|
attr_reader :file_config, :sections, :charts
|
|
@@ -51,7 +51,10 @@ class HtmlReportConfig
|
|
|
51
51
|
@file_config.project_config.all_boards.each_value do |board|
|
|
52
52
|
raise 'Multiple cycletimes not supported' if board.cycletime
|
|
53
53
|
|
|
54
|
-
board.cycletime = CycleTimeConfig.new(
|
|
54
|
+
board.cycletime = CycleTimeConfig.new(
|
|
55
|
+
possible_statuses: file_config.project_config, label: label, block: block,
|
|
56
|
+
file_system: file_system, settings: settings
|
|
57
|
+
)
|
|
55
58
|
end
|
|
56
59
|
end
|
|
57
60
|
|
|
@@ -70,10 +73,7 @@ class HtmlReportConfig
|
|
|
70
73
|
|
|
71
74
|
html create_footer
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
css = load_css html_directory: html_directory
|
|
75
|
-
erb = ERB.new file_system.load(File.join(html_directory, 'index.erb'))
|
|
76
|
-
file_system.save_file content: erb.result(binding), filename: @file_config.output_filename
|
|
76
|
+
create_html output_filename: @file_config.output_filename, settings: settings
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def file_system
|
|
@@ -84,24 +84,6 @@ class HtmlReportConfig
|
|
|
84
84
|
file_system.log message
|
|
85
85
|
end
|
|
86
86
|
|
|
87
|
-
def load_css html_directory:
|
|
88
|
-
base_css_filename = File.join(html_directory, 'index.css')
|
|
89
|
-
base_css = file_system.load(base_css_filename)
|
|
90
|
-
log("Loaded CSS: #{base_css_filename}")
|
|
91
|
-
|
|
92
|
-
extra_css_filename = settings['include_css']
|
|
93
|
-
if extra_css_filename
|
|
94
|
-
if File.exist?(extra_css_filename)
|
|
95
|
-
base_css << "\n\n" << file_system.load(extra_css_filename)
|
|
96
|
-
log("Loaded CSS: #{extra_css_filename}")
|
|
97
|
-
else
|
|
98
|
-
log("Unable to find specified CSS file: #{extra_css_filename}")
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
base_css
|
|
103
|
-
end
|
|
104
|
-
|
|
105
87
|
def board_id id
|
|
106
88
|
@board_id = id
|
|
107
89
|
end
|
|
@@ -160,7 +142,7 @@ class HtmlReportConfig
|
|
|
160
142
|
chart.time_range = project_config.time_range
|
|
161
143
|
chart.timezone_offset = timezone_offset
|
|
162
144
|
chart.settings = settings
|
|
163
|
-
chart.
|
|
145
|
+
chart.atlassian_document_format = project_config.atlassian_document_format
|
|
164
146
|
|
|
165
147
|
chart.all_boards = project_config.all_boards
|
|
166
148
|
chart.board_id = find_board_id
|
|
@@ -173,6 +155,7 @@ class HtmlReportConfig
|
|
|
173
155
|
after_init_block&.call chart
|
|
174
156
|
|
|
175
157
|
@charts << chart
|
|
158
|
+
chart.before_run
|
|
176
159
|
html chart.run
|
|
177
160
|
end
|
|
178
161
|
|