""" api.py HIFFI Intelligence REST API Service. Serves multi-platform composite rankings, verified sales telemetry, and isolated commercial simulation models. """ from enum import Enum from pathlib import Path from typing import Optional from fastapi import FastAPI, HTTPException, Query from pydantic import BaseModel import pandas as pd from core.cpi_engine import CommercialPowerEngine # Module-level FastAPI declaration app = FastAPI( title="HIFFI Artist Intelligence API", description="Multi-platform analytics, sales audit telemetry, and simulation engine.", version="1.0.0", ) DATA_PATH = Path("data/hiffi_multiplatform_500.csv") engine = CommercialPowerEngine() class ScopeChoice(str, Enum): all = "all" verified = "verified" def get_dataset() -> pd.DataFrame: if not DATA_PATH.exists(): raise HTTPException( status_code=500, detail="Master dataset data/hiffi_multiplatform_500.csv not found.", ) return pd.read_csv(DATA_PATH) class SimulationRequest(BaseModel): commercial_weight: float = 0.15 mode: str = "global_redistribution" # 'matched_only' or 'global_redistribution' @app.get("/health", tags=["System"]) def health_check(): """Returns engine heartbeat and dataset count.""" try: df = get_dataset() return { "status": "healthy", "total_tracked_artists": len(df), "engine": "CommercialPowerEngine v1.0", } except Exception as e: return {"status": "unhealthy", "error": str(e)} @app.get("/telemetry/coverage", tags=["Governance"]) def telemetry_coverage(): """Returns data provenance tier distribution.""" df = get_dataset() cspc_count = int((df.get("sales_data_status") == "VERIFIED_CSPC").sum()) riaa_count = int((df.get("sales_data_status") == "VERIFIED_RIAA").sum()) sea_count = int((df.get("sales_data_status") == "ESTIMATED_SEA").sum()) unranked_count = len(df) - cspc_count - riaa_count - sea_count return { "total_monitored": len(df), "verified_cspc": cspc_count, "verified_riaa": riaa_count, "estimated_sea": sea_count, "unranked": unranked_count, "active_coverage_percentage": round( ((cspc_count + riaa_count + sea_count) / len(df)) * 100.0, 2 ), } @app.get("/artists", tags=["Rankings"]) def list_artists( scope: ScopeChoice = ScopeChoice.all, limit: int = Query(50, ge=1, le=500), vertical: Optional[str] = None, ): """Returns ranked artist list respecting data governance scopes.""" df = get_dataset() if scope == ScopeChoice.verified: df = df[df["sales_data_status"].isin(["VERIFIED_CSPC", "VERIFIED_RIAA"])].copy() if vertical and "subgenre" in df.columns: df = df[df["subgenre"].str.lower() == vertical.lower()].copy() sort_col = "hiffi_xp_published_rank" if "hiffi_xp_published_rank" in df.columns else "hiffi_rank" df = df.sort_values(by=sort_col, ascending=True).head(limit) return { "count": len(df), "scope": scope.value, "records": df.to_dict(orient="records"), } @app.post("/simulate", tags=["Simulation"]) def run_simulation(req: SimulationRequest): """Calculates rank shifts in-memory without mutating published rankings.""" df = get_dataset() if req.mode == "matched_only": df = df[df["sales_data_status"] == "VERIFIED_CSPC"].copy() simulated_df = engine.simulate_ranking( df, commercial_weight=req.commercial_weight, mode=req.mode ) metrics = engine.compute_simulation_metrics(simulated_df) output_cols = [ "name", "hiffi_xp_published_rank", "simulated_rank", "simulation_rank_delta", "hiffi_xp_score", "simulated_hiffi_score", "sales_data_status", ] render_cols = [c for c in output_cols if c in simulated_df.columns] return { "parameters": { "commercial_weight": req.commercial_weight, "mode": req.mode, }, "metrics": metrics, "rankings": simulated_df[render_cols].to_dict(orient="records"), } if __name__ == "__main__": import uvicorn uvicorn.run("api:app", host="0.0.0.0", port=8000, reload=True)