jirametrics 2.8 → 2.9.1pre1
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.
- checksums.yaml +4 -4
- data/lib/jirametrics/aging_work_bar_chart.rb +7 -5
- data/lib/jirametrics/board.rb +2 -3
- data/lib/jirametrics/board_config.rb +6 -2
- data/lib/jirametrics/chart_base.rb +36 -9
- data/lib/jirametrics/cycletime_config.rb +10 -4
- data/lib/jirametrics/data_quality_report.rb +53 -34
- data/lib/jirametrics/examples/standard_project.rb +2 -2
- data/lib/jirametrics/exporter.rb +0 -12
- data/lib/jirametrics/file_config.rb +12 -0
- data/lib/jirametrics/file_system.rb +19 -4
- data/lib/jirametrics/groupable_issue_chart.rb +1 -3
- data/lib/jirametrics/html/aging_work_bar_chart.erb +1 -10
- data/lib/jirametrics/html/aging_work_table.erb +1 -1
- data/lib/jirametrics/html/cycletime_scatterplot.erb +1 -10
- data/lib/jirametrics/html/daily_wip_chart.erb +1 -10
- data/lib/jirametrics/html/expedited_chart.erb +1 -10
- data/lib/jirametrics/html/hierarchy_table.erb +1 -1
- data/lib/jirametrics/html/sprint_burndown.erb +1 -10
- data/lib/jirametrics/html/throughput_chart.erb +1 -10
- data/lib/jirametrics/html_report_config.rb +18 -23
- data/lib/jirametrics/issue.rb +51 -27
- data/lib/jirametrics/project_config.rb +102 -45
- data/lib/jirametrics/status.rb +23 -1
- data/lib/jirametrics/status_collection.rb +69 -68
- data/lib/jirametrics/value_equality.rb +2 -2
- data/lib/jirametrics.rb +0 -1
- metadata +4 -9
- data/lib/jirametrics/discard_changes_before.rb +0 -37
@@ -4,105 +4,106 @@ class StatusNotFoundError < StandardError
|
|
4
4
|
end
|
5
5
|
|
6
6
|
class StatusCollection
|
7
|
+
attr_reader :historical_status_mappings
|
8
|
+
|
7
9
|
def initialize
|
8
10
|
@list = []
|
11
|
+
@historical_status_mappings = {} # 'name:id' => category
|
9
12
|
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
# Return the status matching this id or nil if it can't be found.
|
15
|
+
def find_by_id id
|
16
|
+
@list.find { |status| status.id == id }
|
17
|
+
end
|
14
18
|
|
15
|
-
|
16
|
-
|
17
|
-
including.any? { |s| s.name == status.name }
|
18
|
-
keep = false if excluding.any? { |s| s.name == status.name }
|
19
|
+
def find_all_by_name identifier
|
20
|
+
name, id = parse_name_id identifier
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
-
|
22
|
+
if id
|
23
|
+
status = find_by_id id
|
24
|
+
return [] if status.nil?
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
names_or_ids = [names_or_ids] unless names_or_ids.is_a? Array
|
29
|
-
|
30
|
-
names_or_ids.each do |name_or_id|
|
31
|
-
status = @list.find { |s| s.name == name_or_id || s.id == name_or_id }
|
32
|
-
if status.nil?
|
33
|
-
if block_given?
|
34
|
-
yield name_or_id
|
35
|
-
next
|
36
|
-
else
|
37
|
-
all_status_names = @list.collect { |s| "#{s.name.inspect}:#{s.id.inspect}" }.uniq.sort.join(', ')
|
38
|
-
raise StatusNotFoundError, "Status not found: \"#{name_or_id}\". Possible statuses are: #{all_status_names}"
|
39
|
-
end
|
26
|
+
if name && status.name != name
|
27
|
+
raise "Specified status ID of #{id} does not match specified name #{name.inspect}. " \
|
28
|
+
"You might have meant one of these: #{self}."
|
40
29
|
end
|
41
|
-
|
42
|
-
|
30
|
+
[status]
|
31
|
+
else
|
32
|
+
@list.select { |status| status.name == name }
|
43
33
|
end
|
44
|
-
result
|
45
34
|
end
|
46
35
|
|
47
|
-
def
|
48
|
-
|
36
|
+
def find_all_categories
|
37
|
+
@list
|
38
|
+
.collect(&:category)
|
39
|
+
.uniq
|
40
|
+
.sort_by(&:id)
|
49
41
|
end
|
50
42
|
|
51
|
-
def
|
52
|
-
|
43
|
+
def parse_name_id name
|
44
|
+
# Names could arrive in one of the following formats: "Done:3", "3", "Done"
|
45
|
+
if name =~ /^(.*):(\d+)$/
|
46
|
+
[$1, $2.to_i]
|
47
|
+
elsif name.match?(/^\d+$/)
|
48
|
+
[nil, name.to_i]
|
49
|
+
else
|
50
|
+
[name, nil]
|
51
|
+
end
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
54
|
+
def find_all_categories_by_name identifier
|
55
|
+
key = nil
|
56
|
+
id = nil
|
58
57
|
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
if identifier.is_a? Symbol
|
59
|
+
key = identifier.to_s
|
60
|
+
else
|
61
|
+
name, id = parse_name_id identifier
|
62
|
+
end
|
63
|
+
|
64
|
+
find_all_categories.select { |c| c.id == id || c.name == name || c.key == key }
|
62
65
|
end
|
63
66
|
|
64
|
-
def
|
65
|
-
|
67
|
+
def collect(&block) = @list.collect(&block)
|
68
|
+
def find(&block) = @list.find(&block)
|
69
|
+
def each(&block) = @list.each(&block)
|
70
|
+
def select(&block) = @list.select(&block)
|
71
|
+
def <<(arg) = @list << arg
|
72
|
+
def empty? = @list.empty?
|
73
|
+
def clear = @list.clear
|
74
|
+
def delete(object) = @list.delete(object)
|
75
|
+
|
76
|
+
def to_s
|
77
|
+
"[#{@list.sort.join(', ')}]"
|
66
78
|
end
|
67
79
|
|
68
|
-
def
|
69
|
-
|
70
|
-
unless category
|
71
|
-
set = Set.new
|
72
|
-
@list.each do |status|
|
73
|
-
set << status.category.to_s
|
74
|
-
end
|
75
|
-
raise "Unable to find status category #{name.inspect} in [#{set.to_a.sort.join(', ')}]"
|
76
|
-
end
|
77
|
-
category
|
80
|
+
def inspect
|
81
|
+
"StatusCollection#{self}"
|
78
82
|
end
|
79
83
|
|
80
|
-
# This is used to create a status that was found in the history but has since been deleted.
|
81
84
|
def fabricate_status_for id:, name:
|
82
|
-
|
83
|
-
|
85
|
+
category = @historical_status_mappings["#{name.inspect}:#{id.inspect}"]
|
86
|
+
category = in_progress_category if category.nil?
|
84
87
|
|
85
88
|
status = Status.new(
|
86
89
|
name: name,
|
87
90
|
id: id,
|
88
|
-
category_name:
|
89
|
-
category_id:
|
90
|
-
category_key:
|
91
|
+
category_name: category.name,
|
92
|
+
category_id: category.id,
|
93
|
+
category_key: category.key,
|
94
|
+
artificial: true
|
91
95
|
)
|
92
|
-
|
96
|
+
@list << status
|
93
97
|
status
|
94
98
|
end
|
95
99
|
|
96
|
-
|
97
|
-
def find(&block) = @list.find(&block)
|
98
|
-
def each(&block) = @list.each(&block)
|
99
|
-
def select(&block) = @list.select(&block)
|
100
|
-
def <<(arg) = @list << arg
|
101
|
-
def empty? = @list.empty?
|
102
|
-
def clear = @list.clear
|
103
|
-
def delete(object) = @list.delete(object)
|
100
|
+
private
|
104
101
|
|
105
|
-
|
106
|
-
|
102
|
+
# Return the in-progress category or raise an error if we can't find one.
|
103
|
+
def in_progress_category
|
104
|
+
first_in_progress_status = find { |s| s.category.indeterminate? }
|
105
|
+
raise "Can't find even one in-progress status in #{self}" unless first_in_progress_status
|
106
|
+
|
107
|
+
first_in_progress_status.category
|
107
108
|
end
|
108
109
|
end
|
@@ -9,9 +9,9 @@ module ValueEquality
|
|
9
9
|
names = object.instance_variables
|
10
10
|
if object.respond_to? :value_equality_ignored_variables
|
11
11
|
ignored_variables = object.value_equality_ignored_variables
|
12
|
-
names.reject! { |n| ignored_variables.include? n }
|
12
|
+
names.reject! { |n| ignored_variables.include? n.to_sym }
|
13
13
|
end
|
14
|
-
names.map { |variable| instance_variable_get variable }
|
14
|
+
names.map { |variable| object.instance_variable_get variable }
|
15
15
|
end
|
16
16
|
|
17
17
|
code.call(self) == code.call(other)
|
data/lib/jirametrics.rb
CHANGED
@@ -68,7 +68,6 @@ class JiraMetrics < Thor
|
|
68
68
|
require 'jirametrics/grouping_rules'
|
69
69
|
require 'jirametrics/daily_wip_chart'
|
70
70
|
require 'jirametrics/groupable_issue_chart'
|
71
|
-
require 'jirametrics/discard_changes_before'
|
72
71
|
require 'jirametrics/css_variable'
|
73
72
|
|
74
73
|
require 'jirametrics/aggregate_config'
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jirametrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.9.1pre1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Bowler
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-21 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: random-word
|
@@ -52,8 +51,7 @@ dependencies:
|
|
52
51
|
- - "~>"
|
53
52
|
- !ruby/object:Gem::Version
|
54
53
|
version: 1.2.2
|
55
|
-
description:
|
56
|
-
CSV files
|
54
|
+
description: Extract metrics from Jira and export to either a report or to CSV files
|
57
55
|
email: mbowler@gargoylesoftware.com
|
58
56
|
executables:
|
59
57
|
- jirametrics
|
@@ -84,7 +82,6 @@ files:
|
|
84
82
|
- lib/jirametrics/daily_wip_chart.rb
|
85
83
|
- lib/jirametrics/data_quality_report.rb
|
86
84
|
- lib/jirametrics/dependency_chart.rb
|
87
|
-
- lib/jirametrics/discard_changes_before.rb
|
88
85
|
- lib/jirametrics/download_config.rb
|
89
86
|
- lib/jirametrics/downloader.rb
|
90
87
|
- lib/jirametrics/estimate_accuracy_chart.rb
|
@@ -139,7 +136,6 @@ metadata:
|
|
139
136
|
bug_tracker_uri: https://github.com/mikebowler/jirametrics/issues
|
140
137
|
changelog_uri: https://jirametrics.org/changes
|
141
138
|
documentation_uri: https://jirametrics.org
|
142
|
-
post_install_message:
|
143
139
|
rdoc_options: []
|
144
140
|
require_paths:
|
145
141
|
- lib
|
@@ -154,8 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
154
150
|
- !ruby/object:Gem::Version
|
155
151
|
version: '0'
|
156
152
|
requirements: []
|
157
|
-
rubygems_version: 3.
|
158
|
-
signing_key:
|
153
|
+
rubygems_version: 3.6.2
|
159
154
|
specification_version: 4
|
160
155
|
summary: Extract Jira metrics
|
161
156
|
test_files: []
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module DiscardChangesBefore
|
4
|
-
def discard_changes_before status_becomes: nil, &block
|
5
|
-
if status_becomes
|
6
|
-
status_becomes = [status_becomes] unless status_becomes.is_a? Array
|
7
|
-
|
8
|
-
block = lambda do |issue|
|
9
|
-
trigger_statuses = status_becomes.collect do |status_name|
|
10
|
-
if status_name == :backlog
|
11
|
-
issue.board.backlog_statuses.collect(&:name)
|
12
|
-
else
|
13
|
-
status_name
|
14
|
-
end
|
15
|
-
end.flatten
|
16
|
-
|
17
|
-
time = nil
|
18
|
-
issue.changes.each do |change|
|
19
|
-
time = change.time if change.status? && trigger_statuses.include?(change.value) && change.artificial? == false
|
20
|
-
end
|
21
|
-
time
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
issues_cutoff_times = []
|
26
|
-
issues.each do |issue|
|
27
|
-
cutoff_time = block.call(issue)
|
28
|
-
issues_cutoff_times << [issue, cutoff_time] if cutoff_time
|
29
|
-
end
|
30
|
-
|
31
|
-
discard_changes_before_hook issues_cutoff_times
|
32
|
-
|
33
|
-
issues_cutoff_times.each do |issue, cutoff_time|
|
34
|
-
issue.discard_changes_before cutoff_time
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|