ssn_validator 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/History.txt +41 -0
- data/LICENSE +20 -0
- data/PostInstall.txt +29 -0
- data/README.rdoc +98 -0
- data/Rakefile +60 -0
- data/VERSION.yml +5 -0
- data/generators/death_master_file_migration/death_master_file_migration_generator.rb +12 -0
- data/generators/death_master_file_migration/templates/migration.rb +26 -0
- data/generators/ssn_validator_migration/ssn_validator_migration_generator.rb +12 -0
- data/generators/ssn_validator_migration/templates/migration.rb +18 -0
- data/lib/ssn_validator.rb +12 -0
- data/lib/ssn_validator/models/death_master_file.rb +5 -0
- data/lib/ssn_validator/models/death_master_file_loader.rb +235 -0
- data/lib/ssn_validator/models/ssn_high_group_code.rb +5 -0
- data/lib/ssn_validator/models/ssn_high_group_code_loader.rb +87 -0
- data/lib/ssn_validator/models/ssn_validator.rb +94 -0
- data/lib/ssn_validator/ntis.rb +9 -0
- data/lib/tasks/ssn_validator.rake +24 -0
- data/rdoc/classes/DeathMasterFile.html +111 -0
- data/rdoc/classes/DeathMasterFileLoader.html +298 -0
- data/rdoc/classes/SsnHighGroupCode.html +111 -0
- data/rdoc/classes/SsnHighGroupCodeLoader.html +202 -0
- data/rdoc/classes/SsnValidator.html +116 -0
- data/rdoc/classes/SsnValidator/Ntis.html +111 -0
- data/rdoc/classes/SsnValidator/Ssn.html +315 -0
- data/rdoc/created.rid +1 -0
- data/rdoc/files/LICENSE.html +129 -0
- data/rdoc/files/README_rdoc.html +262 -0
- data/rdoc/files/lib/ssn_validator/models/death_master_file_loader_rb.html +112 -0
- data/rdoc/files/lib/ssn_validator/models/death_master_file_rb.html +108 -0
- data/rdoc/files/lib/ssn_validator/models/ssn_high_group_code_loader_rb.html +108 -0
- data/rdoc/files/lib/ssn_validator/models/ssn_high_group_code_rb.html +108 -0
- data/rdoc/files/lib/ssn_validator/models/ssn_validator_rb.html +101 -0
- data/rdoc/files/lib/ssn_validator/ntis_rb.html +101 -0
- data/rdoc/files/lib/ssn_validator_rb.html +113 -0
- data/rdoc/fr_class_index.html +33 -0
- data/rdoc/fr_file_index.html +35 -0
- data/rdoc/fr_method_index.html +37 -0
- data/rdoc/index.html +24 -0
- data/rdoc/rdoc-style.css +208 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/ssn_validator.gemspec +130 -0
- data/test/files/test_dmf_funky_data_load.txt +6 -0
- data/test/files/test_dmf_initial_load.txt +5 -0
- data/test/files/test_dmf_update_load.txt +5 -0
- data/test/files/valid_csv_from_funky_data_file.txt +6 -0
- data/test/mocks/test/death_master_file_loader.rb +40 -0
- data/test/test_death_master_file_loader.rb +113 -0
- data/test/test_helper.rb +62 -0
- data/test/test_ssn_high_group_code_loader.rb +22 -0
- data/test/test_ssn_validator.rb +85 -0
- metadata +145 -0
data/.gitignore
ADDED
data/History.txt
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
== 1.0.4 2010-02-05
|
2
|
+
|
3
|
+
* 1 bug fixes:
|
4
|
+
* The ssa website changed it's process for the most recent month. Modified the rake task
|
5
|
+
to determine the most recent month loaded and bring it up to date.
|
6
|
+
|
7
|
+
== 1.0.3 2009-04-24
|
8
|
+
|
9
|
+
* 2 minor enhancement:
|
10
|
+
* Added method #death_master_file_record to SsnValidator::Ssn to return the actual dmf record if the SSN exists
|
11
|
+
* Changed the method #death_master_file_hit? to simply return true or false.
|
12
|
+
|
13
|
+
== 1.0.2 2009-04-24
|
14
|
+
|
15
|
+
* 1 minor enhancement:
|
16
|
+
* Using jeweler gem to create gemspec and rdoc files
|
17
|
+
|
18
|
+
== 1.0.1 2009-04-23
|
19
|
+
|
20
|
+
* 2 bug fixes:
|
21
|
+
* Fixed dmf mysql load error when single quote is in a data file record.
|
22
|
+
* Fixed ssn_validator:update_data rake task that broke on the last build.
|
23
|
+
|
24
|
+
== 1.0.0 2009-04-22
|
25
|
+
|
26
|
+
* 1 major enhancement:
|
27
|
+
* Added the death master file to determine if the ssn belongs to the deceased.
|
28
|
+
|
29
|
+
* 1 bug fix:
|
30
|
+
* Fixed bug where an error was thrown if the ssn area has not been assigned yet.
|
31
|
+
|
32
|
+
== 0.1.2 2009-04-15
|
33
|
+
|
34
|
+
* 2 minor enhancements:
|
35
|
+
* Added rdoc files
|
36
|
+
* Updated documentation
|
37
|
+
|
38
|
+
== 0.1.0 2009-04-14
|
39
|
+
|
40
|
+
* 1 major enhancement:
|
41
|
+
* Initial release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Kevin Tyll
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/PostInstall.txt
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
For more information on ssn_validator, see http://kevintyll.github.com/ssn_validator/
|
3
|
+
|
4
|
+
* To create the necessary db migration, from the command line, run:
|
5
|
+
script/generate ssn_validator_migration
|
6
|
+
* Require the gem in your environment.rb file in the Rails::Initializer block:
|
7
|
+
config.gem 'kevintyll-ssn_validator', :lib => 'ssn_validator'
|
8
|
+
* To load your table with the current SSN data, from the command line, run:
|
9
|
+
rake ssn_validator:update_data
|
10
|
+
* The SSN data is updated monthly, so you'll want to run this rake task monthly to keep your validations accurate.
|
11
|
+
* If you've purchased the death master file data, create the death_master_files migration:
|
12
|
+
script/generate death_master_file_migration
|
13
|
+
* To load the dmf files you receive from ntis:
|
14
|
+
rake ssn_validator:death_master_file:load_file PATH='path/to/file' AS_OF='2009-03-01'
|
15
|
+
* You'll need to pass in the full path to where the file is on disk. You'll also need
|
16
|
+
to pass in the date this file's data are as of in the format yyyy-mm-dd.
|
17
|
+
* This task must be used to load the initial file you receive from ntis on CD. It can optionally be used
|
18
|
+
to load the monthly update files you download from the website. If you manually download the update files,
|
19
|
+
you do not need to add your user name and password to the environment.rb file. For a more automated approach
|
20
|
+
to loading the update files, add your user name and password to the environment.rb file and use the 2nd rake task.
|
21
|
+
* To load the monthly updates from the ntis website:
|
22
|
+
* Add your user_name and password to the environment.rb file
|
23
|
+
SsnValidator::Ntis.user_name = 'REPLACE WITH YOUR dmf.ntis.gov USER NAME'
|
24
|
+
SsnValidator::Ntis.password = 'REPLACE WITH YOUR dmf.ntis.gov PASSWORD'
|
25
|
+
* Run the rake task:
|
26
|
+
rake ssn_validator:death_master_file:update_data
|
27
|
+
* This rake task will determine the most recent file that has been loaded, and loads all subsequent files in order
|
28
|
+
from the dmf.ntis.gov website.
|
29
|
+
* The death master file data is updated monthly, so you'll want to run this rake task monthly to keep your validations accurate.
|
data/README.rdoc
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
= ssn_validator
|
2
|
+
|
3
|
+
* http://kevintyll.github.com/ssn_validator
|
4
|
+
* http://www.drexel-labs.com
|
5
|
+
|
6
|
+
== DESCRIPTION:
|
7
|
+
|
8
|
+
ssn_validator is a ruby gem that validates whether an SSN has likely been issued or not.
|
9
|
+
What exactly does that mean "likely" been issued? We can't tell for sure if an SSN belongs
|
10
|
+
to a particular person, but knowing the "rules"[http://www.socialsecurity.gov/employer/ssnweb.htm]
|
11
|
+
on how the social security administration assigns numbers, we can determine whether a number
|
12
|
+
has ever been issued or not.
|
13
|
+
|
14
|
+
ssn_validator started as a need for the company I work for, Clarity Services Inc. Incredibly, we
|
15
|
+
couldn't find an existing gem or service that provided what we needed. Since we were going to have
|
16
|
+
to roll our own solution, we decided to create a gem out of it and share it with the community. Much
|
17
|
+
thanks goes to the management at Clarity Services Inc. for allowing this code to be open sourced.
|
18
|
+
|
19
|
+
|
20
|
+
== FEATURES/PROBLEMS:
|
21
|
+
|
22
|
+
* What it can do:
|
23
|
+
Validates the likelyhood that an SSN has been issued to someone.
|
24
|
+
Checks the Death Master File if the SSN belongs to a dead person. This will require you to purchase the dmf data from https://dmf.ntis.gov
|
25
|
+
|
26
|
+
* What it cannot do:
|
27
|
+
Validate that an SSN actually belongs to a particular person.
|
28
|
+
|
29
|
+
* What it's planned to do:
|
30
|
+
Determine when an SSN was issued...if i can find the historical data. This can be used to further validate an SSN by comparing it to a Date of Birth.
|
31
|
+
|
32
|
+
== SYNOPSIS:
|
33
|
+
|
34
|
+
* Just instantiate the object with an SSN.
|
35
|
+
ssn = SsnValidator::Ssn.new('123-45-6789')
|
36
|
+
|
37
|
+
* Then check if it's valid
|
38
|
+
ssn.valid?
|
39
|
+
ssn.death_master_file_hit?
|
40
|
+
|
41
|
+
ssn.valid? only checks to see if the number itself is valid. Check the death_master_file_hit? method
|
42
|
+
as well to verify the ssn does not belong the the deceased.
|
43
|
+
|
44
|
+
The death_master_file_hit? method will only return true of false if you have purchased the dmf data and
|
45
|
+
loaded it into your database. There is a generator and rake task to facilitate this, but you'll have
|
46
|
+
to purchase the data yourself at https://dmf.ntis.gov.
|
47
|
+
|
48
|
+
* You can check the errors array to see why it's not valid.
|
49
|
+
ssn.errors
|
50
|
+
|
51
|
+
== REQUIREMENTS:
|
52
|
+
|
53
|
+
* Rails 2.0.0 or greater
|
54
|
+
|
55
|
+
== INSTALL:
|
56
|
+
|
57
|
+
* To install the gem:
|
58
|
+
sudo gem install kevintyll-ssn_validator
|
59
|
+
* To create the necessary db migration, from the command line, run:
|
60
|
+
script/generate ssn_validator_migration
|
61
|
+
* Require the gem in your environment.rb file in the Rails::Initializer block:
|
62
|
+
config.gem 'kevintyll-ssn_validator', :lib => 'ssn_validator'
|
63
|
+
* To load your table with the current SSN data, from the command line, run:
|
64
|
+
rake ssn_validator:update_data
|
65
|
+
|
66
|
+
* The SSN data is updated monthly, so you'll want to run this rake task monthly to keep your validations accurate.
|
67
|
+
|
68
|
+
* If you've purchased the death master file data, create the death_master_files migration:
|
69
|
+
script/generate death_master_file_migration
|
70
|
+
* To load the dmf files you receive from ntis:
|
71
|
+
rake ssn_validator:death_master_file:load_file PATH='path/to/file' AS_OF='2009-03-01'
|
72
|
+
|
73
|
+
* You'll need to pass in the full path to where the file is on disk. You'll also need
|
74
|
+
to pass in the date this file's data are as of in the format yyyy-mm-dd.
|
75
|
+
|
76
|
+
* This task must be used to load the initial file you receive from ntis on CD. It can optionally be used
|
77
|
+
to load the monthly update files you download from the website. If you manually download the update files,
|
78
|
+
you do not need to add your user name and password to the environment.rb file. For a more automated approach
|
79
|
+
to loading the update files, add your user name and password to the environment.rb file and use the 2nd rake task.
|
80
|
+
|
81
|
+
* To load the monthly updates from the ntis website:
|
82
|
+
* Add your user_name and password to the environment.rb file
|
83
|
+
|
84
|
+
SsnValidator::Ntis.user_name = 'REPLACE WITH YOUR dmf.ntis.gov USER NAME'
|
85
|
+
SsnValidator::Ntis.password = 'REPLACE WITH YOUR dmf.ntis.gov PASSWORD'
|
86
|
+
|
87
|
+
* Run the rake task:
|
88
|
+
|
89
|
+
rake ssn_validator:death_master_file:update_data
|
90
|
+
|
91
|
+
* This rake task will determine the most recent file that has been loaded, and loads all subsequent files in order
|
92
|
+
from the dmf.ntis.gov website.
|
93
|
+
|
94
|
+
* The death master file data is updated monthly, so you'll want to run this rake task monthly to keep your validations accurate.
|
95
|
+
|
96
|
+
== Copyright
|
97
|
+
|
98
|
+
Copyright (c) 2009 Kevin Tyll. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gemspec|
|
7
|
+
gemspec.name = "ssn_validator"
|
8
|
+
gemspec.author = "Kevin Tyll"
|
9
|
+
gemspec.email = "kevintyll@gmail.com"
|
10
|
+
gemspec.homepage = %q{http://kevintyll.git.com/ssn_validator}
|
11
|
+
gemspec.summary = "Validates whether an SSN has likely been issued or not."
|
12
|
+
gemspec.description = "Validates whether an SSN has likely been issued or not."
|
13
|
+
gemspec.post_install_message = File.readlines("PostInstall.txt").join("")
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/testtask'
|
23
|
+
Rake::TestTask.new(:test) do |test|
|
24
|
+
test.libs << 'test'
|
25
|
+
test.pattern = 'test/test_*.rb'
|
26
|
+
test.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'rcov/rcovtask'
|
31
|
+
Rcov::RcovTask.new do |test|
|
32
|
+
test.libs << 'test'
|
33
|
+
test.pattern = 'test/**/*_test.rb'
|
34
|
+
test.verbose = true
|
35
|
+
end
|
36
|
+
rescue LoadError
|
37
|
+
task :rcov do
|
38
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
task :default => :test
|
44
|
+
|
45
|
+
require 'rake/rdoctask'
|
46
|
+
Rake::RDocTask.new do |rdoc|
|
47
|
+
if File.exist?('VERSION.yml')
|
48
|
+
config = YAML.load(File.read('VERSION.yml'))
|
49
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
50
|
+
else
|
51
|
+
version = ""
|
52
|
+
end
|
53
|
+
|
54
|
+
rdoc.rdoc_dir = 'rdoc'
|
55
|
+
rdoc.title = "ssn_validator #{version}"
|
56
|
+
rdoc.rdoc_files.include('README*')
|
57
|
+
rdoc.rdoc_files.include('LICENSE*')
|
58
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
59
|
+
end
|
60
|
+
|
data/VERSION.yml
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class DeathMasterFileMigrationGenerator < Rails::Generator::Base
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
#m.directory File.join('db')
|
5
|
+
m.migration_template 'migration.rb', 'db/migrate'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
def file_name
|
10
|
+
"create_death_master_file_table"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateDeathMasterFileTable < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
create_table :death_master_files do |t|
|
5
|
+
t.string :social_security_number
|
6
|
+
t.string :last_name
|
7
|
+
t.string :name_suffix
|
8
|
+
t.string :first_name
|
9
|
+
t.string :middle_name
|
10
|
+
t.string :verify_proof_code
|
11
|
+
t.date :date_of_death
|
12
|
+
t.date :date_of_birth
|
13
|
+
t.string :state_of_residence
|
14
|
+
t.string :last_known_zip_residence
|
15
|
+
t.string :last_known_zip_payment
|
16
|
+
t.time_stamps
|
17
|
+
t.date :as_of
|
18
|
+
end
|
19
|
+
add_index :death_master_files, :social_security_number, :unique => true
|
20
|
+
add_index :death_master_files, :as_of
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.down
|
24
|
+
drop_table :death_master_files
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateSsnValidatorTables < ActiveRecord::Migration
|
2
|
+
|
3
|
+
def self.up
|
4
|
+
create_table :ssn_high_group_codes do |t|
|
5
|
+
t.date :as_of
|
6
|
+
t.string :area
|
7
|
+
t.string :group
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :ssn_high_group_codes, [:area]
|
12
|
+
add_index :ssn_high_group_codes, [:area, :as_of]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :ssn_high_group_codes
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'ssn_validator/models/ssn_high_group_code'
|
5
|
+
require 'ssn_validator/models/ssn_high_group_code_loader'
|
6
|
+
require 'ssn_validator/models/ssn_validator'
|
7
|
+
require 'ssn_validator/models/death_master_file'
|
8
|
+
require 'ssn_validator/models/death_master_file_loader'
|
9
|
+
require 'rake'
|
10
|
+
|
11
|
+
# Load rake file
|
12
|
+
import "#{File.dirname(__FILE__)}/tasks/ssn_validator.rake"
|
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'net/https'
|
3
|
+
require 'activerecord'
|
4
|
+
require 'active_record/connection_adapters/mysql_adapter'
|
5
|
+
require 'ssn_validator/ntis'
|
6
|
+
|
7
|
+
class DeathMasterFileLoader
|
8
|
+
|
9
|
+
# path_or_url is the full path to the file to load on disk, or the url of an update file.
|
10
|
+
# as_of is a string in the formatt YYYY-MM-DD for which the file data is accurate.
|
11
|
+
def initialize(path_or_url,file_as_of)
|
12
|
+
@file_path_or_url = path_or_url
|
13
|
+
@file_as_of = file_as_of
|
14
|
+
valid?
|
15
|
+
end
|
16
|
+
|
17
|
+
def valid?
|
18
|
+
raise(ArgumentError, "path_or_url not specified") unless @file_path_or_url
|
19
|
+
raise(ArgumentError, "as_of not specified") unless @file_as_of
|
20
|
+
max_as_of = DeathMasterFile.maximum(:as_of)
|
21
|
+
raise(ArgumentError, "A more recent file has already been processed. DB as_of date #{max_as_of}") if max_as_of && (max_as_of >= @file_as_of.to_date)
|
22
|
+
|
23
|
+
if File.exists?(@file_path_or_url)
|
24
|
+
@download_file = File.open(@file_path_or_url)
|
25
|
+
elsif URI.parse(@file_path_or_url).kind_of?(URI::HTTP)
|
26
|
+
@download_file = get_file_from_web
|
27
|
+
else
|
28
|
+
raise(Errno::ENOENT, @file_path_or_url)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_file
|
33
|
+
|
34
|
+
if DeathMasterFile.connection.kind_of?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
|
35
|
+
puts "Converting file to csv format for Mysql import. This could take several minutes."
|
36
|
+
|
37
|
+
csv_file = convert_file_to_csv
|
38
|
+
|
39
|
+
bulk_mysql_update(csv_file)
|
40
|
+
else
|
41
|
+
active_record_file_load
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_file_from_web
|
47
|
+
uri = URI.parse(@file_path_or_url)
|
48
|
+
|
49
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
50
|
+
request.basic_auth(SsnValidator::Ntis.user_name,SsnValidator::Ntis.password)
|
51
|
+
|
52
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
53
|
+
http.use_ssl = (uri.port == 443)
|
54
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
55
|
+
|
56
|
+
response = http.request(request)
|
57
|
+
|
58
|
+
raise(ArgumentError, "Invalid URL: #{@file_path_or_url}") if response.kind_of?(Net::HTTPNotFound)
|
59
|
+
raise(ArgumentError, "Authorization Required: Invalid username or password. Set the variables SsnValidator::Ntis.user_name and SsnValidator::Ntis.password in your environment.rb file.") if response.kind_of?(Net::HTTPUnauthorized)
|
60
|
+
|
61
|
+
return response.body
|
62
|
+
end
|
63
|
+
|
64
|
+
#Loads all the update files from dmf.ntis.gov.
|
65
|
+
#It starts with the last file loaded, and loads each
|
66
|
+
#missing file in sequence up to the current file.
|
67
|
+
def self.load_update_files_from_web
|
68
|
+
max_as_of = DeathMasterFile.maximum(:as_of)
|
69
|
+
run_file_date = max_as_of.beginning_of_month.next_month
|
70
|
+
last_file_date = Date.today.beginning_of_month
|
71
|
+
while run_file_date <= last_file_date
|
72
|
+
url = "https://dmf.ntis.gov/dmldata/monthly/MA#{run_file_date.strftime("%y%m%d")}"
|
73
|
+
puts "Loading file #{url}"
|
74
|
+
DeathMasterFileLoader.new(url,run_file_date.strftime("%Y-%m-%d")).load_file
|
75
|
+
run_file_date += 1.month
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# Processes 28 million rows in 23 minutes. Input file 2.6GB output: 2.9GB.
|
82
|
+
# Used to convert a packed fixed-length file into csv for mysql import.
|
83
|
+
def convert_file_to_csv
|
84
|
+
|
85
|
+
csv_file = Tempfile.new("dmf") # create temp file for converted csv formmat.
|
86
|
+
|
87
|
+
|
88
|
+
start = Time.now
|
89
|
+
timenow = start.to_s(:db)
|
90
|
+
|
91
|
+
@delete_ssns = []
|
92
|
+
|
93
|
+
@download_file.each_with_index do |line,i|
|
94
|
+
action = record_action(line)
|
95
|
+
attributes_hash = text_to_hash(line)
|
96
|
+
if action == 'D'
|
97
|
+
#keep track of all the records to delete. We'll delete at the end all at once.
|
98
|
+
@delete_ssns << attributes_hash[:social_security_number]
|
99
|
+
else
|
100
|
+
# empty field for id to be generated by mysql.
|
101
|
+
newline = "``," +
|
102
|
+
# social_security_number
|
103
|
+
"`#{attributes_hash[:social_security_number]}`," +
|
104
|
+
# last_name
|
105
|
+
"`#{attributes_hash[:last_name]}`," +
|
106
|
+
# name_suffix
|
107
|
+
"`#{attributes_hash[:name_suffix]}`," +
|
108
|
+
# first_name
|
109
|
+
"`#{attributes_hash[:first_name]}`," +
|
110
|
+
# middle_name
|
111
|
+
"`#{attributes_hash[:middle_name]}`," +
|
112
|
+
# verify_proof_code
|
113
|
+
"`#{attributes_hash[:verify_proof_code]}`," +
|
114
|
+
# date_of_death - need YYYY-MM-DD.
|
115
|
+
"`#{attributes_hash[:date_of_death]}`," +
|
116
|
+
# date_of_birth - need YYYY-MM-DD.
|
117
|
+
"`#{attributes_hash[:date_of_birth]}`," +
|
118
|
+
# state_of_residence - must be code between 01 and 65 or else nil.
|
119
|
+
"`#{attributes_hash[:state_of_residence]}`," +
|
120
|
+
# last_known_zip_residence
|
121
|
+
"`#{attributes_hash[:last_known_zip_residence]}`," +
|
122
|
+
# last_known_zip_payment
|
123
|
+
"`#{attributes_hash[:last_known_zip_payment]}`," +
|
124
|
+
# created_at
|
125
|
+
"`#{timenow}`," +
|
126
|
+
# updated_at
|
127
|
+
"`#{timenow}`," +
|
128
|
+
# as_of
|
129
|
+
"`#{attributes_hash[:as_of]}`" +"\n"
|
130
|
+
|
131
|
+
csv_file.syswrite newline
|
132
|
+
puts "#{i} records processed." if (i % 25000 == 0) && (i > 0)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
puts "File conversion ran for #{(Time.now - start) / 60} minutes."
|
136
|
+
return csv_file
|
137
|
+
end
|
138
|
+
|
139
|
+
#Uses active record to load the data.
|
140
|
+
#The benefit is it will work on any database.
|
141
|
+
#The downside is it's really slow.
|
142
|
+
def active_record_file_load
|
143
|
+
puts 'Importing file into database. This could take many minutes.'
|
144
|
+
|
145
|
+
@download_file.each_with_index do |line,i|
|
146
|
+
action = record_action(line)
|
147
|
+
attributes_hash = text_to_hash(line)
|
148
|
+
if action == 'D'
|
149
|
+
DeathMasterFile.destroy_all(['social_security_number = ?', attributes_hash[:social_security_number]])
|
150
|
+
else
|
151
|
+
|
152
|
+
# empty field for id to be generated by mysql.
|
153
|
+
# record_hash = {
|
154
|
+
# :as_of => @file_as_of.to_date.to_s(:db),
|
155
|
+
# :social_security_number => parse_record(line,:social_security_number),
|
156
|
+
# :last_name => parse_record(line,:last_name),
|
157
|
+
# :name_suffix => parse_record(line,:name_suffix),
|
158
|
+
# :first_name => parse_record(line,:first_name),
|
159
|
+
# :middle_name => parse_record(line,:middle_name),
|
160
|
+
# :verify_proof_code => parse_record(line,:verify_proof_code),
|
161
|
+
# :date_of_death => parse_record(line,:date_of_death),
|
162
|
+
# :date_of_birth => parse_record(line,:date_of_birth),
|
163
|
+
# # - must be code between 01 and 65 or else nil.
|
164
|
+
# :state_of_residence=> parse_record(line,:state_of_residence=),
|
165
|
+
# :last_known_zip_residence => parse_record(line,:last_known_zip_residence),
|
166
|
+
# :last_known_zip_payment => parse_record(line,:last_known_zip_payment)
|
167
|
+
# }
|
168
|
+
|
169
|
+
case action
|
170
|
+
when '',nil,' '
|
171
|
+
#the initial file leaves this field blank
|
172
|
+
DeathMasterFile.create(attributes_hash)
|
173
|
+
else
|
174
|
+
dmf = DeathMasterFile.find_by_social_security_number(attributes_hash[:social_security_number])
|
175
|
+
if dmf
|
176
|
+
#a record already exists, update this record
|
177
|
+
dmf.update_attributes(attributes_hash)
|
178
|
+
else
|
179
|
+
#create a new record
|
180
|
+
DeathMasterFile.create(attributes_hash)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
puts "#{i} records processed." if (i % 2500 == 0) && (i > 0)
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
puts "Import complete."
|
190
|
+
end
|
191
|
+
|
192
|
+
# For mysql, use:
|
193
|
+
# LOAD DATA LOCAL INFILE 'ssdm1.csv' INTO TABLE death_master_files FIELDS TERMINATED BY ',' ENCLOSED BY "'" LINES TERMINATED BY '\n';
|
194
|
+
# This is a much faster way of loading large amounts of data into mysql. For information on the LOAD DATA command
|
195
|
+
# see http://dev.mysql.com/doc/refman/5.1/en/load-data.html
|
196
|
+
def bulk_mysql_update(csv_file)
|
197
|
+
puts "Importing into Mysql..."
|
198
|
+
|
199
|
+
#delete all the 'D' records
|
200
|
+
DeathMasterFile.delete_all(:social_security_number => @delete_ssns)
|
201
|
+
|
202
|
+
#This will insert new records, and replace records with existing ssns.
|
203
|
+
#This only works because there is a unique index on social_security_number.
|
204
|
+
mysql_command = <<-TEXT
|
205
|
+
LOAD DATA LOCAL INFILE '#{csv_file.path}' REPLACE INTO TABLE death_master_files FIELDS TERMINATED BY ',' ENCLOSED BY "`" LINES TERMINATED BY '\n';
|
206
|
+
TEXT
|
207
|
+
|
208
|
+
DeathMasterFile.connection.execute(mysql_command)
|
209
|
+
puts "Mysql import complete."
|
210
|
+
|
211
|
+
end
|
212
|
+
|
213
|
+
def record_action(line)
|
214
|
+
line[0,1].to_s.strip
|
215
|
+
end
|
216
|
+
|
217
|
+
def text_to_hash(line)
|
218
|
+
|
219
|
+
{:as_of => @file_as_of.to_date.to_s(:db),
|
220
|
+
:social_security_number => line[1,9].to_s.strip,
|
221
|
+
:last_name => line[10,20].to_s.strip,
|
222
|
+
:name_suffix => line[30,4].to_s.strip,
|
223
|
+
:first_name => line[34,15].to_s.strip,
|
224
|
+
:middle_name => line[49,15].to_s.strip,
|
225
|
+
:verify_proof_code => line[64,1].to_s.strip,
|
226
|
+
:date_of_death => (Date.strptime(line[65,8].to_s.strip,'%m%d%Y') rescue nil),
|
227
|
+
:date_of_birth => (Date.strptime(line[73,8].to_s.strip,'%m%d%Y') rescue nil),
|
228
|
+
# - must be code between 01 and 65 or else nil.
|
229
|
+
:state_of_residence => (line[81,2].to_s.strip.between?('01', '65') ? line[81,2].to_s.strip : nil),
|
230
|
+
:last_known_zip_residence => line[83,5].to_s.strip,
|
231
|
+
:last_known_zip_payment => line[88,5].to_s.strip}
|
232
|
+
rescue Exception => e
|
233
|
+
puts '@@@@@@@@@ Error = ' + e.message + ': ' + line.inspect
|
234
|
+
end
|
235
|
+
end
|