ncs_mdes_warehouse 0.1.1 → 0.2.0
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.
- data/.rspec +2 -0
- data/CHANGELOG.md +20 -0
- data/Rakefile +2 -3
- data/generated_models/ncs_navigator/warehouse/models/two_point_zero/spec_blood.rb +3 -3
- data/lib/ncs_navigator/warehouse.rb +1 -0
- data/lib/ncs_navigator/warehouse/cli.rb +46 -1
- data/lib/ncs_navigator/warehouse/comparator.rb +257 -0
- data/lib/ncs_navigator/warehouse/data_mapper.rb +20 -0
- data/lib/ncs_navigator/warehouse/database_initializer.rb +3 -4
- data/lib/ncs_navigator/warehouse/transform_status.rb +5 -1
- data/lib/ncs_navigator/warehouse/transformers/enum_transformer.rb +18 -3
- data/lib/ncs_navigator/warehouse/updating_shell.rb +1 -0
- data/lib/ncs_navigator/warehouse/version.rb +1 -1
- data/lib/ncs_navigator/warehouse/xml_emitter.rb +95 -13
- data/ncs_mdes_warehouse.gemspec +1 -1
- data/spec/ncs_navigator/warehouse/data_mapper_spec.rb +39 -0
- data/spec/ncs_navigator/warehouse/transform_load_spec.rb +3 -1
- data/spec/ncs_navigator/warehouse/transformers/database_spec.rb +3 -3
- data/spec/ncs_navigator/warehouse/transformers/enum_transformer_spec.rb +36 -5
- data/spec/ncs_navigator/warehouse/xml_emitter_spec.rb +176 -30
- data/spec/spec_helper.rb +54 -17
- metadata +39 -38
@@ -41,7 +41,10 @@ module NcsNavigator::Warehouse
|
|
41
41
|
|
42
42
|
def unsuccessful_record(record, message)
|
43
43
|
self.transform_errors <<
|
44
|
-
TransformError.new(
|
44
|
+
TransformError.new(
|
45
|
+
:model_class => record.class.name,
|
46
|
+
:record_id => (record.key.first if record && record.key),
|
47
|
+
:message => message)
|
45
48
|
end
|
46
49
|
end
|
47
50
|
|
@@ -54,6 +57,7 @@ module NcsNavigator::Warehouse
|
|
54
57
|
property :id, Serial
|
55
58
|
property :message, Text, :required => true
|
56
59
|
property :model_class, String, :length => 255
|
60
|
+
property :record_id, String, :length => 255
|
57
61
|
|
58
62
|
belongs_to :transform_status, TransformStatus, :required => true
|
59
63
|
end
|
@@ -37,16 +37,17 @@ module NcsNavigator::Warehouse::Transformers
|
|
37
37
|
# @return [void]
|
38
38
|
def transform(status)
|
39
39
|
enum.each do |record|
|
40
|
+
apply_global_values_if_necessary(record)
|
40
41
|
if record.valid?
|
41
42
|
log.debug("Saving valid record #{record_ident record}.")
|
42
43
|
begin
|
43
44
|
unless record.save
|
44
|
-
msg = "Could not save.
|
45
|
+
msg = "Could not save."
|
45
46
|
log.error msg
|
46
47
|
status.unsuccessful_record(record, msg)
|
47
48
|
end
|
48
49
|
rescue => e
|
49
|
-
msg = "Error on save. #{e.class}: #{e}.
|
50
|
+
msg = "Error on save. #{e.class}: #{e}."
|
50
51
|
log.error msg
|
51
52
|
status.unsuccessful_record(record, msg)
|
52
53
|
end
|
@@ -57,7 +58,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
57
58
|
"#{e} (#{prop}=#{v.inspect})."
|
58
59
|
}
|
59
60
|
}.flatten
|
60
|
-
msg = "Invalid record. #{messages.join(' ')}
|
61
|
+
msg = "Invalid record. #{messages.join(' ')}"
|
61
62
|
log.error msg
|
62
63
|
status.unsuccessful_record(record, msg)
|
63
64
|
end
|
@@ -72,5 +73,19 @@ module NcsNavigator::Warehouse::Transformers
|
|
72
73
|
'%s %s=%s' % [
|
73
74
|
rec.class.name.demodulize, rec.class.key.first.name, rec.key.try(:first).inspect]
|
74
75
|
end
|
76
|
+
|
77
|
+
def apply_global_values_if_necessary(record)
|
78
|
+
{
|
79
|
+
:psu_id => @configuration.navigator.psus.first.id,
|
80
|
+
:recruit_type => @configuration.navigator.recruitment_type_id
|
81
|
+
}.each do |attr, value|
|
82
|
+
setter = :"#{attr}="
|
83
|
+
if record.respond_to?(setter) && record.respond_to?(attr)
|
84
|
+
unless record.send(attr)
|
85
|
+
record.send(setter, value)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
75
90
|
end
|
76
91
|
end
|
@@ -7,12 +7,28 @@ require 'fileutils'
|
|
7
7
|
require 'forwardable'
|
8
8
|
|
9
9
|
module NcsNavigator::Warehouse
|
10
|
+
##
|
11
|
+
# Generates VDR XML from a warehouse instance. This is the object
|
12
|
+
# which implements the `emit-xml` tool in the `mdes-wh` command line
|
13
|
+
# client.
|
10
14
|
class XmlEmitter
|
11
15
|
extend Forwardable
|
12
16
|
|
17
|
+
##
|
18
|
+
# @return [Configuration] the warehouse configuration used by this
|
19
|
+
# emitter.
|
13
20
|
attr_reader :configuration
|
21
|
+
|
22
|
+
##
|
23
|
+
# @return [Pathname] the file to which the XML will be emitted.
|
14
24
|
attr_reader :filename
|
15
25
|
|
26
|
+
##
|
27
|
+
# @return [Array<Models::MdesModel>] the models whose data will be
|
28
|
+
# emitted. This is determined from the `:tables` option to
|
29
|
+
# {#initialize}.
|
30
|
+
attr_reader :models
|
31
|
+
|
16
32
|
def_delegators :@configuration, :shell, :log
|
17
33
|
|
18
34
|
HEADER_TEMPLATE = ERB.new(<<-XML_ERB)
|
@@ -39,6 +55,10 @@ XML_ERB
|
|
39
55
|
</ncs:recruitment_substudy_transmission_envelope>
|
40
56
|
XML
|
41
57
|
|
58
|
+
##
|
59
|
+
# @param configuration [Configuration]
|
60
|
+
# @return [Pathname] the default filename to use for a VDR XML
|
61
|
+
# submission. The format is `{county}-{YYYYMMDD}.xml`.
|
42
62
|
def self.default_filename(configuration)
|
43
63
|
psu_type = configuration.mdes.types.detect { |type| type.name =~ /^psu_cl/ }
|
44
64
|
unless psu_type
|
@@ -48,7 +68,7 @@ XML
|
|
48
68
|
psu_id = configuration.navigator.psus.first.id
|
49
69
|
psu_entry = psu_type.code_list.detect { |cle| cle.value == psu_id }
|
50
70
|
unless psu_entry
|
51
|
-
fail "Cannot find PSU #{psu_id} in #{psu_type.name}. Please specify a filename manually"
|
71
|
+
fail "Cannot find PSU #{psu_id} in #{psu_type.name}. Please specify a filename manually."
|
52
72
|
end
|
53
73
|
|
54
74
|
Pathname.new '%s-%s.xml' % [
|
@@ -57,7 +77,26 @@ XML
|
|
57
77
|
]
|
58
78
|
end
|
59
79
|
|
60
|
-
|
80
|
+
##
|
81
|
+
# Create a new {XmlEmitter}.
|
82
|
+
#
|
83
|
+
# @param [Configuration] config the configuration for the
|
84
|
+
# warehouse from which to emit records.
|
85
|
+
# @param [Pathname,#to_s,nil] filename the filename to which the output
|
86
|
+
# will be written. If `nil`, the {.default_filename} is used.
|
87
|
+
#
|
88
|
+
# @option options [Fixnum] :block-size (5000) the maximum number
|
89
|
+
# of records to load into memory before writing them to the XML
|
90
|
+
# file. Reduce this to reduce the memory load of the emitter.
|
91
|
+
# Increasing it will probably not improve performance, even if
|
92
|
+
# you have sufficient memory to load more records.
|
93
|
+
# @option options [Boolean] :include-pii (false) should PII
|
94
|
+
# variable values be included in the XML?
|
95
|
+
# @option options [Array<#to_s>] :tables (all for current MDES
|
96
|
+
# version) the tables to include in the emitted XML.
|
97
|
+
# @option options [Boolean] :zip (true) should a ZIP file be
|
98
|
+
# produced alongside the XML file?
|
99
|
+
def initialize(config, filename, options={})
|
61
100
|
@configuration = config
|
62
101
|
@filename = case filename
|
63
102
|
when Pathname
|
@@ -68,8 +107,23 @@ XML
|
|
68
107
|
Pathname.new(filename.to_s)
|
69
108
|
end
|
70
109
|
@record_count = 0
|
110
|
+
@block_size = options[:'block-size'] || 5000
|
111
|
+
@zip = options.has_key?(:zip) ? options[:zip] : true
|
112
|
+
@include_pii = options[:'include-pii']
|
113
|
+
@models =
|
114
|
+
if options[:tables]
|
115
|
+
options[:tables].collect { |t| t.to_s }.collect { |t|
|
116
|
+
config.models_module.mdes_order.find { |model| model.mdes_table_name == t }
|
117
|
+
}
|
118
|
+
else
|
119
|
+
config.models_module.mdes_order
|
120
|
+
end
|
71
121
|
end
|
72
122
|
|
123
|
+
##
|
124
|
+
# Emit XML from the configured warehouse to {#filename}.
|
125
|
+
#
|
126
|
+
# @return [void]
|
73
127
|
def emit_xml
|
74
128
|
shell.say_line("Exporting to #{filename}")
|
75
129
|
log.info("Beginning XML export to #{filename}")
|
@@ -78,7 +132,7 @@ XML
|
|
78
132
|
filename.open('w') do |f|
|
79
133
|
f.write HEADER_TEMPLATE.result(binding)
|
80
134
|
|
81
|
-
|
135
|
+
models.each do |model|
|
82
136
|
shell.clear_line_then_say('Writing XML for %33s' % model.mdes_table_name)
|
83
137
|
|
84
138
|
write_all_xml_for_model(f, model)
|
@@ -91,14 +145,36 @@ XML
|
|
91
145
|
shell.clear_line_then_say(msg)
|
92
146
|
log.info(msg)
|
93
147
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
148
|
+
if zip?
|
149
|
+
shell.say_line("Zipping to #{zip_filename}")
|
150
|
+
log.info("Zipping to #{zip_filename}")
|
151
|
+
Zip::ZipFile.open(zip_filename, Zip::ZipFile::CREATE) do |zf|
|
152
|
+
zf.add(filename.basename, filename)
|
153
|
+
end
|
154
|
+
log.info("XML export complete")
|
98
155
|
end
|
99
|
-
log.info("XML export complete")
|
100
156
|
end
|
101
157
|
|
158
|
+
##
|
159
|
+
# Will PII be included in the exported XML?
|
160
|
+
#
|
161
|
+
# @return [Boolean]
|
162
|
+
def include_pii?
|
163
|
+
@include_pii
|
164
|
+
end
|
165
|
+
|
166
|
+
##
|
167
|
+
# Will a ZIP archive be created along with the XML?
|
168
|
+
#
|
169
|
+
# @return [Boolean]
|
170
|
+
def zip?
|
171
|
+
@zip
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# @return [Pathname] the filename for the ZIP archive of the XML,
|
176
|
+
# if any. Currently this is always {#filename} + '.zip'.
|
177
|
+
# @see #zip?
|
102
178
|
def zip_filename
|
103
179
|
@zip_filename ||= filename.to_s + '.zip'
|
104
180
|
end
|
@@ -107,11 +183,17 @@ XML
|
|
107
183
|
|
108
184
|
def write_all_xml_for_model(f, model)
|
109
185
|
shell.say(' %20s' % '[loading]')
|
110
|
-
model.
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
186
|
+
count = model.count
|
187
|
+
offset = 0
|
188
|
+
while offset < count
|
189
|
+
shell.back_up_and_say(20, '%20s' % '[loading]')
|
190
|
+
model.all(:limit => @block_size, :offset => offset).each do |instance|
|
191
|
+
instance.write_mdes_xml(f, :indent => 3, :margin => 1, :pii => include_pii?)
|
192
|
+
@record_count += 1
|
193
|
+
|
194
|
+
shell.back_up_and_say(20, '%5d (%5.1f/sec)' % [@record_count, emit_rate])
|
195
|
+
end
|
196
|
+
offset += @block_size
|
115
197
|
end
|
116
198
|
end
|
117
199
|
|
data/ncs_mdes_warehouse.gemspec
CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
16
|
s.require_paths = ["lib", "generated_models"]
|
17
17
|
|
18
|
-
s.add_dependency 'ncs_mdes', '~> 0.
|
18
|
+
s.add_dependency 'ncs_mdes', '~> 0.5'
|
19
19
|
s.add_dependency 'ncs_navigator_configuration', '~> 0.2'
|
20
20
|
|
21
21
|
s.add_dependency 'activesupport', '~> 3.0'
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
module NcsNavigator::Warehouse::DataMapper
|
4
|
+
shared_examples 'a string-based NCS type' do
|
5
|
+
it 'converts a BigDecimal to a simple number string (no powers of ten)' do
|
6
|
+
subject.send(:typecast_to_primitive, BigDecimal.new('0.453E1')).should == '4.53'
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'uses the default method for other types' do
|
10
|
+
subject.send(:typecast_to_primitive, 45).should == '45'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe NcsString, :modifies_warehouse_state do
|
15
|
+
let(:model) {
|
16
|
+
class NcsStringHost
|
17
|
+
include DataMapper::Resource
|
18
|
+
property :haiku, NcsString
|
19
|
+
end
|
20
|
+
}
|
21
|
+
|
22
|
+
subject { model.properties[:haiku] }
|
23
|
+
|
24
|
+
it_behaves_like 'a string-based NCS type'
|
25
|
+
end
|
26
|
+
|
27
|
+
describe NcsText, :modifies_warehouse_state do
|
28
|
+
let(:model) {
|
29
|
+
class NcsTextHost
|
30
|
+
include DataMapper::Resource
|
31
|
+
property :novel, NcsText
|
32
|
+
end
|
33
|
+
}
|
34
|
+
|
35
|
+
subject { model.properties[:novel] }
|
36
|
+
|
37
|
+
it_behaves_like 'a string-based NCS type'
|
38
|
+
end
|
39
|
+
end
|
@@ -48,7 +48,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
48
48
|
end
|
49
49
|
|
50
50
|
describe '#connection_parameters' do
|
51
|
-
describe 'from bcdatabase', :modifies_warehouse_state do
|
51
|
+
describe 'from bcdatabase', :modifies_warehouse_state, :use_test_bcdatabase do
|
52
52
|
let(:cls) {
|
53
53
|
sample_class do
|
54
54
|
bcdatabase :name => 'ncs_staff_portal'
|
@@ -79,7 +79,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
-
describe '#repository' do
|
82
|
+
describe '#repository', :modifies_warehouse_state, :use_test_bcdatabase do
|
83
83
|
include_context 'people_pro'
|
84
84
|
|
85
85
|
let(:cls) do
|
@@ -97,7 +97,7 @@ module NcsNavigator::Warehouse::Transformers
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
-
describe '#each' do
|
100
|
+
describe '#each', :modifies_warehouse_state, :use_test_bcdatabase do
|
101
101
|
include_context 'people_pro'
|
102
102
|
|
103
103
|
describe 'of .produce_records' do
|
@@ -5,6 +5,8 @@ module NcsNavigator::Warehouse::Transformers
|
|
5
5
|
class Sample
|
6
6
|
include ::DataMapper::Resource
|
7
7
|
|
8
|
+
property :psu_id, String
|
9
|
+
property :recruit_type, String
|
8
10
|
property :id, Integer, :key => true
|
9
11
|
property :name, String, :required => true
|
10
12
|
end
|
@@ -14,7 +16,11 @@ module NcsNavigator::Warehouse::Transformers
|
|
14
16
|
end
|
15
17
|
|
16
18
|
describe '#transform' do
|
17
|
-
let(:config) {
|
19
|
+
let(:config) {
|
20
|
+
NcsNavigator::Warehouse::Configuration.new.tap do |c|
|
21
|
+
c.log_file = tmpdir + 'enum_transformer_test.log'
|
22
|
+
end
|
23
|
+
}
|
18
24
|
let(:records) { [
|
19
25
|
Sample.new(:id => 1, :name => 'One'),
|
20
26
|
Sample.new(:id => 2, :name => 'Two'),
|
@@ -33,7 +39,29 @@ module NcsNavigator::Warehouse::Transformers
|
|
33
39
|
transform_status.transform_errors.should be_empty
|
34
40
|
end
|
35
41
|
|
36
|
-
it 'automatically sets the PSU ID if
|
42
|
+
it 'automatically sets the PSU ID if necessary' do
|
43
|
+
records[1].psu_id = '20000042'
|
44
|
+
|
45
|
+
records.each do |m|
|
46
|
+
m.should_receive(:valid?).and_return(true)
|
47
|
+
m.should_receive(:save).and_return(true)
|
48
|
+
end
|
49
|
+
|
50
|
+
subject.transform(transform_status)
|
51
|
+
records.collect(&:psu_id).should == %w(20000030 20000042 20000030)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'automatically sets recruit_type if necessary' do
|
55
|
+
records[2].recruit_type = '1'
|
56
|
+
|
57
|
+
records.each do |m|
|
58
|
+
m.should_receive(:valid?).and_return(true)
|
59
|
+
m.should_receive(:save).and_return(true)
|
60
|
+
end
|
61
|
+
|
62
|
+
subject.transform(transform_status)
|
63
|
+
records.collect(&:recruit_type).should == %w(3 3 1)
|
64
|
+
end
|
37
65
|
|
38
66
|
describe 'with an invalid instance' do
|
39
67
|
before do
|
@@ -47,7 +75,8 @@ module NcsNavigator::Warehouse::Transformers
|
|
47
75
|
it 'records the invalid instance' do
|
48
76
|
err = transform_status.transform_errors.first
|
49
77
|
err.model_class.should == Sample.to_s
|
50
|
-
err.
|
78
|
+
err.record_id.should == '3'
|
79
|
+
err.message.should == 'Invalid record. Name must not be blank (name=nil).'
|
51
80
|
end
|
52
81
|
|
53
82
|
it 'saves the other instances' do
|
@@ -67,7 +96,8 @@ module NcsNavigator::Warehouse::Transformers
|
|
67
96
|
it 'records the unsaveable instance' do
|
68
97
|
err = transform_status.transform_errors.first
|
69
98
|
err.model_class.should == Sample.to_s
|
70
|
-
err.
|
99
|
+
err.record_id.should == '2'
|
100
|
+
err.message.should == 'Could not save.'
|
71
101
|
end
|
72
102
|
|
73
103
|
it 'saves the saveable instances' do
|
@@ -87,8 +117,9 @@ module NcsNavigator::Warehouse::Transformers
|
|
87
117
|
it 'records the failing instance' do
|
88
118
|
err = transform_status.transform_errors.first
|
89
119
|
err.model_class.should == Sample.to_s
|
120
|
+
err.record_id.should == '1'
|
90
121
|
err.message.should ==
|
91
|
-
'Error on save. RuntimeError: No database around these parts.
|
122
|
+
'Error on save. RuntimeError: No database around these parts.'
|
92
123
|
end
|
93
124
|
|
94
125
|
it 'saves the saveable instances' do
|
@@ -5,8 +5,9 @@ require 'zip/zip'
|
|
5
5
|
module NcsNavigator::Warehouse
|
6
6
|
describe XmlEmitter, :use_mdes do
|
7
7
|
let(:filename) { tmpdir + 'export.xml' }
|
8
|
+
let(:options) { {} }
|
8
9
|
let(:xml) {
|
9
|
-
XmlEmitter.new(spec_config, filename).emit_xml
|
10
|
+
XmlEmitter.new(spec_config, filename, options).emit_xml
|
10
11
|
Nokogiri::XML(File.read(filename))
|
11
12
|
}
|
12
13
|
|
@@ -14,33 +15,91 @@ module NcsNavigator::Warehouse
|
|
14
15
|
spec_config.models_module.const_get(:Person)
|
15
16
|
end
|
16
17
|
|
18
|
+
def participant_model
|
19
|
+
spec_config.models_module.const_get(:Participant)
|
20
|
+
end
|
21
|
+
|
22
|
+
def stub_model(model)
|
23
|
+
model.stub!(:count).and_return(0)
|
24
|
+
end
|
25
|
+
|
17
26
|
before do
|
18
|
-
spec_config.models_module.mdes_order.
|
19
|
-
|
27
|
+
spec_config.models_module.mdes_order.reject { |m|
|
28
|
+
[person_model, participant_model].include?(m)
|
29
|
+
}.each do |model|
|
30
|
+
stub_model(model)
|
20
31
|
end
|
21
32
|
end
|
22
33
|
|
23
34
|
# Most of the details of the XML are tested on the MdesModel mixin
|
24
35
|
describe 'the generated XML', :slow do
|
25
|
-
|
26
|
-
|
36
|
+
describe 'global attributes' do
|
37
|
+
before do
|
38
|
+
stub_model(person_model)
|
39
|
+
stub_model(participant_model)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'includes the SC from the configuration' do
|
43
|
+
xml.xpath('//sc_id').text.should == '20000029'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'includes the PSU from the configuration' do
|
47
|
+
xml.xpath('//psu_id').text.should == '20000030'
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'includes the appropriate specification version' do
|
51
|
+
xml.xpath('//specification_version').text.
|
52
|
+
should == spec_config.mdes.specification_version
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_required_attributes(model)
|
57
|
+
model.properties.select { |prop| prop.required? }.inject({}) { |h, prop|
|
58
|
+
h[prop.name] = '-4'; h
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_instance(model, attributes)
|
63
|
+
model.new(default_required_attributes(model).merge(attributes))
|
27
64
|
end
|
28
65
|
|
29
|
-
|
30
|
-
|
66
|
+
def create_person(id, attributes={})
|
67
|
+
create_instance(person_model, { :person_id => id }.merge(attributes))
|
31
68
|
end
|
32
69
|
|
33
|
-
|
34
|
-
|
35
|
-
|
70
|
+
describe 'with exactly one actual record', :slow, :use_database do
|
71
|
+
let(:records) {
|
72
|
+
[
|
73
|
+
create_person('XQ4')
|
74
|
+
]
|
75
|
+
}
|
76
|
+
|
77
|
+
before do
|
78
|
+
pending 'Not working in CI at the moment' if ENV['CI_RUBY']
|
79
|
+
records.each { |rec| rec.save or fail "Save of #{rec.inspect} failed." }
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'contains records for all models' do
|
83
|
+
xml.xpath('//person').size.should == 1
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'contains the right records' do
|
87
|
+
xml.xpath('//person/person_id').collect { |e| e.text.strip }.sort.should == %w(XQ4)
|
88
|
+
end
|
36
89
|
end
|
37
90
|
|
38
|
-
describe 'with actual data' do
|
91
|
+
describe 'with actual data', :slow, :use_database do
|
92
|
+
let(:records) {
|
93
|
+
[
|
94
|
+
create_person('XQ4', :first_name => 'Xavier'),
|
95
|
+
create_person('QX9', :first_name => 'Quentin'),
|
96
|
+
create_instance(participant_model, :p_id => 'P_QX4')
|
97
|
+
]
|
98
|
+
}
|
99
|
+
|
39
100
|
before do
|
40
|
-
|
41
|
-
|
42
|
-
person_model.new(:person_id => 'QX9')
|
43
|
-
])
|
101
|
+
pending 'Not working in CI at the moment' if ENV['CI_RUBY']
|
102
|
+
records.each { |rec| rec.save or fail "Save of #{rec.inspect} failed." }
|
44
103
|
end
|
45
104
|
|
46
105
|
it 'contains records for all models' do
|
@@ -48,7 +107,70 @@ module NcsNavigator::Warehouse
|
|
48
107
|
end
|
49
108
|
|
50
109
|
it 'contains the right records' do
|
51
|
-
xml.xpath('//person/person_id').collect { |e| e.text.strip }.should == %w(XQ4
|
110
|
+
xml.xpath('//person/person_id').collect { |e| e.text.strip }.sort.should == %w(QX9 XQ4)
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'and PII' do
|
114
|
+
let(:xml_first_names) {
|
115
|
+
xml.xpath('//person/first_name').collect { |e| e.text.strip }.sort
|
116
|
+
}
|
117
|
+
|
118
|
+
it 'excludes PII by default' do
|
119
|
+
xml_first_names.should == ['', '']
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'excludes PII when explicitly excluded' do
|
123
|
+
options[:'include-pii'] = false
|
124
|
+
xml_first_names.should == ['', '']
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'includes PII when requested' do
|
128
|
+
options[:'include-pii'] = true
|
129
|
+
xml_first_names.should == %w(Quentin Xavier)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe 'and selected output' do
|
134
|
+
let(:people_count) { xml.xpath('//person').size }
|
135
|
+
let(:p_count) { xml.xpath('//participant').size }
|
136
|
+
|
137
|
+
it 'includes all tables by default' do
|
138
|
+
people_count.should == 2
|
139
|
+
p_count.should == 1
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'includes only the selected tables when requested' do
|
143
|
+
options[:tables] = %w(participant)
|
144
|
+
|
145
|
+
people_count.should == 0
|
146
|
+
p_count.should == 1
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'includes all the requested tables when explicitly requested' do
|
150
|
+
options[:tables] = spec_config.models_module.mdes_order.collect(&:mdes_table_name)
|
151
|
+
|
152
|
+
people_count.should == 2
|
153
|
+
p_count.should == 1
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
describe 'with lots and lots of actual data', :slow, :use_database do
|
159
|
+
let(:count) { 3134 }
|
160
|
+
let(:records) { (0...count).collect { |n| create_person(n) } }
|
161
|
+
let(:actual_ids) { xml.xpath('//person/person_id').collect { |e| e.text.strip } }
|
162
|
+
|
163
|
+
before do
|
164
|
+
pending 'Not working in CI at the moment' if ENV['CI_RUBY']
|
165
|
+
records.each { |rec| rec.save or fail "Save of #{rec.inspect} failed." }
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'contains all the records' do
|
169
|
+
actual_ids.size.should == count
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'contains the right records' do
|
173
|
+
actual_ids.collect(&:to_i).sort[2456].should == 2456
|
52
174
|
end
|
53
175
|
end
|
54
176
|
end
|
@@ -57,27 +179,51 @@ module NcsNavigator::Warehouse
|
|
57
179
|
let(:expected_zipfile) { Pathname.new(filename.to_s + '.zip') }
|
58
180
|
|
59
181
|
before do
|
60
|
-
|
182
|
+
stub_model(person_model)
|
183
|
+
stub_model(participant_model)
|
61
184
|
end
|
62
185
|
|
63
|
-
|
64
|
-
|
65
|
-
|
186
|
+
context do
|
187
|
+
def actual
|
188
|
+
xml
|
189
|
+
expected_zipfile
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'exists by default' do
|
193
|
+
actual.should be_readable
|
194
|
+
end
|
66
195
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
196
|
+
it 'exists if explicitly requested' do
|
197
|
+
options[:zip] = true
|
198
|
+
actual.should be_readable
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'does not exist when excluded' do
|
202
|
+
options[:zip] = false
|
203
|
+
actual.exist?.should be_false
|
71
204
|
end
|
72
|
-
contents.should == [ filename.basename.to_s ]
|
73
205
|
end
|
74
206
|
|
75
|
-
|
76
|
-
|
77
|
-
|
207
|
+
context do
|
208
|
+
before do
|
209
|
+
xml # for side effects
|
210
|
+
end
|
78
211
|
|
79
|
-
|
80
|
-
|
212
|
+
it 'contains just the XML file' do
|
213
|
+
contents = []
|
214
|
+
Zip::ZipFile.foreach(expected_zipfile) do |entry|
|
215
|
+
contents << entry.name
|
216
|
+
end
|
217
|
+
contents.should == [ filename.basename.to_s ]
|
218
|
+
end
|
219
|
+
|
220
|
+
it 'can be opened by something other than rubyzip' do
|
221
|
+
`which unzip`
|
222
|
+
pending "unzip is not available" unless $? == 0
|
223
|
+
|
224
|
+
`unzip -l '#{expected_zipfile}' 2>&1`.should =~ /#{filename.basename}\s/
|
225
|
+
$?.should == 0
|
226
|
+
end
|
81
227
|
end
|
82
228
|
end
|
83
229
|
|