From 9cfd7dee9668d2b77af793ab8fb2293d01792482 Mon Sep 17 00:00:00 2001 From: Chats-001 Date: Fri, 18 Sep 2026 12:04:29 -0400 Subject: [PATCH] fix: honor selected dashboard review capacity --- dashboard/app.py | 26 +++++++++++++++++++------- dashboard/queue_data.py | 18 ++++++++++++++++++ tests/test_dashboard_queue.py | 24 ++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 dashboard/queue_data.py create mode 100644 tests/test_dashboard_queue.py diff --git a/dashboard/app.py b/dashboard/app.py index 3320c2c..05d8554 100644 --- a/dashboard/app.py +++ b/dashboard/app.py @@ -7,12 +7,13 @@ import plotly.express as px import streamlit as st +from dashboard.queue_data import capacity_views + ROOT = Path(__file__).resolve().parents[1] SUMMARY_PATH = ROOT / "artifacts/figures/summary.json" CAPACITY_PATH = ROOT / "artifacts/figures/capacity_results.csv" THRESHOLD_PATH = ROOT / "artifacts/figures/threshold_policies.csv" SCORED_PATH = ROOT / "artifacts/figures/scored_test.csv" -QUEUE_PATH = ROOT / "artifacts/figures/review_queue.csv" ERROR_PATH = ROOT / "artifacts/figures/error_analysis.csv" st.set_page_config(page_title="RiskQueue", page_icon="▦", layout="wide") @@ -37,7 +38,6 @@ capacity = pd.read_csv(CAPACITY_PATH) thresholds = pd.read_csv(THRESHOLD_PATH) scored = pd.read_csv(SCORED_PATH) -queue = pd.read_csv(QUEUE_PATH) errors = pd.read_csv(ERROR_PATH) best = summary["metrics"][summary["best_model"]] @@ -139,12 +139,11 @@ min(250, int(capacity.capacity.max())), 25, ) - nearest = capacity.iloc[ - (capacity.capacity - selected_capacity).abs().argsort()[:3] - ].sort_values("fraud_value_capture", ascending=False) + scorecard, ranked_queue = capacity_views(scored, selected_capacity) + scorecard = scorecard.sort_values("fraud_value_capture", ascending=False) st.subheader("Policy scorecard") st.dataframe( - nearest[ + scorecard[ [ "strategy", "capacity", @@ -170,7 +169,20 @@ options=["critical", "high", "guarded", "low"], default=["critical", "high", "guarded", "low"], ) - shown_queue = queue[queue.risk_band.isin(selected_bands)].head(display_count) + shown_queue = ranked_queue[ranked_queue.risk_band.isin(selected_bands)].head(display_count) + shown_queue = shown_queue[ + [ + "transaction_id", + "step", + "type", + "amount", + "fraud_probability", + "expected_loss", + "risk_band", + "rank", + "isFraud", + ] + ] st.dataframe( shown_queue.style.format( { diff --git a/dashboard/queue_data.py b/dashboard/queue_data.py new file mode 100644 index 0000000..225cd65 --- /dev/null +++ b/dashboard/queue_data.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +import pandas as pd + +from riskqueue.decisions.review_queue import build_review_queue, queue_metrics + + +def capacity_views(scored: pd.DataFrame, capacity: int) -> tuple[pd.DataFrame, pd.DataFrame]: + """Calculate the scorecard and expected-loss queue for the selected capacity.""" + probabilities = scored["fraud_probability"].to_numpy() + rows = [] + selected_queue = None + for strategy in ("probability", "expected_loss", "expected_review_value"): + queue = build_review_queue(scored, probabilities, capacity, strategy) + rows.append({"strategy": strategy, "capacity": capacity, **queue_metrics(queue, scored)}) + if strategy == "expected_loss": + selected_queue = queue + return pd.DataFrame(rows), selected_queue diff --git a/tests/test_dashboard_queue.py b/tests/test_dashboard_queue.py new file mode 100644 index 0000000..6fa90b0 --- /dev/null +++ b/tests/test_dashboard_queue.py @@ -0,0 +1,24 @@ +from pathlib import Path + +import pandas as pd +import pytest + +from dashboard.queue_data import capacity_views + + +def test_capacity_views_use_selected_capacity(): + scored_path = Path(__file__).resolve().parents[1] / "artifacts/figures/scored_test.csv" + scored = pd.read_csv(scored_path) + + scorecard, queue = capacity_views(scored, 75) + + assert set(scorecard.strategy) == { + "probability", + "expected_loss", + "expected_review_value", + } + assert scorecard.capacity.tolist() == [75, 75, 75] + assert scorecard.reviews.tolist() == [75, 75, 75] + assert len(queue) == 75 + assert queue["rank"].tolist() == list(range(1, 76)) + assert queue.iloc[0].expected_loss == pytest.approx(scored.expected_loss.max())