jirametrics 2.13 → 2.20.1
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/anonymizer.rb +8 -6
- data/lib/jirametrics/atlassian_document_format.rb +8 -4
- data/lib/jirametrics/board_config.rb +3 -1
- data/lib/jirametrics/change_item.rb +2 -2
- data/lib/jirametrics/chart_base.rb +5 -2
- data/lib/jirametrics/cycletime_config.rb +22 -3
- data/lib/jirametrics/cycletime_histogram.rb +3 -1
- data/lib/jirametrics/daily_view.rb +49 -42
- data/lib/jirametrics/data_quality_report.rb +6 -3
- data/lib/jirametrics/dependency_chart.rb +4 -1
- data/lib/jirametrics/downloader.rb +34 -99
- data/lib/jirametrics/downloader_for_cloud.rb +202 -0
- data/lib/jirametrics/downloader_for_data_center.rb +94 -0
- data/lib/jirametrics/examples/standard_project.rb +9 -9
- data/lib/jirametrics/expedited_chart.rb +1 -1
- data/lib/jirametrics/exporter.rb +10 -5
- data/lib/jirametrics/file_system.rb +24 -1
- data/lib/jirametrics/flow_efficiency_scatterplot.rb +1 -1
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +1 -1
- data/lib/jirametrics/html/collapsible_issues_panel.erb +2 -2
- data/lib/jirametrics/html/cycletime_histogram.erb +2 -2
- data/lib/jirametrics/html/index.css +5 -10
- data/lib/jirametrics/html/index.erb +2 -34
- data/lib/jirametrics/html/index.js +90 -0
- data/lib/jirametrics/html/sprint_burndown.erb +5 -3
- data/lib/jirametrics/html_report_config.rb +5 -3
- data/lib/jirametrics/issue.rb +31 -19
- data/lib/jirametrics/jira_gateway.rb +55 -17
- data/lib/jirametrics/project_config.rb +30 -3
- data/lib/jirametrics/settings.json +3 -1
- data/lib/jirametrics.rb +19 -70
- metadata +6 -3
|
@@ -51,7 +51,9 @@ class HtmlReportConfig
|
|
|
51
51
|
@file_config.project_config.all_boards.each_value do |board|
|
|
52
52
|
raise 'Multiple cycletimes not supported' if board.cycletime
|
|
53
53
|
|
|
54
|
-
board.cycletime = CycleTimeConfig.new(
|
|
54
|
+
board.cycletime = CycleTimeConfig.new(
|
|
55
|
+
parent_config: self, label: label, block: block, file_system: file_system, settings: settings
|
|
56
|
+
)
|
|
55
57
|
end
|
|
56
58
|
end
|
|
57
59
|
|
|
@@ -72,6 +74,7 @@ class HtmlReportConfig
|
|
|
72
74
|
|
|
73
75
|
html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
|
|
74
76
|
css = load_css html_directory: html_directory
|
|
77
|
+
javascript = file_system.load(File.join(html_directory, 'index.js'))
|
|
75
78
|
erb = ERB.new file_system.load(File.join(html_directory, 'index.erb'))
|
|
76
79
|
file_system.save_file content: erb.result(binding), filename: @file_config.output_filename
|
|
77
80
|
end
|
|
@@ -87,7 +90,6 @@ class HtmlReportConfig
|
|
|
87
90
|
def load_css html_directory:
|
|
88
91
|
base_css_filename = File.join(html_directory, 'index.css')
|
|
89
92
|
base_css = file_system.load(base_css_filename)
|
|
90
|
-
log("Loaded CSS: #{base_css_filename}")
|
|
91
93
|
|
|
92
94
|
extra_css_filename = settings['include_css']
|
|
93
95
|
if extra_css_filename
|
|
@@ -160,7 +162,7 @@ class HtmlReportConfig
|
|
|
160
162
|
chart.time_range = project_config.time_range
|
|
161
163
|
chart.timezone_offset = timezone_offset
|
|
162
164
|
chart.settings = settings
|
|
163
|
-
chart.
|
|
165
|
+
chart.atlassian_document_format = project_config.atlassian_document_format
|
|
164
166
|
|
|
165
167
|
chart.all_boards = project_config.all_boards
|
|
166
168
|
chart.board_id = find_board_id
|
data/lib/jirametrics/issue.rb
CHANGED
|
@@ -19,9 +19,10 @@ class Issue
|
|
|
19
19
|
|
|
20
20
|
# There are cases where we create an Issue of fragments like linked issues and those won't have
|
|
21
21
|
# changelogs.
|
|
22
|
-
|
|
22
|
+
load_history_into_changes if @raw['changelog']
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
# As above with fragments, there may not be a fields section
|
|
25
|
+
return unless @raw['fields']
|
|
25
26
|
|
|
26
27
|
# If this is an older pull of data then comments may not be there.
|
|
27
28
|
load_comments_into_changes if @raw['fields']['comment']
|
|
@@ -152,7 +153,7 @@ class Issue
|
|
|
152
153
|
# Are we currently in this status? If yes, then return the most recent status change.
|
|
153
154
|
def currently_in_status *status_names
|
|
154
155
|
change = most_recent_status_change
|
|
155
|
-
return
|
|
156
|
+
return nil if change.nil?
|
|
156
157
|
|
|
157
158
|
change if change.current_status_matches(*status_names)
|
|
158
159
|
end
|
|
@@ -162,7 +163,7 @@ class Issue
|
|
|
162
163
|
category_ids = find_status_category_ids_by_names category_names
|
|
163
164
|
|
|
164
165
|
change = most_recent_status_change
|
|
165
|
-
return
|
|
166
|
+
return nil if change.nil?
|
|
166
167
|
|
|
167
168
|
status = find_or_create_status id: change.value_id, name: change.value
|
|
168
169
|
change if status && category_ids.include?(status.category.id)
|
|
@@ -212,7 +213,11 @@ class Issue
|
|
|
212
213
|
end
|
|
213
214
|
|
|
214
215
|
def parse_time text
|
|
215
|
-
|
|
216
|
+
if text.is_a? String
|
|
217
|
+
Time.parse(text).getlocal(@timezone_offset)
|
|
218
|
+
else
|
|
219
|
+
Time.at(text / 1000).getlocal(@timezone_offset)
|
|
220
|
+
end
|
|
216
221
|
end
|
|
217
222
|
|
|
218
223
|
def created
|
|
@@ -233,11 +238,11 @@ class Issue
|
|
|
233
238
|
end
|
|
234
239
|
|
|
235
240
|
def assigned_to
|
|
236
|
-
@raw['fields']
|
|
241
|
+
@raw['fields']['assignee']&.[]('displayName')
|
|
237
242
|
end
|
|
238
243
|
|
|
239
244
|
def assigned_to_icon_url
|
|
240
|
-
@raw['fields']
|
|
245
|
+
@raw['fields']['assignee']&.[]('avatarUrls')&.[]('16x16')
|
|
241
246
|
end
|
|
242
247
|
|
|
243
248
|
# Many test failures are simply unreadable because the default inspect on this class goes
|
|
@@ -608,7 +613,7 @@ class Issue
|
|
|
608
613
|
|
|
609
614
|
def dump
|
|
610
615
|
result = +''
|
|
611
|
-
result << "#{key} (#{type}): #{compact_text summary, 200}\n"
|
|
616
|
+
result << "#{key} (#{type}): #{compact_text summary, max: 200}\n"
|
|
612
617
|
|
|
613
618
|
assignee = raw['fields']['assignee']
|
|
614
619
|
result << " [assignee] #{assignee['name'].inspect} <#{assignee['emailAddress']}>\n" unless assignee.nil?
|
|
@@ -681,9 +686,8 @@ class Issue
|
|
|
681
686
|
def done?
|
|
682
687
|
if artificial? || board.cycletime.nil?
|
|
683
688
|
# This was probably loaded as a linked issue, which means we don't know what board it really
|
|
684
|
-
# belonged to. The best we can do is look at the status
|
|
685
|
-
|
|
686
|
-
status.category.name == 'Done'
|
|
689
|
+
# belonged to. The best we can do is look at the status key
|
|
690
|
+
status.category.done?
|
|
687
691
|
else
|
|
688
692
|
board.cycletime.done? self
|
|
689
693
|
end
|
|
@@ -706,6 +710,19 @@ class Issue
|
|
|
706
710
|
board.sprints.select { |s| sprint_ids.include? s.id }
|
|
707
711
|
end
|
|
708
712
|
|
|
713
|
+
def compact_text text, max: 60
|
|
714
|
+
return '' if text.nil?
|
|
715
|
+
|
|
716
|
+
if text.is_a? Hash
|
|
717
|
+
# We can't effectively compact it but we can convert it into a string.
|
|
718
|
+
text = @board.project_config.atlassian_document_format.to_html(text)
|
|
719
|
+
else
|
|
720
|
+
text = text.gsub(/\s+/, ' ').strip
|
|
721
|
+
text = "#{text[0...max]}..." if text.length > max
|
|
722
|
+
end
|
|
723
|
+
text
|
|
724
|
+
end
|
|
725
|
+
|
|
709
726
|
private
|
|
710
727
|
|
|
711
728
|
def load_history_into_changes
|
|
@@ -730,14 +747,6 @@ class Issue
|
|
|
730
747
|
end
|
|
731
748
|
end
|
|
732
749
|
|
|
733
|
-
def compact_text text, max = 60
|
|
734
|
-
return nil if text.nil?
|
|
735
|
-
|
|
736
|
-
text = text.gsub(/\s+/, ' ').strip
|
|
737
|
-
text = "#{text[0..max]}..." if text.length > max
|
|
738
|
-
text
|
|
739
|
-
end
|
|
740
|
-
|
|
741
750
|
def sort_changes!
|
|
742
751
|
@changes.sort! do |a, b|
|
|
743
752
|
# It's common that a resolved will happen at the same time as a status change.
|
|
@@ -755,6 +764,9 @@ class Issue
|
|
|
755
764
|
first_status = nil
|
|
756
765
|
first_status_id = nil
|
|
757
766
|
|
|
767
|
+
# There won't be a created timestamp in cases where this was a linked issue
|
|
768
|
+
return unless @raw['fields']['created']
|
|
769
|
+
|
|
758
770
|
created_time = parse_time @raw['fields']['created']
|
|
759
771
|
first_change = @changes.find { |change| change.field == field_name }
|
|
760
772
|
if first_change.nil?
|
|
@@ -3,21 +3,61 @@
|
|
|
3
3
|
require 'cgi'
|
|
4
4
|
require 'json'
|
|
5
5
|
require 'English'
|
|
6
|
+
require 'open3'
|
|
6
7
|
|
|
7
8
|
class JiraGateway
|
|
8
|
-
attr_accessor :ignore_ssl_errors
|
|
9
|
+
attr_accessor :ignore_ssl_errors
|
|
10
|
+
attr_reader :jira_url, :settings, :file_system
|
|
9
11
|
|
|
10
|
-
def initialize file_system:
|
|
12
|
+
def initialize file_system:, jira_config:, settings:
|
|
11
13
|
@file_system = file_system
|
|
14
|
+
load_jira_config(jira_config)
|
|
15
|
+
@settings = settings
|
|
16
|
+
@ignore_ssl_errors = settings['ignore_ssl_errors']
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def post_request relative_url:, payload:
|
|
20
|
+
command = make_curl_command url: "#{@jira_url}#{relative_url}", method: 'POST'
|
|
21
|
+
exec_and_parse_response command: command, stdin_data: payload
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def exec_and_parse_response command:, stdin_data:
|
|
25
|
+
log_entry = " #{command.gsub(/\s+/, ' ')}"
|
|
26
|
+
log_entry = sanitize_message log_entry
|
|
27
|
+
@file_system.log log_entry
|
|
28
|
+
|
|
29
|
+
stdout, stderr, status = capture3(command, stdin_data: stdin_data)
|
|
30
|
+
unless status.success?
|
|
31
|
+
@file_system.log "Failed call with exit status #{status.exitstatus}!"
|
|
32
|
+
@file_system.log "Returned (stdout): #{stdout.inspect}"
|
|
33
|
+
@file_system.log "Returned (stderr): #{stderr.inspect}"
|
|
34
|
+
raise "Failed call with exit status #{status.exitstatus}. " \
|
|
35
|
+
"See #{@file_system.logfile_name} for details"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
@file_system.log "Returned (stderr): #{stderr.inspect}" unless stderr == ''
|
|
39
|
+
raise 'no response from curl on stdout' if stdout == ''
|
|
40
|
+
|
|
41
|
+
parse_response(command: command, result: stdout)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def capture3 command, stdin_data:
|
|
45
|
+
# In it's own method so we can mock it out in tests
|
|
46
|
+
Open3.capture3(command, stdin_data: stdin_data)
|
|
12
47
|
end
|
|
13
48
|
|
|
14
49
|
def call_url relative_url:
|
|
15
50
|
command = make_curl_command url: "#{@jira_url}#{relative_url}"
|
|
16
|
-
|
|
51
|
+
exec_and_parse_response command: command, stdin_data: nil
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def parse_response command:, result:
|
|
17
55
|
begin
|
|
18
56
|
json = JSON.parse(result)
|
|
19
57
|
rescue # rubocop:disable Style/RescueStandardError
|
|
20
|
-
|
|
58
|
+
message = "Unable to parse results from #{sanitize_message(command)}"
|
|
59
|
+
@file_system.error message, more: result
|
|
60
|
+
raise message
|
|
21
61
|
end
|
|
22
62
|
|
|
23
63
|
raise "Download failed with: #{JSON.pretty_generate(json)}" unless json_successful?(json)
|
|
@@ -25,18 +65,11 @@ class JiraGateway
|
|
|
25
65
|
json
|
|
26
66
|
end
|
|
27
67
|
|
|
28
|
-
def
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@file_system.log log_entry
|
|
68
|
+
def sanitize_message message
|
|
69
|
+
token = @jira_api_token || @jira_personal_access_token
|
|
70
|
+
return message unless token # cookie based authentication
|
|
32
71
|
|
|
33
|
-
|
|
34
|
-
@file_system.log result unless $CHILD_STATUS.success?
|
|
35
|
-
return result if $CHILD_STATUS.success?
|
|
36
|
-
|
|
37
|
-
@file_system.log "Failed call with exit status #{$CHILD_STATUS.exitstatus}."
|
|
38
|
-
raise "Failed call with exit status #{$CHILD_STATUS.exitstatus}. " \
|
|
39
|
-
"See #{@file_system.logfile_name} for details"
|
|
72
|
+
message.gsub(token, '[API_TOKEN]')
|
|
40
73
|
end
|
|
41
74
|
|
|
42
75
|
def load_jira_config jira_config
|
|
@@ -56,7 +89,7 @@ class JiraGateway
|
|
|
56
89
|
@cookies = (jira_config['cookies'] || []).collect { |key, value| "#{key}=#{value}" }.join(';')
|
|
57
90
|
end
|
|
58
91
|
|
|
59
|
-
def make_curl_command url:
|
|
92
|
+
def make_curl_command url:, method: 'GET'
|
|
60
93
|
command = +''
|
|
61
94
|
command << 'curl'
|
|
62
95
|
command << ' -L' # follow redirects
|
|
@@ -65,8 +98,13 @@ class JiraGateway
|
|
|
65
98
|
command << " --cookie #{@cookies.inspect}" unless @cookies.empty?
|
|
66
99
|
command << " --user #{@jira_email}:#{@jira_api_token}" if @jira_api_token
|
|
67
100
|
command << " -H \"Authorization: Bearer #{@jira_personal_access_token}\"" if @jira_personal_access_token
|
|
68
|
-
command <<
|
|
101
|
+
command << " --request #{method}"
|
|
102
|
+
if method == 'POST'
|
|
103
|
+
command << ' --data @-'
|
|
104
|
+
command << ' --header "Content-Type: application/json"'
|
|
105
|
+
end
|
|
69
106
|
command << ' --header "Accept: application/json"'
|
|
107
|
+
command << ' --show-error --fail' # Better diagnostics when the server returns an error
|
|
70
108
|
command << " --url \"#{url}\""
|
|
71
109
|
command
|
|
72
110
|
end
|
|
@@ -114,10 +114,14 @@ class ProjectConfig
|
|
|
114
114
|
def file_prefix prefix
|
|
115
115
|
# The file_prefix has to be set before almost everything else. It really should have been an attribute
|
|
116
116
|
# on the project declaration itself. Hindsight is 20/20.
|
|
117
|
+
|
|
118
|
+
# There can only be one of these
|
|
117
119
|
if @file_prefix
|
|
118
|
-
raise "file_prefix
|
|
120
|
+
raise "file_prefix can only be set once. Was #{@file_prefix.inspect} and now changed to #{prefix.inspect}."
|
|
119
121
|
end
|
|
120
122
|
|
|
123
|
+
raise_if_prefix_already_used(prefix)
|
|
124
|
+
|
|
121
125
|
@file_prefix = prefix
|
|
122
126
|
|
|
123
127
|
# Yes, this is a wierd place to be initializing this. Unfortunately, it has to happen after the file_prefix
|
|
@@ -130,8 +134,21 @@ class ProjectConfig
|
|
|
130
134
|
@file_prefix
|
|
131
135
|
end
|
|
132
136
|
|
|
133
|
-
def
|
|
134
|
-
|
|
137
|
+
def raise_if_prefix_already_used prefix
|
|
138
|
+
@exporter.project_configs.each do |project|
|
|
139
|
+
next unless project.get_file_prefix(raise_if_not_set: false) == prefix && project.target_path == target_path
|
|
140
|
+
|
|
141
|
+
raise "Project #{name.inspect} specifies file prefix #{prefix.inspect}, " \
|
|
142
|
+
"but that is already used by project #{project.name.inspect} in the same target path #{target_path.inspect}. " \
|
|
143
|
+
'This is almost guaranteed to be too much copy and paste in your configuration. ' \
|
|
144
|
+
'File prefixes must be unique within a directory.'
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def get_file_prefix raise_if_not_set: true
|
|
149
|
+
if @file_prefix.nil? && raise_if_not_set
|
|
150
|
+
raise 'file_prefix has not been set yet. Move it to the top of the project declaration.'
|
|
151
|
+
end
|
|
135
152
|
|
|
136
153
|
@file_prefix
|
|
137
154
|
end
|
|
@@ -335,6 +352,12 @@ class ProjectConfig
|
|
|
335
352
|
json.each { |user_data| @users << User.new(raw: user_data) }
|
|
336
353
|
end
|
|
337
354
|
|
|
355
|
+
def atlassian_document_format
|
|
356
|
+
@atlassian_document_format ||= AtlassianDocumentFormat.new(
|
|
357
|
+
users: @users, timezone_offset: exporter.timezone_offset
|
|
358
|
+
)
|
|
359
|
+
end
|
|
360
|
+
|
|
338
361
|
def to_time string, end_of_day: false
|
|
339
362
|
time = end_of_day ? '23:59:59' : '00:00:00'
|
|
340
363
|
string = "#{string}T#{time}#{exporter.timezone_offset}" if string.match?(/^\d{4}-\d{2}-\d{2}$/)
|
|
@@ -526,6 +549,7 @@ class ProjectConfig
|
|
|
526
549
|
end
|
|
527
550
|
|
|
528
551
|
def discard_changes_before status_becomes: nil, &block
|
|
552
|
+
cycletimes_touched = Set.new
|
|
529
553
|
if status_becomes
|
|
530
554
|
status_becomes = [status_becomes] unless status_becomes.is_a? Array
|
|
531
555
|
|
|
@@ -558,6 +582,7 @@ class ProjectConfig
|
|
|
558
582
|
next if original_start_time.nil?
|
|
559
583
|
|
|
560
584
|
issue.discard_changes_before cutoff_time
|
|
585
|
+
cycletimes_touched << issue.board.cycletime
|
|
561
586
|
|
|
562
587
|
next unless cutoff_time
|
|
563
588
|
next if original_start_time > cutoff_time # ie the cutoff would have made no difference.
|
|
@@ -568,5 +593,7 @@ class ProjectConfig
|
|
|
568
593
|
issue: issue
|
|
569
594
|
}
|
|
570
595
|
end
|
|
596
|
+
|
|
597
|
+
cycletimes_touched.each { |c| c.flush_cache }
|
|
571
598
|
end
|
|
572
599
|
end
|
|
@@ -7,5 +7,7 @@
|
|
|
7
7
|
"flagged_means_blocked": true,
|
|
8
8
|
|
|
9
9
|
"expedited_priority_names": ["Critical", "Highest"],
|
|
10
|
-
"priority_order": ["Lowest", "Low", "Medium", "High", "Highest"]
|
|
10
|
+
"priority_order": ["Lowest", "Low", "Medium", "High", "Highest"],
|
|
11
|
+
|
|
12
|
+
"cache_cycletime_calculations": true
|
|
11
13
|
}
|
data/lib/jirametrics.rb
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'thor'
|
|
4
|
+
require 'require_all'
|
|
5
|
+
|
|
6
|
+
# This one does need to be loaded early. The rest will be loaded later.
|
|
7
|
+
require 'jirametrics/file_system'
|
|
4
8
|
|
|
5
9
|
class JiraMetrics < Thor
|
|
6
10
|
def self.exit_on_failure?
|
|
@@ -43,81 +47,26 @@ class JiraMetrics < Thor
|
|
|
43
47
|
|
|
44
48
|
option :config
|
|
45
49
|
desc 'info', 'Dump information about one issue'
|
|
46
|
-
def info
|
|
50
|
+
def info key
|
|
47
51
|
load_config options[:config]
|
|
48
|
-
Exporter.instance.info(
|
|
52
|
+
Exporter.instance.info(key, name_filter: options[:name] || '*')
|
|
49
53
|
end
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
no_commands do
|
|
56
|
+
def load_config config_file, file_system: FileSystem.new
|
|
57
|
+
config_file = './config.rb' if config_file.nil?
|
|
52
58
|
|
|
53
|
-
|
|
54
|
-
|
|
59
|
+
if File.exist? config_file
|
|
60
|
+
# The fact that File.exist can see the file does not mean that require will be
|
|
61
|
+
# able to load it. Convert this to an absolute pathname now for require.
|
|
62
|
+
config_file = File.absolute_path(config_file).to_s
|
|
63
|
+
else
|
|
64
|
+
file_system.error "Cannot find configuration file #{config_file.inspect}"
|
|
65
|
+
exit 1
|
|
66
|
+
end
|
|
55
67
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
# able to load it. Convert this to an absolute pathname now for require.
|
|
59
|
-
config_file = File.absolute_path(config_file).to_s
|
|
60
|
-
else
|
|
61
|
-
puts "Cannot find configuration file #{config_file.inspect}"
|
|
62
|
-
exit 1
|
|
68
|
+
require_rel 'jirametrics'
|
|
69
|
+
load config_file
|
|
63
70
|
end
|
|
64
|
-
|
|
65
|
-
require 'jirametrics/value_equality'
|
|
66
|
-
require 'jirametrics/chart_base'
|
|
67
|
-
require 'jirametrics/rules'
|
|
68
|
-
require 'jirametrics/grouping_rules'
|
|
69
|
-
require 'jirametrics/daily_wip_chart'
|
|
70
|
-
require 'jirametrics/groupable_issue_chart'
|
|
71
|
-
require 'jirametrics/css_variable'
|
|
72
|
-
require 'jirametrics/issue_collection'
|
|
73
|
-
|
|
74
|
-
require 'jirametrics/aggregate_config'
|
|
75
|
-
require 'jirametrics/expedited_chart'
|
|
76
|
-
require 'jirametrics/board_config'
|
|
77
|
-
require 'jirametrics/file_config'
|
|
78
|
-
require 'jirametrics/jira_gateway'
|
|
79
|
-
require 'jirametrics/trend_line_calculator'
|
|
80
|
-
require 'jirametrics/status'
|
|
81
|
-
require 'jirametrics/issue_link'
|
|
82
|
-
require 'jirametrics/estimate_accuracy_chart'
|
|
83
|
-
require 'jirametrics/status_collection'
|
|
84
|
-
require 'jirametrics/sprint'
|
|
85
|
-
require 'jirametrics/issue'
|
|
86
|
-
require 'jirametrics/daily_wip_by_age_chart'
|
|
87
|
-
require 'jirametrics/daily_wip_by_parent_chart'
|
|
88
|
-
require 'jirametrics/aging_work_in_progress_chart'
|
|
89
|
-
require 'jirametrics/cycletime_scatterplot'
|
|
90
|
-
require 'jirametrics/flow_efficiency_scatterplot'
|
|
91
|
-
require 'jirametrics/sprint_issue_change_data'
|
|
92
|
-
require 'jirametrics/cycletime_histogram'
|
|
93
|
-
require 'jirametrics/daily_wip_by_blocked_stalled_chart'
|
|
94
|
-
require 'jirametrics/html_report_config'
|
|
95
|
-
require 'jirametrics/data_quality_report'
|
|
96
|
-
require 'jirametrics/aging_work_bar_chart'
|
|
97
|
-
require 'jirametrics/change_item'
|
|
98
|
-
require 'jirametrics/project_config'
|
|
99
|
-
require 'jirametrics/dependency_chart'
|
|
100
|
-
require 'jirametrics/cycletime_config'
|
|
101
|
-
require 'jirametrics/tree_organizer'
|
|
102
|
-
require 'jirametrics/aging_work_table'
|
|
103
|
-
require 'jirametrics/sprint_burndown'
|
|
104
|
-
require 'jirametrics/self_or_issue_dispatcher'
|
|
105
|
-
require 'jirametrics/throughput_chart'
|
|
106
|
-
require 'jirametrics/exporter'
|
|
107
|
-
require 'jirametrics/file_system'
|
|
108
|
-
require 'jirametrics/blocked_stalled_change'
|
|
109
|
-
require 'jirametrics/board_column'
|
|
110
|
-
require 'jirametrics/anonymizer'
|
|
111
|
-
require 'jirametrics/downloader'
|
|
112
|
-
require 'jirametrics/fix_version'
|
|
113
|
-
require 'jirametrics/download_config'
|
|
114
|
-
require 'jirametrics/columns_config'
|
|
115
|
-
require 'jirametrics/hierarchy_table'
|
|
116
|
-
require 'jirametrics/estimation_configuration'
|
|
117
|
-
require 'jirametrics/board'
|
|
118
|
-
require 'jirametrics/daily_view'
|
|
119
|
-
require 'jirametrics/user'
|
|
120
|
-
require 'jirametrics/atlassian_document_format'
|
|
121
|
-
load config_file
|
|
122
71
|
end
|
|
123
72
|
end
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: jirametrics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.20.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Bowler
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: random-word
|
|
@@ -87,6 +87,8 @@ files:
|
|
|
87
87
|
- lib/jirametrics/dependency_chart.rb
|
|
88
88
|
- lib/jirametrics/download_config.rb
|
|
89
89
|
- lib/jirametrics/downloader.rb
|
|
90
|
+
- lib/jirametrics/downloader_for_cloud.rb
|
|
91
|
+
- lib/jirametrics/downloader_for_data_center.rb
|
|
90
92
|
- lib/jirametrics/estimate_accuracy_chart.rb
|
|
91
93
|
- lib/jirametrics/estimation_configuration.rb
|
|
92
94
|
- lib/jirametrics/examples/aggregated_project.rb
|
|
@@ -113,6 +115,7 @@ files:
|
|
|
113
115
|
- lib/jirametrics/html/hierarchy_table.erb
|
|
114
116
|
- lib/jirametrics/html/index.css
|
|
115
117
|
- lib/jirametrics/html/index.erb
|
|
118
|
+
- lib/jirametrics/html/index.js
|
|
116
119
|
- lib/jirametrics/html/sprint_burndown.erb
|
|
117
120
|
- lib/jirametrics/html/throughput_chart.erb
|
|
118
121
|
- lib/jirametrics/html_report_config.rb
|
|
@@ -156,7 +159,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
156
159
|
- !ruby/object:Gem::Version
|
|
157
160
|
version: '0'
|
|
158
161
|
requirements: []
|
|
159
|
-
rubygems_version: 3.6.
|
|
162
|
+
rubygems_version: 3.6.9
|
|
160
163
|
specification_version: 4
|
|
161
164
|
summary: Extract Jira metrics
|
|
162
165
|
test_files: []
|