Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vebt 558 #19957

Merged
merged 32 commits into from
Dec 24, 2024
Merged

Vebt 558 #19957

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8d049a7
Create boilerplate for 10282 form
alexchan-va Nov 13, 2024
8b6c175
Merge branch 'vebt-781' of https://github.com/department-of-veterans-…
alexchan-va Nov 22, 2024
d93b1ec
Add 10282 form to save to db
alexchan-va Dec 5, 2024
f1640ac
Make new Create Daily Excel Files for 10282 form
alexchan-va Dec 5, 2024
a041aba
Write to CSV instead of XLSX
alexchan-va Dec 6, 2024
7ce4ff0
Add logic to not repeat processed submissions
alexchan-va Dec 6, 2024
b3271e2
Fix lint errors
alexchan-va Dec 6, 2024
23ee7a1
Fix linting errors
alexchan-va Dec 9, 2024
a1d8b3b
Add mailer functionality for generated excel file
alexchan-va Dec 13, 2024
6217542
Add VANotify email to 10282
alexchan-va Dec 13, 2024
c967e30
Add back in line removed for testing
alexchan-va Dec 13, 2024
40bf897
Fix linter
alexchan-va Dec 13, 2024
8f2ef3f
Merge branch 'vebt-555' into vebt-731
alexchan-va Dec 13, 2024
3df8d05
Add 10282 Form specs
alexchan-va Dec 13, 2024
87b665f
Add excel file event tests
alexchan-va Dec 16, 2024
53fce20
Add remainder of 10282 test
alexchan-va Dec 17, 2024
0a16b3f
Clean up linting and naming
alexchan-va Dec 17, 2024
c0f35bf
Reformat mailer classes for 10282
alexchan-va Dec 18, 2024
44e4c2f
Modify tests to reduce redundancy
alexchan-va Dec 19, 2024
12103fc
Merge branch 'master' into vebt-558
alexchan-va Dec 19, 2024
3733d7e
Remove migration from PR
alexchan-va Dec 19, 2024
6e7e6a4
Remove schema changes from this PR
alexchan-va Dec 19, 2024
b65e46a
Remove test to go under PR line limit
alexchan-va Dec 19, 2024
a4eca83
Add proper codeowners
alexchan-va Dec 19, 2024
2c614bc
Add emails for mailers
alexchan-va Dec 19, 2024
f7a4467
Add 10282 to daily year report spec
alexchan-va Dec 20, 2024
1fd4b22
Fix fiscal year spec
alexchan-va Dec 20, 2024
009bee6
Remove excel file event spec
alexchan-va Dec 20, 2024
cbe4418
Change conditional for mailer
alexchan-va Dec 20, 2024
bfa4276
Merge branch 'vebt-558' of github.com:department-of-veterans-affairs/…
alexchan-va Dec 20, 2024
f41e7ab
Fix name of subject email
alexchan-va Dec 20, 2024
0a53127
Separate variables in mailer
alexchan-va Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ app/controllers/v1/post911_gi_bill_statuses_controller.rb @department-of-veteran
app/controllers/v2/higher_level_reviews_controller.rb @department-of-veterans-affairs/benefits-decision-reviews-be @department-of-veterans-affairs/backend-review-group
app/mailers/application_mailer.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/mailers/ch31_submissions_report_mailer.rb @department-of-veterans-affairs/benefits-non-disability @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/mailers/create_excel_files_mailer.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/mailers/create_daily_spool_files_mailer.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/mailers/create_staging_spool_files_mailer.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/mailers/dependents_application_failure_mailer.rb @department-of-veterans-affairs/benefits-dependents-management @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
Expand Down Expand Up @@ -241,6 +242,7 @@ app/models/eligible_data_class.rb @department-of-veterans-affairs/vfs-vaos @depa
app/models/evss_claim_document.rb @department-of-veterans-affairs/benefits-management-tools-be @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/evss_claim.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/evss_claims_sync_status_tracker.rb @department-of-veterans-affairs/benefits-management-tools-be @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/excel_file_event.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/expiry_scanner.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/external_services_redis/status.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/feature_toggle_event.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
Expand Down Expand Up @@ -304,6 +306,7 @@ app/models/rate_limited_search.rb @department-of-veterans-affairs/va-api-enginee
app/models/saml_request_tracker.rb @department-of-veterans-affairs/octo-identity
app/models/saved_claim.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/saved_claim/education_benefits/va_10203.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/saved_claim/education_benefits/va_10282.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/saved_claim/disability_compensation.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/saved_claim/dependency_claim.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
app/models/session.rb @department-of-veterans-affairs/octo-identity
Expand Down Expand Up @@ -1199,6 +1202,7 @@ spec/factories/education_career_counseling_claim_no_vet_information.rb @departme
spec/factories/education_career_counseling_claim.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/education_stem_automated_decisions.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/eligible_data_classes.rb @department-of-veterans-affairs/vfs-vaos @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/excel_file_events.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/extract_statuses.rb @department-of-veterans-affairs/vfs-vaos @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/evss_claims.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/evss_intent_to_files.rb @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
Expand Down Expand Up @@ -1270,6 +1274,7 @@ spec/factories/user_verifications.rb @department-of-veterans-affairs/octo-identi
spec/factories/va0993.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va0994.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va10203.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va10282.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va1990e.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va1990n.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/factories/va1990.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
Expand Down Expand Up @@ -1589,6 +1594,7 @@ spec/models/education_benefits_claim_spec.rb @department-of-veterans-affairs/my-
spec/models/education_benefits_submission_spec.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/education_stem_automated_decision_spec.rb @department-of-veterans-affairs/my-education-benefits @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/evss_claims_sync_status_tracker_spec.rb @department-of-veterans-affairs/vfs-authenticated-experience-backend @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/excel_file_event_spec.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/external_services_redis @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/folder_spec.rb @department-of-veterans-affairs/vfs-vaos @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/models/form1010cg @department-of-veterans-affairs/vfs-10-10 @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
Expand Down Expand Up @@ -1848,6 +1854,7 @@ spec/support/database_cleaner.rb @department-of-veterans-affairs/va-api-engineer
spec/support/default_configuration_helper.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/support/disability_compensation_form @department-of-veterans-affairs/Disability-Experience @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/support/error_details.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/support/excel_helpers.rb @department-of-veterans-affairs/govcio-vfep-codereviewers @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/support/factory_bot.rb @department-of-veterans-affairs/va-api-engineers @department-of-veterans-affairs/backend-review-group
spec/support/fake_api_key_for_lighthouse.txt @department-of-veterans-affairs/backend-review-group
spec/support/financial_status_report_helpers.rb @department-of-veterans-affairs/vsa-debt-resolution @department-of-veterans-affairs/backend-review-group
Expand Down
21 changes: 21 additions & 0 deletions app/mailers/create_excel_files_mailer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

class CreateExcelFilesMailer < ApplicationMailer
def build(filename)
date = Time.zone.now.strftime('%m/%d/%Y')
file_contents = File.read("tmp/#{filename}")
headers['Content-Disposition'] = "attachment; filename=#{filename}"

# rubocop:disable Layout/LineLength
recipients = Settings.vsp_environment.eql?('production') ? Settings.edu.production_excel_contents.emails : Settings.edu.staging_excel_contents.emails
subject = Settings.vsp_environment.eql?('production') ? "22-10282 Form CSV file for #{date}" : "Staging CSV file for #{date}"
# rubocop:enable Layout/LineLength

mail(
to: recipients,
subject: subject,
content_type: 'text/csv',
body: file_contents
)
end
end
2 changes: 1 addition & 1 deletion app/models/education_benefits_claim.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true

class EducationBenefitsClaim < ApplicationRecord
FORM_TYPES = %w[1990 1995 1990e 5490 5495 1990n 0993 0994 10203 1990s].freeze
FORM_TYPES = %w[1990 1995 1990e 5490 5495 1990n 0993 0994 10203 1990s 10282].freeze

APPLICATION_TYPES = %w[
chapter33
Expand Down
20 changes: 20 additions & 0 deletions app/models/excel_file_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

class ExcelFileEvent < ApplicationRecord
validates :filename, uniqueness: true

# Look for an existing row with same filename
# and increase retry attempt if wasn't successful from previous attempt
# Otherwise create a new event
def self.build_event(filename)
filename_date = filename.match(/(.+)_/)[1]
event = find_by('filename like ?', "#{filename_date}%")

if event.present?
event.update(retry_attempt: event.retry_attempt + 1) if event.successful_at.nil?
return event
end

create(filename: filename)
end
end
11 changes: 11 additions & 0 deletions app/models/form_profiles/va_10282.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class FormProfiles::VA10282 < FormProfile
def metadata
{
version: 0,
prefill: true,
returnUrl: '/applicant/information'
}
end
end
29 changes: 29 additions & 0 deletions app/models/saved_claim/education_benefits/va_10282.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

class SavedClaim::EducationBenefits::VA10282 < SavedClaim::EducationBenefits
add_form_and_validation('22-10282')

def after_submit(_user)
return unless Flipper.enabled?(:form22_10282_confirmation_email)

parsed_form_data = JSON.parse(form)
email = parsed_form_data['email']
return if email.blank?

send_confirmation_email(parsed_form_data, email)
end

private

def send_confirmation_email(parsed_form_data, email)
VANotify::EmailJob.perform_async(
email,
Settings.vanotify.services.va_gov.template_id.form22_10282_confirmation_email,
{
'first_name' => parsed_form_data.dig('veteranFullName', 'first')&.upcase.presence,
'date_submitted' => Time.zone.today.strftime('%B %d, %Y'),
'confirmation_number' => education_benefits_claim.confirmation_number
}
)
end
end
194 changes: 194 additions & 0 deletions app/sidekiq/education_form/create_daily_excel_files.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
# frozen_string_literal: true

require 'sentry_logging'
require 'sftp_writer/factory'

module EducationForm
class DailyExcelFileError < StandardError
end

class CreateDailyExcelFiles
MAX_RETRIES = 5
STATSD_KEY = 'worker.education_benefits_claim'
STATSD_FAILURE_METRIC = "#{STATSD_KEY}.failed_excel_file".freeze
LIVE_FORM_TYPES = ['22-10282'].freeze
AUTOMATED_DECISIONS_STATES = [nil, 'denied', 'processed'].freeze
EXCEL_FIELDS = %w[
name
first_name
last_name
military_affiliation
phone_number
email_address
country
state
race_ethnicity
gender
education_level
employment_status
salary
technology_industry
].freeze
HEADERS = ['Name', 'First Name', 'Last Name', 'Select Military Affiliation',
'Phone Number', 'Email Address', 'Country', 'State', 'Race/Ethnicity',
'Gender of Applicant', 'What is your highest level of education?',
'Are you currently employed?', 'What is your current salary?',
'Are you currently working in the technology industry? (If so, please select one)'].freeze
include Sidekiq::Job
include SentryLogging
sidekiq_options queue: 'default',
unique_for: 30.minutes,
retry: 5

# rubocop:disable Metrics/MethodLength
def perform
retry_count = 0
filename = "22-10282_#{Time.zone.now.strftime('%m%d%Y_%H%M%S')}.csv"
excel_file_event = ExcelFileEvent.build_event(filename)
begin
records = EducationBenefitsClaim
.unprocessed
.joins(:saved_claim)
.where(
saved_claims: {
form_id: LIVE_FORM_TYPES
}
)
return false if federal_holiday?

if records.count.zero?
log_info('No records to process.')
return true
elsif retry_count.zero?
log_info("Processing #{records.count} application(s)")
end

# Format the records and write to CSV file
formatted_records = format_records(records)
write_csv_file(formatted_records, filename)

email_excel_files(filename)

# Make records processed and add excel file event for rake job
records.each { |r| r.update(processed_at: Time.zone.now) }
excel_file_event.update(number_of_submissions: records.count, successful_at: Time.zone.now)
rescue => e
StatsD.increment("#{STATSD_FAILURE_METRIC}.general")
if retry_count < MAX_RETRIES
log_exception(DailyExcelFileError.new("Error creating excel files.\n\n#{e}
Retry count: #{retry_count}. Retrying..... "))
retry_count += 1
sleep(10 * retry_count) # exponential backoff for retries
retry
else
log_exception(DailyExcelFileError.new("Error creating excel files.
Job failed after #{MAX_RETRIES} retries \n\n#{e}"))
end
end
true
end

def write_csv_file(records, filename)
retry_count = 0

begin
# Generate CSV string content instead of writing to file
csv_contents = CSV.generate do |csv|
# Add headers
csv << HEADERS

# Add data rows
records.each_with_index do |record, index|
log_info("Processing record #{index + 1}: #{record.inspect}")

begin
row_data = EXCEL_FIELDS.map do |field|
value = record.public_send(field)
value.is_a?(Hash) ? value.to_s : value
end

csv << row_data
rescue => e
log_exception(DailyExcelFileError.new("Failed to add row #{index + 1}:\n"))
log_exception(DailyExcelFileError.new("#{e.message}\nRecord: #{record.inspect}"))
next
end
end
end

# Write to file for backup/audit purposes
File.write("tmp/#{filename}", csv_contents)
log_info('Successfully created CSV file')

# Return the CSV contents
csv_contents
rescue => e
StatsD.increment("#{STATSD_FAILURE_METRIC}.general")
log_exception(DailyExcelFileError.new('Error creating CSV files.'))

if retry_count < MAX_RETRIES
log_exception(DailyExcelFileError.new("Retry count: #{retry_count}. Retrying..... "))
retry_count += 1
sleep(5)
retry
else
log_exception(DailyExcelFileError.new("Job failed after #{MAX_RETRIES} retries \n\n#{e}"))
end
end
end
# rubocop:enable Metrics/MethodLength

def format_records(records)
records.map do |record|
format_application(record)
end.compact
end

def format_application(data)
form = EducationForm::Forms::Base.build(data)
track_form_type("22-#{data.form_type}")
form
rescue => e
inform_on_error(data, e)
nil
end

def inform_on_error(claim, error = nil)
StatsD.increment("#{STATSD_KEY}.failed_formatting.22-#{claim.form_type}")
exception = if error.present?
FormattingError.new("Could not format #{claim.confirmation_number}.\n\n#{error}")
else
FormattingError.new("Could not format #{claim.confirmation_number}")
end
log_exception(exception)
end

private

def federal_holiday?
holiday = Holidays.on(Time.zone.today, :us, :observed)
if holiday.empty?
false
else
log_info("Skipping on a Holiday: #{holiday.first[:name]}")
true
end
end

def track_form_type(type)
StatsD.gauge("#{STATSD_KEY}.transmissions.#{type}", 1)
end

def log_exception(exception)
log_exception_to_sentry(exception)
end

def log_info(message)
logger.info(message)
end

def email_excel_files(contents)
CreateExcelFilesMailer.build(contents).deliver_now
end
end
end
Loading
Loading