csa-ccm 0.1.2 → 0.1.3
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/CAIQ_v3.0.1-09-01-2017_FINAL_filled.answers.yaml +1380 -0
- data/CAIQ_v3.0.1-09-01-2017_FINAL_filled.control.yaml +2141 -0
- data/appveyor.yml +36 -0
- data/caiq-3.0.1.yaml +531 -419
- data/caiq.yaml +2141 -0
- data/lib/csa/ccm/answer.rb +6 -29
- data/lib/csa/ccm/cli/command.rb +67 -62
- data/lib/csa/ccm/cli/resource.rb +0 -9
- data/lib/csa/ccm/cli/version.rb +1 -1
- data/lib/csa/ccm/control.rb +3 -5
- data/lib/csa/ccm/control_domain.rb +2 -5
- data/lib/csa/ccm/matrix.rb +167 -46
- data/resources/csa-caiq-v3.0.1-12-05-2016.yaml +2141 -0
- data/samples/ccm-answers.schema.yaml +21 -0
- data/samples/ccm-answers.yaml +1 -1
- data/samples/ccm.schema.yaml +35 -0
- data/tmp/ccm-301-2.yaml +2141 -0
- data/tmp/ccm-301.yaml +531 -419
- data/tmp/test.answers.yaml +597 -0
- data/tmp/test.control.yaml +2141 -0
- metadata +13 -6
- data/3.0.1.yaml +0 -1517
- data/resources/~$csa-caiq-v3.0.1-09-01-2017.xlsx +0 -0
data/lib/csa/ccm/answer.rb
CHANGED
@@ -3,32 +3,19 @@ module Csa::Ccm
|
|
3
3
|
class Answer
|
4
4
|
|
5
5
|
ATTRIBS = %i(
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
control_id
|
7
|
+
question_id
|
8
|
+
answer
|
9
|
+
comment
|
9
10
|
)
|
10
11
|
|
11
12
|
attr_accessor *ATTRIBS
|
12
13
|
|
13
14
|
def initialize(options={})
|
14
|
-
@examples = []
|
15
|
-
@notes = []
|
16
|
-
|
17
|
-
# puts "options #{options.inspect}"
|
18
|
-
|
19
15
|
options.each_pair do |k, v|
|
20
|
-
|
21
|
-
case k
|
22
|
-
when /^example/
|
23
|
-
@examples << v
|
24
|
-
when /^note/
|
25
|
-
@notes << v
|
26
|
-
else
|
27
|
-
# puts"Key #{k}"
|
28
|
-
key = k.gsub("-", "_")
|
29
|
-
self.send("#{key}=", v)
|
30
|
-
end
|
16
|
+
self.send("#{k}=", v)
|
31
17
|
end
|
18
|
+
|
32
19
|
self
|
33
20
|
end
|
34
21
|
|
@@ -42,16 +29,6 @@ class Answer
|
|
42
29
|
end
|
43
30
|
end
|
44
31
|
end
|
45
|
-
|
46
|
-
# # entry-status
|
47
|
-
# ## Must be one of notValid valid superseded retired
|
48
|
-
# def entry_status=(value)
|
49
|
-
# unless %w(notValid valid superseded retired).include?(value)
|
50
|
-
# value = "notValid"
|
51
|
-
# end
|
52
|
-
# @entry_status = value
|
53
|
-
# end
|
54
|
-
|
55
32
|
end
|
56
33
|
|
57
34
|
end
|
data/lib/csa/ccm/cli/command.rb
CHANGED
@@ -11,101 +11,106 @@ module Csa
|
|
11
11
|
desc 'ccm-yaml VERSION', 'Generating a machine-readable CCM/CAIQ'
|
12
12
|
option :output_file, aliases: :o, type: :string, desc: 'Optional output YAML file. If missed, the input file’s name will be used'
|
13
13
|
|
14
|
-
def ccm_yaml(
|
15
|
-
input_files = Resource.lookup_version(
|
14
|
+
def ccm_yaml(caiq_version)
|
15
|
+
input_files = Resource.lookup_version(caiq_version)
|
16
16
|
|
17
|
-
unless input_files
|
18
|
-
UI.say("No file found for #{
|
17
|
+
unless input_files && !input_files.empty?
|
18
|
+
UI.say("No file found for #{caiq_version} version")
|
19
19
|
return
|
20
20
|
end
|
21
21
|
|
22
22
|
input_file = input_files.first
|
23
23
|
|
24
24
|
unless File.exist? input_file
|
25
|
-
UI.say('
|
25
|
+
UI.say("#{input_file} doesn't exists for #{caiq_version} version")
|
26
26
|
return
|
27
27
|
end
|
28
28
|
|
29
|
-
matrix = Matrix.from_xlsx(
|
29
|
+
matrix = Matrix.from_xlsx(input_file)
|
30
30
|
|
31
|
-
output_file = options[:output_file] || "caiq-#{version}.yaml"
|
32
|
-
matrix.
|
31
|
+
output_file = options[:output_file] || "caiq-#{matrix.version}.yaml"
|
32
|
+
matrix.to_control_file(output_file)
|
33
33
|
end
|
34
34
|
|
35
|
-
desc "xlsx2yaml
|
35
|
+
desc "xlsx2yaml XLSX_PATH", "Converting CCM XSLX to YAML"
|
36
36
|
option :output_file, aliases: :o, type: :string, desc: "Optional output YAML file. If missed, the input file’s name will be used"
|
37
37
|
|
38
|
-
def xlsx2yaml(
|
39
|
-
|
38
|
+
def xlsx2yaml(input_xlsx_file)
|
39
|
+
unless input_xlsx_file
|
40
|
+
UI.say("#{input_xlsx_file} file doesn't exists")
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
matrix = Matrix.from_xlsx(input_xlsx_file)
|
45
|
+
|
46
|
+
output_file = options[:output_file] || input_xlsx_file.gsub('.xlsx', '.yaml')
|
47
|
+
matrix.to_control_file(output_file)
|
40
48
|
end
|
41
49
|
|
42
|
-
desc "caiq2yaml
|
50
|
+
desc "caiq2yaml XLSX_PATH", "Converting a filled CAIQ to YAML"
|
43
51
|
option :output_name, aliases: :n, type: :string, desc: "Optional output CAIQ YAML file. If missed, the input file’s name will be used"
|
44
52
|
option :output_path, aliases: :p, type: :string, desc: "Optional output directory for result file. If missed pwd will be used"
|
45
53
|
|
46
|
-
def caiq2yaml(
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
# if Pathname.new(xslt_path).extname != ".xlsx"
|
53
|
-
# puts 'Error: filepath given must have extension .xlsx.'
|
54
|
-
# exit 1
|
55
|
-
# end
|
56
|
-
|
57
|
-
# workbook = Csa::Ccm::MatrixWorkbook.new(xslt_path)
|
58
|
-
# workbook.glossary_info.metadata_section.structure
|
59
|
-
# workbook.glossary_info.metadata_section.attributes
|
60
|
-
|
61
|
-
# languages = {}
|
62
|
-
|
63
|
-
# workbook.languages_supported.map do |lang|
|
64
|
-
# puts "************** WORKING ON LANGUAGE (#{lang})"
|
65
|
-
# sheet = workbook.language_sheet(lang)
|
66
|
-
# termsec = sheet.terms_section
|
67
|
-
# languages[sheet.language_code] = termsec.terms
|
68
|
-
# end
|
54
|
+
def caiq2yaml(input_xlsx_file)
|
55
|
+
unless input_xlsx_file
|
56
|
+
UI.say("#{input_xlsx_file} file doesn't exists")
|
57
|
+
return
|
58
|
+
end
|
69
59
|
|
70
|
-
|
60
|
+
matrix = Matrix.from_xlsx(input_xlsx_file)
|
71
61
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# end
|
62
|
+
base_output_file = options[:output_name] || File.basename(input_xlsx_file.gsub('.xlsx', ''))
|
63
|
+
if options[:output_path]
|
64
|
+
base_output_file = File.join(options[:output_path], base_output_file)
|
65
|
+
end
|
77
66
|
|
78
|
-
|
67
|
+
control_output_file = "#{base_output_file}.control.yaml"
|
68
|
+
answers_output_file = "#{base_output_file}.answers.yaml"
|
79
69
|
|
80
|
-
|
81
|
-
|
82
|
-
|
70
|
+
matrix.to_control_file(control_output_file)
|
71
|
+
matrix.to_answers_file(answers_output_file)
|
72
|
+
end
|
83
73
|
|
84
|
-
|
74
|
+
desc "generate-with-answers ANSWERS_YAML", "Writing to the CAIQ XSLX template using YAML"
|
75
|
+
option :template_path, aliases: :t, type: :string, desc: "Optional input template CAIQ XSLT file. If missed -r will be checked"
|
76
|
+
option :caiq_version, aliases: :r, type: :string, default: "3.0.1", desc: "Optional input template CAIQ XSLT version. If missed -t will be checked"
|
77
|
+
option :output_file, aliases: :o, type: :string, desc: 'Optional output XSLT file. If missed, the input file’s name will be used'
|
85
78
|
|
86
|
-
|
79
|
+
def generate_with_answers(answers_yaml_path)
|
80
|
+
unless File.exist? answers_yaml_path
|
81
|
+
UI.say("#{answers_yaml_path} file doesn't exists")
|
82
|
+
return
|
83
|
+
end
|
87
84
|
|
88
|
-
|
85
|
+
unless options[:template_path] || options[:caiq_version]
|
86
|
+
UI.say("No input template specified by -r or -t")
|
87
|
+
return
|
88
|
+
end
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
|
90
|
+
template_xslt_path = options[:template_path]
|
91
|
+
unless template_xslt_path
|
92
|
+
caiq_version = options[:caiq_version]
|
93
|
+
input_files = Resource.lookup_version(caiq_version)
|
93
94
|
|
94
|
-
|
95
|
-
|
96
|
-
|
95
|
+
unless input_files && !input_files.empty?
|
96
|
+
UI.say("No file found for #{caiq_version} version")
|
97
|
+
return
|
98
|
+
end
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
-
# english.terms_section.terms
|
100
|
+
template_xslt_path = input_files.first
|
101
|
+
end
|
101
102
|
|
102
|
-
|
103
|
-
|
103
|
+
unless File.exist? template_xslt_path
|
104
|
+
UI.say("#{template_xslt_path} file doesn't exists")
|
105
|
+
return
|
106
|
+
end
|
104
107
|
|
105
|
-
|
108
|
+
output_file = options[:output_file]
|
109
|
+
unless options[:output_file]
|
110
|
+
output_file = "#{answers_yaml_path.gsub('answers.yaml', '')}.xlsx"
|
111
|
+
end
|
106
112
|
|
107
|
-
|
108
|
-
raise 'Not implemented yet'
|
113
|
+
Matrix.fill_answers(answers_yaml_path, template_xslt_path, output_file)
|
109
114
|
end
|
110
115
|
end
|
111
116
|
end
|
data/lib/csa/ccm/cli/resource.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'rubyXL'
|
2
1
|
require 'yaml'
|
3
2
|
|
4
3
|
require_relative '../../../ext/string'
|
@@ -15,14 +14,6 @@ module Csa
|
|
15
14
|
Dir["#{root_gem}/resources/**/*v#{version}*.xlsx"]
|
16
15
|
end
|
17
16
|
|
18
|
-
def self.from_xlsx(xslt_path)
|
19
|
-
raise 'Not implemented yet'
|
20
|
-
end
|
21
|
-
|
22
|
-
def from_caiq(xslt_path, output_name, output_path)
|
23
|
-
raise 'Not implemented yet'
|
24
|
-
end
|
25
|
-
|
26
17
|
def self.to_file(hash, output_file)
|
27
18
|
File.open(output_file, 'w') { |f| f.write hash.to_yaml(line_width: 9999) }
|
28
19
|
rescue Errno::ENOENT => e
|
data/lib/csa/ccm/cli/version.rb
CHANGED
data/lib/csa/ccm/control.rb
CHANGED
@@ -2,7 +2,7 @@ module Csa::Ccm
|
|
2
2
|
|
3
3
|
class Control
|
4
4
|
ATTRIBS = %i(
|
5
|
-
id
|
5
|
+
id title specification questions
|
6
6
|
)
|
7
7
|
|
8
8
|
attr_accessor *ATTRIBS
|
@@ -22,11 +22,9 @@ class Control
|
|
22
22
|
value = self.send(attrib)
|
23
23
|
|
24
24
|
unless value.nil?
|
25
|
+
|
25
26
|
if attrib == :questions
|
26
|
-
value = value.
|
27
|
-
acc << v.to_hash
|
28
|
-
acc
|
29
|
-
end
|
27
|
+
value = value.values.map(&:to_hash)
|
30
28
|
end
|
31
29
|
|
32
30
|
acc.merge(attrib.to_s => value)
|
@@ -4,7 +4,7 @@ module Csa::Ccm
|
|
4
4
|
|
5
5
|
class ControlDomain
|
6
6
|
ATTRIBS = %i(
|
7
|
-
id
|
7
|
+
id title controls
|
8
8
|
)
|
9
9
|
|
10
10
|
attr_accessor *ATTRIBS
|
@@ -25,10 +25,7 @@ class ControlDomain
|
|
25
25
|
|
26
26
|
unless value.nil?
|
27
27
|
if attrib == :controls
|
28
|
-
value = value.
|
29
|
-
acc << v.to_hash
|
30
|
-
acc
|
31
|
-
end
|
28
|
+
value = value.values.map(&:to_hash)
|
32
29
|
end
|
33
30
|
|
34
31
|
acc.merge(attrib.to_s => value)
|
data/lib/csa/ccm/matrix.rb
CHANGED
@@ -1,26 +1,36 @@
|
|
1
|
-
require_relative
|
2
|
-
require_relative
|
3
|
-
require_relative
|
1
|
+
require_relative 'control_domain'
|
2
|
+
require_relative 'control'
|
3
|
+
require_relative 'question'
|
4
|
+
require_relative 'answer'
|
5
|
+
|
6
|
+
require 'rubyXL'
|
7
|
+
require 'rubyXL/convenience_methods/cell'
|
8
|
+
require 'rubyXL/convenience_methods/workbook'
|
9
|
+
require 'rubyXL/convenience_methods/worksheet'
|
4
10
|
|
5
11
|
module Csa::Ccm
|
6
12
|
|
7
13
|
class Matrix
|
8
|
-
|
9
|
-
|
10
|
-
|
14
|
+
|
15
|
+
ATTRIBS = %i[
|
16
|
+
version title source_file workbook source_path control_domains answers
|
17
|
+
].freeze
|
11
18
|
|
12
19
|
attr_accessor *ATTRIBS
|
13
20
|
|
14
|
-
def initialize(options={})
|
15
|
-
options.each_pair do |k,v|
|
16
|
-
|
21
|
+
def initialize(options = {})
|
22
|
+
options.each_pair do |k, v|
|
23
|
+
send("#{k}=", v)
|
17
24
|
end
|
18
25
|
|
19
26
|
if source_path
|
20
27
|
@workbook = RubyXL::Parser.parse(source_path)
|
28
|
+
|
29
|
+
parse_version if version.nil?
|
21
30
|
end
|
22
31
|
|
23
32
|
@control_domains ||= {}
|
33
|
+
@answers ||= []
|
24
34
|
|
25
35
|
self
|
26
36
|
end
|
@@ -29,10 +39,25 @@ class Matrix
|
|
29
39
|
@source_file || File.basename(source_path)
|
30
40
|
end
|
31
41
|
|
42
|
+
def parse_version
|
43
|
+
title_prefix = 'consensus assessments initiative questionnaire v'
|
44
|
+
first_row = workbook[0][0]
|
45
|
+
|
46
|
+
if first_row[2].value.downcase.start_with? title_prefix # version v3.0.1
|
47
|
+
self.version = first_row[2].value.downcase[title_prefix.length..-1]
|
48
|
+
elsif first_row[0].value.downcase.start_with? title_prefix # version v1.1
|
49
|
+
self.version = first_row[0].value.downcase[title_prefix.length..-1]
|
50
|
+
else # version 1.0
|
51
|
+
self.version = workbook[1][0][0].value[/(?<=Version )(\d+\.?)+(?= \()/]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
32
55
|
def worksheet
|
33
56
|
workbook.worksheets.first
|
34
57
|
end
|
35
58
|
|
59
|
+
attr_reader :workbook
|
60
|
+
|
36
61
|
def title
|
37
62
|
worksheet = workbook.worksheets.first
|
38
63
|
worksheet[0][2].value
|
@@ -47,11 +72,11 @@ class Matrix
|
|
47
72
|
end
|
48
73
|
|
49
74
|
class Row
|
50
|
-
ATTRIBS = %i
|
75
|
+
ATTRIBS = %i[
|
51
76
|
control_domain_id control_id question_id control_spec
|
52
|
-
question_content answer_yes answer_no answer_na
|
77
|
+
question_content answer_yes answer_no answer_na comment
|
53
78
|
control_domain_description
|
54
|
-
|
79
|
+
].freeze
|
55
80
|
|
56
81
|
attr_accessor *ATTRIBS
|
57
82
|
|
@@ -64,10 +89,21 @@ class Matrix
|
|
64
89
|
@answer_yes = ruby_xl_row[5].value
|
65
90
|
@answer_no = ruby_xl_row[6].value
|
66
91
|
@answer_na = ruby_xl_row[7].value
|
92
|
+
@comment = ruby_xl_row[8].value
|
93
|
+
|
94
|
+
# In 3.0.1 2017-09-01, question_id for "AIS-02.2" is listed as "AIS- 02.2"
|
95
|
+
%w[control_id question_id].each do |field|
|
96
|
+
if val = send(field)
|
97
|
+
send("#{field}=", val.gsub(/\s/, ''))
|
98
|
+
end
|
99
|
+
end
|
67
100
|
|
68
101
|
# In 3.0.1 2017-09-01, Rows 276 and 277's control ID says "LG-02" but it should be "STA-05" instead.
|
69
|
-
@control_id
|
70
|
-
|
102
|
+
if @control_id.nil? && @question_id
|
103
|
+
@control_id = @question_id.split('.').first
|
104
|
+
end
|
105
|
+
|
106
|
+
@control_domain_id = control_id.split('-').first if @control_id
|
71
107
|
|
72
108
|
# puts "HERE IN ROW! #{ruby_xl_row.cells.map(&:value)}"
|
73
109
|
|
@@ -78,16 +114,18 @@ class Matrix
|
|
78
114
|
self
|
79
115
|
end
|
80
116
|
|
81
|
-
def
|
117
|
+
def control_domain_title
|
82
118
|
return nil if control_domain_description.nil?
|
83
|
-
|
119
|
+
|
120
|
+
name, = control_domain_description.split(/(\n)/)
|
84
121
|
name
|
85
122
|
end
|
86
123
|
|
87
|
-
def
|
124
|
+
def control_title
|
88
125
|
return nil if control_domain_description.nil?
|
89
|
-
|
90
|
-
|
126
|
+
|
127
|
+
_, _, control_title = control_domain_description.split(/(\n)/)
|
128
|
+
control_title
|
91
129
|
end
|
92
130
|
end
|
93
131
|
|
@@ -95,27 +133,34 @@ class Matrix
|
|
95
133
|
Row.new(worksheet[i])
|
96
134
|
end
|
97
135
|
|
98
|
-
def self.
|
136
|
+
def self.get_start_row(version)
|
137
|
+
case version
|
138
|
+
when '1.0'
|
139
|
+
2
|
140
|
+
when '1.1'
|
141
|
+
3
|
142
|
+
else
|
143
|
+
# '3.0.1' and assume beyond
|
144
|
+
4
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.version_from_filepath(input_file)
|
149
|
+
input_file[/(?<=v)[0-9\.]*(?=-)/] || 'unknown'
|
150
|
+
end
|
151
|
+
|
152
|
+
def self.from_xlsx(input_file)
|
99
153
|
matrix = Matrix.new(
|
100
|
-
version: version,
|
101
154
|
source_path: input_file
|
102
155
|
)
|
103
156
|
|
104
157
|
all_rows = matrix.worksheet.sheet_data.rows
|
105
|
-
start_row = 4 # FIXME add some basic logic to calculate it
|
106
|
-
|
107
|
-
last_control_domain = nil
|
108
|
-
last_control_id = nil
|
109
|
-
last_control_specification = nil
|
110
158
|
|
111
|
-
|
112
|
-
|
113
|
-
row_number = start_row
|
159
|
+
start_row = get_start_row(matrix.version)
|
114
160
|
max_row_number = all_rows.length - 1
|
115
161
|
|
116
162
|
# We loop over all Questions
|
117
163
|
(start_row..max_row_number).each do |row_number|
|
118
|
-
|
119
164
|
# puts "looping row #{row_number}"
|
120
165
|
row = matrix.row(row_number)
|
121
166
|
# Skip row if there is no question-id
|
@@ -130,10 +175,10 @@ class Matrix
|
|
130
175
|
unless domain_id.nil?
|
131
176
|
|
132
177
|
control_domain = matrix.control_domains[domain_id] ||
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
178
|
+
ControlDomain.new(
|
179
|
+
id: row.control_domain_id,
|
180
|
+
title: row.control_domain_title
|
181
|
+
)
|
137
182
|
|
138
183
|
# puts"control_domain #{control_domain.to_hash}"
|
139
184
|
|
@@ -147,7 +192,7 @@ class Matrix
|
|
147
192
|
control_domain = matrix.control_domains[domain_id]
|
148
193
|
control = control_domain.controls[control_id] || Control.new(
|
149
194
|
id: row.control_id,
|
150
|
-
|
195
|
+
title: row.control_title,
|
151
196
|
specification: row.control_spec
|
152
197
|
)
|
153
198
|
|
@@ -160,28 +205,104 @@ class Matrix
|
|
160
205
|
# Store the Question
|
161
206
|
# putsquestion.to_hash
|
162
207
|
control.questions[row.question_id] = Question.new(id: row.question_id, content: row.question_content)
|
208
|
+
|
209
|
+
answer = if row.answer_na
|
210
|
+
'NA'
|
211
|
+
elsif row.answer_no
|
212
|
+
'no'
|
213
|
+
elsif row.answer_yes
|
214
|
+
'yes'
|
215
|
+
end
|
216
|
+
|
217
|
+
matrix.answers << Answer.new(
|
218
|
+
question_id: row.question_id,
|
219
|
+
control_id: control_id,
|
220
|
+
answer: answer,
|
221
|
+
comment: row.comment
|
222
|
+
)
|
163
223
|
end
|
164
224
|
|
165
225
|
matrix
|
166
226
|
end
|
167
227
|
|
168
|
-
def
|
228
|
+
def self.fill_answers(answers_yaml_path, template_xslt_path, output_xslt_path)
|
229
|
+
ccm = YAML.safe_load(File.read(answers_yaml_path, encoding: 'UTF-8'))['ccm']
|
230
|
+
answers = ccm['answers']
|
231
|
+
answers_hash = Hash[*answers.collect { |a| [a['question-id'], a] }.flatten]
|
232
|
+
answers_version = ccm['metadata']['version']
|
233
|
+
template_version = version_from_filepath(template_xslt_path)
|
234
|
+
|
235
|
+
unless template_version == answers_version
|
236
|
+
raise "Template XLSX & answers YAML version missmatch #{template_version} vs. #{answers_version}"
|
237
|
+
end
|
238
|
+
|
239
|
+
matrix = Matrix.new(
|
240
|
+
version: template_version,
|
241
|
+
source_path: template_xslt_path
|
242
|
+
)
|
243
|
+
|
244
|
+
worksheet = matrix.worksheet
|
245
|
+
all_rows = worksheet.sheet_data.rows
|
246
|
+
|
247
|
+
start_row = get_start_row(matrix.version)
|
248
|
+
max_row_number = all_rows.length - 1
|
249
|
+
|
250
|
+
(start_row..max_row_number).each do |row_number|
|
251
|
+
question_id = worksheet[row_number][2].value
|
252
|
+
|
253
|
+
next unless answers_hash.key?(question_id)
|
254
|
+
|
255
|
+
answer = answers_hash[question_id]
|
256
|
+
answer_value = answer['answer']
|
257
|
+
|
258
|
+
answer_col = case answer_value
|
259
|
+
when 'yes', true
|
260
|
+
5
|
261
|
+
when 'no', false
|
262
|
+
6
|
263
|
+
when 'NA'
|
264
|
+
7
|
265
|
+
end
|
266
|
+
|
267
|
+
worksheet[row_number][answer_col].change_contents(answer['notes'])
|
268
|
+
end
|
269
|
+
|
270
|
+
matrix.workbook.write(output_xslt_path)
|
271
|
+
worksheet
|
272
|
+
end
|
273
|
+
|
274
|
+
def to_control_hash
|
169
275
|
{
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
end
|
276
|
+
'ccm' => {
|
277
|
+
'metadata' => metadata.to_hash,
|
278
|
+
'control_domains' => control_domains.each_with_object([]) do |(_k, v), acc|
|
279
|
+
acc << v.to_hash
|
280
|
+
end
|
176
281
|
}
|
177
282
|
}
|
178
283
|
end
|
179
284
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
285
|
+
def to_answers_hash
|
286
|
+
{
|
287
|
+
'ccm' => {
|
288
|
+
'metadata' => metadata.to_hash,
|
289
|
+
'answers' => answers.each_with_object([]) do |v, acc|
|
290
|
+
acc << v.to_hash
|
291
|
+
end
|
292
|
+
}
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
def to_control_file(filename)
|
297
|
+
File.open(filename, 'w') do |file|
|
298
|
+
file.write(to_control_hash.to_yaml)
|
183
299
|
end
|
184
300
|
end
|
185
301
|
|
302
|
+
def to_answers_file(filename)
|
303
|
+
File.open(filename, 'w') do |file|
|
304
|
+
file.write(to_answers_hash.to_yaml)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
186
308
|
end
|
187
|
-
end
|