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
@@ -0,0 +1,25 @@
module Db
module UspsConfirmationCode
class LettersSentAndVerifiedSince
def self.call(start_date)
params = {
start: ActiveRecord::Base.connection.quote(start_date),
}
sql = format(<<~SQL, params)
SELECT COUNT(*)
FROM profiles
WHERE active = true
AND activated_at IS NOT NULL
AND verified_at IS NOT NULL
AND id IN (
SELECT profile_id
FROM usps_confirmation_codes
WHERE %{start} <= created_at
)
SQL
recs = ActiveRecord::Base.connection.execute(sql)
recs[0]['count'].to_i
end
end
end
end
17 changes: 17 additions & 0 deletions app/services/db/usps_confirmation_code/letters_sent_since.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Db
module UspsConfirmationCode
class LettersSentSince
def self.call(start_date)
params = {
start: ActiveRecord::Base.connection.quote(start_date),
}
sql = format(<<~SQL, params)
SELECT COUNT(*)
FROM usps_confirmation_codes WHERE %{start}<=created_at
SQL
recs = ActiveRecord::Base.connection.execute(sql)
recs[0]['count'].to_i
end
end
end
end
43 changes: 43 additions & 0 deletions app/services/reports/usps_report.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
require 'login_gov/hostdata'

module Reports
class UspsReport < BaseReport
REPORT_NAME = 'usps-report'.freeze

def initialize
@results = {
today: Time.zone.today.to_s,
letters_sent_since_days: {},
letters_sent_and_validated_since_days: {},
percent_sent_and_validated_since_days: {},
}
end

def call
create_reports
save_report(REPORT_NAME, @results.to_json)
@results.to_json
end

private

def create_reports
[7, 14, 30, 60, 90, 10_000].each do |days_ago|
sent = letters_sent_since(days_ago)
validated = letters_sent_and_validated_since(days_ago)
Comment on lines +25 to +27
Copy link
Contributor

Choose a reason for hiding this comment

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

this is 6 x 2 = 12 table scans of the usps_confirmation_codes table, would it make sense to see if we can parameterize some of this and make one aggregate query? It might be some gnarly CASE statements

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i could do a one pass but the usps_confirmation_codes table currently has a few thousand rows. it would need to get several orders of magnitude bigger before it ever took longer than a sub second total.

@results[:percent_sent_and_validated_since_days][days_ago] =
validated.zero? ? 0 : (validated * 100.0 / sent).round(2)
end
end

def letters_sent_since(days_ago)
@results[:letters_sent_since_days][days_ago] =
Db::UspsConfirmationCode::LettersSentSince.call(days_ago.days.ago)
end

def letters_sent_and_validated_since(days_ago)
@results[:letters_sent_and_validated_since_days][days_ago] =
Db::UspsConfirmationCode::LettersSentAndVerifiedSince.call(days_ago.days.ago)
end
end
end
8 changes: 8 additions & 0 deletions config/initializers/job_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,11 @@
timeout: 300,
callback: -> { Reports::DeletedUserAccountsReport.new.call },
)

# Send USPS Report to S3
JobRunner::Runner.add_config JobRunner::JobConfiguration.new(
name: 'USPS report',
interval: 24 * 60 * 60, # 24 hours
timeout: 300,
callback: -> { Reports::UspsReport.new.call },
)
12 changes: 12 additions & 0 deletions spec/config/initializers/job_configurations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -243,5 +243,17 @@

expect(job.callback.call).to eq 'the report test worked'
end

it 'runs the USPS report' do
job = JobRunner::Runner.configurations.find { |c| c.name == 'USPS report' }
expect(job).to be_instance_of(JobRunner::JobConfiguration)
expect(job.interval).to eq 24 * 60 * 60

service = instance_double(Reports::UspsReport)
expect(Reports::UspsReport).to receive(:new).and_return(service)
expect(service).to receive(:call).and_return('the report test worked')

expect(job.callback.call).to eq 'the report test worked'
end
end
end
51 changes: 51 additions & 0 deletions spec/services/reports/usps_report_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
require 'rails_helper'

describe Reports::UspsReport do
subject { described_class }
let(:empty_report) do
{
'letters_sent_and_validated_since_days' => {
'10000'=>0, '14'=>0, '30'=>0, '60'=>0, '7'=>0, '90'=>0
},
'letters_sent_since_days' => {
'10000'=>0, '14'=>0, '30'=>0, '60'=>0, '7'=>0, '90'=>0
},
'percent_sent_and_validated_since_days' => {
'10000'=>0, '14'=>0, '30'=>0, '60'=>0, '7'=>0, '90'=>0
},
'today' => Time.zone.today.to_s,
}
end
let(:one_letter_sent_and_verified_report) do
{
'letters_sent_and_validated_since_days' => {
'10000'=>1, '14'=>1, '30'=>1, '60'=>1, '7'=>1, '90'=>1
},
'letters_sent_since_days' => {
'10000'=>1, '14'=>1, '30'=>1, '60'=>1, '7'=>1, '90'=>1
},
'percent_sent_and_validated_since_days' => {
'10000'=>100.0, '14'=>100.0, '30'=>100.0, '60'=>100.0, '7'=>100.0, '90'=>100.0
},
'today' => Time.zone.today.to_s,
}
end
let(:user) { create(:user) }
let(:profile) { build(:profile, :active, :verified, user: user, pii: { ssn: '1234' }) }

it 'correctly reports zero letters sent' do
expect(JSON.parse(subject.new.call)).to eq(empty_report)
end

it 'correctly reports one letter sent that was verified' do
create_ucc_for(profile)
expect(JSON.parse(subject.new.call)).to eq(one_letter_sent_and_verified_report)
end

def create_ucc_for(profile)
UspsConfirmationCode.create(
profile: profile,
otp_fingerprint: 'foo',
)
end
end