Class: ChartBase

Inherits:
Object show all
Defined in:
lib/jirametrics/chart_base.rb

Constant Summary collapse

@@chart_counter =
0

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeChartBase

Returns a new instance of ChartBase.



11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/jirametrics/chart_base.rb', line 11

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 (writeonly)

Sets the attribute aggregated_project

Parameters:

  • value

    the value to set the attribute aggregated_project to.



6
7
8
# File 'lib/jirametrics/chart_base.rb', line 6

def aggregated_project=(value)
  @aggregated_project = value
end

#all_boardsObject

Returns the value of attribute all_boards.



4
5
6
# File 'lib/jirametrics/chart_base.rb', line 4

def all_boards
  @all_boards
end

#board_idObject

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_heightObject (readonly)

Returns the value of attribute canvas_height.



7
8
9
# File 'lib/jirametrics/chart_base.rb', line 7

def canvas_height
  @canvas_height
end

#canvas_widthObject (readonly)

Returns the value of attribute canvas_width.



7
8
9
# File 'lib/jirametrics/chart_base.rb', line 7

def canvas_width
  @canvas_width
end

#data_qualityObject

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_rangeObject

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_systemObject

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_datesObject

Returns the value of attribute holiday_dates.



4
5
6
# File 'lib/jirametrics/chart_base.rb', line 4

def holiday_dates
  @holiday_dates
end

#issuesObject

Returns the value of attribute issues.



4
5
6
# File 'lib/jirametrics/chart_base.rb', line 4

def issues
  @issues
end

#settingsObject

Returns the value of attribute settings.



4
5
6
# File 'lib/jirametrics/chart_base.rb', line 4

def settings
  @settings
end

#time_rangeObject

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_offsetObject

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

Returns:

  • (Boolean)


24
25
26
# File 'lib/jirametrics/chart_base.rb', line 24

def aggregated_project?
  @aggregated_project
end

#canvas(width:, height:, responsive: true) ⇒ Object



224
225
226
227
228
# File 'lib/jirametrics/chart_base.rb', line 224

def canvas width:, height:, responsive: true
  @canvas_width = width
  @canvas_height = height
  @canvas_responsive = responsive
end

#canvas_responsive?Boolean

Returns:

  • (Boolean)


230
231
232
# File 'lib/jirametrics/chart_base.rb', line 230

def canvas_responsive?
  @canvas_responsive
end

#chart_format(object) ⇒ Object



163
164
165
166
167
168
169
170
# File 'lib/jirametrics/chart_base.rb', line 163

def chart_format object
  if object.is_a? Time
    # "2022-04-09T11:38:30-07:00"
    object.strftime '%Y-%m-%dT%H:%M:%S%z'
  else
    object.to_s
  end
end

#collapsible_issues_panel(issue_descriptions, *args) ⇒ Object



106
107
108
109
110
111
112
113
# File 'lib/jirametrics/chart_base.rb', line 106

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



234
235
236
237
238
239
240
# File 'lib/jirametrics/chart_base.rb', line 234

def color_block color, title: nil
  result = +''
  result << "<div class='color_block' style='background: var(#{color});'"
  result << " title=#{title.inspect}" if title
  result << '></div>'
  result
end

#color_for(type:) ⇒ Object



68
69
70
# File 'lib/jirametrics/chart_base.rb', line 68

def color_for type:
  @chart_colors[type] ||= random_color
end

#completed_issues_in_range(include_unstarted: false) ⇒ Object



152
153
154
155
156
157
158
159
160
161
# File 'lib/jirametrics/chart_base.rb', line 152

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) && # Remove outside range
      (include_unstarted || (started_time && (stopped_time >= started_time)))
  end
end

#current_boardObject

Return only the board columns for the current board.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/jirametrics/chart_base.rb', line 137

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

#daily_chart_dataset(date_issues_list:, color:, label:, positive: true) ⇒ Object



80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/jirametrics/chart_base.rb', line 80

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_daysObject



242
243
244
245
246
247
248
249
# File 'lib/jirametrics/chart_base.rb', line 242

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 = nil) ⇒ Object



177
178
179
180
# File 'lib/jirametrics/chart_base.rb', line 177

def description_text text = nil
  @description_text = text if text
  @description_text
end

#format_integer(number) ⇒ Object



182
183
184
# File 'lib/jirametrics/chart_base.rb', line 182

def format_integer number
  number.to_s.reverse.scan(/.{1,3}/).join(',').reverse
end

#format_status(name_or_id, board:, is_category: false) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/jirametrics/chart_base.rb', line 186

def format_status name_or_id, board:, is_category: false
  begin
    statuses = board.possible_statuses.expand_statuses([name_or_id])
  rescue StatusNotFoundError
    return "<span style='color: red'>#{name_or_id}</span>"
  end

  status = statuses.first
  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_name : status.name
  "<span title='Category: #{status.category_name}'>#{color_block color.name} #{text}</span>#{visibility}"
end

#header_text(text = nil) ⇒ Object



172
173
174
175
# File 'lib/jirametrics/chart_base.rb', line 172

def header_text text = nil
  @header_text = text if text
  @header_text
end

#holidays(date_range: @date_range) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/jirametrics/chart_base.rb', line 115

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_directoryObject



28
29
30
31
32
33
34
# File 'lib/jirametrics/chart_base.rb', line 28

def html_directory
  pathname = Pathname.new(File.realpath(__FILE__))
  # basename = pathname.basename.to_s
  # raise "Unexpected filename #{basename.inspect}" unless basename.match?(/^(.+)\.rb$/)

  "#{pathname.dirname}/html"
end

#icon_span(title:, icon:) ⇒ Object



207
208
209
# File 'lib/jirametrics/chart_base.rb', line 207

def icon_span title:, icon:
  "<span title='#{title}' style='font-size: 0.8em;'>#{icon}</span>"
end

#label_days(days) ⇒ Object



72
73
74
# File 'lib/jirametrics/chart_base.rb', line 72

def label_days days
  "#{days} day#{'s' unless days == 1}"
end

#label_issues(count) ⇒ Object



76
77
78
# File 'lib/jirametrics/chart_base.rb', line 76

def label_issues count
  "#{count} issue#{'s' unless count == 1}"
end


98
99
100
101
102
103
104
# File 'lib/jirametrics/chart_base.rb', line 98

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_idObject



64
65
66
# File 'lib/jirametrics/chart_base.rb', line 64

def next_id
  @@chart_counter += 1
end

#random_colorObject



220
221
222
# File 'lib/jirametrics/chart_base.rb', line 220

def random_color
  "##{Random.bytes(3).unpack1('H*')}"
end

#render(caller_binding, file) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/jirametrics/chart_base.rb', line 36

def render caller_binding, file
  pathname = Pathname.new(File.realpath(file))
  basename = pathname.basename.to_s
  raise "Unexpected filename #{basename.inspect}" unless basename =~ /^(.+)\.rb$/

  # Insert a incrementing chart_id so that all the chart names on the page are unique
  caller_binding.eval "chart_id='chart#{next_id}'" # chart_id=chart3

  # @html_directory = "#{pathname.dirname}/html"
  erb = ERB.new file_system.load "#{html_directory}/#{$1}.erb"
  erb.result(caller_binding)
end

#render_top_text(caller_binding) ⇒ Object



49
50
51
52
53
54
# File 'lib/jirametrics/chart_base.rb', line 49

def render_top_text caller_binding
  result = +''
  result << "<h1>#{@header_text}</h1>" if @header_text
  result << ERB.new(@description_text).result(caller_binding) if @description_text
  result
end

#status_category_color(status) ⇒ Object



211
212
213
214
215
216
217
218
# File 'lib/jirametrics/chart_base.rb', line 211

def status_category_color status
  case status.category_name
  when 'To Do' then CssVariable['--status-category-todo-color']
  when 'In Progress' then CssVariable['--status-category-inprogress-color']
  when 'Done' then CssVariable['--status-category-done-color']
  else 'black' # Theoretically impossible but seen in prod.
  end
end

#wrap_and_render(caller_binding, file) ⇒ Object

Render the file and then wrap it with standard headers and quality checks.



57
58
59
60
61
62
# File 'lib/jirametrics/chart_base.rb', line 57

def wrap_and_render caller_binding, file
  result = +''
  result << render_top_text(caller_binding)
  result << render(caller_binding, file)
  result
end