Adisri99's picture
Upload 26 files
1ce499f verified
from itertools import combinations
from app.config import settings
from app.feature_engineering import combine_features, orbital_shell_key, FEATURE_COLUMNS
from app.graph_features import build_graph, pair_graph_features
from app.repository import upsert_space_object, save_pair_score, insert_pair_history, create_run, get_pair_history, list_objects
from app.ml import predict_local
from app.utils import new_id, dumps
from app.explanations import build_top_factors, analyst_summary, structured_explanation, recommended_action
def demo_objects(db,limit=200):
rows=list_objects(db,limit=limit)
return [{"object_id":r.object_id,"object_name":r.object_name,"object_type":r.object_type,"mean_motion":r.mean_motion,"inclination":r.inclination,"eccentricity":r.eccentricity,"raan":r.raan,"bstar":r.bstar,"launch_year":r.launch_year} for r in rows]
def generate_candidate_pairs(objects):
grouped={}
for obj in objects: grouped.setdefault(orbital_shell_key(obj), []).append(obj)
candidates=[]
for group in grouped.values():
if len(group)<2: continue
for a,b in combinations(group[:120],2):
candidates.append((a,b))
if len(candidates)>=settings.MAX_CANDIDATE_PAIRS: return candidates
return candidates
def _trend_features(db,pair_id):
hist=get_pair_history(db,pair_id,limit=10)
if len(hist)<2: return {"recurrence_count":float(len(hist)),"trend_delta_score":0.0,"score_volatility_proxy":0.0}
scores=[h.final_score for h in hist]; avg=sum(scores)/len(scores); vol=sum(abs(x-avg) for x in scores)/len(scores)
return {"recurrence_count":float(len(hist)),"trend_delta_score":float(scores[0]-scores[-1]),"score_volatility_proxy":float(vol)}
def score_pair(db,a,b,graph_feats=None):
pair_id=f"{a['object_id']}__{b['object_id']}"; trend=_trend_features(db,pair_id); graph=graph_feats or {"graph_degree_sum":0.0,"graph_common_neighbors":0.0,"graph_jaccard":0.0,"graph_local_density":0.0}
features=combine_features(a,b,trend,graph); vector=[float(features.get(c,0.0)) for c in FEATURE_COLUMNS]; risk,anomaly,final=predict_local(vector)
label="critical" if final>=0.9 else "high" if final>=0.75 else "medium" if final>=0.45 else "low"
top=build_top_factors(features,anomaly,final); action=recommended_action(label); summary=analyst_summary(features,top,final); structured=structured_explanation(features,top,final,action)
return {"pair_id":pair_id,"risk_score":risk,"anomaly_score":anomaly,"final_score":final,"risk_label":label,"top_factors":top,"analyst_summary":summary,"structured_explanation":structured,"recommended_action":action,"features":features}
def scoring_cycle(db,objects,source="demo"):
run_id=new_id("run"); create_run(db,{"run_id":run_id,"source":source,"object_count":len(objects),"candidate_pair_count":0,"scored_pair_count":0,"completed":False})
for obj in objects: upsert_space_object(db,obj)
db.commit(); candidates=generate_candidate_pairs(objects); graph=build_graph([(a["object_id"],b["object_id"]) for a,b in candidates]); count=0
for a,b in candidates:
result=score_pair(db,a,b,pair_graph_features(graph,a["object_id"],b["object_id"])); hist=get_pair_history(db,result["pair_id"],limit=20); recurrence=len(hist)+1; trend_delta=result["final_score"]-hist[-1].final_score if hist else 0.0
save_pair_score(db,{"pair_id":result["pair_id"],"primary_object_id":a["object_id"],"secondary_object_id":b["object_id"],"latest_run_id":run_id,"risk_score":result["risk_score"],"anomaly_score":result["anomaly_score"],"final_score":result["final_score"],"risk_label":result["risk_label"],"recurrence_count":recurrence,"trend_delta_24h":trend_delta,"shell_key":orbital_shell_key(a),"top_factors_json":dumps(result["top_factors"]),"feature_payload_json":dumps(result["features"]|{"analyst_summary":result["analyst_summary"],"structured_explanation":result["structured_explanation"]})})
insert_pair_history(db,{"history_id":new_id("hist"),"pair_id":result["pair_id"],"run_id":run_id,"risk_score":result["risk_score"],"anomaly_score":result["anomaly_score"],"final_score":result["final_score"]}); count+=1
from app.models import ScoringRun
run=db.get(ScoringRun,run_id); run.candidate_pair_count=len(candidates); run.scored_pair_count=count; run.completed=True; db.add(run); db.commit()
return {"run_id":run_id,"object_count":len(objects),"candidate_pair_count":len(candidates),"scored_pair_count":count}