jirametrics 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []