Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require 'identity/hostdata'
require 'csv'
require 'fugit'

module Reports
class VerificationErrorsReport < BaseReport
REPORT_NAME = 'verification-errors-report'.freeze
class VerificationFailuresReport < BaseReport
REPORT_NAME = 'verification-failures-report'.freeze

include GoodJob::ActiveJobExtensions::Concurrency

Expand Down Expand Up @@ -31,7 +32,7 @@ def verification_errors_data_for_issuers(date, report_name, issuers)
csv << %w[uuid welcome_view_at error_code]
issuers.each do |issuer|
transaction_with_timeout do
rows = ::VerificationErrorsReport.call(
rows = ::VerificationFailuresReport.call(
issuer,
(date.beginning_of_day - 1).beginning_of_day,
date.beginning_of_day,
Expand All @@ -42,26 +43,36 @@ def verification_errors_data_for_issuers(date, report_name, issuers)
end
end
data = csv.string
save_report("#{REPORT_NAME}.#{report_name}", data, extension: 'csv')
save_report("#{REPORT_NAME}/#{report_name}", data, extension: 'csv')
data
end

def ial2_error_code(row)
welcome_at = row['welcome_view_at']
verify_at = row['verify_submit_at']
phone_submit_at = row['verify_phone_submit_at']
doc_at = row['document_capture_submit_at']
encrypt_at = row['encrypt_view_at']
ssn_at = row['ssn_view_at']
phone_view_at = row['verify_phone_view_at']
return 'PHONE_FAIL' if submit_failed?(welcome_at, phone_submit_at, encrypt_at)
return 'VERIFY_FAIL' if submit_failed?(welcome_at, verify_at, phone_view_at)
return 'DOCUMENT_FAIL' if submit_failed?(welcome_at, doc_at, ssn_at)
if submit_failed?(welcome_at, row['document_capture_submit_at'], ssn_at) ||
submit_failed?(welcome_at, row['back_image_submit_at'], ssn_at) ||
submit_failed?(welcome_at, row['capture_mobile_back_image_submit_at'], ssn_at) ||
submit_failed?(welcome_at, row['mobile_back_image_submit_at'], ssn_at)
return 'DOCUMENT_FAIL'
end
'ABANDON'
end

def submit_failed?(welcome_at, submit_at, next_step_at)
submit_at && submit_at >= welcome_at && (!next_step_at || next_step_at < submit_at)
good_job_cron = Rails.application.config.good_job.cron
cron_entry = good_job_cron.values.find { |c| c[:class] == self.class.name }&.[](:cron)
interval = cron_entry ? (Fugit.parse(cron_entry).rough_frequency - 3600).seconds : 23.hours
return unless submit_at # need a submit
return unless (submit_at + interval) >= welcome_at # need to be in range
return true unless next_step_at # submit must have failed if we did not get to next step
(submit_at + interval) > next_step_at
end
end
end
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
class VerificationErrorsReport
class VerificationFailuresReport
def self.call(service_provider, start_time, end_time)
report_sql = <<~SQL
SELECT
agency_identities.uuid,
document_capture_submit_at,
back_image_submit_at,
capture_mobile_back_image_submit_at,
mobile_back_image_submit_at,
encrypt_view_at,
enter_info_view_at,
ssn_view_at,
Expand Down
2 changes: 1 addition & 1 deletion config/initializers/job_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
},
# Upload list of verification errors to S3
verification_errors_report: {
class: 'Reports::VerificationErrorsReport',
class: 'Reports::VerificationFailuresReport',
cron: cron_24h,
args: -> { [Time.zone.today] },
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class AddBackImageSubmitAtToDocAuthLogs < ActiveRecord::Migration[6.1]
def change
add_column :doc_auth_logs, :back_image_submit_at, :datetime
add_column :doc_auth_logs, :capture_mobile_back_image_submit_at, :datetime
add_column :doc_auth_logs, :mobile_back_image_submit_at, :datetime
end
end
6 changes: 4 additions & 2 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2022_04_22_193820) do
ActiveRecord::Schema.define(version: 2022_05_17_103312) do

# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
Expand Down Expand Up @@ -174,7 +174,9 @@
t.integer "verify_phone_submit_count", default: 0
t.datetime "verify_phone_submit_at"
t.datetime "document_capture_submit_at"
t.index ["issuer"], name: "index_doc_auth_logs_on_issuer"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was it intended to remove this line? When running the migration locally, the line was added back.

t.datetime "back_image_submit_at"
t.datetime "capture_mobile_back_image_submit_at"
t.datetime "mobile_back_image_submit_at"
t.index ["user_id"], name: "index_doc_auth_logs_on_user_id", unique: true
t.index ["verified_view_at"], name: "index_doc_auth_logs_on_verified_view_at"
end
Expand Down
3 changes: 3 additions & 0 deletions spec/features/reports/doc_auth_funnel_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@
'verify_phone_submit_percent' => 0.0,
'document_capture_submit_percent' => 100.0,
'verify_submit_percent' => 100.0,
'back_image_submit_percent' => 0.0,
'capture_mobile_back_image_submit_percent' => 0.0,
'mobile_back_image_submit_percent' => 0.0,
}
end

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'rails_helper'

describe Reports::VerificationErrorsReport do
describe Reports::VerificationFailuresReport do
let(:issuer) { 'urn:gov:gsa:openidconnect:sp:sinatra' }
let(:email) { 'foo@bar.com' }
let(:name) { 'An SP' }
Expand Down Expand Up @@ -64,6 +64,54 @@
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
end

it 'sends out a document error if the user submits desktop back image and fails' do
DocAuthLog.create(
user_id: user.id,
welcome_view_at: now,
back_image_submit_at: now + 1.second,
issuer: issuer,
)

reports = run_reports
expect(reports.length).to eq(1)
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
Comment on lines +77 to +80
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we parse the CSV with headers: true we can get some nice fields to check:

Suggested change
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
csv = CSV.parse(reports.first, headers: true)
expect(csv.length).to eq(1)
expect(csv.first['uuid']).to eq(uuid)
expect(csv.first['welcome_view_at']).to eq(now.to_time.utc.iso8601)
expect(csv.first['error_code']).to eq('DOCUMENT_FAIL')

end

it 'sends out a document error if the user submits hybrid back image and fails' do
DocAuthLog.create(
user_id: user.id,
welcome_view_at: now,
capture_mobile_back_image_submit_at: now + 1.second,
issuer: issuer,
)

reports = run_reports
expect(reports.length).to eq(1)
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
end

it 'sends out a document error if the user submits mobile back image and fails' do
DocAuthLog.create(
user_id: user.id,
welcome_view_at: now,
mobile_back_image_submit_at: now + 1.second,
issuer: issuer,
)

reports = run_reports
expect(reports.length).to eq(1)
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
end

it 'sends out a verify error if the user submits PII but does not progress forward' do
DocAuthLog.create(
user_id: user.id,
Expand Down Expand Up @@ -115,6 +163,38 @@
expect(csv[2]).to eq([uuid2, now.to_time.utc.iso8601, 'ABANDON'])
end

it 'allows submit to be recent and not just after welcome' do
DocAuthLog.create(
user_id: user.id,
welcome_view_at: now,
mobile_back_image_submit_at: now - 12.hours,
issuer: issuer,
)

reports = run_reports
expect(reports.length).to eq(1)
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'DOCUMENT_FAIL'])
end

it 'does not consider old submits as fails' do
DocAuthLog.create(
user_id: user.id,
welcome_view_at: now,
document_capture_submit_at: now - 24.hours,
issuer: issuer,
)

reports = run_reports
expect(reports.length).to eq(1)
csv = CSV.parse(reports[0])
expect(csv.length).to eq(2)
expect(csv.first).to eq(['uuid', 'welcome_view_at', 'error_code'])
expect(csv[1]).to eq([uuid, now.to_time.utc.iso8601, 'ABANDON'])
end

describe '#good_job_concurrency_key' do
let(:date) { Time.zone.today }

Expand All @@ -133,6 +213,6 @@ def run_reports
[{ 'name' => name, 'issuers' => [issuer], 'emails' => [email] }],
)

Reports::VerificationErrorsReport.new.perform(Time.zone.today)
Reports::VerificationFailuresReport.new.perform(Time.zone.today)
end
end