jirametrics 2.7 → 2.11
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 +4 -4
- data/lib/jirametrics/aging_work_bar_chart.rb +7 -5
- data/lib/jirametrics/aging_work_in_progress_chart.rb +105 -41
- data/lib/jirametrics/aging_work_table.rb +50 -2
- data/lib/jirametrics/board.rb +33 -5
- data/lib/jirametrics/board_config.rb +6 -2
- data/lib/jirametrics/board_movement_calculator.rb +147 -0
- data/lib/jirametrics/change_item.rb +19 -6
- data/lib/jirametrics/chart_base.rb +59 -21
- data/lib/jirametrics/css_variable.rb +1 -1
- data/lib/jirametrics/cycletime_config.rb +37 -5
- data/lib/jirametrics/cycletime_histogram.rb +67 -2
- data/lib/jirametrics/data_quality_report.rb +174 -35
- data/lib/jirametrics/download_config.rb +2 -2
- data/lib/jirametrics/downloader.rb +44 -25
- data/lib/jirametrics/examples/aggregated_project.rb +2 -5
- data/lib/jirametrics/examples/standard_project.rb +4 -6
- data/lib/jirametrics/expedited_chart.rb +7 -7
- data/lib/jirametrics/exporter.rb +10 -20
- data/lib/jirametrics/file_config.rb +23 -6
- data/lib/jirametrics/file_system.rb +39 -4
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +2 -4
- data/lib/jirametrics/groupable_issue_chart.rb +1 -3
- data/lib/jirametrics/html/aging_work_bar_chart.erb +3 -12
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +22 -5
- data/lib/jirametrics/html/aging_work_table.erb +6 -4
- data/lib/jirametrics/html/cycletime_histogram.erb +74 -0
- data/lib/jirametrics/html/cycletime_scatterplot.erb +1 -10
- data/lib/jirametrics/html/daily_wip_chart.erb +1 -10
- data/lib/jirametrics/html/expedited_chart.erb +1 -10
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/index.css +28 -5
- data/lib/jirametrics/html/index.erb +8 -4
- data/lib/jirametrics/html/sprint_burndown.erb +1 -10
- data/lib/jirametrics/html/throughput_chart.erb +1 -10
- data/lib/jirametrics/html_report_config.rb +32 -23
- data/lib/jirametrics/issue.rb +104 -44
- data/lib/jirametrics/jira_gateway.rb +16 -3
- data/lib/jirametrics/project_config.rb +223 -120
- data/lib/jirametrics/sprint_burndown.rb +1 -1
- data/lib/jirametrics/status.rb +81 -26
- data/lib/jirametrics/status_collection.rb +74 -40
- data/lib/jirametrics/throughput_chart.rb +1 -1
- data/lib/jirametrics/value_equality.rb +2 -2
- data/lib/jirametrics.rb +7 -1
- metadata +8 -13
- data/lib/jirametrics/discard_changes_before.rb +0 -37
- data/lib/jirametrics/html/data_quality_report.erb +0 -138
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
<div class="chart">
|
|
2
2
|
<canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
|
|
3
3
|
</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
|
+
%>
|
|
4
55
|
<script>
|
|
5
56
|
new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
6
57
|
{
|
|
@@ -21,6 +72,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
21
72
|
grid: {
|
|
22
73
|
color: <%= CssVariable['--grid-line-color'].to_json %>
|
|
23
74
|
},
|
|
75
|
+
min: 0,
|
|
76
|
+
offset: false, // Gets rid of the ugly padding on left.
|
|
24
77
|
},
|
|
25
78
|
y: {
|
|
26
79
|
stacked: true,
|
|
@@ -34,6 +87,27 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
34
87
|
}
|
|
35
88
|
},
|
|
36
89
|
plugins: {
|
|
90
|
+
annotation: {
|
|
91
|
+
annotations: {
|
|
92
|
+
<%
|
|
93
|
+
results = the_stats[:all][:percentiles]
|
|
94
|
+
results.each do |percentile, value|
|
|
95
|
+
%>
|
|
96
|
+
percentile<%= percentile.to_s %>: {
|
|
97
|
+
type: 'line',
|
|
98
|
+
scaleID: 'x',
|
|
99
|
+
value: <%= value %>,
|
|
100
|
+
borderWidth: 1,
|
|
101
|
+
drawTime: 'beforeDatasetsDraw',
|
|
102
|
+
label: {
|
|
103
|
+
enabled: true,
|
|
104
|
+
content: '<%= "#{percentile}%" %>',
|
|
105
|
+
position: 'start',
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
<% end %>
|
|
109
|
+
},
|
|
110
|
+
},
|
|
37
111
|
tooltip: {
|
|
38
112
|
callbacks: {
|
|
39
113
|
label: function(context) {
|
|
@@ -53,16 +53,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
53
53
|
autocolors: false,
|
|
54
54
|
annotation: {
|
|
55
55
|
annotations: {
|
|
56
|
-
|
|
57
|
-
holiday<%= index %>: {
|
|
58
|
-
drawTime: 'beforeDraw',
|
|
59
|
-
type: 'box',
|
|
60
|
-
xMin: '<%= range.begin %>T00:00:00',
|
|
61
|
-
xMax: '<%= range.end %>T23:59:59',
|
|
62
|
-
backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
|
|
63
|
-
borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
|
|
64
|
-
},
|
|
65
|
-
<% end %>
|
|
56
|
+
<%= working_days_annotation %>
|
|
66
57
|
|
|
67
58
|
<% @percentage_lines.each_with_index do |args, index| %>
|
|
68
59
|
<% percent, color = args %>
|
|
@@ -50,16 +50,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
|
|
|
50
50
|
},
|
|
51
51
|
annotation: {
|
|
52
52
|
annotations: {
|
|
53
|
-
|
|
54
|
-
holiday<%= index %>: {
|
|
55
|
-
drawTime: 'beforeDraw',
|
|
56
|
-
type: 'box',
|
|
57
|
-
xMin: '<%= range.begin %>T00:00:00',
|
|
58
|
-
xMax: '<%= range.end %>T23:59:59',
|
|
59
|
-
backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
|
|
60
|
-
borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
|
|
61
|
-
},
|
|
62
|
-
<% end %>
|
|
53
|
+
<%= working_days_annotation %>
|
|
63
54
|
}
|
|
64
55
|
},
|
|
65
56
|
legend: {
|
|
@@ -55,16 +55,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
55
55
|
autocolors: false,
|
|
56
56
|
annotation: {
|
|
57
57
|
annotations: {
|
|
58
|
-
|
|
59
|
-
holiday<%= index %>: {
|
|
60
|
-
drawTime: 'beforeDraw',
|
|
61
|
-
type: 'box',
|
|
62
|
-
xMin: '<%= range.begin %>T00:00:00',
|
|
63
|
-
xMax: '<%= range.end %>T23:59:59',
|
|
64
|
-
backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
|
|
65
|
-
borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
|
|
66
|
-
},
|
|
67
|
-
<% end %>
|
|
58
|
+
<%= working_days_annotation %>
|
|
68
59
|
}
|
|
69
60
|
}
|
|
70
61
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
</span>
|
|
23
23
|
</td>
|
|
24
24
|
<td><span style="color: <%= color %>; font-style: italic;"><%= issue.summary[0..80] %></span></td>
|
|
25
|
-
<td><%= format_status issue.status
|
|
25
|
+
<td><%= format_status issue.status, board: issue.board %></td>
|
|
26
26
|
</tr>
|
|
27
27
|
<% end %>
|
|
28
28
|
</tbody>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
--body-background: white;
|
|
3
3
|
--default-text-color: black;
|
|
4
4
|
--grid-line-color: lightgray;
|
|
5
|
+
--warning-banner: yellow;
|
|
5
6
|
|
|
6
7
|
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
7
8
|
|
|
@@ -19,6 +20,7 @@
|
|
|
19
20
|
--status-category-todo-color: gray;
|
|
20
21
|
--status-category-inprogress-color: #2663ff;
|
|
21
22
|
--status-category-done-color: #00ff00;
|
|
23
|
+
--status-category-unknown-color: black;
|
|
22
24
|
|
|
23
25
|
--aging-work-bar-chart-percentage-line-color: red;
|
|
24
26
|
--aging-work-bar-chart-separator-color: white;
|
|
@@ -26,8 +28,16 @@
|
|
|
26
28
|
--throughput_chart_total_line_color: gray;
|
|
27
29
|
|
|
28
30
|
--aging-work-in-progress-chart-shading-color: lightgray;
|
|
31
|
+
--aging-work-in-progress-chart-shading-50-color: #2E8BC0; // green;
|
|
32
|
+
--aging-work-in-progress-chart-shading-85-color: #ADD8E6; // yellow;
|
|
33
|
+
--aging-work-in-progress-chart-shading-98-color: #FF8A8A; // orange;
|
|
34
|
+
--aging-work-in-progress-chart-shading-100-color: #FF2E2E; // red;
|
|
35
|
+
|
|
29
36
|
--aging-work-in-progress-by-age-trend-line-color: gray;
|
|
30
37
|
|
|
38
|
+
--aging-work-table-date-in-jeopardy: yellow;
|
|
39
|
+
--aging-work-table-date-overdue: red;
|
|
40
|
+
|
|
31
41
|
--hierarchy-table-inactive-item-text-color: gray;
|
|
32
42
|
|
|
33
43
|
--wip-chart-completed-color: #00ff00;
|
|
@@ -99,9 +109,6 @@ table.standard {
|
|
|
99
109
|
background-color: #eee;
|
|
100
110
|
}
|
|
101
111
|
}
|
|
102
|
-
.quality_note_bullet {
|
|
103
|
-
color: red;
|
|
104
|
-
}
|
|
105
112
|
|
|
106
113
|
.chart {
|
|
107
114
|
background-color: white;
|
|
@@ -119,8 +126,26 @@ div.color_block {
|
|
|
119
126
|
border: 1px solid black;
|
|
120
127
|
}
|
|
121
128
|
|
|
129
|
+
ul.quality_report {
|
|
130
|
+
list-style-type: '⮕';
|
|
131
|
+
::marker {
|
|
132
|
+
color: red;
|
|
133
|
+
}
|
|
134
|
+
li {
|
|
135
|
+
padding: 0.2em;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
#footer {
|
|
140
|
+
text-align: center;
|
|
141
|
+
margin-top: 1em;
|
|
142
|
+
border-top: 1px solid gray;
|
|
143
|
+
}
|
|
144
|
+
|
|
122
145
|
@media screen and (prefers-color-scheme: dark) {
|
|
123
146
|
:root {
|
|
147
|
+
--warning-banner: #9F2B00;
|
|
148
|
+
|
|
124
149
|
--non-working-days-color: #2f2f2f;
|
|
125
150
|
--type-story-color: #6fb86f;
|
|
126
151
|
--type-task-color: #0021b3;
|
|
@@ -136,8 +161,6 @@ div.color_block {
|
|
|
136
161
|
--dead-color: black;
|
|
137
162
|
--wip-chart-active-color: #2551c1;
|
|
138
163
|
|
|
139
|
-
--aging-work-in-progress-chart-shading-color: #b4b4b4;
|
|
140
|
-
|
|
141
164
|
--status-category-inprogress-color: #1c49bb;
|
|
142
165
|
|
|
143
166
|
--cycletime-scatterplot-overall-trendline-color: gray;
|
|
@@ -23,16 +23,20 @@
|
|
|
23
23
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
|
|
24
24
|
location.reload()
|
|
25
25
|
})
|
|
26
|
-
|
|
27
26
|
</script>
|
|
28
27
|
<style>
|
|
29
28
|
<%= css %>
|
|
30
29
|
</style>
|
|
30
|
+
<script type="text/javascript">
|
|
31
|
+
Chart.defaults.color = getComputedStyle(document.documentElement).getPropertyValue('--default-text-color');
|
|
32
|
+
</script>
|
|
31
33
|
</head>
|
|
32
34
|
<body>
|
|
33
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
<noscript>
|
|
36
|
+
<div style="padding: 1em; background: gray; color: white; font-size: 2em;">
|
|
37
|
+
Javascript is currently disabled and that means that almost all of the charts in this report won't render. If you'd loaded this from a folder on SharePoint then save it locally and load it again.
|
|
38
|
+
</div>
|
|
39
|
+
</noscript>
|
|
36
40
|
<%= "\n" + @sections.collect { |text, type| text if type == :header }.compact.join("\n\n") %>
|
|
37
41
|
<%= "\n" + @sections.collect { |text, type| text if type == :body }.compact.join("\n\n") %>
|
|
38
42
|
</body>
|
|
@@ -56,16 +56,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
56
56
|
},
|
|
57
57
|
annotation: {
|
|
58
58
|
annotations: {
|
|
59
|
-
|
|
60
|
-
holiday<%= index %>: {
|
|
61
|
-
drawTime: 'beforeDraw',
|
|
62
|
-
type: 'box',
|
|
63
|
-
xMin: '<%= range.begin %>T00:00:00',
|
|
64
|
-
xMax: '<%= range.end %>T23:59:59',
|
|
65
|
-
backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
|
|
66
|
-
borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
|
|
67
|
-
},
|
|
68
|
-
<% end %>
|
|
59
|
+
<%= working_days_annotation %>
|
|
69
60
|
}
|
|
70
61
|
}
|
|
71
62
|
}
|
|
@@ -52,16 +52,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
|
|
|
52
52
|
},
|
|
53
53
|
annotation: {
|
|
54
54
|
annotations: {
|
|
55
|
-
|
|
56
|
-
holiday<%= index %>: {
|
|
57
|
-
drawTime: 'beforeDraw',
|
|
58
|
-
type: 'box',
|
|
59
|
-
xMin: '<%= range.begin %>T00:00:00',
|
|
60
|
-
xMax: '<%= range.end %>T23:59:59',
|
|
61
|
-
backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
|
|
62
|
-
borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
|
|
63
|
-
},
|
|
64
|
-
<% end %>
|
|
55
|
+
<%= working_days_annotation %>
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
58
|
}
|
|
@@ -5,16 +5,15 @@ require 'jirametrics/self_or_issue_dispatcher'
|
|
|
5
5
|
|
|
6
6
|
class HtmlReportConfig
|
|
7
7
|
include SelfOrIssueDispatcher
|
|
8
|
-
include DiscardChangesBefore
|
|
9
8
|
|
|
10
|
-
attr_reader :file_config, :sections
|
|
9
|
+
attr_reader :file_config, :sections, :charts
|
|
11
10
|
|
|
12
11
|
def self.define_chart name:, classname:, deprecated_warning: nil, deprecated_date: nil
|
|
13
12
|
lines = []
|
|
14
13
|
lines << "def #{name} &block"
|
|
15
14
|
lines << ' block = ->(_) {} unless block'
|
|
16
15
|
if deprecated_warning
|
|
17
|
-
lines << " deprecated date: #{deprecated_date.inspect}, message: #{deprecated_warning.inspect}"
|
|
16
|
+
lines << " file_system.deprecated date: #{deprecated_date.inspect}, message: #{deprecated_warning.inspect}"
|
|
18
17
|
end
|
|
19
18
|
lines << " execute_chart #{classname}.new(block)"
|
|
20
19
|
lines << 'end'
|
|
@@ -43,14 +42,15 @@ class HtmlReportConfig
|
|
|
43
42
|
def initialize file_config:, block:
|
|
44
43
|
@file_config = file_config
|
|
45
44
|
@block = block
|
|
46
|
-
@sections = []
|
|
45
|
+
@sections = [] # Where we store the chunks of text that will be assembled into the HTML
|
|
46
|
+
@charts = [] # Where we store all the charts we executed so we can assert against them.
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def cycletime label = nil, &block
|
|
50
50
|
@file_config.project_config.all_boards.each_value do |board|
|
|
51
51
|
raise 'Multiple cycletimes not supported' if board.cycletime
|
|
52
52
|
|
|
53
|
-
board.cycletime = CycleTimeConfig.new(parent_config: self, label: label, block: block)
|
|
53
|
+
board.cycletime = CycleTimeConfig.new(parent_config: self, label: label, block: block, file_system: file_system)
|
|
54
54
|
end
|
|
55
55
|
end
|
|
56
56
|
|
|
@@ -64,9 +64,11 @@ class HtmlReportConfig
|
|
|
64
64
|
|
|
65
65
|
# The quality report has to be generated last because otherwise cycletime won't have been
|
|
66
66
|
# set. Then we have to rotate it to the first position so it's at the top of the report.
|
|
67
|
-
execute_chart DataQualityReport.new(
|
|
67
|
+
execute_chart DataQualityReport.new(file_config.project_config.discarded_changes_data)
|
|
68
68
|
@sections.rotate!(-1)
|
|
69
69
|
|
|
70
|
+
html create_footer
|
|
71
|
+
|
|
70
72
|
html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
|
|
71
73
|
css = load_css html_directory: html_directory
|
|
72
74
|
erb = ERB.new file_system.load(File.join(html_directory, 'index.erb'))
|
|
@@ -99,9 +101,8 @@ class HtmlReportConfig
|
|
|
99
101
|
base_css
|
|
100
102
|
end
|
|
101
103
|
|
|
102
|
-
def board_id id
|
|
103
|
-
@board_id = id
|
|
104
|
-
@board_id
|
|
104
|
+
def board_id id
|
|
105
|
+
@board_id = id
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
def timezone_offset
|
|
@@ -141,19 +142,6 @@ class HtmlReportConfig
|
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
|
|
144
|
-
def discard_changes_before_hook issues_cutoff_times
|
|
145
|
-
# raise 'Cycletime must be defined before using discard_changes_before' unless @cycletime
|
|
146
|
-
|
|
147
|
-
@original_issue_times = {}
|
|
148
|
-
issues_cutoff_times.each do |issue, cutoff_time|
|
|
149
|
-
started = issue.board.cycletime.started_stopped_times(issue).first
|
|
150
|
-
if started && started <= cutoff_time
|
|
151
|
-
# We only need to log this if data was discarded
|
|
152
|
-
@original_issue_times[issue] = { cutoff_time: cutoff_time, started_time: started }
|
|
153
|
-
end
|
|
154
|
-
end
|
|
155
|
-
end
|
|
156
|
-
|
|
157
145
|
def dependency_chart &block
|
|
158
146
|
execute_chart DependencyChart.new block
|
|
159
147
|
end
|
|
@@ -173,7 +161,7 @@ class HtmlReportConfig
|
|
|
173
161
|
chart.settings = settings
|
|
174
162
|
|
|
175
163
|
chart.all_boards = project_config.all_boards
|
|
176
|
-
chart.board_id = find_board_id
|
|
164
|
+
chart.board_id = find_board_id
|
|
177
165
|
chart.holiday_dates = project_config.exporter.holiday_dates
|
|
178
166
|
|
|
179
167
|
time_range = @file_config.project_config.time_range
|
|
@@ -182,6 +170,7 @@ class HtmlReportConfig
|
|
|
182
170
|
|
|
183
171
|
after_init_block&.call chart
|
|
184
172
|
|
|
173
|
+
@charts << chart
|
|
185
174
|
html chart.run
|
|
186
175
|
end
|
|
187
176
|
|
|
@@ -202,4 +191,24 @@ class HtmlReportConfig
|
|
|
202
191
|
def boards
|
|
203
192
|
@file_config.project_config.board_configs.collect(&:id).collect { |id| find_board id }
|
|
204
193
|
end
|
|
194
|
+
|
|
195
|
+
def create_footer now: DateTime.now
|
|
196
|
+
now = now.new_offset(timezone_offset)
|
|
197
|
+
version = Gem.loaded_specs['jirametrics']&.version || 'Next'
|
|
198
|
+
|
|
199
|
+
<<~HTML
|
|
200
|
+
<section id="footer">
|
|
201
|
+
Report generated on <b>#{now.strftime('%Y-%b-%d')}</b> at <b>#{now.strftime('%I:%M:%S%P %Z')}</b>
|
|
202
|
+
with <a href="https://jirametrics.org">JiraMetrics</a> <b>v#{version}</b>
|
|
203
|
+
</section>
|
|
204
|
+
HTML
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def discard_changes_before status_becomes: nil, &block
|
|
208
|
+
file_system.deprecated(
|
|
209
|
+
date: '2025-01-09',
|
|
210
|
+
message: 'discard_changes_before is now only supported at the project level'
|
|
211
|
+
)
|
|
212
|
+
file_config.project_config.discard_changes_before status_becomes: status_becomes, &block
|
|
213
|
+
end
|
|
205
214
|
end
|