diff --git a/app/services/reporting/agency_and_sp_report.rb b/app/services/reporting/agency_and_sp_report.rb index e120c81236d..2ab63c8320d 100644 --- a/app/services/reporting/agency_and_sp_report.rb +++ b/app/services/reporting/agency_and_sp_report.rb @@ -10,15 +10,26 @@ def initialize(report_date = Time.zone.today) def agency_and_sp_report idv_sps, auth_sps = service_providers.partition { |sp| sp.ial.present? && sp.ial >= 2 } + idv_agency_ids = idv_sps.map(&:agency_id).uniq idv_agencies, auth_agencies = active_agencies.partition do |agency| idv_agency_ids.include?(agency.id) end + idv_sps_facial_match, idv_sps_legacy = idv_sps.partition do |sp| + facial_match_issuers.include?(sp.issuer) + end + + idv_agency_facial_match_ids = idv_sps_facial_match.map(&:agency_id) + idv_facial_match_agencies, idv_legacy_agencies = idv_agencies.partition do |agency| + idv_agency_facial_match_ids.include?(agency.id) + end + [ ['', 'Number of apps (SPs)', 'Number of agencies and states'], ['Auth', auth_sps.count, auth_agencies.count], - ['IDV', idv_sps.count, idv_agencies.count], + ['IDV (Legacy IDV)', idv_sps_legacy.count, idv_legacy_agencies.count], + ['IDV (Facial matching)', idv_sps_facial_match.count, idv_facial_match_agencies.count], ['Total', auth_sps.count + idv_sps.count, auth_agencies.count + idv_agencies.count], ] rescue ActiveRecord::QueryCanceled => err @@ -54,5 +65,13 @@ def service_providers ServiceProvider.where(issuer: issuers).active.external end end + + def facial_match_issuers + @facial_match_issuers ||= Profile.where(active: true).where( + 'verified_at <= ?', + report_date.end_of_day, + ).where(idv_level: Profile::FACIAL_MATCH_IDV_LEVELS). + pluck(:initiating_service_provider_issuer).uniq + end end end diff --git a/app/services/reporting/total_user_count_report.rb b/app/services/reporting/total_user_count_report.rb index 7d372e282c1..62ac028906d 100644 --- a/app/services/reporting/total_user_count_report.rb +++ b/app/services/reporting/total_user_count_report.rb @@ -10,20 +10,43 @@ def initialize(report_date = Time.zone.today) def total_user_count_report [ - ['Metric', 'All Users', 'Verified users', 'Time Range Start', 'Time Range End'], - ['All-time count', total_user_count, verified_user_count, '-', report_date.to_date], - ['All-time fully registered', total_fully_registered, '-', '-', report_date.to_date], + [ + 'Metric', + 'All Users', + 'Verified users (Legacy IDV)', + 'Verified users (Facial Matching)', + 'Time Range Start', + 'Time Range End', + ], + [ + 'All-time count', + total_user_count, + verified_legacy_idv_user_count, + verified_facial_match_user_count, + '-', + report_date.to_date, + ], + [ + 'All-time fully registered', + total_fully_registered, + '-', + '-', + '-', + report_date.to_date, + ], [ 'New users count', new_user_count, - new_verified_user_count, + new_verified_legacy_idv_user_count, + new_verified_facial_match_user_count, current_month.begin.to_date, current_month.end.to_date, ], [ 'Annual users count', annual_total_user_count, - annual_verified_user_count, + annual_verified_legacy_idv_user_count, + annual_verified_facial_match_user_count, annual_start_date.to_date, annual_end_date.to_date, ], @@ -58,15 +81,35 @@ def total_user_count end end - def verified_user_count + def verified_legacy_idv_user_count Reports::BaseReport.transaction_with_timeout do - Profile.where(active: true).where('verified_at <= ?', end_date).count + Profile.where(active: true).where( + 'verified_at <= ?', + end_date, + ).count - verified_facial_match_user_count end end - def new_verified_user_count + def verified_facial_match_user_count + @verified_facial_match_user_count ||= Reports::BaseReport.transaction_with_timeout do + Profile.where(active: true).where( + 'verified_at <= ?', + end_date, + ).where(idv_level: Profile::FACIAL_MATCH_IDV_LEVELS).count + end + end + + def new_verified_legacy_idv_user_count Reports::BaseReport.transaction_with_timeout do - Profile.where(active: true).where(verified_at: current_month).count + Profile.where(active: true).where(verified_at: current_month).count - + new_verified_facial_match_user_count + end + end + + def new_verified_facial_match_user_count + @new_verified_facial_match_user_count ||= Reports::BaseReport.transaction_with_timeout do + Profile.where(active: true).where(verified_at: current_month). + where(idv_level: Profile::FACIAL_MATCH_IDV_LEVELS).count end end @@ -76,11 +119,17 @@ def annual_total_user_count end end - def annual_verified_user_count + def annual_verified_legacy_idv_user_count Reports::BaseReport.transaction_with_timeout do - Profile.where(active: true). - where(verified_at: annual_start_date..annual_end_date). - count + Profile.where(active: true).where(verified_at: annual_start_date..annual_end_date).count - + annual_verified_facial_match_user_count + end + end + + def annual_verified_facial_match_user_count + @annual_verified_facial_match_user_count ||= Reports::BaseReport.transaction_with_timeout do + Profile.where(active: true).where(verified_at: annual_start_date..annual_end_date). + where(idv_level: Profile::FACIAL_MATCH_IDV_LEVELS).count end end diff --git a/spec/services/reporting/agency_and_sp_report_spec.rb b/spec/services/reporting/agency_and_sp_report_spec.rb index 9711359f667..7ed81700074 100644 --- a/spec/services/reporting/agency_and_sp_report_spec.rb +++ b/spec/services/reporting/agency_and_sp_report_spec.rb @@ -53,7 +53,8 @@ [ header_row, ['Auth', 1, 1], - ['IDV', 0, 0], + ['IDV (Facial matching)', 0, 0], + ['IDV (Legacy IDV)', 0, 0], ['Total', 1, 1], ] end @@ -69,7 +70,8 @@ [ header_row, ['Auth', 0, 1], - ['IDV', 0, 0], + ['IDV (Facial matching)', 0, 0], + ['IDV (Legacy IDV)', 0, 0], ['Total', 0, 1], ] end @@ -94,7 +96,8 @@ [ header_row, ['Auth', 1, 1], - ['IDV', 0, 0], + ['IDV (Facial matching)', 0, 0], + ['IDV (Legacy IDV)', 0, 0], ['Total', 1, 1], ] end @@ -103,7 +106,8 @@ [ header_row, ['Auth', 1, 0], - ['IDV', 1, 1], + ['IDV (Facial matching)', 0, 0], + ['IDV (Legacy IDV)', 1, 1], ['Total', 2, 1], ] end @@ -127,7 +131,7 @@ end context 'when adding an IDV SP' do - let!(:idv_sp) do + let!(:idv_legacy_sp) do create( :service_provider, :external, @@ -138,15 +142,32 @@ ) end + let!(:idv_facial_match_sp) do + create( + :service_provider, + :external, + :idv, + :active, + agency:, + identities: [build(:service_provider_identity, service_provider: 'https://facialmatch.com')], + ) + end + let(:expected_report) do [ header_row, ['Auth', 0, 0], - ['IDV', 1, 1], - ['Total', 1, 1], + ['IDV (Facial matching)', 1, 1], + ['IDV (Legacy IDV)', 1, 0], + ['Total', 2, 1], ] end + before do + allow_any_instance_of(Reporting::AgencyAndSpReport).to receive(:facial_match_issuers). + and_return([idv_facial_match_sp.issuer]) + end + it 'counts the SP and its Agency as IDV' do expect(subject).to match_array(expected_report) end diff --git a/spec/services/reporting/total_user_count_report_spec.rb b/spec/services/reporting/total_user_count_report_spec.rb index 9135cd25e67..1f4ba98ef25 100644 --- a/spec/services/reporting/total_user_count_report_spec.rb +++ b/spec/services/reporting/total_user_count_report_spec.rb @@ -8,26 +8,43 @@ let(:expected_report) do [ - ['Metric', 'All Users', 'Verified users', 'Time Range Start', 'Time Range End'], - ['All-time count', expected_total_count, expected_verified_count, '-', Date.new(2021, 3, 1)], + [ + 'Metric', + 'All Users', + 'Verified users (Legacy IDV)', + 'Verified users (Facial Matching)', + 'Time Range Start', + 'Time Range End', + ], + [ + 'All-time count', + expected_total_count, + expected_verified_legacy_idv_count, + expected_verified_facial_match_count, + '-', + Date.new(2021, 3, 1), + ], [ 'All-time fully registered', expected_total_fully_registered, '-', '-', + '-', Date.new(2021, 3, 1), ], [ 'New users count', expected_new_count, - expected_new_verified_count, + expected_new_verified_legacy_idv_count, + expected_new_verified_facial_match_count, Date.new(2021, 3, 1), Date.new(2021, 3, 31), ], [ 'Annual users count', expected_annual_count, - expected_annual_verified_count, + expected_annual_verified_legacy_idv_count, + expected_annual_verified_facial_match_count, Date.new(2020, 10, 1), Date.new(2021, 9, 30), ], @@ -50,12 +67,15 @@ context 'with only a non-verified user' do before { create(:user) } let(:expected_total_count) { 1 } - let(:expected_verified_count) { 0 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 0 } let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } - let(:expected_new_verified_count) { 0 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 0 } let(:expected_annual_count) { expected_total_count } - let(:expected_annual_verified_count) { 0 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 0 } it_behaves_like 'a report with the specified counts' end @@ -65,17 +85,20 @@ let!(:old_user) { create(:user, created_at: report_date - 13.months) } let(:expected_total_count) { 2 } - let(:expected_verified_count) { 0 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 0 } let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } - let(:expected_new_verified_count) { 0 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 0 } let(:expected_annual_count) { 1 } - let(:expected_annual_verified_count) { 0 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 0 } it_behaves_like 'a report with the specified counts' end - context 'with one verified and one non-verified user' do + context 'with one legacy verified and one non-verified user' do before do user1 = create(:user) user2 = create(:user) @@ -86,12 +109,37 @@ user2.profiles.first.deactivate(:password_reset) end let(:expected_total_count) { 2 } - let(:expected_verified_count) { 1 } + let(:expected_verified_legacy_idv_count) { 1 } + let(:expected_verified_facial_match_count) { 0 } + let(:expected_total_fully_registered) { 0 } + let(:expected_new_count) { 2 } + let(:expected_new_verified_legacy_idv_count) { 1 } + let(:expected_new_verified_facial_match_count) { 0 } + let(:expected_annual_count) { expected_total_count } + let(:expected_annual_verified_legacy_idv_count) { 1 } + let(:expected_annual_verified_facial_match_count) { 0 } + + it_behaves_like 'a report with the specified counts' + end + + context 'with one facial match verified and one non-verified user' do + before do + user1 = create(:user) + user2 = create(:user) + create(:profile, :active, :facial_match_proof, user: user1) + create(:profile, :active, :verified, user: user2) + user2.profiles.first.deactivate(:password_reset) + end + let(:expected_total_count) { 2 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 1 } let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 2 } - let(:expected_new_verified_count) { 1 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 1 } let(:expected_annual_count) { expected_total_count } - let(:expected_annual_verified_count) { 1 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 1 } it_behaves_like 'a report with the specified counts' end @@ -104,12 +152,15 @@ # A suspended user is still a total user: let(:expected_total_count) { 1 } - let(:expected_verified_count) { 0 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 0 } let(:expected_total_fully_registered) { 0 } let(:expected_new_count) { 1 } - let(:expected_new_verified_count) { 0 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 0 } let(:expected_annual_count) { 1 } - let(:expected_annual_verified_count) { 0 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 0 } it_behaves_like 'a report with the specified counts' end @@ -119,12 +170,15 @@ # A user with a fraud rejection is still a total user let(:expected_total_count) { 1 } - let(:expected_verified_count) { 0 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 0 } let(:expected_total_fully_registered) { 1 } let(:expected_new_count) { 1 } - let(:expected_new_verified_count) { 0 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 0 } let(:expected_annual_count) { 1 } - let(:expected_annual_verified_count) { 0 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 0 } it_behaves_like 'a report with the specified counts' end @@ -137,12 +191,15 @@ end end let(:expected_total_count) { 3 } - let(:expected_verified_count) { 0 } + let(:expected_verified_legacy_idv_count) { 0 } + let(:expected_verified_facial_match_count) { 0 } let(:expected_total_fully_registered) { 2 } let(:expected_new_count) { 3 } - let(:expected_new_verified_count) { 0 } + let(:expected_new_verified_legacy_idv_count) { 0 } + let(:expected_new_verified_facial_match_count) { 0 } let(:expected_annual_count) { 3 } - let(:expected_annual_verified_count) { 0 } + let(:expected_annual_verified_legacy_idv_count) { 0 } + let(:expected_annual_verified_facial_match_count) { 0 } it_behaves_like 'a report with the specified counts' end