jirametrics 2.22 → 2.24

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jirametrics/aggregate_config.rb +10 -2
  3. data/lib/jirametrics/aging_work_bar_chart.rb +15 -1
  4. data/lib/jirametrics/aging_work_table.rb +1 -1
  5. data/lib/jirametrics/anonymizer.rb +74 -1
  6. data/lib/jirametrics/atlassian_document_format.rb +104 -93
  7. data/lib/jirametrics/blocked_stalled_change.rb +5 -3
  8. data/lib/jirametrics/board.rb +20 -8
  9. data/lib/jirametrics/board_feature.rb +14 -0
  10. data/lib/jirametrics/change_item.rb +4 -3
  11. data/lib/jirametrics/chart_base.rb +87 -1
  12. data/lib/jirametrics/css_variable.rb +1 -1
  13. data/lib/jirametrics/{cycletime_config.rb → cycle_time_config.rb} +1 -2
  14. data/lib/jirametrics/cycletime_histogram.rb +15 -103
  15. data/lib/jirametrics/cycletime_scatterplot.rb +8 -97
  16. data/lib/jirametrics/daily_view.rb +32 -9
  17. data/lib/jirametrics/daily_wip_chart.rb +27 -7
  18. data/lib/jirametrics/data_quality_report.rb +31 -7
  19. data/lib/jirametrics/download_config.rb +15 -0
  20. data/lib/jirametrics/downloader.rb +76 -5
  21. data/lib/jirametrics/downloader_for_cloud.rb +39 -0
  22. data/lib/jirametrics/downloader_for_data_center.rb +2 -1
  23. data/lib/jirametrics/estimate_accuracy_chart.rb +42 -4
  24. data/lib/jirametrics/examples/aggregated_project.rb +1 -1
  25. data/lib/jirametrics/examples/standard_project.rb +20 -9
  26. data/lib/jirametrics/expedited_chart.rb +2 -0
  27. data/lib/jirametrics/exporter.rb +3 -1
  28. data/lib/jirametrics/file_system.rb +4 -0
  29. data/lib/jirametrics/flow_efficiency_scatterplot.rb +2 -0
  30. data/lib/jirametrics/github_gateway.rb +106 -0
  31. data/lib/jirametrics/groupable_issue_chart.rb +2 -0
  32. data/lib/jirametrics/grouping_rules.rb +21 -3
  33. data/lib/jirametrics/html/aging_work_bar_chart.erb +3 -4
  34. data/lib/jirametrics/html/aging_work_table.erb +3 -0
  35. data/lib/jirametrics/html/daily_wip_chart.erb +5 -4
  36. data/lib/jirametrics/html/estimate_accuracy_chart.erb +2 -12
  37. data/lib/jirametrics/html/expedited_chart.erb +3 -13
  38. data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +2 -8
  39. data/lib/jirametrics/html/index.css +114 -0
  40. data/lib/jirametrics/html/index.erb +5 -0
  41. data/lib/jirametrics/html/index.js +52 -2
  42. data/lib/jirametrics/html/sprint_burndown.erb +7 -13
  43. data/lib/jirametrics/html/throughput_chart.erb +5 -8
  44. data/lib/jirametrics/html/{cycletime_histogram.erb → time_based_histogram.erb} +57 -59
  45. data/lib/jirametrics/html/{cycletime_scatterplot.erb → time_based_scatterplot.erb} +3 -4
  46. data/lib/jirametrics/html_report_config.rb +2 -0
  47. data/lib/jirametrics/issue.rb +84 -95
  48. data/lib/jirametrics/issue_printer.rb +97 -0
  49. data/lib/jirametrics/jira_gateway.rb +6 -3
  50. data/lib/jirametrics/project_config.rb +66 -6
  51. data/lib/jirametrics/pull_request.rb +30 -0
  52. data/lib/jirametrics/pull_request_review.rb +13 -0
  53. data/lib/jirametrics/raw_javascript.rb +4 -0
  54. data/lib/jirametrics/settings.json +3 -1
  55. data/lib/jirametrics/sprint_burndown.rb +2 -0
  56. data/lib/jirametrics/stitcher.rb +2 -1
  57. data/lib/jirametrics/throughput_chart.rb +7 -1
  58. data/lib/jirametrics/time_based_histogram.rb +139 -0
  59. data/lib/jirametrics/time_based_scatterplot.rb +100 -0
  60. metadata +12 -5
@@ -0,0 +1,139 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jirametrics/groupable_issue_chart'
4
+
5
+ class TimeBasedHistogram < ChartBase
6
+ include GroupableIssueChart
7
+
8
+ attr_reader :show_stats
9
+
10
+ def initialize
11
+ super
12
+
13
+ percentiles [50, 85, 98]
14
+ @show_stats = true
15
+ end
16
+
17
+ def percentiles percs = nil
18
+ @percentiles = percs unless percs.nil?
19
+ @percentiles
20
+ end
21
+
22
+ def disable_stats
23
+ @show_stats = false
24
+ end
25
+
26
+ def run
27
+ histogram_items = all_items
28
+ rules_to_items = group_issues histogram_items
29
+
30
+ the_stats = {}
31
+
32
+ overall_histogram = histogram_data_for(items: histogram_items).transform_values(&:size)
33
+ the_stats[:all] = stats_for histogram_data: overall_histogram, percentiles: @percentiles
34
+ data_sets = rules_to_items.keys.collect do |rules|
35
+ the_label = rules.label
36
+ the_histogram = histogram_data_for(items: rules_to_items[rules])
37
+ if @show_stats
38
+ the_stats[the_label] = stats_for(
39
+ histogram_data: the_histogram.transform_values(&:size), percentiles: @percentiles
40
+ )
41
+ end
42
+
43
+ data_set_for(
44
+ histogram_data: the_histogram,
45
+ label: the_label,
46
+ color: rules.color
47
+ )
48
+ end
49
+
50
+ if data_sets.empty?
51
+ return "<h1 class='foldable'>#{@header_text}</h1>" \
52
+ '<div>No data matched the selected criteria. Nothing to show.</div>'
53
+ end
54
+
55
+ wrap_and_render(binding, __FILE__)
56
+ end
57
+
58
+ def histogram_data_for items:
59
+ items_hash = {}
60
+ items.each do |item|
61
+ days = value_for_item item
62
+ (items_hash[days] ||= []) << item if days.positive?
63
+ end
64
+ items_hash
65
+ end
66
+
67
+ def stats_for histogram_data:, percentiles:
68
+ return {} if histogram_data.empty?
69
+
70
+ total_values = histogram_data.values.sum
71
+
72
+ # Calculate the average
73
+ weighted_sum = histogram_data.reduce(0) { |sum, (value, frequency)| sum + (value * frequency) }
74
+ average = total_values.zero? ? 0 : weighted_sum.to_f / total_values
75
+
76
+ # Find the mode (or modes!) and the spread of the distribution
77
+ sorted_histogram = histogram_data.sort_by { |_value, frequency| frequency }
78
+ max_freq = sorted_histogram[-1][1]
79
+ mode = sorted_histogram.select { |_v, f| f == max_freq }
80
+
81
+ minmax = histogram_data.keys.minmax
82
+
83
+ # Calculate percentiles
84
+ sorted_values = histogram_data.keys.sort
85
+ cumulative_counts = {}
86
+ cumulative_sum = 0
87
+
88
+ sorted_values.each do |value|
89
+ cumulative_sum += histogram_data[value]
90
+ cumulative_counts[value] = cumulative_sum
91
+ end
92
+
93
+ percentile_results = {}
94
+ percentiles.each do |percentile|
95
+ rank = (percentile / 100.0) * total_values
96
+ percentile_value = sorted_values.find { |value| cumulative_counts[value] >= rank }
97
+ percentile_results[percentile] = percentile_value
98
+ end
99
+
100
+ {
101
+ average: average,
102
+ mode: mode.collect(&:first).sort,
103
+ min: minmax[0],
104
+ max: minmax[1],
105
+ percentiles: percentile_results
106
+ }
107
+ end
108
+
109
+ def sort_items items
110
+ items
111
+ end
112
+
113
+ def label_for_item item, hint:
114
+ raise NotImplementedError, "#{self.class} must implement label_for_item"
115
+ end
116
+
117
+ def data_set_for histogram_data:, label:, color:
118
+ {
119
+ type: 'bar',
120
+ label: label,
121
+ data: histogram_data.keys.sort.filter_map do |days|
122
+ items = histogram_data[days]
123
+ next if items.empty?
124
+
125
+ {
126
+ x: days,
127
+ y: items.size,
128
+ title: [title_for_item(count: items.size, value: days)] +
129
+ sort_items(items).collect do |item|
130
+ hint = @issue_hints&.fetch(item, nil)
131
+ label_for_item(item, hint: hint)
132
+ end
133
+ }
134
+ end,
135
+ backgroundColor: color,
136
+ borderRadius: 0
137
+ }
138
+ end
139
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'jirametrics/groupable_issue_chart'
4
+
5
+ class TimeBasedScatterplot < ChartBase
6
+ include GroupableIssueChart
7
+
8
+ def initialize
9
+ super
10
+
11
+ @percentage_lines = []
12
+ @highest_y_value = 0
13
+ end
14
+
15
+ def run
16
+ items = all_items
17
+ data_sets = create_datasets items
18
+ overall_percent_line = calculate_percent_line(items)
19
+ @percentage_lines << [overall_percent_line, CssVariable['--cycletime-scatterplot-overall-trendline-color']]
20
+
21
+ return "<h1>#{@header_text}</h1>No data matched the selected criteria. Nothing to show." if data_sets.empty?
22
+
23
+ wrap_and_render(binding, __FILE__)
24
+ end
25
+
26
+ def create_datasets items
27
+ data_sets = []
28
+
29
+ group_issues(items).each do |rules, items_by_type|
30
+ label = rules.label
31
+ color = rules.color
32
+ percent_line = calculate_percent_line items_by_type
33
+ data = items_by_type.filter_map { |item| data_for_item(item) }
34
+ data_sets << {
35
+ label: "#{label} (85% at #{label_days(percent_line)})",
36
+ data: data,
37
+ fill: false,
38
+ showLine: false,
39
+ backgroundColor: color
40
+ }
41
+
42
+ data_sets << trend_line_data_set(label: label, data: data, color: color)
43
+
44
+ @percentage_lines << [percent_line, color]
45
+ end
46
+ data_sets
47
+ end
48
+
49
+ def show_trend_lines
50
+ @show_trend_lines = true
51
+ end
52
+
53
+ def trend_line_data_set label:, data:, color:
54
+ points = data.collect do |hash|
55
+ [Time.parse(hash[:x]).to_i, hash[:y]]
56
+ end
57
+
58
+ # The trend calculation works with numbers only so convert Time to an int and back
59
+ calculator = TrendLineCalculator.new(points)
60
+ data_points = calculator.chart_datapoints(
61
+ range: time_range.begin.to_i..time_range.end.to_i,
62
+ max_y: @highest_y_value
63
+ )
64
+ data_points.each do |point_hash|
65
+ point_hash[:x] = chart_format Time.at(point_hash[:x])
66
+ end
67
+
68
+ {
69
+ type: 'line',
70
+ label: "#{label} Trendline",
71
+ data: data_points,
72
+ fill: false,
73
+ borderWidth: 1,
74
+ markerType: 'none',
75
+ borderColor: color,
76
+ borderDash: [6, 3],
77
+ pointStyle: 'dash',
78
+ hidden: !@show_trend_lines
79
+ }
80
+ end
81
+
82
+ def data_for_item item
83
+ y = y_value(item)
84
+ return nil if y < 1 # These will get called out on the quality report
85
+
86
+ @highest_y_value = y if @highest_y_value < y
87
+
88
+ {
89
+ y: y,
90
+ x: chart_format(x_value(item)),
91
+ title: [title_value(item)]
92
+ }
93
+ end
94
+
95
+ def calculate_percent_line items
96
+ times = items.collect { |item| y_value(item) }
97
+ index = times.size * 85 / 100
98
+ times.sort[index]
99
+ end
100
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jirametrics
3
3
  version: !ruby/object:Gem::Version
4
- version: '2.22'
4
+ version: '2.24'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Bowler
@@ -71,12 +71,13 @@ files:
71
71
  - lib/jirametrics/board.rb
72
72
  - lib/jirametrics/board_column.rb
73
73
  - lib/jirametrics/board_config.rb
74
+ - lib/jirametrics/board_feature.rb
74
75
  - lib/jirametrics/board_movement_calculator.rb
75
76
  - lib/jirametrics/change_item.rb
76
77
  - lib/jirametrics/chart_base.rb
77
78
  - lib/jirametrics/columns_config.rb
78
79
  - lib/jirametrics/css_variable.rb
79
- - lib/jirametrics/cycletime_config.rb
80
+ - lib/jirametrics/cycle_time_config.rb
80
81
  - lib/jirametrics/cycletime_histogram.rb
81
82
  - lib/jirametrics/cycletime_scatterplot.rb
82
83
  - lib/jirametrics/daily_view.rb
@@ -100,6 +101,7 @@ files:
100
101
  - lib/jirametrics/file_system.rb
101
102
  - lib/jirametrics/fix_version.rb
102
103
  - lib/jirametrics/flow_efficiency_scatterplot.rb
104
+ - lib/jirametrics/github_gateway.rb
103
105
  - lib/jirametrics/groupable_issue_chart.rb
104
106
  - lib/jirametrics/grouping_rules.rb
105
107
  - lib/jirametrics/hierarchy_table.rb
@@ -107,8 +109,6 @@ files:
107
109
  - lib/jirametrics/html/aging_work_in_progress_chart.erb
108
110
  - lib/jirametrics/html/aging_work_table.erb
109
111
  - lib/jirametrics/html/collapsible_issues_panel.erb
110
- - lib/jirametrics/html/cycletime_histogram.erb
111
- - lib/jirametrics/html/cycletime_scatterplot.erb
112
112
  - lib/jirametrics/html/daily_wip_chart.erb
113
113
  - lib/jirametrics/html/estimate_accuracy_chart.erb
114
114
  - lib/jirametrics/html/expedited_chart.erb
@@ -119,13 +119,18 @@ files:
119
119
  - lib/jirametrics/html/index.js
120
120
  - lib/jirametrics/html/sprint_burndown.erb
121
121
  - lib/jirametrics/html/throughput_chart.erb
122
+ - lib/jirametrics/html/time_based_histogram.erb
123
+ - lib/jirametrics/html/time_based_scatterplot.erb
122
124
  - lib/jirametrics/html_generator.rb
123
125
  - lib/jirametrics/html_report_config.rb
124
126
  - lib/jirametrics/issue.rb
125
127
  - lib/jirametrics/issue_collection.rb
126
128
  - lib/jirametrics/issue_link.rb
129
+ - lib/jirametrics/issue_printer.rb
127
130
  - lib/jirametrics/jira_gateway.rb
128
131
  - lib/jirametrics/project_config.rb
132
+ - lib/jirametrics/pull_request.rb
133
+ - lib/jirametrics/pull_request_review.rb
129
134
  - lib/jirametrics/raw_javascript.rb
130
135
  - lib/jirametrics/rules.rb
131
136
  - lib/jirametrics/self_or_issue_dispatcher.rb
@@ -137,6 +142,8 @@ files:
137
142
  - lib/jirametrics/status_collection.rb
138
143
  - lib/jirametrics/stitcher.rb
139
144
  - lib/jirametrics/throughput_chart.rb
145
+ - lib/jirametrics/time_based_histogram.rb
146
+ - lib/jirametrics/time_based_scatterplot.rb
140
147
  - lib/jirametrics/tree_organizer.rb
141
148
  - lib/jirametrics/trend_line_calculator.rb
142
149
  - lib/jirametrics/user.rb
@@ -163,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
163
170
  - !ruby/object:Gem::Version
164
171
  version: '0'
165
172
  requirements: []
166
- rubygems_version: 3.6.9
173
+ rubygems_version: 4.0.8
167
174
  specification_version: 4
168
175
  summary: Extract Jira metrics
169
176
  test_files: []