jirametrics 2.2.1 → 2.4pre1
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/aggregate_config.rb +13 -25
- data/lib/jirametrics/aging_work_bar_chart.rb +57 -39
- data/lib/jirametrics/aging_work_in_progress_chart.rb +1 -1
- data/lib/jirametrics/aging_work_table.rb +9 -26
- data/lib/jirametrics/blocked_stalled_change.rb +24 -4
- data/lib/jirametrics/board_config.rb +2 -2
- data/lib/jirametrics/change_item.rb +13 -5
- data/lib/jirametrics/chart_base.rb +27 -39
- data/lib/jirametrics/columns_config.rb +4 -0
- data/lib/jirametrics/cycletime_histogram.rb +1 -1
- data/lib/jirametrics/cycletime_scatterplot.rb +1 -1
- data/lib/jirametrics/daily_wip_by_age_chart.rb +1 -1
- data/lib/jirametrics/daily_wip_by_blocked_stalled_chart.rb +3 -16
- data/lib/jirametrics/daily_wip_chart.rb +1 -13
- data/lib/jirametrics/data_quality_report.rb +4 -1
- data/lib/jirametrics/dependency_chart.rb +1 -1
- data/lib/jirametrics/{story_point_accuracy_chart.rb → estimate_accuracy_chart.rb} +31 -25
- data/lib/jirametrics/examples/standard_project.rb +1 -1
- data/lib/jirametrics/expedited_chart.rb +3 -1
- data/lib/jirametrics/exporter.rb +3 -3
- data/lib/jirametrics/file_config.rb +12 -8
- data/lib/jirametrics/file_system.rb +11 -2
- data/lib/jirametrics/groupable_issue_chart.rb +2 -4
- data/lib/jirametrics/hierarchy_table.rb +4 -4
- data/lib/jirametrics/html/aging_work_table.erb +3 -3
- data/lib/jirametrics/html/index.erb +1 -0
- data/lib/jirametrics/html_report_config.rb +61 -74
- data/lib/jirametrics/issue.rb +129 -57
- data/lib/jirametrics/project_config.rb +13 -7
- data/lib/jirametrics/sprint_burndown.rb +11 -0
- data/lib/jirametrics/status_collection.rb +4 -1
- data/lib/jirametrics/throughput_chart.rb +1 -1
- data/lib/jirametrics.rb +1 -1
- metadata +5 -7
- data/lib/jirametrics/experimental/generator.rb +0 -210
- data/lib/jirametrics/experimental/info.rb +0 -77
- /data/lib/jirametrics/html/{story_point_accuracy_chart.erb → estimate_accuracy_chart.erb} +0 -0
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
class
|
4
|
-
def initialize configuration_block
|
3
|
+
class EstimateAccuracyChart < ChartBase
|
4
|
+
def initialize configuration_block
|
5
5
|
super()
|
6
6
|
|
7
7
|
header_text 'Estimate Accuracy'
|
@@ -12,7 +12,7 @@ class StoryPointAccuracyChart < ChartBase
|
|
12
12
|
</div>
|
13
13
|
<div class="p">
|
14
14
|
The #{color_block '--estimate-accuracy-chart-completed-fill-color'} completed dots indicate
|
15
|
-
cycletimes.
|
15
|
+
cycletimes.
|
16
16
|
<% if @has_aging_data %>
|
17
17
|
The #{color_block '--estimate-accuracy-chart-active-fill-color'} aging dots
|
18
18
|
(click on the legend to turn them on) show the current
|
@@ -27,7 +27,7 @@ class StoryPointAccuracyChart < ChartBase
|
|
27
27
|
@y_axis_block = ->(issue, start_time) { story_points_at(issue: issue, start_time: start_time)&.to_f }
|
28
28
|
@y_axis_sort_order = nil
|
29
29
|
|
30
|
-
instance_eval(&configuration_block)
|
30
|
+
instance_eval(&configuration_block)
|
31
31
|
end
|
32
32
|
|
33
33
|
def run
|
@@ -39,26 +39,7 @@ class StoryPointAccuracyChart < ChartBase
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def scan_issues
|
42
|
-
aging_hash =
|
43
|
-
completed_hash = {}
|
44
|
-
|
45
|
-
issues.each do |issue|
|
46
|
-
cycletime = issue.board.cycletime
|
47
|
-
start_time = cycletime.started_time(issue)
|
48
|
-
stop_time = cycletime.stopped_time(issue)
|
49
|
-
|
50
|
-
next unless start_time
|
51
|
-
|
52
|
-
hash = stop_time ? completed_hash : aging_hash
|
53
|
-
|
54
|
-
estimate = @y_axis_block.call issue, start_time
|
55
|
-
cycle_time = ((stop_time&.to_date || date_range.end) - start_time.to_date).to_i + 1
|
56
|
-
|
57
|
-
next if estimate.nil?
|
58
|
-
|
59
|
-
key = [estimate, cycle_time]
|
60
|
-
(hash[key] ||= []) << issue
|
61
|
-
end
|
42
|
+
completed_hash, aging_hash = split_into_completed_and_aging issues: issues
|
62
43
|
|
63
44
|
@has_aging_data = !aging_hash.empty?
|
64
45
|
|
@@ -93,7 +74,32 @@ class StoryPointAccuracyChart < ChartBase
|
|
93
74
|
'borderColor' => border_color,
|
94
75
|
'hidden' => starts_hidden
|
95
76
|
}
|
96
|
-
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def split_into_completed_and_aging issues:
|
81
|
+
aging_hash = {}
|
82
|
+
completed_hash = {}
|
83
|
+
|
84
|
+
issues.each do |issue|
|
85
|
+
cycletime = issue.board.cycletime
|
86
|
+
start_time = cycletime.started_time(issue)
|
87
|
+
stop_time = cycletime.stopped_time(issue)
|
88
|
+
|
89
|
+
next unless start_time
|
90
|
+
|
91
|
+
hash = stop_time ? completed_hash : aging_hash
|
92
|
+
|
93
|
+
estimate = @y_axis_block.call issue, start_time
|
94
|
+
cycle_time = ((stop_time&.to_date || date_range.end) - start_time.to_date).to_i + 1
|
95
|
+
|
96
|
+
next if estimate.nil?
|
97
|
+
|
98
|
+
key = [estimate, cycle_time]
|
99
|
+
(hash[key] ||= []) << issue
|
100
|
+
end
|
101
|
+
|
102
|
+
[completed_hash, aging_hash]
|
97
103
|
end
|
98
104
|
|
99
105
|
def hash_sorter
|
@@ -20,7 +20,7 @@ class ExpeditedChart < ChartBase
|
|
20
20
|
attr_accessor :issues, :cycletime, :possible_statuses, :date_range
|
21
21
|
attr_reader :expedited_label
|
22
22
|
|
23
|
-
def initialize
|
23
|
+
def initialize block
|
24
24
|
super()
|
25
25
|
|
26
26
|
header_text 'Expedited work'
|
@@ -38,6 +38,8 @@ class ExpeditedChart < ChartBase
|
|
38
38
|
</div>
|
39
39
|
#{describe_non_working_days}
|
40
40
|
HTML
|
41
|
+
|
42
|
+
instance_eval(&block)
|
41
43
|
end
|
42
44
|
|
43
45
|
def run
|
data/lib/jirametrics/exporter.rb
CHANGED
@@ -5,7 +5,7 @@ require 'fileutils'
|
|
5
5
|
class Object
|
6
6
|
def deprecated message:, date:
|
7
7
|
text = +''
|
8
|
-
text << "Deprecated(#{date}):"
|
8
|
+
text << "Deprecated(#{date}): "
|
9
9
|
text << message
|
10
10
|
text << "\n-> Called from #{caller(1..1).first}"
|
11
11
|
warn text
|
@@ -32,11 +32,12 @@ class Exporter
|
|
32
32
|
|
33
33
|
def initialize file_system: FileSystem.new
|
34
34
|
@project_configs = []
|
35
|
-
@timezone_offset = '+00:00'
|
36
35
|
@target_path = '.'
|
37
36
|
@holiday_dates = []
|
38
37
|
@downloading = false
|
39
38
|
@file_system = file_system
|
39
|
+
|
40
|
+
timezone_offset '+00:00'
|
40
41
|
end
|
41
42
|
|
42
43
|
def export name_filter:
|
@@ -79,7 +80,6 @@ class Exporter
|
|
79
80
|
end
|
80
81
|
|
81
82
|
def project name: nil, &block
|
82
|
-
raise 'target_path was never set!' if @target_path.nil?
|
83
83
|
raise 'jira_config not set' if @jira_config.nil?
|
84
84
|
|
85
85
|
@project_configs << ProjectConfig.new(
|
@@ -5,10 +5,11 @@ require 'csv'
|
|
5
5
|
class FileConfig
|
6
6
|
attr_reader :project_config, :issues
|
7
7
|
|
8
|
-
def initialize project_config:, block:
|
8
|
+
def initialize project_config:, block:, today: Date.today
|
9
9
|
@project_config = project_config
|
10
10
|
@block = block
|
11
11
|
@columns = nil
|
12
|
+
@today = today
|
12
13
|
end
|
13
14
|
|
14
15
|
def run
|
@@ -18,11 +19,8 @@ class FileConfig
|
|
18
19
|
if @columns
|
19
20
|
all_lines = prepare_grid
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
file.puts CSV.generate_line(output_line)
|
24
|
-
end
|
25
|
-
end
|
22
|
+
content = all_lines.collect { |line| CSV.generate_line line }.join
|
23
|
+
project_config.exporter.file_system.save_file content: content, filename: output_filename
|
26
24
|
elsif @html_report
|
27
25
|
@html_report.run
|
28
26
|
else
|
@@ -59,7 +57,7 @@ class FileConfig
|
|
59
57
|
segments = []
|
60
58
|
segments << project_config.target_path
|
61
59
|
segments << project_config.file_prefix
|
62
|
-
segments << (@file_suffix || "-#{
|
60
|
+
segments << (@file_suffix || "-#{@today}.csv")
|
63
61
|
segments.join
|
64
62
|
end
|
65
63
|
|
@@ -68,7 +66,9 @@ class FileConfig
|
|
68
66
|
# is that all empty values in the first column should be at the bottom.
|
69
67
|
def sort_output all_lines
|
70
68
|
all_lines.sort do |a, b|
|
71
|
-
if a[0]
|
69
|
+
if a[0] == b[0]
|
70
|
+
a[1..] <=> b[1..]
|
71
|
+
elsif a[0].nil?
|
72
72
|
1
|
73
73
|
elsif b[0].nil?
|
74
74
|
-1
|
@@ -112,6 +112,10 @@ class FileConfig
|
|
112
112
|
object.to_s
|
113
113
|
end
|
114
114
|
|
115
|
+
def to_integer object
|
116
|
+
object.to_i
|
117
|
+
end
|
118
|
+
|
115
119
|
def file_suffix suffix = nil
|
116
120
|
@file_suffix = suffix unless suffix.nil?
|
117
121
|
@file_suffix
|
@@ -5,17 +5,26 @@ require 'json'
|
|
5
5
|
class FileSystem
|
6
6
|
attr_accessor :logfile, :logfile_name
|
7
7
|
|
8
|
+
# Effectively the same as File.read except it forces the encoding to UTF-8
|
9
|
+
def load filename
|
10
|
+
File.read filename, encoding: 'UTF-8'
|
11
|
+
end
|
12
|
+
|
8
13
|
def load_json filename, fail_on_error: true
|
9
14
|
return nil if fail_on_error == false && File.exist?(filename) == false
|
10
15
|
|
11
|
-
JSON.parse
|
16
|
+
JSON.parse load(filename)
|
12
17
|
end
|
13
18
|
|
14
19
|
def save_json json:, filename:
|
20
|
+
save_file content: JSON.pretty_generate(compress json), filename: filename
|
21
|
+
end
|
22
|
+
|
23
|
+
def save_file content:, filename:
|
15
24
|
file_path = File.dirname(filename)
|
16
25
|
FileUtils.mkdir_p file_path unless File.exist?(file_path)
|
17
26
|
|
18
|
-
File.write(filename,
|
27
|
+
File.write(filename, content)
|
19
28
|
end
|
20
29
|
|
21
30
|
def log message
|
@@ -5,10 +5,8 @@ require 'jirametrics/grouping_rules'
|
|
5
5
|
|
6
6
|
module GroupableIssueChart
|
7
7
|
def init_configuration_block user_provided_block, &default_block
|
8
|
-
|
9
|
-
|
10
|
-
return if @group_by_block
|
11
|
-
end
|
8
|
+
instance_eval(&user_provided_block)
|
9
|
+
return if @group_by_block
|
12
10
|
|
13
11
|
instance_eval(&default_block)
|
14
12
|
end
|
@@ -3,15 +3,15 @@
|
|
3
3
|
require 'jirametrics/chart_base'
|
4
4
|
|
5
5
|
class HierarchyTable < ChartBase
|
6
|
-
def initialize block
|
6
|
+
def initialize block
|
7
7
|
super()
|
8
8
|
|
9
9
|
header_text 'Hierarchy Table'
|
10
|
-
description_text
|
11
|
-
<p>
|
10
|
+
description_text <<~HTML
|
11
|
+
<p>Shows all issues through this time period and the full hierarchy of their parents.</p>
|
12
12
|
HTML
|
13
13
|
|
14
|
-
instance_eval(&block)
|
14
|
+
instance_eval(&block)
|
15
15
|
end
|
16
16
|
|
17
17
|
def run
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<th>Issue</th>
|
8
8
|
<th>Status</th>
|
9
9
|
<th>Fix versions</th>
|
10
|
-
<% if any_scrum_boards
|
10
|
+
<% if any_scrum_boards %>
|
11
11
|
<th>Sprints</th>
|
12
12
|
<% end %>
|
13
13
|
<th><%= aggregated_project? ? 'Board' : 'Who' %></th>
|
@@ -40,9 +40,9 @@
|
|
40
40
|
</div>
|
41
41
|
<% end %>
|
42
42
|
</td>
|
43
|
-
<td><%= format_status issue.status.name, board: issue.board
|
43
|
+
<td><%= format_status issue.status.name, board: issue.board %></td>
|
44
44
|
<td><%= fix_versions_text(issue) %></td>
|
45
|
-
<% if any_scrum_boards
|
45
|
+
<% if any_scrum_boards %>
|
46
46
|
<td><%= sprints_text(issue) %></td>
|
47
47
|
<% end %>
|
48
48
|
<td><%= aggregated_project? ? issue.board.name : issue.assigned_to %></td>
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<meta charset="UTF-8">
|
4
|
+
<link rel="icon" type="image/png" href="https://github.com/mikebowler/jirametrics/blob/main/favicon.png?raw=true" />
|
4
5
|
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1/moment.js"></script>
|
5
6
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
6
7
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@^1"></script>
|
@@ -9,18 +9,45 @@ class HtmlReportConfig
|
|
9
9
|
|
10
10
|
attr_reader :file_config, :sections
|
11
11
|
|
12
|
+
def self.define_chart name:, classname:, deprecated_warning: nil, deprecated_date: nil
|
13
|
+
lines = []
|
14
|
+
lines << "def #{name} &block"
|
15
|
+
lines << ' block = ->(_) {} unless block'
|
16
|
+
if deprecated_warning
|
17
|
+
lines << " deprecated date: #{deprecated_date.inspect}, message: #{deprecated_warning.inspect}"
|
18
|
+
end
|
19
|
+
lines << " execute_chart #{classname}.new(block)"
|
20
|
+
lines << 'end'
|
21
|
+
module_eval lines.join("\n"), __FILE__, __LINE__
|
22
|
+
end
|
23
|
+
|
24
|
+
define_chart name: 'aging_work_bar_chart', classname: 'AgingWorkBarChart'
|
25
|
+
define_chart name: 'aging_work_table', classname: 'AgingWorkTable'
|
26
|
+
define_chart name: 'cycletime_scatterplot', classname: 'CycletimeScatterplot'
|
27
|
+
define_chart name: 'daily_wip_chart', classname: 'DailyWipChart'
|
28
|
+
define_chart name: 'daily_wip_by_age_chart', classname: 'DailyWipByAgeChart'
|
29
|
+
define_chart name: 'daily_wip_by_blocked_stalled_chart', classname: 'DailyWipByBlockedStalledChart'
|
30
|
+
define_chart name: 'daily_wip_by_parent_chart', classname: 'DailyWipByParentChart'
|
31
|
+
define_chart name: 'throughput_chart', classname: 'ThroughputChart'
|
32
|
+
define_chart name: 'expedited_chart', classname: 'ExpeditedChart'
|
33
|
+
define_chart name: 'cycletime_histogram', classname: 'CycletimeHistogram'
|
34
|
+
define_chart name: 'estimate_accuracy_chart', classname: 'EstimateAccuracyChart'
|
35
|
+
define_chart name: 'hierarchy_table', classname: 'HierarchyTable'
|
36
|
+
|
37
|
+
define_chart name: 'daily_wip_by_type', classname: 'DailyWipChart',
|
38
|
+
deprecated_warning: 'This is the same as daily_wip_chart. Please use that one', deprecated_date: '2024-05-23'
|
39
|
+
define_chart name: 'story_point_accuracy_chart', classname: 'EstimateAccuracyChart',
|
40
|
+
deprecated_warning: 'Renamed to estimate_accuracy_chart. Please use that one', deprecated_date: '2024-05-23'
|
41
|
+
|
12
42
|
def initialize file_config:, block:
|
13
43
|
@file_config = file_config
|
14
44
|
@block = block
|
15
|
-
# @cycletimes = []
|
16
45
|
@sections = []
|
17
46
|
end
|
18
47
|
|
19
48
|
def cycletime label = nil, &block
|
20
|
-
# TODO: This is about to become deprecated
|
21
|
-
|
22
49
|
@file_config.project_config.all_boards.each_value do |board|
|
23
|
-
raise 'Multiple cycletimes not supported
|
50
|
+
raise 'Multiple cycletimes not supported' if board.cycletime
|
24
51
|
|
25
52
|
board.cycletime = CycleTimeConfig.new(parent_config: self, label: label, block: block)
|
26
53
|
end
|
@@ -39,21 +66,36 @@ class HtmlReportConfig
|
|
39
66
|
execute_chart DataQualityReport.new(@original_issue_times || {})
|
40
67
|
@sections.rotate!(-1)
|
41
68
|
|
42
|
-
File.
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
69
|
+
html_directory = "#{Pathname.new(File.realpath(__FILE__)).dirname}/html"
|
70
|
+
css = load_css html_directory: html_directory
|
71
|
+
erb = ERB.new file_system.load(File.join(html_directory, 'index.erb'))
|
72
|
+
file_system.save_file content: erb.result(binding), filename: @file_config.output_filename
|
73
|
+
end
|
74
|
+
|
75
|
+
def file_system
|
76
|
+
@file_config.project_config.exporter.file_system
|
77
|
+
end
|
78
|
+
|
79
|
+
def log message
|
80
|
+
file_system.log message
|
48
81
|
end
|
49
82
|
|
50
83
|
def load_css html_directory:
|
51
|
-
|
84
|
+
base_css_filename = File.join(html_directory, 'index.css')
|
85
|
+
base_css = file_system.load(base_css_filename)
|
86
|
+
log("Loaded CSS: #{base_css_filename}")
|
87
|
+
|
52
88
|
extra_css_filename = settings['include_css']
|
53
|
-
|
89
|
+
if extra_css_filename
|
90
|
+
if File.exist?(extra_css_filename)
|
91
|
+
base_css << "\n\n" << file_system.load(extra_css_filename)
|
92
|
+
log("Loaded CSS: #{extra_css_filename}")
|
93
|
+
else
|
94
|
+
log("Unable to find specified CSS file: #{extra_css_filename}")
|
95
|
+
end
|
96
|
+
end
|
54
97
|
|
55
|
-
|
56
|
-
base_css << "\n\n" << File.read(extra_css_filename)
|
98
|
+
base_css
|
57
99
|
end
|
58
100
|
|
59
101
|
def board_id id = nil
|
@@ -66,6 +108,8 @@ class HtmlReportConfig
|
|
66
108
|
end
|
67
109
|
|
68
110
|
def aging_work_in_progress_chart board_id: nil, &block
|
111
|
+
block ||= ->(_) {}
|
112
|
+
|
69
113
|
if board_id.nil?
|
70
114
|
ids = issues.collect { |i| i.board.id }.uniq.sort
|
71
115
|
else
|
@@ -79,50 +123,6 @@ class HtmlReportConfig
|
|
79
123
|
end
|
80
124
|
end
|
81
125
|
|
82
|
-
def aging_work_bar_chart &block
|
83
|
-
execute_chart AgingWorkBarChart.new(block)
|
84
|
-
end
|
85
|
-
|
86
|
-
def aging_work_table &block
|
87
|
-
execute_chart AgingWorkTable.new(block)
|
88
|
-
end
|
89
|
-
|
90
|
-
def cycletime_scatterplot &block
|
91
|
-
execute_chart CycletimeScatterplot.new block
|
92
|
-
end
|
93
|
-
|
94
|
-
def daily_wip_chart &block
|
95
|
-
execute_chart DailyWipChart.new(block)
|
96
|
-
end
|
97
|
-
|
98
|
-
def daily_wip_by_age_chart &block
|
99
|
-
execute_chart DailyWipByAgeChart.new block
|
100
|
-
end
|
101
|
-
|
102
|
-
def daily_wip_by_type &block
|
103
|
-
execute_chart DailyWipChart.new block
|
104
|
-
end
|
105
|
-
|
106
|
-
def daily_wip_by_blocked_stalled_chart
|
107
|
-
execute_chart DailyWipByBlockedStalledChart.new
|
108
|
-
end
|
109
|
-
|
110
|
-
def daily_wip_by_parent_chart &block
|
111
|
-
execute_chart DailyWipByParentChart.new block
|
112
|
-
end
|
113
|
-
|
114
|
-
def throughput_chart &block
|
115
|
-
execute_chart ThroughputChart.new(block)
|
116
|
-
end
|
117
|
-
|
118
|
-
def expedited_chart
|
119
|
-
execute_chart ExpeditedChart.new
|
120
|
-
end
|
121
|
-
|
122
|
-
def cycletime_histogram &block
|
123
|
-
execute_chart CycletimeHistogram.new block
|
124
|
-
end
|
125
|
-
|
126
126
|
def random_color
|
127
127
|
"##{Random.bytes(3).unpack1('H*')}"
|
128
128
|
end
|
@@ -140,14 +140,6 @@ class HtmlReportConfig
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
-
def story_point_accuracy_chart &block
|
144
|
-
execute_chart StoryPointAccuracyChart.new block
|
145
|
-
end
|
146
|
-
|
147
|
-
def hierarchy_table &block
|
148
|
-
execute_chart HierarchyTable.new block
|
149
|
-
end
|
150
|
-
|
151
143
|
def discard_changes_before_hook issues_cutoff_times
|
152
144
|
# raise 'Cycletime must be defined before using discard_changes_before' unless @cycletime
|
153
145
|
|
@@ -173,6 +165,7 @@ class HtmlReportConfig
|
|
173
165
|
def execute_chart chart, &after_init_block
|
174
166
|
project_config = @file_config.project_config
|
175
167
|
|
168
|
+
chart.file_system = file_system
|
176
169
|
chart.issues = issues
|
177
170
|
chart.time_range = project_config.time_range
|
178
171
|
chart.timezone_offset = timezone_offset
|
@@ -199,19 +192,13 @@ class HtmlReportConfig
|
|
199
192
|
@file_config.issues
|
200
193
|
end
|
201
194
|
|
195
|
+
# For use by the user config
|
202
196
|
def find_board id
|
203
197
|
@file_config.project_config.all_boards[id]
|
204
198
|
end
|
205
199
|
|
206
|
-
|
207
|
-
@file_config.project_config.name
|
208
|
-
end
|
209
|
-
|
200
|
+
# For use by the user config
|
210
201
|
def boards
|
211
202
|
@file_config.project_config.board_configs.collect(&:id).collect { |id| find_board id }
|
212
203
|
end
|
213
|
-
|
214
|
-
def find_project_by_name name
|
215
|
-
@file_config.project_config.exporter.project_configs.find { |p| p.name == name }
|
216
|
-
end
|
217
204
|
end
|