jirametrics 2.0.1 → 2.2.0

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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jirametrics/aggregate_config.rb +1 -1
  3. data/lib/jirametrics/aging_work_bar_chart.rb +18 -13
  4. data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
  5. data/lib/jirametrics/aging_work_table.rb +21 -16
  6. data/lib/jirametrics/anonymizer.rb +6 -5
  7. data/lib/jirametrics/chart_base.rb +35 -19
  8. data/lib/jirametrics/css_variable.rb +33 -0
  9. data/lib/jirametrics/cycletime_scatterplot.rb +8 -9
  10. data/lib/jirametrics/daily_wip_by_age_chart.rb +43 -17
  11. data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +32 -17
  12. data/lib/jirametrics/daily_wip_by_parent_chart.rb +42 -0
  13. data/lib/jirametrics/daily_wip_chart.rb +67 -6
  14. data/lib/jirametrics/data_quality_report.rb +1 -1
  15. data/lib/jirametrics/dependency_chart.rb +18 -14
  16. data/lib/jirametrics/downloader.rb +13 -11
  17. data/lib/jirametrics/examples/aggregated_project.rb +17 -20
  18. data/lib/jirametrics/examples/standard_project.rb +10 -18
  19. data/lib/jirametrics/expedited_chart.rb +17 -15
  20. data/lib/jirametrics/exporter.rb +26 -20
  21. data/lib/jirametrics/grouping_rules.rb +7 -1
  22. data/lib/jirametrics/html/aging_work_bar_chart.erb +12 -6
  23. data/lib/jirametrics/html/aging_work_in_progress_chart.erb +8 -2
  24. data/lib/jirametrics/html/aging_work_table.erb +11 -19
  25. data/lib/jirametrics/html/cycletime_histogram.erb +9 -3
  26. data/lib/jirametrics/html/cycletime_scatterplot.erb +10 -4
  27. data/lib/jirametrics/html/daily_wip_chart.erb +18 -5
  28. data/lib/jirametrics/html/expedited_chart.erb +11 -5
  29. data/lib/jirametrics/html/hierarchy_table.erb +1 -1
  30. data/lib/jirametrics/html/index.css +186 -0
  31. data/lib/jirametrics/html/index.erb +8 -36
  32. data/lib/jirametrics/html/sprint_burndown.erb +11 -6
  33. data/lib/jirametrics/html/story_point_accuracy_chart.erb +9 -4
  34. data/lib/jirametrics/html/throughput_chart.erb +11 -5
  35. data/lib/jirametrics/html_report_config.rb +28 -3
  36. data/lib/jirametrics/issue.rb +5 -3
  37. data/lib/jirametrics/jira_gateway.rb +5 -2
  38. data/lib/jirametrics/project_config.rb +14 -19
  39. data/lib/jirametrics/settings.json +7 -0
  40. data/lib/jirametrics/sprint_burndown.rb +10 -4
  41. data/lib/jirametrics/status_collection.rb +1 -1
  42. data/lib/jirametrics/story_point_accuracy_chart.rb +20 -10
  43. data/lib/jirametrics/throughput_chart.rb +10 -2
  44. data/lib/jirametrics.rb +2 -0
  45. metadata +7 -3
@@ -1,20 +1,3 @@
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
1
  <table class='standard'>
19
2
  <thead>
20
3
  <tr>
@@ -31,14 +14,23 @@
31
14
  </tr>
32
15
  </thead>
33
16
  <tbody>
17
+ <% show_age_cutoff_bar_at = age_cutoff %>
34
18
  <% aging_issues.each do |issue| %>
19
+ <% issue_age = issue.board.cycletime.age(issue, today: @today) %>
20
+ <% if show_age_cutoff_bar_at && issue_age&.<(show_age_cutoff_bar_at) %>
21
+ <tr><th colspan=100 style="text-align: left; padding-top: 1em;">
22
+ The items below are less than <%= label_days age_cutoff %> old, and are only on this report
23
+ because they're either expedited or blocked.
24
+ </th></tr>
25
+ <% show_age_cutoff_bar_at = nil %>
26
+ <% end %>
35
27
  <tr>
36
- <td style="text-align: right;"><%= issue.board.cycletime.age(issue, today: @today) || 'Not started' %></td>
28
+ <td style="text-align: right;"><%= issue_age || 'Not started' %></td>
37
29
  <td><%= expedited_text(issue) %></td>
38
30
  <td><%= blocked_text(issue) %></td>
39
31
  <td>
40
32
  <% parent_hierarchy(issue).each_with_index do |parent, index| %>
41
- <% color = (parent == issue ? 'black' : 'gray') %>
33
+ <% color = parent != issue ? "var(--hierarchy-table-inactive-item-text-color)" : 'var(--default-text-color)' %>
42
34
  <div style="padding-left: <%= index %>em; color: <%= color %>">
43
35
  <span style="white-space: nowrap;">
44
36
  <img src="<%= parent.type_icon_url %>" title="<%= parent.type %>"/>
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="chart">
2
2
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
3
3
  </div>
4
4
  <script>
@@ -17,14 +17,20 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
17
17
  title: {
18
18
  display: true,
19
19
  text: 'Cycletime in days'
20
- }
20
+ },
21
+ grid: {
22
+ color: <%= CssVariable['--grid-line-color'].to_json %>
23
+ },
21
24
  },
22
25
  y: {
23
26
  stacked: true,
24
27
  title: {
25
28
  display: true,
26
29
  text: 'Number of items that had that cycletime'
27
- }
30
+ },
31
+ grid: {
32
+ color: <%= CssVariable['--grid-line-color'].to_json %>
33
+ },
28
34
  }
29
35
  },
30
36
  plugins: {
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="chart">
2
2
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
3
3
  </div>
4
4
  <script>
@@ -20,6 +20,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
20
20
  display: true,
21
21
  labelString: 'Date Completed'
22
22
  },
23
+ grid: {
24
+ color: <%= CssVariable['--grid-line-color'].to_json %>
25
+ },
23
26
  min: "<%= date_range.begin.to_s %>",
24
27
  max: "<%= (date_range.end + 1).to_s %>"
25
28
  },
@@ -34,6 +37,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
34
37
  display: true,
35
38
  text: 'Cycle time in days'
36
39
  },
40
+ grid: {
41
+ color: <%= CssVariable['--grid-line-color'].to_json %>
42
+ },
37
43
  }
38
44
  },
39
45
  plugins: {
@@ -53,8 +59,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
53
59
  type: 'box',
54
60
  xMin: '<%= range.begin %>T00:00:00',
55
61
  xMax: '<%= range.end %>T23:59:59',
56
- backgroundColor: '#F0F0F0',
57
- borderColor: '#F0F0F0'
62
+ backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
63
+ borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
58
64
  },
59
65
  <% end %>
60
66
 
@@ -64,7 +70,7 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
64
70
  type: 'line',
65
71
  yMin: <%= percent %>,
66
72
  yMax: <%= percent %>,
67
- borderColor: '<%= color %>',
73
+ borderColor: <%= color.to_json %>,
68
74
  borderWidth: 1,
69
75
  drawTime: 'beforeDraw'
70
76
  },
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="chart">
2
2
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
3
3
  </div>
4
4
  <script>
@@ -20,7 +20,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
20
20
  time: {
21
21
  unit: 'day'
22
22
  },
23
- stacked: true
23
+ stacked: true,
24
+ grid: {
25
+ color: <%= CssVariable['--grid-line-color'].to_json %>
26
+ },
24
27
  },
25
28
  y: {
26
29
  stacked: true,
@@ -32,7 +35,9 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
32
35
  display: true,
33
36
  text: 'Count of items'
34
37
  },
35
-
38
+ grid: {
39
+ color: <%= CssVariable['--grid-line-color'].to_json %>
40
+ },
36
41
  }
37
42
  },
38
43
  plugins: {
@@ -51,11 +56,19 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'),
51
56
  type: 'box',
52
57
  xMin: '<%= range.begin %>T00:00:00',
53
58
  xMax: '<%= range.end %>T23:59:59',
54
- backgroundColor: '#F0F0F0',
55
- borderColor: '#F0F0F0'
59
+ backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
60
+ borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
56
61
  },
57
62
  <% end %>
58
63
  }
64
+ },
65
+ legend: {
66
+ labels: {
67
+ filter: function(item, chart) {
68
+ // Logic to remove a particular legend item goes here
69
+ return !item.text.includes('Trendline');
70
+ }
71
+ }
59
72
  }
60
73
  }
61
74
  }
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="chart">
2
2
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
3
3
  </div>
4
4
  <script>
@@ -24,7 +24,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
24
24
  labelString: 'Date Completed'
25
25
  },
26
26
  min: "<%= date_range.begin.to_s %>",
27
- max: "<%= date_range.end.to_s %>"
27
+ max: "<%= date_range.end.to_s %>",
28
+ grid: {
29
+ color: <%= CssVariable['--grid-line-color'].to_json %>
30
+ },
28
31
  },
29
32
  y: {
30
33
  scaleLabel: {
@@ -35,7 +38,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
35
38
  display: true,
36
39
  text: 'Age in days'
37
40
  },
38
- min: 0
41
+ min: 0,
42
+ grid: {
43
+ color: <%= CssVariable['--grid-line-color'].to_json %>
44
+ },
39
45
  }
40
46
  },
41
47
  plugins: {
@@ -55,8 +61,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
55
61
  type: 'box',
56
62
  xMin: '<%= range.begin %>T00:00:00',
57
63
  xMax: '<%= range.end %>T23:59:59',
58
- backgroundColor: '#F0F0F0',
59
- borderColor: '#F0F0F0'
64
+ backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
65
+ borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
60
66
  },
61
67
  <% end %>
62
68
  }
@@ -14,7 +14,7 @@
14
14
  <tr>
15
15
  <td style="text-align: right;"><%= issue.board.cycletime.age(issue, today: @today) || 'Not started' %></td>
16
16
  <td>
17
- <% color = (node.children? ? 'gray' : 'black') %>
17
+ <% color = "var(--hierarchy-table-#{ 'in' if node.children? }active-item-text-color)" %>
18
18
  <span style="padding-left: <%= depth - 1 %>em;" />
19
19
  <span style="white-space: nowrap;">
20
20
  <img src="<%= issue.type_icon_url %>" title="<%= issue.type %>"/>
@@ -0,0 +1,186 @@
1
+ :root {
2
+ --body-background: white;
3
+ --default-text-color: black;
4
+ --grid-line-color: lightgray;
5
+
6
+ --cycletime-scatterplot-overall-trendline-color: gray;
7
+
8
+ --non-working-days-color: #F0F0F0;
9
+ --expedited-color: red;
10
+ --blocked-color: #FF7400;
11
+ --stalled-color: orange;
12
+ --dead-color: black;
13
+
14
+ --type-story-color: #4bc14b;
15
+ --type-task-color: blue;
16
+ --type-bug-color: orange;
17
+ --type-spike-color: #9400D3;
18
+
19
+ --status-category-todo-color: gray;
20
+ --status-category-inprogress-color: #2663ff;
21
+ --status-category-done-color: #00ff00;
22
+
23
+ --aging-work-bar-chart-percentage-line-color: red;
24
+ --aging-work-bar-chart-separator-color: white;
25
+
26
+ --throughput_chart_total_line_color: gray;
27
+
28
+ --aging-work-in-progress-chart-shading-color: lightgray;
29
+ --aging-work-in-progress-by-age-trend-line-color: gray;
30
+
31
+ --hierarchy-table-inactive-item-text-color: gray;
32
+
33
+ --wip-chart-completed-color: #00ff00;
34
+ --wip-chart-completed-but-not-started-color: #99FF99;
35
+ --wip-chart-duration-less-than-day-color: #ffef41;
36
+ --wip-chart-duration-week-or-less-color: #dcc900;
37
+ --wip-chart-duration-two-weeks-or-less-color: #dfa000;
38
+ --wip-chart-duration-four-weeks-or-less-color: #eb7200;
39
+ --wip-chart-duration-more-than-four-weeks-color: #e70000;
40
+ --wip-chart-active-color: #326cff;
41
+ --wip-chart-border-color: gray;
42
+
43
+ --estimate-accuracy-chart-completed-fill-color: #00ff00;
44
+ --estimate-accuracy-chart-completed-border-color: green;
45
+ --estimate-accuracy-chart-active-fill-color: #FFCCCB;
46
+ --estimate-accuracy-chart-active-border-color: red;
47
+
48
+ --expedited-chart-no-longer-expedited: gray;
49
+ --expedited-chart-dot-issue-started-color: orange;
50
+ --expedited-chart-dot-issue-stopped-color: green;
51
+ --expedited-chart-dot-expedite-started-color: red;
52
+ --expedited-chart-dot-expedite-stopped-color: green;
53
+
54
+ --sprint-burndown-sprint-color-1: blue;
55
+ --sprint-burndown-sprint-color-2: orange;
56
+ --sprint-burndown-sprint-color-3: green;
57
+ --sprint-burndown-sprint-color-4: red;
58
+ --sprint-burndown-sprint-color-5: brown;
59
+
60
+
61
+ }
62
+
63
+ body {
64
+ background-color: var(--body-background);
65
+ color: var(--default-text-color);
66
+ }
67
+
68
+ h1 {
69
+ border: 1px solid black;
70
+ background: lightgray;
71
+ padding-left: 0.2em;
72
+ }
73
+ dl, dd, dt {
74
+ padding: 0;
75
+ margin: 0;
76
+ }
77
+ dd {
78
+ margin-bottom: 0.4em;
79
+ }
80
+ span.highlight {
81
+ background: #FDD5B1;
82
+ }
83
+ a.issue_key {
84
+ white-space: nowrap;
85
+ }
86
+ table.standard {
87
+ th {
88
+ border-bottom: 1px solid gray;
89
+ position: sticky;
90
+ top: 0;
91
+ background: white;
92
+ }
93
+ td {
94
+ padding-left: 0.5em;
95
+ padding-right: 0.5em;
96
+ vertical-align: top;
97
+ }
98
+ tr:nth-child(odd){
99
+ background-color: #eee;
100
+ }
101
+ }
102
+ .quality_note_bullet {
103
+ color: red;
104
+ }
105
+
106
+ .chart {
107
+ background-color: white;
108
+ }
109
+
110
+ div.p {
111
+ margin: 0.5em 0;
112
+ padding: 0;
113
+ }
114
+
115
+ div.color_block {
116
+ display: inline-block;
117
+ width: 0.9em;
118
+ height: 0.9em;
119
+ border: 1px solid black;
120
+ }
121
+
122
+ @media screen and (prefers-color-scheme: dark) {
123
+ :root {
124
+ --non-working-days-color: #2f2f2f;
125
+ --type-story-color: #6fb86f;
126
+ --type-task-color: #0021b3;
127
+ --type-bug-color: #bb5603;
128
+
129
+ --body-background: #343434;
130
+ --default-text-color: #aaa;
131
+ --grid-line-color: #424242;
132
+
133
+ --expedited-color: #b90000;
134
+ --blocked-color: #c75b02;
135
+ --stalled-color: #ae7202;
136
+ --dead-color: black;
137
+ --wip-chart-active-color: #2551c1;
138
+
139
+ --aging-work-in-progress-chart-shading-color: #b4b4b4;
140
+
141
+ --status-category-inprogress-color: #1c49bb;
142
+
143
+ --cycletime-scatterplot-overall-trendline-color: gray;
144
+
145
+ --hierarchy-table-inactive-item-text-color: #939393;
146
+
147
+ --wip-chart-completed-color: #03cb03;
148
+ --wip-chart-completed-but-not-started-color: #99FF99;
149
+ --wip-chart-duration-less-than-day-color: #d2d988;
150
+ --wip-chart-duration-week-or-less-color: #dfcd00;
151
+ --wip-chart-duration-two-weeks-or-less-color: #cf9400;
152
+ --wip-chart-duration-four-weeks-or-less-color: #c25e00;
153
+ --wip-chart-duration-more-than-four-weeks-color: #8e0000;
154
+ }
155
+
156
+ h1 {
157
+ color: #e0e0e0;
158
+ background-color: #656565;
159
+ }
160
+
161
+ a[href] {
162
+ color: #1e8ad6;
163
+ }
164
+
165
+ a[href]:hover {
166
+ color: #3ba0e6;
167
+ }
168
+
169
+ .chart {
170
+ background: var(--body-background);
171
+ }
172
+
173
+ table.standard {
174
+ th {
175
+ border-bottom: 1px solid gray;
176
+ background: var(--body-background);
177
+ }
178
+ tr:nth-child(odd){
179
+ background-color: #656565;
180
+ }
181
+ }
182
+
183
+ div.color_block {
184
+ border: 1px solid lightgray;
185
+ }
186
+ }
@@ -17,44 +17,16 @@
17
17
  document.getElementById(issues_id).style.display = 'none'
18
18
  }
19
19
  }
20
+ // If we switch between light/dark mode then force a refresh so all charts will redraw correctly
21
+ // in the other colour scheme.
22
+ window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', event => {
23
+ location.reload()
24
+ })
25
+
20
26
  </script>
21
27
  <style>
22
- h1 {
23
- border: 1px solid black;
24
- background: lightgray;
25
- padding-left: 0.2em;
26
- }
27
- dl, dd, dt {
28
- padding: 0;
29
- margin: 0;
30
- }
31
- dd {
32
- margin-bottom: 0.4em;
33
- }
34
- span.highlight {
35
- background: #FDD5B1;
36
- }
37
- a.issue_key {
38
- white-space: nowrap;
39
- }
40
- table.standard thead tr th {
41
- border-bottom: 1px solid gray;
42
- position: sticky;
43
- top: 0;
44
- background: white;
45
- }
46
- table.standard tbody tr td {
47
- padding-left: 0.5em;
48
- padding-right: 0.5em;
49
- vertical-align: top;
50
- }
51
- table.standard tbody tr:nth-child(odd){
52
- background-color: #eee;
53
- }
54
- .quality_note_bullet {
55
- color: red;
56
- }
57
- </style>
28
+ <%= css %>
29
+ </style>
58
30
  </head>
59
31
  <body>
60
32
  <div>
@@ -1,6 +1,6 @@
1
1
  <h2>Burndown by <%= y_axis_title %></h2>
2
2
 
3
- <div>
3
+ <div class="chart">
4
4
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
5
5
  </div>
6
6
  <script>
@@ -26,8 +26,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
26
26
  labelString: 'Date'
27
27
  },
28
28
  min: "<%= date_range.begin.to_s %>",
29
- max: "<%= (date_range.end + 1).to_s %>"
30
-
29
+ max: "<%= (date_range.end + 1).to_s %>",
30
+ grid: {
31
+ color: <%= CssVariable['--grid-line-color'].to_json %>
32
+ },
31
33
  },
32
34
  y: {
33
35
  scaleLabel: {
@@ -38,7 +40,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
38
40
  display: true,
39
41
  text: "<%= y_axis_title %>"
40
42
  },
41
- min: 0.0
43
+ min: 0.0,
44
+ grid: {
45
+ color: <%= CssVariable['--grid-line-color'].to_json %>
46
+ },
42
47
  }
43
48
  },
44
49
  plugins: {
@@ -57,8 +62,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
57
62
  type: 'box',
58
63
  xMin: '<%= range.begin %>T00:00:00',
59
64
  xMax: '<%= range.end %>T23:59:59',
60
- backgroundColor: '#F0F0F0',
61
- borderColor: '#F0F0F0'
65
+ backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
66
+ borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
62
67
  },
63
68
  <% end %>
64
69
  }
@@ -1,4 +1,4 @@
1
- <div>
1
+ <div class="chart">
2
2
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
3
3
  </div>
4
4
  <script>
@@ -24,8 +24,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
24
24
  display: true,
25
25
  text: "Cycletime (days)"
26
26
  },
27
- min: 0
28
-
27
+ min: 0,
28
+ grid: {
29
+ color: <%= CssVariable['--grid-line-color'].to_json %>
30
+ },
29
31
  },
30
32
  y: {
31
33
  type: "<%= @y_axis_type %>",
@@ -40,7 +42,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
40
42
  display: true,
41
43
  text: "<%= @y_axis_label %>"
42
44
  },
43
- min: 0.0
45
+ min: 0.0,
46
+ grid: {
47
+ color: <%= CssVariable['--grid-line-color'].to_json %>
48
+ },
44
49
  }
45
50
  },
46
51
  plugins: {
@@ -1,5 +1,5 @@
1
1
 
2
- <div>
2
+ <div class="chart">
3
3
  <canvas id="<%= chart_id %>" width="<%= canvas_width %>" height="<%= canvas_height %>"></canvas>
4
4
  </div>
5
5
  <script>
@@ -23,7 +23,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
23
23
  scaleLabel: {
24
24
  display: true,
25
25
  labelString: 'Date Completed'
26
- }
26
+ },
27
+ grid: {
28
+ color: <%= CssVariable['--grid-line-color'].to_json %>
29
+ },
27
30
  },
28
31
  y: {
29
32
  scaleLabel: {
@@ -33,7 +36,10 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
33
36
  display: true,
34
37
  text: 'Count of items'
35
38
  },
36
- min: 0
39
+ min: 0,
40
+ grid: {
41
+ color: <%= CssVariable['--grid-line-color'].to_json %>
42
+ },
37
43
  },
38
44
  },
39
45
  plugins: {
@@ -52,8 +58,8 @@ new Chart(document.getElementById('<%= chart_id %>').getContext('2d'), {
52
58
  type: 'box',
53
59
  xMin: '<%= range.begin %>T00:00:00',
54
60
  xMax: '<%= range.end %>T23:59:59',
55
- backgroundColor: '#F0F0F0',
56
- borderColor: '#F0F0F0'
61
+ backgroundColor: <%= CssVariable.new('--non-working-days-color').to_json %>,
62
+ borderColor: <%= CssVariable.new('--non-working-days-color').to_json %>
57
63
  },
58
64
  <% end %>
59
65
  }
@@ -26,6 +26,11 @@ class HtmlReportConfig
26
26
  end
27
27
  end
28
28
 
29
+ # Mostly this is its own method so it can be called from the config
30
+ def included_projects
31
+ @file_config.project_config.aggregate_config.included_projects
32
+ end
33
+
29
34
  def run
30
35
  instance_eval(&@block)
31
36
 
@@ -36,11 +41,21 @@ class HtmlReportConfig
36
41
 
37
42
  File.open @file_config.output_filename, 'w' do |file|
38
43
  html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
39
- erb = ERB.new File.read("#{html_directory}/index.erb")
44
+ css = load_css html_directory: html_directory
45
+ erb = ERB.new File.read(File.join(html_directory, 'index.erb'))
40
46
  file.puts erb.result(binding)
41
47
  end
42
48
  end
43
49
 
50
+ def load_css html_directory:
51
+ base_css = File.read(File.join(html_directory, 'index.css'))
52
+ extra_css_filename = settings['include_css']
53
+ return base_css unless extra_css_filename && File.exist?(extra_css_filename)
54
+
55
+ @file_config.project_config.exporter.file_system.log("including css from file: #{extra_css_filename}")
56
+ base_css << "\n\n" << File.read(extra_css_filename)
57
+ end
58
+
44
59
  def board_id id = nil
45
60
  @board_id = id unless id.nil?
46
61
  @board_id
@@ -92,6 +107,10 @@ class HtmlReportConfig
92
107
  execute_chart DailyWipByBlockedStalledChart.new
93
108
  end
94
109
 
110
+ def daily_wip_by_parent_chart &block
111
+ execute_chart DailyWipByParentChart.new block
112
+ end
113
+
95
114
  def throughput_chart &block
96
115
  execute_chart ThroughputChart.new(block)
97
116
  end
@@ -109,7 +128,8 @@ class HtmlReportConfig
109
128
  end
110
129
 
111
130
  def html string, type: :body
112
- raise "Unexpected type: #{type}" unless %i[body header].include? type
131
+ allowed_types = %i[body header]
132
+ raise "Unexpected type: #{type} allowed_types: #{allowed_types.inspect}" unless allowed_types.include? type
113
133
 
114
134
  @sections << [string, type]
115
135
  end
@@ -145,13 +165,18 @@ class HtmlReportConfig
145
165
  execute_chart DependencyChart.new block
146
166
  end
147
167
 
168
+ # have an explicit method here so that index.erb can call 'settings' just as any other erb can.
169
+ def settings
170
+ @file_config.project_config.settings
171
+ end
172
+
148
173
  def execute_chart chart, &after_init_block
149
174
  project_config = @file_config.project_config
150
175
 
151
176
  chart.issues = issues
152
177
  chart.time_range = project_config.time_range
153
178
  chart.timezone_offset = timezone_offset
154
- chart.settings = project_config.settings
179
+ chart.settings = settings
155
180
 
156
181
  chart.all_boards = project_config.all_boards
157
182
  chart.board_id = find_board_id if chart.respond_to? :board_id=
@@ -30,7 +30,8 @@ class Issue
30
30
  fabricate_change(field_name: 'status'),
31
31
  fabricate_change(field_name: 'priority')
32
32
  ].compact + @changes
33
- rescue => e
33
+ rescue # rubocop:disable Style/RescueStandardError
34
+ # All we're doing is adding information to the existing exception and letting it propogate up
34
35
  raise "Unable to initialize #{raw['key']}"
35
36
  end
36
37
 
@@ -252,7 +253,7 @@ class Issue
252
253
  end
253
254
 
254
255
  blocked_link_texts = settings['blocked_link_text']
255
- stalled_threshold = settings['stalled_threshold']
256
+ stalled_threshold = settings['stalled_threshold_days']
256
257
 
257
258
  blocking_issue_keys = []
258
259
 
@@ -283,8 +284,9 @@ class Issue
283
284
  blocking_status = change.value
284
285
  end
285
286
  elsif change.link?
287
+ # Example: "This issue is satisfied by ANON-30465"
286
288
  unless /^This issue (?<link_text>.+) (?<issue_key>.+)$/ =~ (change.value || change.old_value)
287
- puts "Can't parse link text: #{change.value || change.old_value}"
289
+ puts "Issue(#{key}) Can't parse link text: #{change.value || change.old_value}"
288
290
  next
289
291
  end
290
292