csa-ccm 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|