jirametrics 1.0.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 (68) hide show
  1. checksums.yaml +7 -0
  2. data/bin/jirametrics +4 -0
  3. data/lib/jirametrics/aggregate_config.rb +89 -0
  4. data/lib/jirametrics/aging_work_bar_chart.rb +235 -0
  5. data/lib/jirametrics/aging_work_in_progress_chart.rb +148 -0
  6. data/lib/jirametrics/aging_work_table.rb +149 -0
  7. data/lib/jirametrics/anonymizer.rb +186 -0
  8. data/lib/jirametrics/blocked_stalled_change.rb +43 -0
  9. data/lib/jirametrics/board.rb +85 -0
  10. data/lib/jirametrics/board_column.rb +14 -0
  11. data/lib/jirametrics/board_config.rb +31 -0
  12. data/lib/jirametrics/change_item.rb +80 -0
  13. data/lib/jirametrics/chart_base.rb +239 -0
  14. data/lib/jirametrics/columns_config.rb +42 -0
  15. data/lib/jirametrics/cycletime_config.rb +69 -0
  16. data/lib/jirametrics/cycletime_histogram.rb +74 -0
  17. data/lib/jirametrics/cycletime_scatterplot.rb +128 -0
  18. data/lib/jirametrics/daily_wip_by_age_chart.rb +88 -0
  19. data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +77 -0
  20. data/lib/jirametrics/daily_wip_chart.rb +123 -0
  21. data/lib/jirametrics/data_quality_report.rb +278 -0
  22. data/lib/jirametrics/dependency_chart.rb +217 -0
  23. data/lib/jirametrics/discard_changes_before.rb +37 -0
  24. data/lib/jirametrics/download_config.rb +41 -0
  25. data/lib/jirametrics/downloader.rb +337 -0
  26. data/lib/jirametrics/examples/aggregated_project.rb +36 -0
  27. data/lib/jirametrics/examples/standard_project.rb +111 -0
  28. data/lib/jirametrics/expedited_chart.rb +169 -0
  29. data/lib/jirametrics/experimental/generator.rb +209 -0
  30. data/lib/jirametrics/experimental/info.rb +77 -0
  31. data/lib/jirametrics/exporter.rb +127 -0
  32. data/lib/jirametrics/file_config.rb +119 -0
  33. data/lib/jirametrics/fix_version.rb +21 -0
  34. data/lib/jirametrics/groupable_issue_chart.rb +44 -0
  35. data/lib/jirametrics/grouping_rules.rb +13 -0
  36. data/lib/jirametrics/hierarchy_table.rb +31 -0
  37. data/lib/jirametrics/html/aging_work_bar_chart.erb +72 -0
  38. data/lib/jirametrics/html/aging_work_in_progress_chart.erb +52 -0
  39. data/lib/jirametrics/html/aging_work_table.erb +60 -0
  40. data/lib/jirametrics/html/collapsible_issues_panel.erb +32 -0
  41. data/lib/jirametrics/html/cycletime_histogram.erb +41 -0
  42. data/lib/jirametrics/html/cycletime_scatterplot.erb +103 -0
  43. data/lib/jirametrics/html/daily_wip_chart.erb +63 -0
  44. data/lib/jirametrics/html/data_quality_report.erb +126 -0
  45. data/lib/jirametrics/html/expedited_chart.erb +67 -0
  46. data/lib/jirametrics/html/hierarchy_table.erb +29 -0
  47. data/lib/jirametrics/html/index.erb +66 -0
  48. data/lib/jirametrics/html/sprint_burndown.erb +116 -0
  49. data/lib/jirametrics/html/story_point_accuracy_chart.erb +57 -0
  50. data/lib/jirametrics/html/throughput_chart.erb +65 -0
  51. data/lib/jirametrics/html_report_config.rb +217 -0
  52. data/lib/jirametrics/issue.rb +521 -0
  53. data/lib/jirametrics/issue_link.rb +60 -0
  54. data/lib/jirametrics/json_file_loader.rb +9 -0
  55. data/lib/jirametrics/project_config.rb +442 -0
  56. data/lib/jirametrics/rules.rb +34 -0
  57. data/lib/jirametrics/self_or_issue_dispatcher.rb +15 -0
  58. data/lib/jirametrics/sprint.rb +43 -0
  59. data/lib/jirametrics/sprint_burndown.rb +335 -0
  60. data/lib/jirametrics/sprint_issue_change_data.rb +31 -0
  61. data/lib/jirametrics/status.rb +26 -0
  62. data/lib/jirametrics/status_collection.rb +67 -0
  63. data/lib/jirametrics/story_point_accuracy_chart.rb +139 -0
  64. data/lib/jirametrics/throughput_chart.rb +91 -0
  65. data/lib/jirametrics/tree_organizer.rb +96 -0
  66. data/lib/jirametrics/trend_line_calculator.rb +74 -0
  67. data/lib/jirametrics.rb +85 -0
  68. metadata +167 -0
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TreeOrganizer
4
+ attr_reader :cyclical_links
5
+
6
+ class Node
7
+ attr_accessor :parent, :issue
8
+ attr_reader :children
9
+
10
+ def initialize issue: nil
11
+ @issue = issue
12
+ @children = []
13
+ end
14
+
15
+ def to_s
16
+ "Node(#{@issue&.key || 'Root'}, parent: #{@parent&.issue&.key || 'Root'}, children: #{@children.inspect})"
17
+ end
18
+
19
+ def inspect
20
+ to_s
21
+ end
22
+
23
+ def <=> other
24
+ issue <=> other.issue
25
+ end
26
+
27
+ def children?
28
+ !@children.empty?
29
+ end
30
+ end
31
+
32
+ def initialize issues:
33
+ @issues = issues
34
+ @root = Node.new
35
+ @cyclical_links = []
36
+ @node_hash = {}
37
+
38
+ @issues.each do |issue|
39
+ add issue
40
+ end
41
+ end
42
+
43
+ def add issue, bread_crumbs = []
44
+ parent_node = @root
45
+
46
+ issue_node = find_node issue.key
47
+ return issue_node if issue_node
48
+
49
+ parent_issue = issue.parent
50
+ if parent_issue
51
+ cyclical = bread_crumbs.include? parent_issue.key
52
+ bread_crumbs << issue.key
53
+ if cyclical
54
+ @cyclical_links << bread_crumbs.reverse
55
+ else
56
+ parent_node = find_node parent_issue.key
57
+ end
58
+ parent_node = add parent_issue, bread_crumbs if parent_node.nil?
59
+ end
60
+
61
+ issue_node = Node.new(issue: issue)
62
+ if parent_node
63
+ parent_node.children << issue_node
64
+ @node_hash[issue_node.issue.key] = issue_node
65
+ end
66
+ issue_node
67
+ end
68
+
69
+ def find_node issue_key
70
+ @node_hash[issue_key]
71
+ # @root.children.find { |node| node.issue.key == issue_key }
72
+ end
73
+
74
+ def find_issue issue_key
75
+ @issues.find { |issue| issue.key == issue_key }
76
+ end
77
+
78
+ def flattened_issue_keys
79
+ flattened_nodes.collect do |node, depth|
80
+ [node.issue.key, depth]
81
+ end
82
+ end
83
+
84
+ def flattened_nodes
85
+ list = []
86
+ walk_children parent: @root, list: list, depth: 1
87
+ list
88
+ end
89
+
90
+ def walk_children parent:, list:, depth:
91
+ parent.children.sort.each do |node|
92
+ list << [node, depth]
93
+ walk_children parent: node, list: list, depth: depth + 1
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ class TrendLineCalculator
4
+ # Using math from https://math.stackexchange.com/questions/204020/what-is-the-equation-used-to-calculate-a-linear-trendline
5
+
6
+ def initialize points
7
+ # We can't do trend calculations with less than two data points
8
+ @valid = points.size >= 2
9
+ return unless valid?
10
+
11
+ sum_of_x = points.collect { |x, _y| x }.sum
12
+ sum_of_y = points.collect { |_x, y| y }.sum
13
+ sum_of_xy = points.collect { |x, y| x * y }.sum
14
+ sum_of_x2 = points.collect { |x, _y| x * x }.sum
15
+ n = points.size.to_f
16
+
17
+ @slope = ((n * sum_of_xy) - (sum_of_x * sum_of_y)) / ((n * sum_of_x2) - (sum_of_x * sum_of_x))
18
+ @offset = (sum_of_y - (@slope * sum_of_x)) / n
19
+
20
+ @valid = false if vertical?
21
+ end
22
+
23
+ def valid? = @valid
24
+ def horizontal? = @slope.zero?
25
+ def vertical? = @slope.nan? || @slope.infinite?
26
+
27
+ def calc_y x: # rubocop:disable Naming/MethodParameterName
28
+ ((x * @slope) + @offset).to_i
29
+ end
30
+
31
+ def line_crosses_at y: # rubocop:disable Naming/MethodParameterName
32
+ raise "line will never cross #{y}. Trend is perfectly horizontal" if horizontal?
33
+
34
+ ((y.to_f - @offset) / @slope).to_i
35
+ end
36
+
37
+ # If the trend line can't be calculated then return an empty array. Otherwise, return
38
+ # an array with two (x,y) points, with which you can draw the trend line.
39
+ def chart_datapoints range:, max_y:, min_y: 0
40
+ raise 'max_y is nil' if max_y.nil?
41
+ return [] unless valid?
42
+
43
+ data_points = []
44
+ x_start = range.begin
45
+ y_start = calc_y(x: range.begin).to_i
46
+ x_end = range.end
47
+ y_end = calc_y(x: range.end).to_i
48
+
49
+ if y_start < min_y
50
+ x_start = line_crosses_at y: 0
51
+ y_start = 0
52
+ end
53
+
54
+ if y_start > max_y
55
+ x_start = line_crosses_at y: max_y
56
+ y_start = max_y
57
+ end
58
+
59
+ if y_end < min_y
60
+ x_end = line_crosses_at y: 0
61
+ y_end = 0
62
+ end
63
+
64
+ if y_end > max_y
65
+ x_end = line_crosses_at y: max_y
66
+ y_end = max_y
67
+ end
68
+
69
+ data_points << { x: x_start, y: y_start }
70
+ data_points << { x: x_end, y: y_end }
71
+
72
+ data_points
73
+ end
74
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ class JiraMetrics < Thor
6
+ option :config
7
+ desc 'export', "Export data into either reports or CSV's as per the configuration"
8
+ def export
9
+ load_config options[:config]
10
+ Exporter.instance.export
11
+ end
12
+
13
+ option :config
14
+ desc 'download', 'Download data from Jira'
15
+ def download
16
+ load_config options[:config]
17
+ Exporter.instance.download
18
+ end
19
+
20
+ private
21
+
22
+ def load_config config_file
23
+ config_file = './config.rb' if config_file.nil?
24
+
25
+ if File.exist? config_file
26
+ # The fact that File.exist can see the file does not mean that require will be
27
+ # able to load it. Convert this to an absolute pathname now for require.
28
+ config_file = File.absolute_path(config_file).to_s
29
+ else
30
+ puts "Cannot find configuration file #{config_file.inspect}"
31
+ exit 1
32
+ end
33
+
34
+ require 'jirametrics/chart_base'
35
+ require 'jirametrics/rules'
36
+ require 'jirametrics/grouping_rules'
37
+ require 'jirametrics/daily_wip_chart'
38
+ require 'jirametrics/groupable_issue_chart'
39
+ require 'jirametrics/discard_changes_before'
40
+
41
+ require 'jirametrics/aggregate_config'
42
+ require 'jirametrics/expedited_chart'
43
+ require 'jirametrics/board_config'
44
+ require 'jirametrics/file_config'
45
+ require 'jirametrics/trend_line_calculator'
46
+ require 'jirametrics/status'
47
+ require 'jirametrics/issue_link'
48
+ require 'jirametrics/story_point_accuracy_chart'
49
+ require 'jirametrics/status_collection'
50
+ require 'jirametrics/sprint'
51
+ require 'jirametrics/issue'
52
+ require 'jirametrics/daily_wip_by_age_chart'
53
+ require 'jirametrics/aging_work_in_progress_chart'
54
+ require 'jirametrics/cycletime_scatterplot'
55
+ require 'jirametrics/sprint_issue_change_data'
56
+ require 'jirametrics/cycletime_histogram'
57
+ require 'jirametrics/daily_wip_by_blocked_stalled_chart'
58
+ require 'jirametrics/html_report_config'
59
+ require 'jirametrics/data_quality_report'
60
+ require 'jirametrics/aging_work_bar_chart'
61
+ require 'jirametrics/change_item'
62
+ require 'jirametrics/project_config'
63
+ require 'jirametrics/dependency_chart'
64
+ require 'jirametrics/cycletime_config'
65
+ require 'jirametrics/tree_organizer'
66
+ require 'jirametrics/aging_work_table'
67
+ require 'jirametrics/sprint_burndown'
68
+ require 'jirametrics/self_or_issue_dispatcher'
69
+ require 'jirametrics/throughput_chart'
70
+ require 'jirametrics/exporter'
71
+ require 'jirametrics/json_file_loader'
72
+ require 'jirametrics/blocked_stalled_change'
73
+ require 'jirametrics/board_column'
74
+ require 'jirametrics/anonymizer'
75
+ require 'jirametrics/downloader'
76
+ require 'jirametrics/fix_version'
77
+ require 'jirametrics/download_config'
78
+ require 'jirametrics/columns_config'
79
+ require 'jirametrics/hierarchy_table'
80
+ require 'jirametrics/board'
81
+ require config_file
82
+ end
83
+
84
+ # Dir.foreach('lib/jirametrics') {|file| puts "require 'jirametrics/#{$1}'" if file =~ /^(.+)\.rb$/}
85
+ end
metadata ADDED
@@ -0,0 +1,167 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jirametrics
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mike Bowler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: random-word
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: require_all
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.2
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.2
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.4'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.4'
69
+ description: Tool to extract metrics from Jira and export to either a report or to
70
+ CSV files
71
+ email: mbowler@gargoylesoftware.com
72
+ executables:
73
+ - jirametrics
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - bin/jirametrics
78
+ - lib/jirametrics.rb
79
+ - lib/jirametrics/aggregate_config.rb
80
+ - lib/jirametrics/aging_work_bar_chart.rb
81
+ - lib/jirametrics/aging_work_in_progress_chart.rb
82
+ - lib/jirametrics/aging_work_table.rb
83
+ - lib/jirametrics/anonymizer.rb
84
+ - lib/jirametrics/blocked_stalled_change.rb
85
+ - lib/jirametrics/board.rb
86
+ - lib/jirametrics/board_column.rb
87
+ - lib/jirametrics/board_config.rb
88
+ - lib/jirametrics/change_item.rb
89
+ - lib/jirametrics/chart_base.rb
90
+ - lib/jirametrics/columns_config.rb
91
+ - lib/jirametrics/cycletime_config.rb
92
+ - lib/jirametrics/cycletime_histogram.rb
93
+ - lib/jirametrics/cycletime_scatterplot.rb
94
+ - lib/jirametrics/daily_wip_by_age_chart.rb
95
+ - lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb
96
+ - lib/jirametrics/daily_wip_chart.rb
97
+ - lib/jirametrics/data_quality_report.rb
98
+ - lib/jirametrics/dependency_chart.rb
99
+ - lib/jirametrics/discard_changes_before.rb
100
+ - lib/jirametrics/download_config.rb
101
+ - lib/jirametrics/downloader.rb
102
+ - lib/jirametrics/examples/aggregated_project.rb
103
+ - lib/jirametrics/examples/standard_project.rb
104
+ - lib/jirametrics/expedited_chart.rb
105
+ - lib/jirametrics/experimental/generator.rb
106
+ - lib/jirametrics/experimental/info.rb
107
+ - lib/jirametrics/exporter.rb
108
+ - lib/jirametrics/file_config.rb
109
+ - lib/jirametrics/fix_version.rb
110
+ - lib/jirametrics/groupable_issue_chart.rb
111
+ - lib/jirametrics/grouping_rules.rb
112
+ - lib/jirametrics/hierarchy_table.rb
113
+ - lib/jirametrics/html/aging_work_bar_chart.erb
114
+ - lib/jirametrics/html/aging_work_in_progress_chart.erb
115
+ - lib/jirametrics/html/aging_work_table.erb
116
+ - lib/jirametrics/html/collapsible_issues_panel.erb
117
+ - lib/jirametrics/html/cycletime_histogram.erb
118
+ - lib/jirametrics/html/cycletime_scatterplot.erb
119
+ - lib/jirametrics/html/daily_wip_chart.erb
120
+ - lib/jirametrics/html/data_quality_report.erb
121
+ - lib/jirametrics/html/expedited_chart.erb
122
+ - lib/jirametrics/html/hierarchy_table.erb
123
+ - lib/jirametrics/html/index.erb
124
+ - lib/jirametrics/html/sprint_burndown.erb
125
+ - lib/jirametrics/html/story_point_accuracy_chart.erb
126
+ - lib/jirametrics/html/throughput_chart.erb
127
+ - lib/jirametrics/html_report_config.rb
128
+ - lib/jirametrics/issue.rb
129
+ - lib/jirametrics/issue_link.rb
130
+ - lib/jirametrics/json_file_loader.rb
131
+ - lib/jirametrics/project_config.rb
132
+ - lib/jirametrics/rules.rb
133
+ - lib/jirametrics/self_or_issue_dispatcher.rb
134
+ - lib/jirametrics/sprint.rb
135
+ - lib/jirametrics/sprint_burndown.rb
136
+ - lib/jirametrics/sprint_issue_change_data.rb
137
+ - lib/jirametrics/status.rb
138
+ - lib/jirametrics/status_collection.rb
139
+ - lib/jirametrics/story_point_accuracy_chart.rb
140
+ - lib/jirametrics/throughput_chart.rb
141
+ - lib/jirametrics/tree_organizer.rb
142
+ - lib/jirametrics/trend_line_calculator.rb
143
+ homepage: https://rubygems.org/gems/jirametrics
144
+ licenses:
145
+ - Apache-2.0
146
+ metadata:
147
+ rubygems_mfa_required: 'true'
148
+ post_install_message:
149
+ rdoc_options: []
150
+ require_paths:
151
+ - lib
152
+ required_ruby_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: 3.0.0
157
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ requirements: []
163
+ rubygems_version: 3.2.15
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: Extract Jira metrics
167
+ test_files: []