File size: 2,817 Bytes
1ce499f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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