jirametrics 2.0.1 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jirametrics/aging_work_bar_chart.rb +17 -9
  3. data/lib/jirametrics/aging_work_in_progress_chart.rb +8 -6
  4. data/lib/jirametrics/aging_work_table.rb +8 -15
  5. data/lib/jirametrics/anonymizer.rb +6 -5
  6. data/lib/jirametrics/chart_base.rb +19 -17
  7. data/lib/jirametrics/css_variable.rb +33 -0
  8. data/lib/jirametrics/cycletime_scatterplot.rb +9 -7
  9. data/lib/jirametrics/daily_wip_by_age_chart.rb +9 -9
  10. data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +22 -14
  11. data/lib/jirametrics/daily_wip_chart.rb +2 -2
  12. data/lib/jirametrics/dependency_chart.rb +18 -14
  13. data/lib/jirametrics/downloader.rb +6 -7
  14. data/lib/jirametrics/examples/aggregated_project.rb +3 -1
  15. data/lib/jirametrics/examples/standard_project.rb +10 -4
  16. data/lib/jirametrics/expedited_chart.rb +5 -2
  17. data/lib/jirametrics/exporter.rb +26 -20
  18. data/lib/jirametrics/grouping_rules.rb +7 -1
  19. data/lib/jirametrics/html/aging_work_bar_chart.erb +12 -6
  20. data/lib/jirametrics/html/aging_work_in_progress_chart.erb +8 -2
  21. data/lib/jirametrics/html/aging_work_table.erb +1 -1
  22. data/lib/jirametrics/html/cycletime_histogram.erb +9 -3
  23. data/lib/jirametrics/html/cycletime_scatterplot.erb +10 -4
  24. data/lib/jirametrics/html/daily_wip_chart.erb +10 -5
  25. data/lib/jirametrics/html/expedited_chart.erb +11 -5
  26. data/lib/jirametrics/html/hierarchy_table.erb +1 -1
  27. data/lib/jirametrics/html/index.css +174 -0
  28. data/lib/jirametrics/html/index.erb +8 -36
  29. data/lib/jirametrics/html/sprint_burndown.erb +11 -6
  30. data/lib/jirametrics/html/story_point_accuracy_chart.erb +9 -4
  31. data/lib/jirametrics/html/throughput_chart.erb +11 -5
  32. data/lib/jirametrics/html_report_config.rb +17 -2
  33. data/lib/jirametrics/issue.rb +3 -2
  34. data/lib/jirametrics/jira_gateway.rb +1 -1
  35. data/lib/jirametrics/project_config.rb +13 -18
  36. data/lib/jirametrics/settings.json +7 -0
  37. data/lib/jirametrics/sprint_burndown.rb +2 -2
  38. data/lib/jirametrics/status_collection.rb +1 -1
  39. data/lib/jirametrics/story_point_accuracy_chart.rb +8 -4
  40. data/lib/jirametrics/throughput_chart.rb +4 -1
  41. data/lib/jirametrics.rb +1 -0
  42. metadata +6 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26b8d8955807f88acf9f1cce8443d183a9fb72fce98424aba3c7c29e876a6a38
4
- data.tar.gz: '039eb9fd005ed0a036f20c72e8f9a8c4cb4bad19f030ed3cf0445bdb6afb0902'
3
+ metadata.gz: b424b3a1faea57826e0afa0d607b5edea1b824a14af63d2f4fc6b00e4f0c6190
4
+ data.tar.gz: c4fa2e71b82a118140cbcfaeb8000133917189c5449d7aa03a93d601b2df410b
5
5
  SHA512:
6
- metadata.gz: ecf2036e99a4bed4275da79e58740bac58c00d8a61120872abb0d51459631d7b7f5c80898bf4bb1212a87042c54cdb83ace95c1f86d2f2df43b8611626bbf1a0
7
- data.tar.gz: 5f01cabf487497389977fab8d341d5173e3c5835c8e42422d232aa3ba5b8aaa40af0cb975235374bf953cecf45b2615c5f6b6391560fcdcf6c0f1d77d6ae9e3f
6
+ metadata.gz: b361d8f4ab3952de3fbc3b39f70704e6817a157d9166218ad436f80f13ca80d83b26a10a002c08528da298a9e8a961ed699dbb121fc6a7edda75572209c0501f
7
+ data.tar.gz: 86ce2be29726bf2870d8a8bda0e6fd12184f09946e1cbb04fd66b8666b2ae878ec405e20f2b1658f42ccc6f6b783222853d95cb1603b34158a0ada3e4e37c897
@@ -17,10 +17,15 @@ class AgingWorkBarChart < ChartBase
17
17
  <p>
18
18
  There are potentially three bars for each issue, although a bar may be missing if the issue has no
19
19
  information relevant to that. Hovering over any of the bars will provide more details.
20
- <ol><li>The top bar tells you what status the issue is in at any time.</li>
21
- <li>The middle bar indicates blocked and stalled states. A lighter orange is stalled and a darker,
22
- reddish colour is blocked.</li>
23
- <li>The bottom bar indicated an expedited state.</li></ol>
20
+ <ol>
21
+ <li>The top bar tells you what status the issue is in at any time. The colour indicates the
22
+ status category, which will be one of #{color_block '--status-category-todo-color'} To Do,
23
+ #{color_block '--status-category-inprogress-color'} In Progress,
24
+ or #{color_block '--status-category-done-color'} Done</li>
25
+ <li>The middle bar indicates #{color_block '--blocked-color'} blocked
26
+ or #{color_block '--stalled-color'} stalled.</li>
27
+ <li>The bottom bar indicated #{color_block '--expedited-color'} expedited.</li>
28
+ </ol>
24
29
  </p>
25
30
  <p>
26
31
  The gray backgrounds indicate weekends and the red vertical line indicates the 85% point for all
@@ -65,7 +70,7 @@ class AgingWorkBarChart < ChartBase
65
70
  issue_label: issue_label,
66
71
  title_label: 'Expedited',
67
72
  stack: 'expedited',
68
- color: 'red',
73
+ color: CssVariable['--expedited-color'],
69
74
  start_date: issue_start_date
70
75
  ) { |day| issue.expedited_on_date?(day) }
71
76
  ].compact.flatten.each do |data|
@@ -109,7 +114,7 @@ class AgingWorkBarChart < ChartBase
109
114
  title: "#{issue.type} : #{change.value}"
110
115
  }],
111
116
  backgroundColor: status_category_color(status),
112
- borderColor: 'white',
117
+ borderColor: CssVariable['--aging-work-bar-chart-separator-color'],
113
118
  borderWidth: {
114
119
  top: 0,
115
120
  right: 1,
@@ -144,10 +149,13 @@ class AgingWorkBarChart < ChartBase
144
149
  end
145
150
 
146
151
  def one_block_change_data_set starting_change:, ending_time:, issue_label:, stack:, issue_start_time:
147
- color = settings['colors']['blocked']
148
- color = settings['colors']['stalled'] if starting_change.stalled?
152
+ deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['blocked_color']
153
+ deprecated message: 'blocked color should be set via css now', date: '2024-05-03' if settings['stalled_color']
154
+
155
+ color = settings['blocked_color'] || '--blocked-color'
156
+ color = settings['stalled_color'] || '--stalled-color' if starting_change.stalled?
149
157
  {
150
- backgroundColor: color,
158
+ backgroundColor: CssVariable[color],
151
159
  data: [
152
160
  {
153
161
  title: starting_change.reasons,
@@ -14,13 +14,14 @@ class AgingWorkInProgressChart < ChartBase
14
14
  description_text <<-HTML
15
15
  <p>
16
16
  This chart shows only work items that have started but not completed, grouped by the column
17
- they're currently in. Hovering over a dot will show you the ID of that work item.
18
- </p>
19
- <p>
20
- The gray area indicates the 85% mark for work items that have passed through here - 85% of
21
- previous work items left this column while still inside the gray area. Any work items above
22
- the gray area are outliers and they are the items that you should pay special attention to.
17
+ they're currently in. Hovering over a dot will show you the ID of that work item.
23
18
  </p>
19
+ <div>
20
+ The #{color_block '--non-working-days-color'} shaded area indicates the 85%
21
+ mark for work items that have passed through here; 85% of
22
+ previous work items left this column while still inside the shaded area. Any work items above
23
+ the shading are outliers and they are the items that you should pay special attention to.
24
+ </div>
24
25
  HTML
25
26
  init_configuration_block(block) do
26
27
  grouping_rules do |issue, rule|
@@ -87,6 +88,7 @@ class AgingWorkInProgressChart < ChartBase
87
88
  'label' => "#{percentage}%",
88
89
  'barPercentage' => 1.0,
89
90
  'categoryPercentage' => 1.0,
91
+ 'backgroundColor' => CssVariable['--aging-work-in-progress-chart-shading-color'],
90
92
  'data' => days_at_percentage_threshold_for_all_columns(percentage: percentage, issues: @issues).drop(1)
91
93
  }
92
94
  end
@@ -7,11 +7,7 @@ class AgingWorkTable < ChartBase
7
7
 
8
8
  def initialize block
9
9
  super()
10
- @blocked_icon = '🛑'
11
- @expedited_icon = '🔥'
12
- @stalled_icon = '🟧'
13
10
  @stalled_threshold = 5
14
- @dead_icon = '⬛'
15
11
  @dead_threshold = 45
16
12
  @age_cutoff = 0
17
13
 
@@ -54,7 +50,7 @@ class AgingWorkTable < ChartBase
54
50
  return unless issue.expedited?
55
51
 
56
52
  name = issue.raw['fields']['priority']['name']
57
- icon_span(title: "Expedited: Has a priority of &quot;#{name}&quot;", icon: @expedited_icon)
53
+ color_block '--expedited-color', title: "Expedited: Has a priority of &quot;#{name}&quot;"
58
54
  end
59
55
 
60
56
  def blocked_text issue
@@ -63,27 +59,24 @@ class AgingWorkTable < ChartBase
63
59
 
64
60
  current = issue.blocked_stalled_changes(end_time: time_range.end)[-1]
65
61
  if current.blocked?
66
- icon_span title: current.reasons, icon: @blocked_icon
62
+ color_block '--blocked-color', title: current.reasons
67
63
  elsif current.stalled?
68
64
  if current.stalled_days && current.stalled_days > @dead_threshold
69
- icon_span(
65
+ color_block(
66
+ '--dead-color',
70
67
  title: "Dead? Hasn&apos;t had any activity in #{label_days current.stalled_days}. " \
71
- 'Does anyone still care about this?',
72
- icon: @dead_icon
68
+ 'Does anyone still care about this?'
73
69
  )
74
70
  else
75
- icon_span(
76
- title: current.reasons,
77
- icon: @stalled_icon
78
- )
71
+ color_block '--stalled-color', title: current.reasons
79
72
  end
80
73
  end
81
74
  end
82
75
 
83
76
  def unmapped_status_text issue
84
77
  icon_span(
85
- title: "The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
86
- icon: ' ⁉️'
78
+ title: "Not visible: The status #{issue.status.name.inspect} is not mapped to any column and will not be visible",
79
+ icon: ' 👀'
87
80
  )
88
81
  end
89
82
 
@@ -12,6 +12,7 @@ class Anonymizer
12
12
  @all_boards = @project_config.all_boards
13
13
  @possible_statuses = @project_config.possible_statuses
14
14
  @date_adjustment = date_adjustment
15
+ @file_system = project_config.exporter.file_system
15
16
  end
16
17
 
17
18
  def run
@@ -20,7 +21,7 @@ class Anonymizer
20
21
  # anonymize_issue_statuses
21
22
  anonymize_board_names
22
23
  shift_all_dates unless @date_adjustment.zero?
23
- puts 'Anonymize done'
24
+ @file_system.log 'Anonymize done'
24
25
  end
25
26
 
26
27
  def random_phrase
@@ -30,7 +31,7 @@ class Anonymizer
30
31
  5.times do |i|
31
32
  return RandomWord.phrases.next.tr('_', ' ')
32
33
  rescue # rubocop:disable Style/RescueStandardError We don't care what exception was thrown.
33
- puts "Random word blew up on attempt #{i + 1}"
34
+ @file_system.log "Random word blew up on attempt #{i + 1}"
34
35
  end
35
36
  end
36
37
 
@@ -55,7 +56,7 @@ class Anonymizer
55
56
 
56
57
  def anonymize_column_names
57
58
  @all_boards.each_key do |board_id|
58
- puts "Anonymizing column names for board #{board_id}"
59
+ @file_system.log "Anonymizing column names for board #{board_id}"
59
60
 
60
61
  column_name = 'Column-A'
61
62
  @all_boards[board_id].visible_columns.each do |column|
@@ -93,7 +94,7 @@ class Anonymizer
93
94
  end
94
95
 
95
96
  def anonymize_issue_statuses
96
- puts 'Anonymizing issue statuses and status categories'
97
+ @file_system.log 'Anonymizing issue statuses and status categories'
97
98
  status_name_hash = build_status_name_hash
98
99
 
99
100
  @issues.each do |issue|
@@ -130,7 +131,7 @@ class Anonymizer
130
131
  end
131
132
 
132
133
  def shift_all_dates
133
- puts "Shifting all dates by #{@date_adjustment} days"
134
+ @file_system.log "Shifting all dates by #{@date_adjustment} days"
134
135
  @issues.each do |issue|
135
136
  issue.changes.each do |change|
136
137
  change.time = change.time + @date_adjustment
@@ -10,17 +10,11 @@ class ChartBase
10
10
 
11
11
  def initialize
12
12
  @chart_colors = {
13
- 'dark:Story' => 'green',
14
- 'dark:Task' => 'blue',
15
- 'dark:Bug' => 'orange',
16
- 'dark:Defect' => 'orange',
17
- 'dark:Spike' => '#9400D3', # dark purple
18
- 'light:Story' => '#90EE90',
19
- 'light:Task' => '#87CEFA',
20
- 'light:Bug' => '#ffdab9',
21
- 'light:Defect' => 'orange',
22
- 'light:Epic' => '#fafad2',
23
- 'light:Spike' => '#DDA0DD' # light purple
13
+ 'Story' => CssVariable['--type-story-color'],
14
+ 'Task' => CssVariable['--type-task-color'],
15
+ 'Bug' => CssVariable['--type-bug-color'],
16
+ 'Defect' => CssVariable['--type-bug-color'],
17
+ 'Spike' => CssVariable['--type-spike-color']
24
18
  }
25
19
  @canvas_width = 800
26
20
  @canvas_height = 200
@@ -57,8 +51,8 @@ class ChartBase
57
51
  @@chart_counter += 1
58
52
  end
59
53
 
60
- def color_for type:, shade: :dark
61
- @chart_colors["#{shade}:#{type}"] ||= random_color
54
+ def color_for type:
55
+ @chart_colors[type] ||= random_color
62
56
  end
63
57
 
64
58
  def label_days days
@@ -199,15 +193,15 @@ class ChartBase
199
193
  color = status_category_color status
200
194
 
201
195
  text = is_category ? status.category_name : status.name
202
- "<span style='color: #{color}'>#{text}</span>"
196
+ "<span title='Category: #{status.category_name}'>#{color_block color.name} #{text}</span>"
203
197
  end
204
198
 
205
199
  def status_category_color status
206
200
  case status.category_name
207
201
  when nil then 'black'
208
- when 'To Do' then 'gray'
209
- when 'In Progress' then 'blue'
210
- when 'Done' then 'green'
202
+ when 'To Do' then CssVariable['--status-category-todo-color']
203
+ when 'In Progress' then CssVariable['--status-category-inprogress-color']
204
+ when 'Done' then CssVariable['--status-category-done-color']
211
205
  end
212
206
  end
213
207
 
@@ -236,4 +230,12 @@ class ChartBase
236
230
  @issues = issues.filter_map { |i| @filter_issues_block.call(i) }.uniq
237
231
  puts @issues.collect(&:key).join(', ')
238
232
  end
233
+
234
+ def color_block color, title: nil
235
+ result = +''
236
+ result << "<div class='color_block' style='background: var(#{color});'"
237
+ result << " title=#{title.inspect}" if title
238
+ result << '></div>'
239
+ result
240
+ end
239
241
  end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CssVariable
4
+ attr_reader :name
5
+
6
+ def self.[](name)
7
+ if name.start_with? '--'
8
+ CssVariable.new name
9
+ else
10
+ name
11
+ end
12
+ end
13
+
14
+ def initialize name
15
+ @name = name
16
+ end
17
+
18
+ def to_json(*_args)
19
+ "getComputedStyle(document.body).getPropertyValue('#{@name}')"
20
+ end
21
+
22
+ def to_s
23
+ "var(#{@name})"
24
+ end
25
+
26
+ def inspect
27
+ "CssVariable['#{@name}']"
28
+ end
29
+
30
+ def == other
31
+ self.class == other.class && @name == other.name
32
+ end
33
+ end
@@ -16,16 +16,18 @@ class CycletimeScatterplot < ChartBase
16
16
  This chart shows only completed work and indicates both what day it completed as well as
17
17
  how many days it took to get done. Hovering over a dot will show you the ID of the work item.
18
18
  </p>
19
- <p>
20
- The gray line indicates the 85th percentile (<%= overall_percent_line %> days). 85% of all
19
+ <div style="padding-bottom: 0.7em">
20
+ The #{color_block '--cycletime-scatterplot-overall-trendline-color'} line indicates the 85th
21
+ percentile (<%= overall_percent_line %> days). 85% of all
21
22
  items on this chart fall on or below the line and the remaining 15% are above the line. 85%
22
23
  is a reasonable proxy for "most" so that we can say that based on this data set, we can
23
24
  predict that most work of this type will complete in <%= overall_percent_line %> days or
24
25
  less. The other lines reflect the 85% line for that respective type of work.
25
- </p>
26
- <p>
27
- The gray vertical bars indicate weekends, when theoretically we aren't working.
28
- </p>
26
+ </div>
27
+ <div>
28
+ The #{color_block '--non-working-days-color'} vertical bars indicate weekends, when theoretically
29
+ we aren't working.
30
+ </div>
29
31
  HTML
30
32
 
31
33
  init_configuration_block block do
@@ -44,7 +46,7 @@ class CycletimeScatterplot < ChartBase
44
46
 
45
47
  data_sets = create_datasets completed_issues
46
48
  overall_percent_line = calculate_percent_line(completed_issues)
47
- @percentage_lines << [overall_percent_line, 'gray']
49
+ @percentage_lines << [overall_percent_line, CssVariable['--cycletime-scatterplot-overall-trendline-color']]
48
50
 
49
51
  return "<h1>#{@header_text}</h1>No data matched the selected criteria. Nothing to show." if data_sets.empty?
50
52
 
@@ -17,7 +17,7 @@ class DailyWipByAgeChart < DailyWipChart
17
17
  <p>
18
18
  "Completed without being started" reflects the fact that while we know that it completed
19
19
  that day, we were unable to determine when it had started. These items will show up in
20
- white at the top. Note that the white is approximate because we don't know exactly when
20
+ white at the top. Note that the this grouping is approximate because we don't know exactly when
21
21
  it started so we're guessing.
22
22
  </p>
23
23
  HTML
@@ -42,11 +42,11 @@ class DailyWipByAgeChart < DailyWipChart
42
42
  def not_started stopped:, rules:, created:
43
43
  if stopped == rules.current_date
44
44
  rules.label = 'Completed but not started'
45
- rules.color = '#66FF66'
45
+ rules.color = '--wip-chart-completed-but-not-started-color'
46
46
  rules.group_priority = -1
47
47
  else
48
48
  rules.label = 'Start date unknown'
49
- rules.color = 'white'
49
+ rules.color = '--body-background'
50
50
  rules.group_priority = 11
51
51
  created_days = rules.current_date - created + 1
52
52
  rules.issue_hint = "(created: #{label_days created_days.to_i} earlier, stopped on #{stopped})"
@@ -55,7 +55,7 @@ class DailyWipByAgeChart < DailyWipChart
55
55
 
56
56
  def stopped_today rules:
57
57
  rules.label = 'Completed'
58
- rules.color = '#009900'
58
+ rules.color = '--wip-chart-completed-color'
59
59
  rules.group_priority = -2
60
60
  end
61
61
 
@@ -65,23 +65,23 @@ class DailyWipByAgeChart < DailyWipChart
65
65
  case age
66
66
  when 1
67
67
  rules.label = 'Less than a day'
68
- rules.color = '#aaaaaa'
68
+ rules.color = '--wip-chart-duration-less-than-day-color'
69
69
  rules.group_priority = 10 # Highest is top
70
70
  when 2..7
71
71
  rules.label = 'A week or less'
72
- rules.color = '#80bfff'
72
+ rules.color = '--wip-chart-duration-week-or-less-color'
73
73
  rules.group_priority = 9
74
74
  when 8..14
75
75
  rules.label = 'Two weeks or less'
76
- rules.color = '#ffd700'
76
+ rules.color = '--wip-chart-duration-two-weeks-or-less-color'
77
77
  rules.group_priority = 8
78
78
  when 15..28
79
79
  rules.label = 'Four weeks or less'
80
- rules.color = '#ce6300'
80
+ rules.color = '--wip-chart-duration-four-weeks-or-less-color'
81
81
  rules.group_priority = 7
82
82
  else
83
83
  rules.label = 'More than four weeks'
84
- rules.color = '#990000'
84
+ rules.color = '--wip-chart-duration-more-than-four-weeks-color'
85
85
  rules.group_priority = 6
86
86
  end
87
87
  end
@@ -9,18 +9,26 @@ class DailyWipByBlockedStalledChart < DailyWipChart
9
9
 
10
10
  def default_description_text
11
11
  <<-HTML
12
+ <div>
13
+ This chart highlights work that is #{color_block '--blocked-color'} blocked,
14
+ #{color_block '--stalled-color'} stalled, or
15
+ #{color_block '--wip-chart-active-color'} active on each given day.
16
+ <ul>
17
+ <li>#{color_block '--blocked-color'} Blocked could mean that the item has been flagged or it's
18
+ in a status that is configured as blocked, or it could have a link showing that it is blocked
19
+ by another item. It all depends how the report has been configured.</li>
20
+ <li>#{color_block '--stalled-color'} Stalled indicates that there has been no activity on this
21
+ item in five days.</li>
22
+ </ul>
23
+ </div>
12
24
  <p>
13
- This chart highlights work that is blocked or stalled on each given day. In Jira terms, blocked
14
- means that the issue has been "flagged". Stalled indicates that the item hasn't had any updates in 5 days.
15
- </p>
16
- <p>
17
- Note that if an item tracks as both blocked and stalled, it will only show up in the flagged totals.
25
+ Note that if an item tracks as both blocked and stalled, it will only show up in the blocked totals.
18
26
  It will not be double counted.
19
27
  </p>
20
- <p>
21
- The white section reflects items that have stopped but for which we can't identify the start date. As
28
+ <div>
29
+ The #{color_block '--body-background'} shaded section reflects items that have stopped but for which we can't identify the start date. As
22
30
  a result, we are unable to properly show the WIP for these items.
23
- </p>
31
+ </div>
24
32
  HTML
25
33
  end
26
34
 
@@ -48,29 +56,29 @@ class DailyWipByBlockedStalledChart < DailyWipChart
48
56
 
49
57
  if stopped_today && started.nil?
50
58
  rules.label = 'Completed but not started'
51
- rules.color = '#66FF66'
59
+ rules.color = '--wip-chart-completed-but-not-started-color'
52
60
  rules.group_priority = -1
53
61
  elsif stopped_today
54
62
  rules.label = 'Completed'
55
- rules.color = '#009900'
63
+ rules.color = '--wip-chart-completed-color'
56
64
  rules.group_priority = -2
57
65
  elsif started.nil?
58
66
  rules.label = 'Start date unknown'
59
- rules.color = 'white'
67
+ rules.color = '--body-background'
60
68
  rules.group_priority = 4
61
69
  elsif change&.blocked?
62
70
  rules.label = 'Blocked'
63
- rules.color = 'red'
71
+ rules.color = '--blocked-color'
64
72
  rules.group_priority = 1
65
73
  rules.issue_hint = "(#{change.reasons})"
66
74
  elsif change&.stalled?
67
75
  rules.label = 'Stalled'
68
- rules.color = 'orange'
76
+ rules.color = '--stalled-color'
69
77
  rules.group_priority = 2
70
78
  rules.issue_hint = "(#{change.reasons})"
71
79
  else
72
80
  rules.label = 'Active'
73
- rules.color = 'lightgray'
81
+ rules.color = '--wip-chart-active-color'
74
82
  rules.group_priority = 3
75
83
  end
76
84
  end
@@ -102,8 +102,8 @@ class DailyWipChart < ChartBase
102
102
  label: grouping_rule.label,
103
103
  data: data,
104
104
  backgroundColor: grouping_rule.color || random_color,
105
- borderColor: 'gray',
106
- borderWidth: grouping_rule.color == 'white' ? 1 : 0,
105
+ borderColor: CssVariable['--wip-chart-border-color'],
106
+ borderWidth: grouping_rule.color.to_s == 'var(--body-background)' ? 1 : 0,
107
107
  borderRadius: positive ? 0 : 5
108
108
  }
109
109
  end
@@ -84,7 +84,8 @@ class DependencyChart < ChartBase
84
84
  result << issue_link.other_issue.key.inspect
85
85
  result << '['
86
86
  result << 'label=' << (link_rules.label || issue_link.label).inspect
87
- result << ',color=' << (link_rules.line_color || 'black').inspect
87
+ result << ',color=' << (link_rules.line_color || 'gray').inspect
88
+ result << ',fontcolor=' << (link_rules.line_color || 'gray').inspect
88
89
  result << ',dir=both' if link_rules.bidirectional_arrows?
89
90
  result << '];'
90
91
  result
@@ -101,12 +102,26 @@ class DependencyChart < ChartBase
101
102
  tooltip = "#{issue.key}: #{issue.summary}"
102
103
  result << ",tooltip=#{tooltip[0..80].inspect}"
103
104
  unless issue_rules.color == :none
104
- result << %(,style=filled,fillcolor="#{issue_rules.color || color_for(type: issue.type, shade: :light)}")
105
+ result << %(,style=filled,fillcolor="#{issue_rules.color || color_for(type: issue.type)}")
105
106
  end
106
107
  result << ']'
107
108
  result
108
109
  end
109
110
 
111
+ # This used to pull colours from chart_base but the migration to CSS colours kept breaking
112
+ # this chart so we moved it here, until we're finished with the rest. TODO: Revisit whether
113
+ # this can also use customizable CSS colours
114
+ def color_for type:
115
+ @chart_colors = {
116
+ 'Story' => '#90EE90',
117
+ 'Task' => '#87CEFA',
118
+ 'Bug' => '#ffdab9',
119
+ 'Defect' => '#ffdab9',
120
+ 'Epic' => '#fafad2',
121
+ 'Spike' => '#DDA0DD' # light purple
122
+ }[type] ||= random_color
123
+ end
124
+
110
125
  def build_dot_graph
111
126
  issue_links = find_links
112
127
 
@@ -148,6 +163,7 @@ class DependencyChart < ChartBase
148
163
  dot_graph = []
149
164
  dot_graph << 'digraph mygraph {'
150
165
  dot_graph << 'rankdir=LR'
166
+ dot_graph << 'bgcolor="transparent"'
151
167
 
152
168
  # Sort the keys so they are proccessed in a deterministic order.
153
169
  visible_issues.values.sort_by(&:key_as_i).each do |issue|
@@ -177,18 +193,6 @@ class DependencyChart < ChartBase
177
193
  message
178
194
  end
179
195
 
180
- def default_color_for_issue issue
181
- {
182
- 'Story' => '#90EE90',
183
- 'Task' => '#87CEFA',
184
- 'Bug' => '#f08080',
185
- 'Defect' => '#f08080',
186
- 'Epic' => '#fafad2',
187
- 'Spike' => '#7fffd4',
188
- 'Sub-task' => '#dcdcdc'
189
- }[issue.type]
190
- end
191
-
192
196
  def shrink_svg svg
193
197
  scale = 0.8
194
198
  svg.sub(/width="([\d.]+)pt" height="([\d.]+)pt"/) do
@@ -10,7 +10,7 @@ class Downloader
10
10
  attr_reader :file_system
11
11
 
12
12
  # For testing only
13
- attr_reader :start_date_in_query
13
+ attr_reader :start_date_in_query, :board_id_to_filter_id
14
14
 
15
15
  def initialize download_config:, file_system:, jira_gateway:
16
16
  @metadata = {}
@@ -55,7 +55,7 @@ class Downloader
55
55
 
56
56
  def log text, both: false
57
57
  @file_system.log text
58
- puts text if both
58
+ puts text if both && !@quiet_mode
59
59
  end
60
60
 
61
61
  def find_board_ids
@@ -93,7 +93,7 @@ class Downloader
93
93
  intercept_jql = @download_config.project_config.settings['intercept_jql']
94
94
  jql = intercept_jql.call jql if intercept_jql
95
95
 
96
- log " #{jql}"
96
+ log " JQL: #{jql}"
97
97
  escaped_jql = CGI.escape jql
98
98
 
99
99
  max_results = 100
@@ -157,10 +157,10 @@ class Downloader
157
157
 
158
158
  def download_statuses
159
159
  log ' Downloading all statuses', both: true
160
- json = @jira_gateway.call_url relative_url: "/rest/api/2/status"
160
+ json = @jira_gateway.call_url relative_url: '/rest/api/2/status'
161
161
 
162
162
  @file_system.save_json(
163
- json: json,
163
+ json: json,
164
164
  filename: "#{@target_path}#{@download_config.project_config.file_prefix}_statuses.json"
165
165
  )
166
166
  end
@@ -282,9 +282,8 @@ class Downloader
282
282
 
283
283
  # Pick up any issues that had a status change in the range
284
284
  start_date_text = @start_date_in_query.strftime '%Y-%m-%d'
285
- end_date_text = today.strftime '%Y-%m-%d'
286
285
  # find_in_range = %((status changed DURING ("#{start_date_text} 00:00","#{end_date_text} 23:59")))
287
- find_in_range = %((updated >= "#{start_date_text} 00:00" AND updated <= "#{end_date_text} 23:59"))
286
+ find_in_range = %(updated >= "#{start_date_text} 00:00")
288
287
 
289
288
  segments << "(#{find_in_range} OR #{catch_all})"
290
289
  end
@@ -10,9 +10,11 @@
10
10
  # single team. For that reason, we look at slightly different things that we would on a single team board.
11
11
 
12
12
  class Exporter
13
- def aggregated_project name:, project_names:
13
+ def aggregated_project name:, project_names:, settings: {}
14
14
  project name: name do
15
15
  puts name
16
+ self.settings.merge! settings
17
+
16
18
  aggregate do
17
19
  project_names.each do |project_name|
18
20
  include_issues_from project_name
@@ -6,13 +6,19 @@
6
6
  # See https://github.com/mikebowler/jirametrics/wiki/Examples-folder for more
7
7
  class Exporter
8
8
  def standard_project name:, file_prefix:, ignore_issues: nil, starting_status: nil, boards: {},
9
- default_board: nil, anonymize: false
9
+ default_board: nil, anonymize: false, settings: {}, status_category_mappings: {}
10
10
 
11
11
  project name: name do
12
12
  puts name
13
13
  self.anonymize if anonymize
14
14
 
15
15
  settings['blocked_link_text'] = ['is blocked by']
16
+ self.settings.merge! settings
17
+
18
+ status_category_mappings.each do |status, category|
19
+ status_category_mapping status: status, category: category
20
+ end
21
+
16
22
  file_prefix file_prefix
17
23
  download do
18
24
  rolling_date_count 90
@@ -50,7 +56,7 @@ class Exporter
50
56
  type: :header
51
57
  end
52
58
 
53
- discard_changes_before status_becomes: (starting_status || :backlog)
59
+ discard_changes_before status_becomes: (starting_status || :backlog) # rubocop:disable Style/RedundantParentheses
54
60
 
55
61
  cycletime_scatterplot do
56
62
  show_trend_lines
@@ -89,7 +95,7 @@ class Exporter
89
95
  TEXT
90
96
  grouping_rules do |issue, rules|
91
97
  rules.label = issue.parent&.key || 'No parent'
92
- rules.color = 'white' if rules.label == 'No parent'
98
+ rules.color = '--body-background' if rules.label == 'No parent'
93
99
  end
94
100
  end
95
101
  expedited_chart
@@ -107,7 +113,7 @@ class Exporter
107
113
  when 'Sync'
108
114
  rules.use_bidirectional_arrows
109
115
  else
110
- # This is a link type that we don't recognized. Dump it to standard out to draw attention
116
+ # This is a link type that we don't recognize. Dump it to standard out to draw attention
111
117
  # to it.
112
118
  puts "name=#{link.name}, label=#{link.label}"
113
119
  end