| import json |
| from fastapi import APIRouter, Depends, HTTPException |
| from app.database import get_db |
| from app.feature_engineering import normalize_object |
| from app.services import score_pair |
| from app.repository import list_high_risk_pairs, get_pair, get_pair_history, pairs_in_same_shell, object_pairs |
| router=APIRouter(prefix="/api/v1", tags=["pairs"]) |
| @router.post("/score/pair") |
| def score_pair_route(payload:dict, db=Depends(get_db)): return score_pair(db, normalize_object(payload["primary"]), normalize_object(payload["secondary"])) |
| @router.get("/pairs/high-risk") |
| def high_risk(limit:int=50, db=Depends(get_db)): |
| rows=list_high_risk_pairs(db, limit) |
| return [{"pair_id":r.pair_id,"risk_score":r.risk_score,"anomaly_score":r.anomaly_score,"final_score":r.final_score,"risk_label":r.risk_label,"recurrence_count":r.recurrence_count,"trend_delta_24h":r.trend_delta_24h,"shell_key":r.shell_key,"top_factors":json.loads(r.top_factors_json)} for r in rows] |
| @router.get("/pairs/{pair_id}") |
| def pair_detail(pair_id:str, db=Depends(get_db)): |
| row=get_pair(db, pair_id) |
| if not row: raise HTTPException(status_code=404, detail="Pair not found") |
| payload=json.loads(row.feature_payload_json) |
| return {"pair_id":row.pair_id,"primary_object_id":row.primary_object_id,"secondary_object_id":row.secondary_object_id,"risk_score":row.risk_score,"anomaly_score":row.anomaly_score,"final_score":row.final_score,"risk_label":row.risk_label,"recurrence_count":row.recurrence_count,"trend_delta_24h":row.trend_delta_24h,"shell_key":row.shell_key,"top_factors":json.loads(row.top_factors_json),"features":payload,"analyst_summary":payload.get("analyst_summary",""),"structured_explanation":payload.get("structured_explanation",{})} |
| @router.get("/pairs/{pair_id}/history") |
| def pair_history(pair_id:str, limit:int=20, db=Depends(get_db)): |
| rows=get_pair_history(db, pair_id, limit) |
| return [{"history_id":r.history_id,"run_id":r.run_id,"risk_score":r.risk_score,"anomaly_score":r.anomaly_score,"final_score":r.final_score,"created_at":r.created_at} for r in rows] |
| @router.get("/pairs/{pair_id}/neighbors") |
| def pair_neighbors(pair_id:str, limit:int=20, db=Depends(get_db)): |
| row=get_pair(db, pair_id) |
| if not row: raise HTTPException(status_code=404, detail="Pair not found") |
| shell_rows=pairs_in_same_shell(db, row.shell_key or "unknown", row.pair_id, limit) |
| object_related=object_pairs(db, row.primary_object_id, limit)+object_pairs(db, row.secondary_object_id, limit) |
| seen={pair_id}; related=[] |
| for r in shell_rows+object_related: |
| if r.pair_id in seen: continue |
| seen.add(r.pair_id); related.append({"pair_id":r.pair_id,"final_score":r.final_score,"risk_label":r.risk_label,"top_factors":json.loads(r.top_factors_json)}) |
| if len(related)>=limit: break |
| return related |
|
|