only the real bad ones?

This commit is contained in:
24c02 2026-01-07 18:29:58 -05:00
parent ec35dfc938
commit 421c6f2bf4
2 changed files with 28 additions and 15 deletions

View file

@ -110,21 +110,21 @@ module Backend
country_code = verification.identity.country
country_name = ISO3166::Country[country_code]&.common_name || country_code
by_country[country_name] ||= { total: 0, reasons: {} }
by_country[country_name] ||= { total: 0, fatal: 0, reasons: {} }
by_country[country_name][:total] += 1
by_country[country_name][:fatal] += 1 if verification.fatal?
reason_name = verification.try(:rejection_reason_name) || verification.rejection_reason.humanize
by_country[country_name][:reasons][reason_name] ||= 0
by_country[country_name][:reasons][reason_name] += 1
by_country[country_name][:reasons][reason_name] ||= { count: 0, fatal: verification.fatal? }
by_country[country_name][:reasons][reason_name][:count] += 1
end
# Sort countries by total rejections, and reasons within each country by count
# Calculate fraud rate and sort by fatal count (most suspicious first)
by_country.transform_values do |data|
data[:reasons] = data[:reasons].sort_by { |_, count| -count }.to_h
data[:top_reason] = data[:reasons].first&.first
data[:top_reason_count] = data[:reasons].first&.last || 0
data[:reasons] = data[:reasons].sort_by { |_, r| [ r[:fatal] ? 0 : 1, -r[:count] ] }.to_h
data[:fatal_rate] = data[:total] > 0 ? (data[:fatal].to_f / data[:total] * 100).round(1) : 0
data
end.sort_by { |_, data| -data[:total] }.to_h
end.sort_by { |_, data| [ -data[:fatal], -data[:total] ] }.to_h
end
end
end

View file

@ -103,30 +103,43 @@
</div>
<hr>
<div class="section">
<div class="section-header"><h3>🌍 rejections by country</h3></div>
<div class="section-content">
<% if @rejections_by_country.any? %>
<table>
<thead>
<tr>
<th>country</th>
<th>rejections</th>
<th>top reason</th>
<th>fatal</th>
<th>total rejected</th>
<th>fatal %</th>
<th>breakdown</th>
</tr>
</thead>
<tbody>
<% @rejections_by_country.each do |country, data| %>
<% next if data[:fatal] == 0 %>
<tr>
<td><b><%= country %></b></td>
<td style="color: var(--color-red-50);"><b><%= data[:fatal] %></b></td>
<td><%= data[:total] %></td>
<td><%= data[:top_reason] %> (<%= data[:top_reason_count] %>)</td>
<td>
<% if data[:fatal_rate] >= 50 %>
<span class="badge danger"><%= data[:fatal_rate] %>%</span>
<% elsif data[:fatal_rate] >= 25 %>
<span class="badge warning"><%= data[:fatal_rate] %>%</span>
<% else %>
<%= data[:fatal_rate] %>%
<% end %>
</td>
<td>
<details>
<summary>view all</summary>
<ul style="margin: 0.5rem 0 0 1rem; padding: 0;">
<% data[:reasons].each do |reason, count| %>
<li><%= reason %>: <%= count %></li>
<% data[:reasons].each do |reason, r| %>
<li>
<%= reason %>: <%= r[:count] %>
<% if r[:fatal] %><span class="badge danger">fatal</span><% end %>
</li>
<% end %>
</ul>
</details>
@ -136,7 +149,7 @@
</tbody>
</table>
<% else %>
<div class="empty-state">no rejections</div>
<div class="empty-state">none</div>
<% end %>
</div>
</div>