Class: ChartBase
- Inherits:
-
Object
show all
- Defined in:
- lib/jirametrics/chart_base.rb
Direct Known Subclasses
AgingWorkBarChart, AgingWorkInProgressChart, AgingWorkTable, Anonymizer, CycletimeHistogram, CycletimeScatterplot, DailyView, DailyWipChart, DataQualityReport, DependencyChart, EstimateAccuracyChart, ExpeditedChart, FlowEfficiencyScatterplot, HierarchyTable, SprintBurndown, ThroughputChart
Constant Summary
collapse
- @@chart_counter =
0
Instance Attribute Summary collapse
Instance Method Summary
collapse
-
#aggregated_project? ⇒ Boolean
-
#before_run ⇒ Object
-
#call_before_run(&proc) ⇒ Object
-
#canvas(width:, height:, responsive: true) ⇒ Object
-
#canvas_responsive? ⇒ Boolean
-
#chart_format(object) ⇒ Object
-
#collapsible_issues_panel(issue_descriptions, *args) ⇒ Object
-
#color_block(color, title: nil) ⇒ Object
-
#color_for(type:) ⇒ Object
-
#completed_issues_in_range(include_unstarted: false) ⇒ Object
-
#current_board ⇒ Object
Return only the board columns for the current board.
-
#cycletime(&block) ⇒ Object
Set a cycletime for just this one chart, overriding the one for the report.
-
#cycletime_for_issue(issue) ⇒ Object
Returns the cycletime in use right now, which may be specific to the chart or across the report.
-
#daily_chart_dataset(date_issues_list:, color:, label:, positive: true) ⇒ Object
-
#describe_non_working_days ⇒ Object
-
#description_text(text = :none) ⇒ Object
-
#format_integer(number) ⇒ Object
Convert a number like 1234567 into the string “1,234,567”.
-
#format_status(object, board:, is_category: false, use_old_status: false) ⇒ Object
object will be either a Status or a ChangeItem if it’s a ChangeItem then use_old_status will specify whether we’re using the new or old Either way, is_category will format the category rather than the status.
-
#header_text(text = :none) ⇒ Object
-
#holidays(date_range: @date_range) ⇒ Object
-
#html_directory ⇒ Object
-
#icon_span(title:, icon:) ⇒ Object
-
#initialize ⇒ ChartBase
constructor
A new instance of ChartBase.
-
#label_days(days) ⇒ Object
-
#label_issues(count) ⇒ Object
-
#link_to_issue(issue, args = {}) ⇒ Object
-
#next_id ⇒ Object
-
#random_color ⇒ Object
-
#render(caller_binding, file) ⇒ Object
-
#render_top_text(caller_binding) ⇒ Object
-
#status_category_color(status) ⇒ Object
-
#working_days_annotation ⇒ Object
-
#wrap_and_render(caller_binding, file) ⇒ Object
Render the file and then wrap it with standard headers and quality checks.
Constructor Details
Returns a new instance of ChartBase.
12
13
14
15
16
17
18
19
20
21
22
23
|
# File 'lib/jirametrics/chart_base.rb', line 12
def initialize
@chart_colors = {
'Story' => CssVariable['--type-story-color'],
'Task' => CssVariable['--type-task-color'],
'Bug' => CssVariable['--type-bug-color'],
'Defect' => CssVariable['--type-bug-color'],
'Spike' => CssVariable['--type-spike-color']
}
@canvas_width = 800
@canvas_height = 200
@canvas_responsive = true
end
|
Instance Attribute Details
#aggregated_project=(value) ⇒ Object
Sets the attribute aggregated_project
7
8
9
|
# File 'lib/jirametrics/chart_base.rb', line 7
def aggregated_project=(value)
@aggregated_project = value
end
|
#all_boards ⇒ Object
Returns the value of attribute all_boards.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def all_boards
@all_boards
end
|
Returns the value of attribute atlassian_document_format.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def atlassian_document_format
@atlassian_document_format
end
|
#board_id ⇒ Object
Returns the value of attribute board_id.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def board_id
@board_id
end
|
#canvas_height ⇒ Object
Returns the value of attribute canvas_height.
8
9
10
|
# File 'lib/jirametrics/chart_base.rb', line 8
def canvas_height
@canvas_height
end
|
#canvas_width ⇒ Object
Returns the value of attribute canvas_width.
8
9
10
|
# File 'lib/jirametrics/chart_base.rb', line 8
def canvas_width
@canvas_width
end
|
#data_quality ⇒ Object
Returns the value of attribute data_quality.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def data_quality
@data_quality
end
|
#date_range ⇒ Object
Returns the value of attribute date_range.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def date_range
@date_range
end
|
#file_system ⇒ Object
Returns the value of attribute file_system.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def file_system
@file_system
end
|
#holiday_dates ⇒ Object
Returns the value of attribute holiday_dates.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def holiday_dates
@holiday_dates
end
|
#issues ⇒ Object
Returns the value of attribute issues.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def issues
@issues
end
|
#settings ⇒ Object
Returns the value of attribute settings.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def settings
@settings
end
|
#time_range ⇒ Object
Returns the value of attribute time_range.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def time_range
@time_range
end
|
#timezone_offset ⇒ Object
Returns the value of attribute timezone_offset.
4
5
6
|
# File 'lib/jirametrics/chart_base.rb', line 4
def timezone_offset
@timezone_offset
end
|
Instance Method Details
#aggregated_project? ⇒ Boolean
33
34
35
|
# File 'lib/jirametrics/chart_base.rb', line 33
def aggregated_project?
@aggregated_project
end
|
#before_run ⇒ Object
29
30
31
|
# File 'lib/jirametrics/chart_base.rb', line 29
def before_run
@call_before_run_procs&.each { |proc| proc.call }
end
|
#call_before_run(&proc) ⇒ Object
25
26
27
|
# File 'lib/jirametrics/chart_base.rb', line 25
def call_before_run &proc
(@call_before_run_procs ||= []) << proc
end
|
#canvas(width:, height:, responsive: true) ⇒ Object
261
262
263
264
265
|
# File 'lib/jirametrics/chart_base.rb', line 261
def canvas width:, height:, responsive: true
@canvas_width = width
@canvas_height = height
@canvas_responsive = responsive
end
|
#canvas_responsive? ⇒ Boolean
267
268
269
|
# File 'lib/jirametrics/chart_base.rb', line 267
def canvas_responsive?
@canvas_responsive
end
|
185
186
187
188
189
190
191
192
|
# File 'lib/jirametrics/chart_base.rb', line 185
def chart_format object
if object.is_a? Time
object.strftime '%Y-%m-%dT%H:%M:%S%z'
else
object.to_s
end
end
|
#collapsible_issues_panel(issue_descriptions, *args) ⇒ Object
113
114
115
116
117
118
119
120
|
# File 'lib/jirametrics/chart_base.rb', line 113
def collapsible_issues_panel issue_descriptions, *args
link_id = next_id
issues_id = next_id
issue_descriptions.sort! { |a, b| a[0].key_as_i <=> b[0].key_as_i }
erb = ERB.new file_system.load File.join(html_directory, 'collapsible_issues_panel.erb')
erb.result(binding)
end
|
#color_block(color, title: nil) ⇒ Object
271
272
273
274
275
276
277
278
279
280
|
# File 'lib/jirametrics/chart_base.rb', line 271
def color_block color, title: nil
result = +''
result << "<div class='color_block' style='"
result << "background: #{CssVariable[color]};" if color
result << 'visibility: hidden;' unless color
result << "'"
result << " title=#{title.inspect}" if title
result << '></div>'
result
end
|
#color_for(type:) ⇒ Object
73
74
75
|
# File 'lib/jirametrics/chart_base.rb', line 73
def color_for type:
@chart_colors[type] ||= random_color
end
|
#completed_issues_in_range(include_unstarted: false) ⇒ Object
174
175
176
177
178
179
180
181
182
183
|
# File 'lib/jirametrics/chart_base.rb', line 174
def completed_issues_in_range include_unstarted: false
issues.select do |issue|
cycletime = issue.board.cycletime
started_time, stopped_time = cycletime.started_stopped_times(issue)
stopped_time &&
date_range.include?(stopped_time.to_date) && (include_unstarted || (started_time && (stopped_time >= started_time)))
end
end
|
#current_board ⇒ Object
Return only the board columns for the current board.
159
160
161
162
163
164
165
166
167
168
169
170
171
172
|
# File 'lib/jirametrics/chart_base.rb', line 159
def current_board
if @board_id.nil?
case @all_boards.size
when 0
raise 'Couldn\'t find any board configurations. Ensure one is set'
when 1
return @all_boards.values[0]
else
raise "Must set board_id so we know which to use. Multiple boards found: #{@all_boards.keys.inspect}"
end
end
@all_boards[@board_id]
end
|
#cycletime(&block) ⇒ Object
Set a cycletime for just this one chart, overriding the one for the report.
292
293
294
295
296
297
298
299
|
# File 'lib/jirametrics/chart_base.rb', line 292
def cycletime &block
call_before_run do
@cycletime = CycleTimeConfig.new(
possible_statuses: possible_statuses, label: nil, block: block, file_system: file_system,
settings: settings
)
end
end
|
#cycletime_for_issue(issue) ⇒ Object
Returns the cycletime in use right now, which may be specific to the chart or across the report.
302
303
304
|
# File 'lib/jirametrics/chart_base.rb', line 302
def cycletime_for_issue issue
@cycletime || issue.board.cycletime
end
|
#daily_chart_dataset(date_issues_list:, color:, label:, positive: true) ⇒ Object
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
# File 'lib/jirametrics/chart_base.rb', line 87
def daily_chart_dataset date_issues_list:, color:, label:, positive: true
{
type: 'bar',
label: label,
data: date_issues_list.collect do |date, issues|
issues.sort_by!(&:key_as_i)
title = "#{label} (#{label_issues issues.size})"
{
x: date,
y: positive ? issues.size : -issues.size,
title: [title] + issues.collect { |i| "#{i.key} : #{i.summary.strip}#{" #{yield date, i}" if block_given?}" }
}
end,
backgroundColor: color,
borderRadius: positive ? 0 : 5
}
end
|
#describe_non_working_days ⇒ Object
282
283
284
285
286
287
288
289
|
# File 'lib/jirametrics/chart_base.rb', line 282
def describe_non_working_days
<<-TEXT
<div class='p'>
The #{color_block '--non-working-days-color'} vertical bars indicate non-working days; weekends
and any other holidays mentioned in the configuration.
</div>
TEXT
end
|
#description_text(text = :none) ⇒ Object
199
200
201
202
|
# File 'lib/jirametrics/chart_base.rb', line 199
def description_text text = :none
@description_text = text unless text == :none
@description_text
end
|
Convert a number like 1234567 into the string “1,234,567”
205
206
207
|
# File 'lib/jirametrics/chart_base.rb', line 205
def format_integer number
number.to_s.reverse.scan(/.{1,3}/).join(',').reverse
end
|
object will be either a Status or a ChangeItem if it’s a ChangeItem then use_old_status will specify whether we’re using the new or old Either way, is_category will format the category rather than the status
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
|
# File 'lib/jirametrics/chart_base.rb', line 212
def format_status object, board:, is_category: false, use_old_status: false
status = nil
error_message = nil
case object
when ChangeItem
id = use_old_status ? object.old_value_id : object.value_id
status = board.possible_statuses.find_by_id(id)
if status.nil?
error_message = use_old_status ? object.old_value : object.value
end
when Status
status = object
else
raise "Unexpected type: #{object.class}"
end
return "<span style='color: red'>#{error_message}</span>" if error_message
color = status_category_color status
visibility = ''
if is_category == false && board.visible_columns.none? { |column| column.status_ids.include? status.id }
visibility = icon_span(
title: "Not visible: The status #{status.name.inspect} is not mapped to any column and will not be visible",
icon: ' 👀'
)
end
text = is_category ? status.category : status
"<span title='Category: #{status.category}'>#{color_block color.name} #{text}</span>#{visibility}"
end
|
194
195
196
197
|
# File 'lib/jirametrics/chart_base.rb', line 194
def text = :none
@header_text = text unless text == :none
@header_text
end
|
#holidays(date_range: @date_range) ⇒ Object
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# File 'lib/jirametrics/chart_base.rb', line 122
def holidays date_range: @date_range
result = []
start_date = nil
end_date = nil
date_range.each do |date|
if date.saturday? || date.sunday? || holiday_dates.include?(date)
if start_date.nil?
start_date = date
else
end_date = date
end
elsif start_date
result << (start_date..(end_date || start_date))
start_date = nil
end_date = nil
end
end
result
end
|
#html_directory ⇒ Object
37
38
39
40
|
# File 'lib/jirametrics/chart_base.rb', line 37
def html_directory
pathname = Pathname.new(File.realpath(__FILE__))
"#{pathname.dirname}/html"
end
|
#icon_span(title:, icon:) ⇒ Object
244
245
246
|
# File 'lib/jirametrics/chart_base.rb', line 244
def icon_span title:, icon:
"<span title='#{title}' style='font-size: 0.8em;'>#{icon}</span>"
end
|
#label_days(days) ⇒ Object
77
78
79
80
81
|
# File 'lib/jirametrics/chart_base.rb', line 77
def label_days days
return 'unknown' if days.nil?
"#{days} day#{'s' unless days == 1}"
end
|
#label_issues(count) ⇒ Object
83
84
85
|
# File 'lib/jirametrics/chart_base.rb', line 83
def label_issues count
"#{count} issue#{'s' unless count == 1}"
end
|
#link_to_issue(issue, args = {}) ⇒ Object
105
106
107
108
109
110
111
|
# File 'lib/jirametrics/chart_base.rb', line 105
def link_to_issue issue, args = {}
attributes = { class: 'issue_key' }
.merge(args)
.collect { |key, value| "#{key}='#{value}'" }
.join(' ')
"<a href='#{issue.url}' #{attributes}>#{issue.key}</a>"
end
|
#next_id ⇒ Object
69
70
71
|
# File 'lib/jirametrics/chart_base.rb', line 69
def next_id
@@chart_counter += 1
end
|
#random_color ⇒ Object
257
258
259
|
# File 'lib/jirametrics/chart_base.rb', line 257
def random_color
"##{Random.bytes(3).unpack1('H*')}"
end
|
#render(caller_binding, file) ⇒ Object
42
43
44
45
46
47
48
49
50
51
52
|
# File 'lib/jirametrics/chart_base.rb', line 42
def render caller_binding, file
pathname = Pathname.new(File.realpath(file))
basename = pathname.basename.to_s
raise "Unexpected filename #{basename.inspect}" unless basename =~ /^(.+)\.rb$/
caller_binding.eval "chart_id='chart#{next_id}'"
erb = ERB.new file_system.load "#{html_directory}/#{$1}.erb"
erb.result(caller_binding)
end
|
#render_top_text(caller_binding) ⇒ Object
54
55
56
57
58
59
|
# File 'lib/jirametrics/chart_base.rb', line 54
def render_top_text caller_binding
result = +''
result << "<h1 class='foldable'>#{@header_text}</h1>" if @header_text
result << ERB.new(@description_text).result(caller_binding) if @description_text
result
end
|
#status_category_color(status) ⇒ Object
248
249
250
251
252
253
254
255
|
# File 'lib/jirametrics/chart_base.rb', line 248
def status_category_color status
case status.category.key
when 'new' then CssVariable['--status-category-todo-color']
when 'indeterminate' then CssVariable['--status-category-inprogress-color']
when 'done' then CssVariable['--status-category-done-color']
else CssVariable['--status-category-unknown-color'] end
end
|
#working_days_annotation ⇒ Object
143
144
145
146
147
148
149
150
151
152
153
154
155
156
|
# File 'lib/jirametrics/chart_base.rb', line 143
def working_days_annotation
holidays.each_with_index.collect do |range, index|
<<~TEXT
holiday#{index}: {
drawTime: 'beforeDraw',
type: 'box',
xMin: '#{range.begin}T00:00:00',
xMax: '#{range.end}T23:59:59',
backgroundColor: #{CssVariable.new('--non-working-days-color').to_json},
borderColor: #{CssVariable.new('--non-working-days-color').to_json}
},
TEXT
end.join
end
|
#wrap_and_render(caller_binding, file) ⇒ Object
Render the file and then wrap it with standard headers and quality checks.
62
63
64
65
66
67
|
# File 'lib/jirametrics/chart_base.rb', line 62
def wrap_and_render caller_binding, file
result = +''
result << render_top_text(caller_binding)
result << render(caller_binding, file)
result
end
|