Skip to content

Commit

Permalink
Bdn import (#16079)
Browse files Browse the repository at this point in the history
* BDN import

* ingress awards also
  • Loading branch information
binq authored Mar 25, 2024
1 parent 81ee32a commit 74bce21
Show file tree
Hide file tree
Showing 11 changed files with 207 additions and 28 deletions.
24 changes: 15 additions & 9 deletions modules/vye/app/models/vye/address_change.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,24 @@ module Vye
class Vye::AddressChange < ApplicationRecord
belongs_to :user_info

ENCRYPTED_ATTRIBUTES = %i[
veteran_name address1 address2 address3 address4 city state zip_code
].freeze

has_kms_key
has_encrypted(*ENCRYPTED_ATTRIBUTES, key: :kms_key, **lockbox_options)

REQUIRED_ATTRIBUTES = %i[
veteran_name address1 city state
].freeze
has_encrypted(
:veteran_name,
:address1, :address2, :address3, :address4, :address5,
:city, :state, :zip_code,
key: :kms_key, **lockbox_options
)

validates(
:veteran_name, :address1, :city, :state,
presence: true, if: -> { origin == 'frontend' }
)

validates(*REQUIRED_ATTRIBUTES, presence: true)
validates(
:veteran_name, :address1,
presence: true, if: -> { origin == 'backend' }
)

enum origin: { frontend: 'f', backend: 'b' }

Expand Down
13 changes: 7 additions & 6 deletions modules/vye/app/models/vye/award.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ class Vye::Award < ApplicationRecord

enum cur_award_ind: { current: 'C', future: 'F', past: 'P' }

REQUIRED_ATTRIBUTES = %i[
award_begin_date award_end_date begin_rsn cur_award_ind end_rsn
monthly_rate number_hours payment_date training_time type_hours type_training
].freeze

validates(*REQUIRED_ATTRIBUTES, presence: true)
validates(
*%i[
award_end_date cur_award_ind end_rsn
monthly_rate number_hours payment_date training_time
].freeze,
presence: true
)
end
end
31 changes: 21 additions & 10 deletions modules/vye/app/models/vye/user_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ module Vye
class Vye::UserInfo < ApplicationRecord
INCLUDES = %i[address_changes awards pending_documents verifications].freeze

self.ignored_columns += %i[
address_line2_ciphertext address_line3_ciphertext address_line4_ciphertext
address_line5_ciphertext address_line6_ciphertext
full_name_ciphertext icn ssn_digest suffix zip_ciphertext
]
self.ignored_columns +=
[
:ssn_digest, :icn, # moved to UserProfile

:suffix, # not needed

:address_line2_ciphertext, :address_line3_ciphertext, # moved to AddressChange
:address_line4_ciphertext, :address_line5_ciphertext, # moved to AddressChange
:address_line6_ciphertext, :full_name_ciphertext, :zip_ciphertext # moved to AddressChange
]

belongs_to :user_profile

Expand All @@ -28,12 +33,12 @@ class Vye::UserInfo < ApplicationRecord
delegate :icn, to: :user_profile, allow_nil: true
delegate :pending_documents, to: :user_profile, allow_nil: true

%i[dob file_number ssn stub_nm].freeze.tap do |attributes|
has_kms_key
has_encrypted(*attributes, key: :kms_key, **lockbox_options)
has_kms_key
has_encrypted(:file_number, :ssn, :dob, :stub_nm, key: :kms_key, **lockbox_options)

validates(*attributes, presence: true)
end
validates :dob, :stub_nm, presence: true

validate :ssn_or_file_number_present

validates(
:cert_issue_date, :date_last_certified, :del_date, :fac_code, :indicator,
Expand All @@ -45,6 +50,12 @@ def verification_required
verifications.empty?
end

def ssn_or_file_number_present
return true if ssn.present? || file_number.present?

errors.add(:base, 'Either SSN or file number must be present.')
end

scope :with_assos, -> { includes(:address_changes, :awards, user_profile: :pending_documents) }
end
end
10 changes: 9 additions & 1 deletion modules/vye/app/models/vye/user_profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Vye::UserProfile < ApplicationRecord
digest_attribute :ssn
digest_attribute :file_number

validates :ssn_digest, :file_number_digest, presence: true
validate :ssn_or_file_number_present

scope :with_assos, -> { includes(:pending_documents, :user_infos) }

Expand All @@ -25,4 +25,12 @@ def self.find_and_update_icn(user:)
result&.update!(icn: user.icn)
end
end

private

def ssn_or_file_number_present
return true if ssn_digest.present? || file_number_digest.present?

errors.add(:base, 'Either SSN or file number must be present.')
end
end
61 changes: 61 additions & 0 deletions modules/vye/config/bdn_line_extraction_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
:main_line:
:ssn: 9
:file_number: 9
:suffix: 2
:dob: 8
:mr_status: 1
:rem_ent: 7
:cert_issue_date: 8
:del_date: 8
:date_last_certified: 8
:veteran_name: 20
:address1: 20
:address2: 20
:address3: 20
:address4: 20
:address5: 20
:zip_code: 9
:stub_nm: 7
:rpo_code: 3
:fac_code: 8
:payment_amt: 7
:award_line:
:award_begin_date: 8
:award_end_date: 8
:training_time: 1
:payment_date: 8
:monthly_rate: 7
:begin_rsn: 2
:end_rsn: 2
:type_training: 1
:number_hours: 2
:type_hours: 1
:cur_award_ind: 1
:mappings:
:profile:
- :ssn
- :file_number
:info:
- :ssn
- :file_number
- :dob
- :mr_status
- :rem_ent
- :cert_issue_date
- :del_date
- :date_last_certified
- :stub_nm
- :rpo_code
- :fac_code
- :payment_amt
- :indicator
:address:
- :veteran_name
- :address1
- :address2
- :address3
- :address4
- :address5
- :zip_code
:indicator: true
16 changes: 15 additions & 1 deletion modules/vye/lib/vye/batch_transfer/ingress_files.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

module VYE
module Vye
module BatchTransfer
module IngressFiles
module_function
Expand All @@ -10,6 +10,20 @@ module IngressFiles

def bdn_feed_filename = BDN_FEED_FILENAME
def tims_feed_filename = TIMS_FEED_FILENAME

def bdn_import(data)
data.each_line do |line|
parsed = BdnLineExtraction.new(line: line.chomp, result: {}, award_lines: [], awards: [])

profile = Vye::UserProfile.build(parsed.attributes[:profile])
info = profile.user_infos.build(parsed.attributes[:info])
info.address_changes.build({ origin: 'backend' }.merge(parsed.attributes[:address]))
parsed.attributes[:awards].each do |award|
info.awards.build(award)
end
profile.save!
end
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# frozen_string_literal: true

module Vye
module BatchTransfer
module IngressFiles
BdnLineExtraction = Struct.new(:line, :result, :award_lines, :awards) do
def initialize(line:, result:, award_lines:, awards:)
super

extract_main
extract_award_lines
extract_indicator
extract_awards
end

def config
@config ||= YAML.load_file Vye::Engine.root / 'config/bdn_line_extraction_config.yaml'
end

def extract_main
config[:main_line]
.each do |field, length|
extracted = line.slice!(0...length).strip
result.update(field => extracted)
end
end

def extract_award_lines
(0...4)
.each do |_i|
dead = line[0...8].strip == ''
extracted = line.slice!(0...41)
award_lines << extracted unless dead
end
end

def extract_awards
# iterate over the fields for each award line and extract the data
# think list comprehensions in python, haskell, or erlang
award_lines
.each_with_index.to_a
.product(config[:award_line].each_pair.to_a)
.each do |(award_line, i), (field, length)|
extracted = award_line.slice!(0...length).strip
awards[i] ||= {}
awards[i].update(field => extracted)
end
end

def extract_indicator
return unless config[:indicator]

result.update(indicator: line.slice!(0...1).strip)
end

def attributes
raise 'incomplete extraction' unless line.blank? && award_lines.all?(&:blank?)

profile = result.slice(*config[:mappings][:profile])
info = result.slice(*config[:mappings][:info])
address = result.slice(*config[:mappings][:address])

{ profile:, info:, address:, awards: }
end
end
end
end
end
1 change: 1 addition & 0 deletions modules/vye/lib/vye/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module Vye
class Engine < Rails::Engine
isolate_namespace Vye
config.generators.api_only = true
config.autoload_paths << (root / 'lib')

initializer 'model_core.factories', after: 'factory_bot.set_factory_paths' do
FactoryBot.definition_file_paths << File.expand_path('../../spec/factories', __dir__) if defined?(FactoryBot)
Expand Down
1 change: 1 addition & 0 deletions modules/vye/spec/factories/vye/address_changes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
city { Faker::Address.city }
state { Faker::Address.state_abbr }
zip_code { Faker::Address.zip_code }
origin { Vye::AddressChange.origins['frontend'] }
end
end
1 change: 1 addition & 0 deletions modules/vye/spec/fixtures/bdn_sample/WAVE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
123456789 19800101E3600000198603281996020519860328JOHN APPLESEED 1 Mockingbird Ln APT 1 Houston TX 77401 JAPPLES316119071110011550 00000000198603281198603280003500 66 00 C A
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
require 'rails_helper'
require 'vye/batch_transfer/ingress_files'

RSpec.describe VYE::BatchTransfer::IngressFiles do
RSpec.describe Vye::BatchTransfer::IngressFiles do
describe '#bdn_feed_filename' do
it 'returns a string' do
expect(described_class.bdn_feed_filename).to be_a(String)
Expand All @@ -15,4 +15,11 @@
expect(described_class.tims_feed_filename).to be_a(String)
end
end

it 'imports lines from BDN extract' do
data = Vye::Engine.root / 'spec/fixtures/bdn_sample/WAVE.txt'
expect do
described_class.bdn_import(data)
end.not_to raise_error
end
end

0 comments on commit 74bce21

Please sign in to comment.