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
97 changes: 40 additions & 57 deletions lib/reporting/irs_verification_report.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
# frozen_string_literal: true

require 'csv'

begin
require 'reporting/cloudwatch_client'
require 'reporting/cloudwatch_query_quoting'
require 'reporting/command_line_options'
rescue LoadError => e
warn 'could not load paths, try running with "bundle exec rails runner"'
warn 'Could not load paths, try running with "bundle exec rails runner"'
raise e
end

Expand Down Expand Up @@ -46,8 +47,7 @@ def progress?
end

def as_tables
[overview_table,
funnel_table]
[overview_table, funnel_table]
end

def as_emailable_reports
Expand All @@ -69,28 +69,26 @@ def as_emailable_reports
filename: 'Overview Report',
),
Reporting::EmailableReport.new(
title: 'Funnel Metrics',
title: 'Verification Funnel Metrics',
subtitle: '',
float_as_percent: true,
precision: 2,
table: funnel_table,
filename: 'Funnel Metrics',
filename: 'Verification Funnel Metrics',
),
]
end

def to_csvs
as_emailable_reports.map do |report|
CSV.generate do |csv|
report.table.each { |row| csv << row }
end
CSV.generate { |csv| report.table.each { |row| csv << row } }
end
end

def overview_table
[
['Report Timeframe', "#{time_range.begin.to_date} to #{time_range.end.to_date}"],
['Report Generated', Date.today.to_s], # rubocop:disable Rails/Date
['Report Generated', Time.zone.today.to_s],
['Issuer', issuers.join(', ')],
]
end
Expand All @@ -112,8 +110,7 @@ def funnel_table
to_percent(info_validation_success, verification_demand)],
['Phone Verification Success', phone_verification_success,
to_percent(phone_verification_success, verification_demand)],
['Verification Successes', total_verified,
to_percent(total_verified, verification_demand)],
['Verification Successes', total_verified, to_percent(total_verified, verification_demand)],
['Verification Failures', verification_demand - total_verified,
to_percent(verification_demand - total_verified, verification_demand)],
]
Expand Down Expand Up @@ -152,85 +149,71 @@ def cloudwatch_client
)
end

# def quote(array)
# '[' + array.map { |e| %("#{e}") }.join(', ') + ']'
# end

def query
def query_for_event(event_name)
params = {
event_name: quote(event_name),
issuers: quote(issuers),
event_names: quote(Events.all_events),
}

format(<<~QUERY, params)
filter properties.sp_request.facial_match
and name in %{event_names}
| fields
(name = '#{Events::VERIFICATION_DEMAND}') as @IdV_IAL2_start,
(name = '#{Events::DOCUMENT_AUTHENTICATION_SUCCESS}') as @Doc_auth_success,
(name = '#{Events::INFORMATION_VALIDATION_SUCCESS}') as @Verify_info_success,
(name = '#{Events::PHONE_VERIFICATION_SUCCESS}') as @Verify_phone_success,
(name = '#{Events::TOTAL_VERIFIED}' and properties.event_properties.ial2) as @Verified_1,
coalesce(@Verified_1, 0) as @Verified,
properties.user_id
| stats
max(@IdV_IAL2_start) as max_idv_ial2_start,
max(@Doc_auth_success) as max_doc_auth_success,
max(@Verify_info_success) as max_verify_info_success,
max(@Verify_phone_success) as max_verify_phone_success,
max(@Verified) as max_verified
by properties.user_id, bin(1y)
| stats
sum(max_idv_ial2_start) as IdV_IAL2_Start_User_count,
sum(max_doc_auth_success) as Doc_auth_success_User_count,
sum(max_verify_info_success) as Verify_info_success_User_count,
sum(max_verify_phone_success) as Verify_phone_success_User_count,
sum(max_verified) as Verified_User_count
| limit 10000
filter properties.sp_request.facial_match
and name = %{event_name}
and properties.service_provider in %{issuers}
| fields properties.user_id
| limit 10000
QUERY
end

def fetch_results
Rails.logger.info('Executing unified query')
def fetch_event_user_ids(event_name)
unless Events.all_events.include?(event_name)
Rails.logger.warn("Event name '#{event_name}' is not in the list of known events.")
return []
end

Rails.logger.info("Fetching results for event: #{event_name}")

results = cloudwatch_client.fetch(
query: query,
query: query_for_event(event_name),
from: time_range.begin.beginning_of_day,
to: time_range.end.end_of_day,
)
Rails.logger.info("Results: #{results.inspect}")
results

if results.nil? || results.empty?
Rails.logger.warn("No results returned for event: #{event_name}")
return []
end

user_ids = results.map { |row| row['properties.user_id'] }.compact.uniq
Rails.logger.info("Fetched #{user_ids.count} unique user IDs for #{event_name}")
user_ids
rescue StandardError => e
Rails.logger.error("Failed to fetch results for unified query: #{e.message}")
Rails.logger.error("Failed to fetch event #{event_name}: #{e.message}")
[]
end

def data
@data ||= fetch_results.first || {}
end

def verification_demand_results
data['IdV_IAL2_Start_User_count'].to_i || 0
fetch_event_user_ids(Events::VERIFICATION_DEMAND).count
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.

Why are we executing multiple CW queries instead of doing a single query and doing counts by event? That's how the other reports utilize the CW Client

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.

this may have been from my request to mimic the way you ran the demographics report @edimeo-gsa where you relied on ruby to parse out users from events for more performant cloudwatch queries.

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.

but a single query to CW and then counting uuids based on event would make more sense.

end

def document_authentication_success_results
data['Doc_auth_success_User_count'].to_i || 0
fetch_event_user_ids(Events::DOCUMENT_AUTHENTICATION_SUCCESS).count
end

def information_validation_success_results
data['Verify_info_success_User_count'].to_i || 0
fetch_event_user_ids(Events::INFORMATION_VALIDATION_SUCCESS).count
end

def phone_verification_success_results
data['Verify_phone_success_User_count'].to_i || 0
fetch_event_user_ids(Events::PHONE_VERIFICATION_SUCCESS).count
end

def total_verified_results
data['Verified_User_count'].to_i || 0
fetch_event_user_ids(Events::TOTAL_VERIFIED).count
end

def to_percent(numerator, denominator)
return 0.0 if denominator.nil? || denominator.zero?
((numerator.to_f / denominator)).round(2)
(numerator.to_f / denominator).round(2)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/reporting/irs_verification_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def previous_week_range

describe '#overview_table' do
it 'generates the overview table with the correct data' do
freeze_time do
travel_to Time.zone.local(2025, 8, 4) do
expected_generated_date = Time.current.utc.to_date.to_s

table = report.overview_table
Expand Down