diff --git a/app/services/db/usps_confirmation_code/letters_sent_and_verified_since.rb b/app/services/db/usps_confirmation_code/letters_sent_and_verified_since.rb new file mode 100644 index 00000000000..3c970ab82f4 --- /dev/null +++ b/app/services/db/usps_confirmation_code/letters_sent_and_verified_since.rb @@ -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 diff --git a/app/services/db/usps_confirmation_code/letters_sent_since.rb b/app/services/db/usps_confirmation_code/letters_sent_since.rb new file mode 100644 index 00000000000..d092256f604 --- /dev/null +++ b/app/services/db/usps_confirmation_code/letters_sent_since.rb @@ -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 diff --git a/app/services/reports/usps_report.rb b/app/services/reports/usps_report.rb new file mode 100644 index 00000000000..2baf940429e --- /dev/null +++ b/app/services/reports/usps_report.rb @@ -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) + @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 diff --git a/config/initializers/job_configurations.rb b/config/initializers/job_configurations.rb index 7457e41b723..24ed363788f 100644 --- a/config/initializers/job_configurations.rb +++ b/config/initializers/job_configurations.rb @@ -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 }, +) diff --git a/spec/config/initializers/job_configurations.rb b/spec/config/initializers/job_configurations.rb index 86c98b907d5..ec0105d68ad 100644 --- a/spec/config/initializers/job_configurations.rb +++ b/spec/config/initializers/job_configurations.rb @@ -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 diff --git a/spec/services/reports/usps_report_spec.rb b/spec/services/reports/usps_report_spec.rb new file mode 100644 index 00000000000..dcb395a403a --- /dev/null +++ b/spec/services/reports/usps_report_spec.rb @@ -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