hflr 0.10.1

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.
@@ -0,0 +1,40 @@
1
+
2
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
3
+ require 'rake/testtask'
4
+
5
+ namespace :test do
6
+
7
+ Rake::TestTask.new(:run) do |t|
8
+ t.libs = PROJ.libs
9
+ t.test_files = if test(?f, PROJ.test.file) then [PROJ.test.file]
10
+ else PROJ.test.files end
11
+ t.ruby_opts += PROJ.ruby_opts
12
+ t.ruby_opts += PROJ.test.opts
13
+ end
14
+
15
+ if HAVE_RCOV
16
+ desc 'Run rcov on the unit tests'
17
+ task :rcov => :clobber_rcov do
18
+ opts = PROJ.rcov.opts.dup << '-o' << PROJ.rcov.dir
19
+ opts = opts.join(' ')
20
+ files = if test(?f, PROJ.test.file) then [PROJ.test.file]
21
+ else PROJ.test.files end
22
+ files = files.join(' ')
23
+ sh "#{RCOV} #{files} #{opts}"
24
+ end
25
+
26
+ task :clobber_rcov do
27
+ rm_r 'coverage' rescue nil
28
+ end
29
+ end
30
+
31
+ end # namespace :test
32
+
33
+ desc 'Alias to test:run'
34
+ task :test => 'test:run'
35
+
36
+ task :clobber => 'test:clobber_rcov' if HAVE_RCOV
37
+
38
+ end
39
+
40
+ # EOF
@@ -0,0 +1,36 @@
1
+ if HAVE_ZENTEST
2
+
3
+ # --------------------------------------------------------------------------
4
+ if test(?e, PROJ.test.file) or not PROJ.test.files.to_a.empty?
5
+ require 'autotest'
6
+
7
+ namespace :test do
8
+ task :autotest do
9
+ Autotest.run
10
+ end
11
+ end
12
+
13
+ desc "Run the autotest loop"
14
+ task :autotest => 'test:autotest'
15
+
16
+ end # if test
17
+
18
+ # --------------------------------------------------------------------------
19
+ if HAVE_SPEC_RAKE_SPECTASK and not PROJ.spec.files.to_a.empty?
20
+ require 'autotest/rspec'
21
+
22
+ namespace :spec do
23
+ task :autotest do
24
+ load '.autotest' if test(?f, '.autotest')
25
+ Autotest::Rspec.run
26
+ end
27
+ end
28
+
29
+ desc "Run the autotest loop"
30
+ task :autotest => 'spec:autotest'
31
+
32
+ end # if rspec
33
+
34
+ end # if HAVE_ZENTEST
35
+
36
+ # EOF
@@ -0,0 +1,5 @@
1
+ CJoe Smith 55455025.53
2
+ O0005233110-10-2008
3
+ CJane Smith 55404015.25
4
+ O0054933310-11-2008
5
+ O0075789110-12-2008
@@ -0,0 +1,2 @@
1
+ Joe Smith 55455025.53
2
+ Jane Smith 55404015.25
@@ -0,0 +1,48 @@
1
+ require '../lib/fl_record_file'
2
+
3
+
4
+ # Read a file with only one record type (no record type markers)
5
+
6
+ # metadata for customer file
7
+ Column = Struct.new(:name,:start,:len)
8
+ columns = {:customer=>[
9
+ Column.new("name",1,25),
10
+ Column.new("zip",26,5),
11
+ Column.new("balance",31,5)]}
12
+
13
+ customer_file = FixedLengthRecordFile.new(File.new("customers.dat"), :customer, columns, 1, [:line_number])
14
+
15
+ customer_file.each do |record|
16
+ puts "Customer #{customer_file.line_number.to_s} #{record.name} #{record.zip} "
17
+ end
18
+
19
+
20
+ # You can get the values by attribute name like a hash
21
+ def show(record)
22
+ print record.members.map{|m| m.to_s + ": " + record[m].to_s}.join(", ") + "\n"
23
+ end
24
+
25
+ # metadata for customer_orders file
26
+ layouts = {:customer=>[
27
+ Column.new("name",1,25),
28
+ Column.new("zip",26,5),
29
+ Column.new("balance",31,5)],
30
+ :order=>[
31
+ Column.new("order_num",1,8),
32
+ Column.new("date",9,10),]}
33
+
34
+
35
+ customer_orders_file = FixedLengthRecordFile.new(
36
+ File.new("customer_orders.dat"),
37
+ {"C"=>:customer,"O"=>:order},# Use these characters as record type markers
38
+ layouts,
39
+ 0, # shift parsed string 0 columns to the left of the indicated start column
40
+ {:customer=>[:line_number,:record_type],:order=>[:line_number,:record_type]}) # Add these columns to the indicated record types post read
41
+
42
+
43
+ customer_orders_file.each do |record|
44
+ show record
45
+ end
46
+
47
+
48
+
@@ -0,0 +1,212 @@
1
+
2
+
3
+
4
+ class FixedLengthRecordFileTest < Test::Unit::TestCase
5
+
6
+
7
+
8
+ def setup
9
+ var_type = Struct.new(:name,:start,:len)
10
+
11
+ # Split up your metadata by record type.
12
+
13
+ @layouts =
14
+ {:household=>[var_type.new(:rectypeh,1,1), var_type.new(:phone,2,1), var_type.new("mortgage",3,1)],
15
+ :person=>[var_type.new(:rectypep,1,1), var_type.new("age",2,3), var_type.new("sex",5,1), var_type.new("marst",6,1)]}
16
+
17
+ # Give the values used in the data for each record type
18
+ @record_types ={"H"=>:household,"P"=>:person}
19
+ end
20
+
21
+ def teardown
22
+ end
23
+
24
+
25
+ def test_initialize
26
+ sample_data_path = File.dirname(__FILE__)
27
+ fwf = FixedLengthRecordFile.new(
28
+ File.new("#{sample_data_path}/sample.dat"),
29
+ @record_types, # Record types to read from the file, all others will be ignored
30
+ @layouts,# metadata for all record types
31
+ 1, # column 0 starts at logical location 1
32
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
33
+ )
34
+
35
+ # Extra columns + record_type accessors should have been created
36
+ hh_struct = fwf.record_template[:household].record_structure.new
37
+ assert hh_struct.respond_to?(:record_type),"household record should have record_type method"
38
+ p_struct = fwf.record_template[:person].record_structure.new
39
+ assert p_struct.respond_to?(:household_id),"Should have household_id as an extra column"
40
+ assert p_struct.respond_to?(:record_type),"Should have record_type method"
41
+
42
+
43
+ fwf = FixedLengthRecordFile.new(
44
+ File.new("#{sample_data_path}/sample.dat"),
45
+ @record_types, # Record types to read from the file, all others will be ignored
46
+ @layouts,# metadata for all record types
47
+ 1 )
48
+
49
+ # Should still have added the record_type method but none of the others
50
+ hh_struct = fwf.record_template[:household].record_structure.new
51
+ assert hh_struct.respond_to?(:record_type),"Should have record_type method"
52
+ assert !hh_struct.respond_to?(:people)
53
+ end
54
+
55
+ def test_get_record_type
56
+ sample_data_path = File.dirname(__FILE__)
57
+ fwf = FixedLengthRecordFile.new(
58
+ File.new("#{sample_data_path}/sample.dat"),
59
+ @record_types, # Record types to read from the file, all others will be ignored
60
+ @layouts,# metadata for all record types
61
+ 1, # column 0 starts at logical location 1
62
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
63
+ )
64
+ assert_nil fwf.get_record_type(nil)
65
+ assert_equal :household,fwf.get_record_type("H123")
66
+ assert_equal :person,fwf.get_record_type("P1234")
67
+ assert_equal nil, fwf.get_record_type("C123")
68
+
69
+ end
70
+
71
+
72
+
73
+
74
+ def test_build_record
75
+ sample_data_path = File.dirname(__FILE__)
76
+ fwf = FixedLengthRecordFile.new(
77
+ File.new("#{sample_data_path}/sample.dat"),
78
+ @record_types, # Record types to read from the file, all others will be ignored
79
+ @layouts,# metadata for all record types
80
+ 1, # column 0 starts at logical location 1
81
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
82
+ )
83
+
84
+ assert_equal nil, fwf.build_record(nil)
85
+ rec = fwf.build_record("H012345666665555444333")
86
+ assert_equal :household,rec[:record_type]
87
+
88
+ assert_raise RuntimeError do
89
+ fwf.build_record("c23abbbc")
90
+ end
91
+
92
+ end
93
+
94
+
95
+ def test_each
96
+ sample_data_path = File.dirname(__FILE__)
97
+ fwf = FixedLengthRecordFile.new(
98
+ File.new("#{sample_data_path}/sample.dat"),
99
+ @record_types,
100
+ @layouts,
101
+ 1,
102
+ {:household=>[:record_type,:people],:person=>[:household_id,:pserial,:record_type]})
103
+
104
+ records = []
105
+
106
+ fwf.each do |record|
107
+ records << record
108
+ end
109
+ assert records.first.respond_to?(:record_type)
110
+ assert_equal :person, records.last.record_type
111
+ assert_equal :household,records[0].record_type
112
+ assert_equal :person, records[1].record_type
113
+
114
+ end
115
+
116
+ def test_next_record
117
+ sample_data_path = File.dirname(__FILE__)
118
+ fwf = FixedLengthRecordFile.new(
119
+ File.new("#{sample_data_path}/sample.dat"), # data is in this file
120
+ @record_types, # Records of different types have these labels
121
+ @layouts, # metadata for creating record structs
122
+ 1, # All metadata starting column locations are to be shifted 1 left
123
+ {:household=>[:people],:person=>[:household_id,:pserial]}) # Extra columns not to come from metadata
124
+
125
+ records = []
126
+ while rec = fwf.next_record do
127
+ records << rec
128
+ end
129
+
130
+ assert_equal :household, records.first.record_type
131
+
132
+ assert records.first.respond_to?(:record_type)
133
+
134
+ # The last record is a person type and should not have a 'people' accessor
135
+ assert !records.last.respond_to?(:people)
136
+
137
+ # Should have added these accessors from the extra_columns argument above
138
+ assert records.first.respond_to?(:people)
139
+ assert records.last.respond_to?(:household_id)
140
+
141
+ assert_equal :household,records[0].record_type
142
+ assert_equal :person, records[1].record_type
143
+
144
+ end
145
+
146
+ def test_open
147
+ record = Struct.new(:rectypeh,:phone,:mortgage,:record_type)
148
+
149
+ sample_data_path = File.dirname(__FILE__)
150
+ FixedLengthRecordFile.open(
151
+ "#{sample_data_path}/sample_out.dat", # data is in this file
152
+ "w",# open file for writing
153
+ @record_types, # Records of different types have these labels
154
+ @layouts, # metadata for creating record structs
155
+ 1) do |fwf|# All metadata starting column locations are to be shifted 1 left
156
+ assert_equal FixedLengthRecordFile, fwf.class
157
+ fwf << record.new("H",1,2,:household)
158
+ fwf << ["H",1,3,:household]
159
+ end
160
+
161
+ assert File.exists?("#{sample_data_path}/sample.dat") # data is in this file
162
+
163
+ var = Struct.new(:name,:start,:len)
164
+ l = {:customer=>[var.new("name",1,25),var.new("age",26,3)]}
165
+ fwf = FixedLengthRecordFile.new(File.new("sample2_out.dat","w"),
166
+ :customer, l,1)
167
+
168
+ fwf << ["joe",25,:customer]
169
+ fwf.close
170
+
171
+ end
172
+
173
+
174
+ def test_line_type
175
+ sample_data_path = File.dirname(__FILE__)
176
+ fwf = FixedLengthRecordFile.new(
177
+ File.new("#{sample_data_path}/sample.dat"),
178
+ @record_types, # Record types to read from the file, all others will be ignored
179
+ @layouts,# metadata for all record types
180
+ 1, # column 0 starts at logical location 1
181
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
182
+ )
183
+
184
+ assert_equal :unknown, fwf.line_type(nil)
185
+ assert_equal :household,fwf.line_type("H123")
186
+ assert_equal :person,fwf.line_type("P123")
187
+ assert_equal :unknown, fwf.line_type("C123")
188
+ end
189
+
190
+ def test_get_next_known_line_type
191
+ sample_data_path = File.dirname(__FILE__)
192
+ fwf = FixedLengthRecordFile.new(
193
+ File.new("#{sample_data_path}/sample_activities.dat"),
194
+ @record_types, # Record types to read from the file, all others will be ignored
195
+ @layouts,# metadata for all record types
196
+ 1, # column 0 starts at logical location 1
197
+ {:household=>[:people],:person=>[:household_id,:pserial]} # extra columns by record type
198
+ )
199
+ # By reading the sample_activities file with only the household and person record types know
200
+ # we should get the activity and who records to be skipped.
201
+ while rec=fwf.get_next_known_line_type
202
+
203
+ unless rec.strip.empty?
204
+
205
+ assert ["P","H"].include?(rec[0..0])
206
+
207
+ end
208
+ end
209
+
210
+ end
211
+
212
+ end
@@ -0,0 +1,174 @@
1
+
2
+
3
+
4
+ class RecordTemplateTest < Test::Unit::TestCase
5
+
6
+
7
+ Var = Struct.new(:name,:start,:len)
8
+
9
+ # Some real world metadata. This is only layout for the starting portion of the ATUS / CPS household record
10
+ # which actually extends to beyond column 117
11
+ AtusHH =[[:RECTYPEH, 1, 1,],
12
+ [:CASEID, 2, 14],
13
+ [:AGEYCHILD, 16, 3],
14
+ [:SERIAL, 19, 7],
15
+ [:HH_NUMADULTS, 26, 2],
16
+ [:FAMBUS_RESP, 28, 2],
17
+ [:FAMBUS_OTHER, 30, 2],
18
+ [:FAMBUS_SPOUSE, 32, 2],
19
+ [:FAMBUS, 34, 2],
20
+ [:HH_CHILD, 36, 2],
21
+ [:HH_NUMKIDS, 38, 2],
22
+ [:HH_SIZE, 40, 3],
23
+ [:HH_NUMEMPLD, 43, 3],
24
+ [:FAMINCOME, 46, 3]]
25
+
26
+
27
+
28
+ def setup
29
+
30
+ hh_vars = AtusHH.map{|v| Var.new(v[0],v[1],v[2])}
31
+
32
+ @vars = {:household=>hh_vars,
33
+ :person=>[Var.new("age",2,3),Var.new("sex",5,1)],
34
+ :activity=>[Var.new("where",1,5),Var.new("activity",6,5)],
35
+ :who=>[Var.new("relatew",1,2)]}
36
+ @record_types = {"H"=>:household,"P"=>:person,"A"=>:activity,"W"=>:who}
37
+ @record_type_symbols = @record_types.invert
38
+ end
39
+
40
+ def test_create
41
+ record_layouts = @vars # variables by record type
42
+ templates = HFLR::RecordTemplate.create(record_layouts, @record_type_symbols, 1)
43
+ assert_equal @vars.keys, templates.keys
44
+
45
+ household_field_pattern = templates[:household].field_pattern
46
+ person_field_pattern =templates[:person].field_pattern
47
+ activity_field_pattern = templates[:activity].field_pattern
48
+ who_field_pattern = templates[:who].field_pattern
49
+
50
+
51
+ assert household_field_pattern.is_a?(String)
52
+ assert person_field_pattern.size>2,"field pattern should have at least one variable"
53
+ assert activity_field_pattern.size>2,"field pattern for activity should have at least one variable"
54
+ assert who_field_pattern.size>2,"field pattern for who should have at least one variable"
55
+
56
+
57
+ household_record_struct = templates[:household].record_structure.new
58
+ assert household_record_struct.respond_to?(:HH_SIZE)
59
+
60
+ end
61
+
62
+ def test_create_template_class
63
+ template = HFLR::RecordTemplate.create_template_class(:person,@record_type_symbols[:person], @vars[:person],1,{})
64
+ assert template.respond_to?(:field_pattern)
65
+ assert template.respond_to?(:record_structure)
66
+ record_structure = template.record_structure
67
+ assert record_structure.new.is_a?(Struct)
68
+ assert record_structure.new.respond_to?(:record_type)
69
+
70
+ end
71
+
72
+
73
+ def test_get_pattern
74
+ household_layout = @vars[:household]
75
+
76
+ pattern = HFLR::RecordTemplate.get_pattern(household_layout)
77
+ assert_equal "@1A1",pattern[0..3]
78
+
79
+ # Adjust the location ('@') leftward (the metadata refers to the 0th column as column 1.)
80
+ pattern = HFLR::RecordTemplate.get_pattern(household_layout,1)
81
+ assert_equal "@0A1",pattern[0..3]
82
+
83
+ vars_in_pattern = pattern.scan("A").size
84
+ assert_equal household_layout.size, vars_in_pattern
85
+
86
+ end
87
+
88
+ def test_build_record
89
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
90
+ hh_str = "1200501010500069980000001020000000000000020009960200009999999999999999991330299902305030201034300000000037110550997797000000000007700100000000200411000000000"
91
+
92
+ # from a string to a record struct
93
+ household_rec = templates[:household].build_record(hh_str)
94
+
95
+ assert household_rec.is_a?(Struct)
96
+ assert household_rec.values.size> @vars[:household].size,"Should be values for the extra columns"
97
+
98
+ # Check a few things...
99
+ assert_equal 1,household_rec[:SERIAL].to_i
100
+
101
+
102
+ assert_equal 1,household_rec.SERIAL.to_i
103
+
104
+ assert_equal "1",household_rec[0]
105
+ assert_equal "1",household_rec.RECTYPEH
106
+ assert_equal "1",household_rec[:RECTYPEH]
107
+
108
+
109
+
110
+ end
111
+
112
+ def test_build_line
113
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
114
+
115
+ hh_str = "H200501010500069980000001020000000000000020009960200009999999999999999991330299902305030201034300000000037110550997797000000000007700100000000200411000000000"
116
+
117
+ # from a string to a record struct
118
+ household_rec = templates[:household].build_record(hh_str)
119
+ assert_equal "002",hh_str[39..41]
120
+ assert_equal "002",household_rec.HH_SIZE
121
+
122
+
123
+ # back to a string
124
+ new_hh_str = templates[:household].build_line(household_rec)
125
+
126
+
127
+ assert_equal "002",new_hh_str[39..41]
128
+
129
+ # Some of the values in hh_str won't be in new_hh_str because not all data
130
+ # in hh_str is mapped by household_layout, but the mapped variables should have
131
+ # the same values.
132
+
133
+ @vars[:household].each do |v|
134
+ format_str = "@#{(v.start-1).to_s}a#{v.len.to_s}"
135
+ orig_data = hh_str.unpack(format_str)
136
+ new_data = new_hh_str.unpack(format_str)
137
+
138
+
139
+ assert_equal new_data, orig_data,"Comparing #{v.name} #{format_str}"
140
+ end
141
+ end
142
+
143
+
144
+ def test_format_fields
145
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
146
+
147
+ formatted_fields = templates[:who].send(:format_fields,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15])
148
+
149
+ widths = @vars[:who].map{|v| v.len}
150
+ formatted_fields.size.times do |i|
151
+ assert_equal formatted_fields[i].size, widths[i],"Width of #{@vars[:who][i].name} should have been #{widths[i].to_s}"
152
+ end
153
+
154
+ end
155
+
156
+ def test_write_format
157
+ templates = HFLR::RecordTemplate.create(@vars,@record_type_symbols,1)
158
+
159
+ assert_equal "abc",templates[:activity].send(:right_format,"abc",3)
160
+ assert_equal "abc ",templates[:activity].send(:right_format,"abc",6)
161
+ assert_equal "3",templates[:activity].send(:right_format,3,1)
162
+ assert_equal "005",templates[:activity].send(:right_format,5,3)
163
+ assert_equal "ZZZ",templates[:activity].send(:right_format,-999998,3)
164
+ end
165
+
166
+
167
+
168
+ end
169
+
170
+
171
+
172
+
173
+
174
+