jirametrics 2.20 → 2.22
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 +176 -134
- data/lib/jirametrics/bar_chart_range.rb +17 -0
- data/lib/jirametrics/board.rb +4 -0
- data/lib/jirametrics/board_config.rb +2 -1
- data/lib/jirametrics/change_item.rb +10 -3
- data/lib/jirametrics/chart_base.rb +31 -0
- data/lib/jirametrics/cycletime_config.rb +4 -5
- data/lib/jirametrics/cycletime_scatterplot.rb +36 -17
- data/lib/jirametrics/daily_wip_by_age_chart.rb +3 -4
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +13 -3
- data/lib/jirametrics/daily_wip_chart.rb +1 -1
- data/lib/jirametrics/data_quality_report.rb +2 -0
- data/lib/jirametrics/exporter.rb +4 -2
- data/lib/jirametrics/fix_version.rb +13 -0
- data/lib/jirametrics/groupable_issue_chart.rb +7 -1
- data/lib/jirametrics/html/aging_work_bar_chart.erb +2 -1
- data/lib/jirametrics/html/aging_work_in_progress_chart.erb +2 -0
- data/lib/jirametrics/html/aging_work_table.erb +2 -0
- data/lib/jirametrics/html/cycletime_histogram.erb +2 -0
- data/lib/jirametrics/html/cycletime_scatterplot.erb +6 -6
- data/lib/jirametrics/html/daily_wip_chart.erb +2 -0
- data/lib/jirametrics/html/estimate_accuracy_chart.erb +2 -0
- data/lib/jirametrics/html/expedited_chart.erb +3 -1
- data/lib/jirametrics/html/flow_efficiency_scatterplot.erb +2 -0
- data/lib/jirametrics/html/index.css +17 -0
- data/lib/jirametrics/html/index.erb +1 -1
- data/lib/jirametrics/html/index.js +24 -0
- data/lib/jirametrics/html/sprint_burndown.erb +6 -0
- data/lib/jirametrics/html/throughput_chart.erb +2 -2
- data/lib/jirametrics/html_generator.rb +31 -0
- data/lib/jirametrics/html_report_config.rb +5 -24
- data/lib/jirametrics/issue.rb +97 -4
- data/lib/jirametrics/jira_gateway.rb +1 -1
- data/lib/jirametrics/project_config.rb +12 -2
- data/lib/jirametrics/raw_javascript.rb +13 -0
- data/lib/jirametrics/sprint.rb +12 -0
- data/lib/jirametrics/sprint_burndown.rb +6 -2
- data/lib/jirametrics/stitcher.rb +75 -0
- data/lib/jirametrics.rb +8 -1
- metadata +5 -1
|
@@ -58,7 +58,16 @@ class ProjectConfig
|
|
|
58
58
|
|
|
59
59
|
def load_settings
|
|
60
60
|
# This is the weird exception that we don't ever want mocked out so we skip FileSystem entirely.
|
|
61
|
-
JSON.parse(File.read(File.join(__dir__, 'settings.json'), encoding: 'UTF-8'))
|
|
61
|
+
settings = JSON.parse(File.read(File.join(__dir__, 'settings.json'), encoding: 'UTF-8'))
|
|
62
|
+
|
|
63
|
+
if settings['blocked_color']
|
|
64
|
+
file_system.deprecated message: 'blocked color should be set via css now', date: '2024-05-03'
|
|
65
|
+
end
|
|
66
|
+
if settings['stalled_color']
|
|
67
|
+
file_system.deprecated message: 'stalled color should be set via css now', date: '2024-05-03'
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
settings
|
|
62
71
|
end
|
|
63
72
|
|
|
64
73
|
def guess_project_id
|
|
@@ -295,8 +304,9 @@ class ProjectConfig
|
|
|
295
304
|
file_system.foreach(@target_path) do |file|
|
|
296
305
|
next unless file =~ /^#{get_file_prefix}_board_(\d+)_sprints_\d+.json$/
|
|
297
306
|
|
|
307
|
+
board_id = $1.to_i
|
|
298
308
|
file_path = File.join(@target_path, file)
|
|
299
|
-
board = @all_boards[
|
|
309
|
+
board = @all_boards[board_id]
|
|
300
310
|
unless board
|
|
301
311
|
@exporter.file_system.log(
|
|
302
312
|
'Found sprint data but can\'t find a matching board in config. ' \
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# When strings are serialized into JSON, they're converted to actual strings. The purpose
|
|
4
|
+
# of this class is to allow raw javascript to be passed through.
|
|
5
|
+
class RawJavascript
|
|
6
|
+
def initialize content
|
|
7
|
+
@content = content
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_json(*_args)
|
|
11
|
+
@content
|
|
12
|
+
end
|
|
13
|
+
end
|
data/lib/jirametrics/sprint.rb
CHANGED
|
@@ -13,6 +13,7 @@ class Sprint
|
|
|
13
13
|
def id = @raw['id']
|
|
14
14
|
def active? = (@raw['state'] == 'active')
|
|
15
15
|
def closed? = (@raw['state'] == 'closed')
|
|
16
|
+
def future? = (@raw['state'] == 'future')
|
|
16
17
|
|
|
17
18
|
def completed_at? time
|
|
18
19
|
completed_at = completed_time
|
|
@@ -36,6 +37,17 @@ class Sprint
|
|
|
36
37
|
def goal = @raw['goal']
|
|
37
38
|
def name = @raw['name']
|
|
38
39
|
|
|
40
|
+
def day_count
|
|
41
|
+
return '' if future?
|
|
42
|
+
|
|
43
|
+
if closed?
|
|
44
|
+
days = (completed_time.to_date - start_time.to_date).to_i + 1
|
|
45
|
+
else
|
|
46
|
+
days = (end_time.to_date - start_time.to_date).to_i + 1
|
|
47
|
+
end
|
|
48
|
+
"#{days} days"
|
|
49
|
+
end
|
|
50
|
+
|
|
39
51
|
private
|
|
40
52
|
|
|
41
53
|
def parse_time time_string
|
|
@@ -48,8 +48,9 @@ class SprintBurndown < ChartBase
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def run
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
return nil unless current_board.scrum?
|
|
52
|
+
|
|
53
|
+
sprints = sprints_in_time_range current_board
|
|
53
54
|
|
|
54
55
|
change_data_by_sprint = {}
|
|
55
56
|
sprints.each do |sprint|
|
|
@@ -110,6 +111,9 @@ class SprintBurndown < ChartBase
|
|
|
110
111
|
|
|
111
112
|
def sprints_in_time_range board
|
|
112
113
|
board.sprints.select do |sprint|
|
|
114
|
+
# If it's never been started then it's just a holding area. Ignore it.
|
|
115
|
+
next if sprint.future?
|
|
116
|
+
|
|
113
117
|
sprint_end_time = sprint.completed_time || sprint.end_time
|
|
114
118
|
sprint_start_time = sprint.start_time
|
|
115
119
|
next false if sprint_start_time.nil?
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Stitcher < HtmlGenerator
|
|
4
|
+
class StitchContent
|
|
5
|
+
include ValueEquality
|
|
6
|
+
|
|
7
|
+
attr_reader :file, :title, :content, :type
|
|
8
|
+
|
|
9
|
+
def initialize file:, title:, type:, content:
|
|
10
|
+
@file = file
|
|
11
|
+
@title = title
|
|
12
|
+
@content = content
|
|
13
|
+
@type = type
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
attr_reader :loaded_files, :all_stitches
|
|
18
|
+
|
|
19
|
+
def initialize file_system:
|
|
20
|
+
super()
|
|
21
|
+
self.file_system = file_system
|
|
22
|
+
@all_stitches = []
|
|
23
|
+
@loaded_files = []
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def run stitch_file:
|
|
27
|
+
output_filename = make_output_filename stitch_file
|
|
28
|
+
file_system.log "Creating file #{output_filename.inspect}", also_write_to_stderr: true
|
|
29
|
+
erb = ERB.new file_system.load(stitch_file)
|
|
30
|
+
@sections = [[erb.result(binding), :body]]
|
|
31
|
+
create_html output_filename: output_filename, settings: {}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def make_output_filename input_filename
|
|
35
|
+
if /^(.+)\.erb$/ =~ input_filename
|
|
36
|
+
"#{$1}.html"
|
|
37
|
+
else
|
|
38
|
+
"#{input_filename}.html"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def grab_by_title title, from_file:, type: 'chart'
|
|
43
|
+
parse_file from_file
|
|
44
|
+
stitch_content = @all_stitches.find { |s| s.file == from_file && s.title == title && s.type == type }
|
|
45
|
+
return stitch_content.content if stitch_content
|
|
46
|
+
|
|
47
|
+
raise "Unable to find content in file #{from_file.inspect} matching title: #{title.inspect}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def parse_file filename
|
|
51
|
+
return false if @loaded_files.include? filename
|
|
52
|
+
|
|
53
|
+
# To match: <!-- seam-start | chart78 | GithubPrScatterplot | PR Scatterplot | chart -->
|
|
54
|
+
regex = /^<!-- seam-(?<seam>start|end) \| (?<id>[^|]+) \| (?<clazz>[^|]+) \| (?<title>[^|]+) \| (?<type>[^|]+) -->$/
|
|
55
|
+
content = nil
|
|
56
|
+
file_system.load(filename).lines do |line|
|
|
57
|
+
matches = line.match(regex)
|
|
58
|
+
if matches
|
|
59
|
+
if matches[:seam] == 'start'
|
|
60
|
+
content = +''
|
|
61
|
+
else
|
|
62
|
+
@all_stitches << Stitcher::StitchContent.new(
|
|
63
|
+
file: filename, title: matches[:title], type: matches[:type], content: content
|
|
64
|
+
)
|
|
65
|
+
content = nil
|
|
66
|
+
end
|
|
67
|
+
elsif content
|
|
68
|
+
content << line
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
@loaded_files << filename
|
|
73
|
+
true
|
|
74
|
+
end
|
|
75
|
+
end
|
data/lib/jirametrics.rb
CHANGED
|
@@ -52,11 +52,18 @@ class JiraMetrics < Thor
|
|
|
52
52
|
Exporter.instance.info(key, name_filter: options[:name] || '*')
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
option :config
|
|
56
|
+
desc 'stitch', 'Dump information about one issue'
|
|
57
|
+
def stitch stitch_file = 'stitcher.erb'
|
|
58
|
+
load_config options[:config]
|
|
59
|
+
Exporter.instance.stitch stitch_file
|
|
60
|
+
end
|
|
61
|
+
|
|
55
62
|
no_commands do
|
|
56
63
|
def load_config config_file, file_system: FileSystem.new
|
|
57
64
|
config_file = './config.rb' if config_file.nil?
|
|
58
65
|
|
|
59
|
-
if
|
|
66
|
+
if file_system.file_exist? config_file
|
|
60
67
|
# The fact that File.exist can see the file does not mean that require will be
|
|
61
68
|
# able to load it. Convert this to an absolute pathname now for require.
|
|
62
69
|
config_file = File.absolute_path(config_file).to_s
|
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.
|
|
4
|
+
version: '2.22'
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Bowler
|
|
@@ -66,6 +66,7 @@ files:
|
|
|
66
66
|
- lib/jirametrics/aging_work_table.rb
|
|
67
67
|
- lib/jirametrics/anonymizer.rb
|
|
68
68
|
- lib/jirametrics/atlassian_document_format.rb
|
|
69
|
+
- lib/jirametrics/bar_chart_range.rb
|
|
69
70
|
- lib/jirametrics/blocked_stalled_change.rb
|
|
70
71
|
- lib/jirametrics/board.rb
|
|
71
72
|
- lib/jirametrics/board_column.rb
|
|
@@ -118,12 +119,14 @@ files:
|
|
|
118
119
|
- lib/jirametrics/html/index.js
|
|
119
120
|
- lib/jirametrics/html/sprint_burndown.erb
|
|
120
121
|
- lib/jirametrics/html/throughput_chart.erb
|
|
122
|
+
- lib/jirametrics/html_generator.rb
|
|
121
123
|
- lib/jirametrics/html_report_config.rb
|
|
122
124
|
- lib/jirametrics/issue.rb
|
|
123
125
|
- lib/jirametrics/issue_collection.rb
|
|
124
126
|
- lib/jirametrics/issue_link.rb
|
|
125
127
|
- lib/jirametrics/jira_gateway.rb
|
|
126
128
|
- lib/jirametrics/project_config.rb
|
|
129
|
+
- lib/jirametrics/raw_javascript.rb
|
|
127
130
|
- lib/jirametrics/rules.rb
|
|
128
131
|
- lib/jirametrics/self_or_issue_dispatcher.rb
|
|
129
132
|
- lib/jirametrics/settings.json
|
|
@@ -132,6 +135,7 @@ files:
|
|
|
132
135
|
- lib/jirametrics/sprint_issue_change_data.rb
|
|
133
136
|
- lib/jirametrics/status.rb
|
|
134
137
|
- lib/jirametrics/status_collection.rb
|
|
138
|
+
- lib/jirametrics/stitcher.rb
|
|
135
139
|
- lib/jirametrics/throughput_chart.rb
|
|
136
140
|
- lib/jirametrics/tree_organizer.rb
|
|
137
141
|
- lib/jirametrics/trend_line_calculator.rb
|