feat(cohort): reconcile 500-artist telemetry, update avatars, and add drift detection
HIFFI Multi-Platform CI / test (3.11) (push) Failing after 50s
HIFFI Multi-Platform CI / test (3.12) (push) Canceled after 46s

- Decouple commercial CSPC sales columns from the 500 cohort telemetry feed
- Synchronize 100% canonical platform URLs (YouTube, Spotify, Instagram) from hiffi_catalog.db to hiffi_multiplatform_500.csv
- Enforce Tier-1 zero-imputation policy for Hak (@haknyc) with RFC 8259-compliant nulls
- Reconcile and crop portrait avatar assets across cohort entities
- Implement automated drift detector and velocity circuit breaker (drift_detector.py)
This commit is contained in:
Srilekha committed 2026-11-06 11:55:25 +05:30
1 parent df5b4a0424
commit ebba583c66
52 files changed
+1922 -2333

No files matched your search

+233 -551
View File
@@ -1,9 +1,9 @@
""" """
app.py app.py
HIFFI Artist Intelligence - Multi-Platform Executive Dashboard HIFFI Artist Intelligence - Multi-Platform Executive Dashboard
Dual-Engine Architecture: Dual-Engine Production Architecture:
1. HIFFI Hip-Hop 500: Multi-platform evidence tiers, YouTube/Spotify/IG telemetry, BVI, CPI engine. 1. HIFFI Hip-Hop 500: Pure Digital Telemetry (45% Spotify, 35% YouTube, 20% Instagram), Evidence Tiers, BVI.
2. Global Master Catalog (17k): Empirical registry verification (CSPC, RIAA, Last.fm) with ordinal tier ranking. 2. Global Master Catalog (17k): Empirical registry verification (CSPC, RIAA, Last.fm).
""" """
from pathlib import Path from pathlib import Path
@@ -13,16 +13,6 @@ import plotly.express as px
import plotly.graph_objects as go import plotly.graph_objects as go
import streamlit as st import streamlit as st
try:
from core.cpi_engine import CommercialPowerEngine
except ImportError:
CommercialPowerEngine = None
try:
from exporters.excel_workbook import generate_executive_sales_workbook
except ImportError:
generate_executive_sales_workbook = None
try: try:
from core.ranking_engine import HIFFIRankingEngine from core.ranking_engine import HIFFIRankingEngine
except ImportError: except ImportError:
@@ -40,7 +30,6 @@ PATH_500 = Path("data/hiffi_multiplatform_500.csv")
PATH_17K = Path("data/master_artists_intelligence_17k_final.csv") PATH_17K = Path("data/master_artists_intelligence_17k_final.csv")
FALLBACK_PATH = Path("enriched_artists.csv") FALLBACK_PATH = Path("enriched_artists.csv")
# Global CSS Styling
st.markdown( st.markdown(
""" """
<style> <style>
@@ -75,35 +64,6 @@ st.markdown(
font-size: 0.72rem; font-size: 0.72rem;
color: #6b7280; color: #6b7280;
} }
.coverage-card {
background: #0f172a;
border: 1px solid #1e293b;
border-radius: 8px;
padding: 12px 18px;
margin-bottom: 16px;
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
}
.sim-kpi-card {
background: #0f172a;
border: 1px solid #334155;
border-radius: 8px;
padding: 10px 14px;
margin-bottom: 10px;
}
.sim-kpi-title {
font-size: 0.68rem;
font-weight: 700;
color: #94a3b8;
text-transform: uppercase;
}
.sim-kpi-val {
font-size: 1.25rem;
font-weight: 800;
margin-top: 2px;
}
.podium-tile { .podium-tile {
background: #111827; background: #111827;
border-radius: 12px; border-radius: 12px;
@@ -125,7 +85,6 @@ st.markdown(
.badge-tier3 { background: rgba(16, 185, 129, 0.2); color: #10b981; } .badge-tier3 { background: rgba(16, 185, 129, 0.2); color: #10b981; }
.badge-tier2 { background: rgba(56, 189, 248, 0.2); color: #38bdf8; } .badge-tier2 { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
.badge-tier1 { background: rgba(245, 158, 11, 0.2); color: #f59e0b; } .badge-tier1 { background: rgba(245, 158, 11, 0.2); color: #f59e0b; }
.badge-dormant { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
.custom-divider { .custom-divider {
height: 1px; height: 1px;
background: #1f2937; background: #1f2937;
@@ -137,10 +96,8 @@ st.markdown(
) )
@st.cache_data(ttl=60)
def load_hiffi_500(): def load_hiffi_500():
df = pd.DataFrame() df = pd.DataFrame()
if PATH_DB.exists(): if PATH_DB.exists():
try: try:
conn = sqlite3.connect(PATH_DB) conn = sqlite3.connect(PATH_DB)
@@ -148,51 +105,35 @@ def load_hiffi_500():
WITH latest_telemetry AS ( WITH latest_telemetry AS (
SELECT t.*, ROW_NUMBER() OVER ( SELECT t.*, ROW_NUMBER() OVER (
PARTITION BY t.artist_id PARTITION BY t.artist_id
ORDER BY t.last_synced_at DESC, t.telemetry_id DESC ORDER BY t.last_synced_at DESC, t.rowid DESC
) as rn ) as rn
FROM telemetry_metrics t FROM telemetry_metrics t
), ),
latest_rankings AS ( latest_rankings AS (
SELECT r.*, ROW_NUMBER() OVER ( SELECT r.*, ROW_NUMBER() OVER (
PARTITION BY r.artist_id PARTITION BY r.artist_id
ORDER BY r.ranking_id DESC ORDER BY r.rowid DESC
) as rn ) as rn
FROM rankings r FROM rankings r
),
latest_commercial AS (
SELECT c.*, ROW_NUMBER() OVER (
PARTITION BY c.artist_id
ORDER BY c.commercial_id DESC
) as rn
FROM commercial_sales c
) )
SELECT SELECT
a.artist_id, a.artist_id,
a.artist_name as name, a.artist_name as name,
a.image_verified_flag,
r.catalog_rank as hiffi_xp_published_rank, r.catalog_rank as hiffi_xp_published_rank,
r.tier_rank, r.tier_rank,
r.catalog_intelligence_tier, COALESCE(t.subscribers_count, 0) as subscriber_count,
t.subscribers_count as subscriber_count, COALESCE(t.total_views, 0) as lifetime_views,
t.total_views as lifetime_views, COALESCE(t.spotify_monthly_listeners, 0) as spotify_monthly_listeners,
t.spotify_monthly_listeners, COALESCE(t.instagram_followers, 0) as instagram_followers,
t.instagram_followers, COALESCE(t.daily_view_velocity, 0) as daily_view_velocity,
t.daily_view_velocity, COALESCE(t.growth_velocity_7d, 0.0) as momentum_7d,
t.growth_velocity_7d as momentum_7d, COALESCE(t.growth_velocity_30d, 0.0) as momentum_30d,
t.growth_velocity_30d as momentum_30d, COALESCE(t.spotify_daily_change, 0) as spotify_daily_change
t.spotify_daily_change, FROM rankings r
t.momentum_score, JOIN artists a ON r.artist_id = a.artist_id
c.pure_album_sales,
c.physical_singles_sales,
c.digital_singles_sales,
c.streaming_eas,
c.total_eas_cspc,
c.sales_data_status
FROM artists a
LEFT JOIN latest_telemetry t ON a.artist_id = t.artist_id AND t.rn = 1 LEFT JOIN latest_telemetry t ON a.artist_id = t.artist_id AND t.rn = 1
LEFT JOIN latest_rankings r ON a.artist_id = r.artist_id AND r.rn = 1 WHERE r.catalog_rank <= 500
LEFT JOIN latest_commercial c ON a.artist_id = c.artist_id AND c.rn = 1 ORDER BY r.catalog_rank ASC;
WHERE a.image_verified_flag = 1
""" """
df = pd.read_sql_query(query, conn) df = pd.read_sql_query(query, conn)
conn.close() conn.close()
@@ -202,7 +143,10 @@ def load_hiffi_500():
if df.empty: if df.empty:
target = PATH_500 if PATH_500.exists() else FALLBACK_PATH target = PATH_500 if PATH_500.exists() else FALLBACK_PATH
if target.exists(): if target.exists():
df = pd.read_csv(target) try:
df = pd.read_csv(target)
except Exception:
df = pd.DataFrame()
if df.empty: if df.empty:
return pd.DataFrame() return pd.DataFrame()
@@ -212,77 +156,71 @@ def load_hiffi_500():
if "subscriber_count" not in df.columns and "subscribers_count" in df.columns: if "subscriber_count" not in df.columns and "subscribers_count" in df.columns:
df["subscriber_count"] = df["subscribers_count"] df["subscriber_count"] = df["subscribers_count"]
elif "subscriber_count" not in df.columns:
df["subscriber_count"] = 0
if "lifetime_views" not in df.columns and "total_views" in df.columns: if "lifetime_views" not in df.columns and "total_views" in df.columns:
df["lifetime_views"] = df["total_views"] df["lifetime_views"] = df["total_views"]
elif "lifetime_views" not in df.columns:
df["lifetime_views"] = 0
if "spotify_monthly_listeners" not in df.columns:
df["spotify_monthly_listeners"] = 0
if "instagram_followers" not in df.columns:
df["instagram_followers"] = 0
if "hiffi_xp_published_rank" not in df.columns: if "hiffi_xp_published_rank" not in df.columns:
df["hiffi_xp_published_rank"] = df.get("catalog_rank", df.get("hiffi_xp_rank", df.get("hiffi_rank", df.index + 1))) df["hiffi_xp_published_rank"] = df.get("catalog_rank", df.get("hiffi_xp_rank", df.index + 1))
has_yt = df.get("subscriber_count", pd.Series(index=df.index)).fillna(0).gt(0).astype(int) has_sp = pd.to_numeric(df["spotify_monthly_listeners"], errors="coerce").fillna(0).gt(0).astype(int)
has_sp = df.get("spotify_monthly_listeners", pd.Series(index=df.index)).fillna(0).gt(0).astype(int) has_yt = pd.to_numeric(df["subscriber_count"], errors="coerce").fillna(0).gt(0).astype(int)
has_ig = df.get("instagram_followers", pd.Series(index=df.index)).fillna(0).gt(0).astype(int) has_ig = pd.to_numeric(df["instagram_followers"], errors="coerce").fillna(0).gt(0).astype(int)
df["platform_evidence_tier"] = has_yt + has_sp + has_ig df["platform_evidence_tier"] = has_sp + has_yt + has_ig
numeric_cols = [ numeric_cols = [
"hiffi_xp_published_rank", "hiffi_xp_rank", "hiffi_xp_score", "production_hiffi_rank", "hiffi_xp_published_rank", "hiffi_xp_rank", "hiffi_xp_score", "platform_evidence_tier",
"production_hiffi_score", "platform_evidence_tier", "breakout_velocity_index", "subscriber_count", "lifetime_views", "spotify_monthly_listeners", "instagram_followers",
"subscriber_count", "daily_view_velocity", "momentum_30d", "spotify_monthly_listeners", "daily_view_velocity", "momentum_7d", "momentum_30d", "breakout_velocity_index"
"spotify_daily_change", "instagram_followers", "confidence_score", "breakout_rank",
"chartmasters_sales_rank", "cohort_sales_rank", "pure_album_sales",
"physical_singles_sales", "digital_singles_sales", "streaming_eas", "total_eas_cspc",
"riaa_certified_album_units", "riaa_certified_singles_units"
] ]
for col in numeric_cols: for col in numeric_cols:
if col in df.columns: if col in df.columns:
df[col] = pd.to_numeric(df[col], errors="coerce") df[col] = pd.to_numeric(df[col], errors="coerce")
# Dynamic BVI computation
if HIFFIRankingEngine is not None and ("breakout_velocity_index" not in df.columns or df["breakout_velocity_index"].isna().all()): if HIFFIRankingEngine is not None and ("breakout_velocity_index" not in df.columns or df["breakout_velocity_index"].isna().all()):
try: try:
eng_rank = HIFFIRankingEngine() eng = HIFFIRankingEngine()
temp_df = df.copy() df["breakout_velocity_index"] = eng.calculate_breakout_velocity(df)
if "status_flag" not in temp_df.columns:
temp_df["status_flag"] = "COMPLETE"
df["breakout_velocity_index"] = eng_rank.calculate_breakout_velocity(temp_df)
except Exception: except Exception:
df["breakout_velocity_index"] = 0.0 df["breakout_velocity_index"] = 0.0
if "breakout_velocity_index" not in df.columns: if "breakout_velocity_index" not in df.columns:
df["breakout_velocity_index"] = 0.0 df["breakout_velocity_index"] = 0.0
# Calculate HIFFI-XP composite if not present if HIFFIRankingEngine is not None:
if "hiffi_xp_score" not in df.columns or df["hiffi_xp_score"].isna().all(): try:
if HIFFIRankingEngine is not None: eng = HIFFIRankingEngine()
try: ranked_df = eng.calculate_hiffi_xp(df)
eng_rank = HIFFIRankingEngine() df["hiffi_xp_score"] = ranked_df["hiffi_xp_score"]
temp_df = df.copy() df["effective_weights"] = ranked_df.get("effective_weights", "sp:45.0%,yt:35.0%,ig:20.0%")
if "status_flag" not in temp_df.columns: df["catalog_rank"] = ranked_df.get("catalog_rank", df["hiffi_xp_published_rank"])
temp_df["status_flag"] = "COMPLETE" df["hiffi_xp_published_rank"] = df["catalog_rank"]
temp_df = eng_rank.score_and_rank(temp_df) except Exception:
temp_df = eng_rank.calculate_hiffi_xp(temp_df) sp_n = df["spotify_monthly_listeners"].fillna(0) / max(df["spotify_monthly_listeners"].max(), 1)
df["hiffi_xp_score"] = temp_df["hiffi_xp_score"] yt_n = df["subscriber_count"].fillna(0) / max(df["subscriber_count"].max(), 1)
df["effective_weights"] = temp_df.get("effective_weights", "yt:50.0%,sp_monthly:25.0%,sp_velocity:5.0%,ig:20.0%") ig_n = df["instagram_followers"].fillna(0) / max(df["instagram_followers"].max(), 1)
if "production_hiffi_score" not in df.columns: df["hiffi_xp_score"] = (sp_n * 45.0 + yt_n * 35.0 + ig_n * 20.0).round(2)
df["production_hiffi_score"] = temp_df.get("production_hiffi_score", 0.0) df["effective_weights"] = "sp:45.0%,yt:35.0%,ig:20.0%"
except Exception: else:
df["hiffi_xp_score"] = 50.0 sp_n = df["spotify_monthly_listeners"].fillna(0) / max(df["spotify_monthly_listeners"].max(), 1)
else: yt_n = df["subscriber_count"].fillna(0) / max(df["subscriber_count"].max(), 1)
df["hiffi_xp_score"] = 50.0 ig_n = df["instagram_followers"].fillna(0) / max(df["instagram_followers"].max(), 1)
df["hiffi_xp_score"] = (sp_n * 45.0 + yt_n * 35.0 + ig_n * 20.0).round(2)
if "effective_weights" not in df.columns: df["effective_weights"] = "sp:45.0%,yt:35.0%,ig:20.0%"
df["effective_weights"] = "yt:50.0%,sp_monthly:25.0%,sp_velocity:5.0%,ig:20.0%"
if "anomaly_flags" not in df.columns: if "anomaly_flags" not in df.columns:
df["anomaly_flags"] = "CLEAN" df["anomaly_flags"] = "CLEAN"
if CommercialPowerEngine is not None:
try:
cpi_eng = CommercialPowerEngine()
df["commercial_power_index"] = cpi_eng.calculate_cpi(df)
except Exception:
df["commercial_power_index"] = None
if "subgenre" not in df.columns: if "subgenre" not in df.columns:
def infer_subgenre(r): def infer_subgenre(r):
subs = r.get("subscriber_count", 0) or 0 subs = r.get("subscriber_count", 0) or 0
@@ -298,7 +236,6 @@ def load_hiffi_500():
return df return df
@st.cache_data(ttl=60)
def load_master_17k(): def load_master_17k():
if not PATH_17K.exists(): if not PATH_17K.exists():
return pd.DataFrame() return pd.DataFrame()
@@ -310,74 +247,54 @@ def load_master_17k():
df_500 = load_hiffi_500() df_500 = load_hiffi_500()
df_17k = load_master_17k() df_17k = load_master_17k()
engine = CommercialPowerEngine() if CommercialPowerEngine is not None else None
def get_provenance_badge(status: str) -> str:
s = str(status).strip()
if "CSPC" in s or s == "VERIFIED_CSPC":
return "✅ VERIFIED (CSPC)"
elif "RIAA" in s or "COMMERCIAL_CORE" in s:
return "🏅 VERIFIED (RIAA)"
elif "LASTFM" in s:
return "📻 VERIFIED (Last.fm)"
elif "ESTIMATED_SEA" in s:
return "📊 ESTIMATED (SEA)"
return "⏳ UNAUDITED / PENDING"
# ----------------- Sidebar Controls -----------------
with st.sidebar: with st.sidebar:
st.markdown("### ⚡ HIFFI Intelligence Suite") st.markdown("### ⚡ HIFFI Intelligence Suite")
st.caption("Hip-Hop 500 & Master Catalog Intelligence") st.caption("Hip-Hop 500 & Master Catalog Intelligence")
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
catalog_source = st.selectbox( catalog_source = st.selectbox(
"🗂️ Active Data Scope:", "Active Data Scope:",
["⚡ Hip-Hop 500 (Executive Telemetry)", "🌐 Global Master Catalog (17,000+ Acts)"], ["Hip-Hop 500 (Executive Telemetry)", "Global Master Catalog (17,000+ Acts)"],
index=0, index=0,
) )
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
# =====================================================================
# MODULE 1: GLOBAL MASTER CATALOG (17,000+ ACTS)
# =====================================================================
if "Global Master Catalog" in catalog_source: if "Global Master Catalog" in catalog_source:
with st.sidebar: with st.sidebar:
st.markdown("##### 🔍 17k Catalog Filters") st.markdown("##### 17k Catalog Filters")
if df_17k.empty: if df_17k.empty:
st.error("Master 17k dataset not found at data/master_artists_intelligence_17k_final.csv") st.error("Master 17k dataset not found at data/master_artists_intelligence_17k_final.csv")
st.stop() st.stop()
tier_list = ["All Tiers"] + sorted(df_17k["catalog_intelligence_tier"].dropna().unique().tolist()) tier_list = ["All Tiers"] + sorted(df_17k.get("catalog_intelligence_tier", pd.Series(dtype=str)).dropna().unique().tolist())
sel_tier = st.selectbox("Intelligence Tier:", tier_list, index=0) sel_tier = st.selectbox("Intelligence Tier:", tier_list, index=0)
search_17k = st.text_input("Search Artist Name:", placeholder="e.g. Eminem, The Roots...") search_17k = st.text_input("Search Artist Name:", placeholder="e.g. Eminem, The Roots...")
filt_17k = df_17k.copy() filt_17k = df_17k.copy()
if sel_tier != "All Tiers": if sel_tier != "All Tiers" and "catalog_intelligence_tier" in filt_17k.columns:
filt_17k = filt_17k[filt_17k["catalog_intelligence_tier"] == sel_tier] filt_17k = filt_17k[filt_17k["catalog_intelligence_tier"] == sel_tier]
if search_17k: if search_17k and "name" in filt_17k.columns:
filt_17k = filt_17k[filt_17k["name"].str.contains(search_17k, case=False, na=False)] filt_17k = filt_17k[filt_17k["name"].str.contains(search_17k, case=False, na=False)]
head1, head2 = st.columns([3, 1]) head1, head2 = st.columns([3, 1])
with head1: with head1:
st.markdown("<h2 style='margin-bottom: 2px;'>🌐 Global Master Artist Catalog (17k)</h2>", unsafe_allow_html=True) st.markdown("<h2 style='margin-bottom: 2px;'>Global Master Artist Catalog (17k)</h2>", unsafe_allow_html=True)
st.markdown("<p style='color: #9ca3af; font-size: 0.92rem;'>Empirical registry validation via CSPC sales, RIAA certifications, and verified Last.fm global scrobbles.</p>", unsafe_allow_html=True) st.markdown("<p style='color: #9ca3af; font-size: 0.92rem;'>Empirical registry validation via CSPC sales, RIAA certifications, and verified Last.fm global scrobbles.</p>", unsafe_allow_html=True)
with head2: with head2:
st.markdown(f"<div style='text-align: right; color: #6b7280; font-size: 0.8rem; padding-top: 10px;'>Filtered: <b>{len(filt_17k):,} / {len(df_17k):,}</b> acts</div>", unsafe_allow_html=True) st.markdown(f"<div style='text-align: right; color: #6b7280; font-size: 0.8rem; padding-top: 10px;'>Filtered: <b>{len(filt_17k):,} / {len(df_17k):,}</b> acts</div>", unsafe_allow_html=True)
# 17k Macro KPIs
mk1, mk2, mk3, mk4, mk5 = st.columns(5) mk1, mk2, mk3, mk4, mk5 = st.columns(5)
with mk1: with mk1:
st.markdown(f'<div class="kpi-box"><div class="kpi-title">Universe Size</div><div class="kpi-value">{len(df_17k):,}</div><div class="kpi-subtitle">Total Cataloged</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title">Universe Size</div><div class="kpi-value">{len(df_17k):,}</div><div class="kpi-subtitle">Total Cataloged</div></div>', unsafe_allow_html=True)
with mk2: with mk2:
ver_cnt = df_17k["catalog_rank"].notna().sum() ver_cnt = df_17k["catalog_rank"].notna().sum() if "catalog_rank" in df_17k.columns else 0
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #10b981;">Verified Entities</div><div class="kpi-value" style="color: #10b981;">{ver_cnt:,}</div><div class="kpi-subtitle">Registry Authenticated</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #10b981;">Verified Entities</div><div class="kpi-value" style="color: #10b981;">{ver_cnt:,}</div><div class="kpi-subtitle">Registry Authenticated</div></div>', unsafe_allow_html=True)
with mk3: with mk3:
t1_cnt = (df_17k["catalog_intelligence_tier"] == "TIER_1_GLOBAL_SUPERSTAR_CSPC").sum() t1_cnt = (df_17k.get("catalog_intelligence_tier") == "TIER_1_GLOBAL_SUPERSTAR_CSPC").sum()
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #f59e0b;">Tier 1 Superstars</div><div class="kpi-value" style="color: #f59e0b;">{t1_cnt:,}</div><div class="kpi-subtitle">Audited CSPC EAS</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #f59e0b;">Tier 1 Superstars</div><div class="kpi-value" style="color: #f59e0b;">{t1_cnt:,}</div><div class="kpi-subtitle">Audited CSPC EAS</div></div>', unsafe_allow_html=True)
with mk4: with mk4:
lfm_cnt = (df_17k["catalog_intelligence_tier"] == "TIER_3_LASTFM_VERIFIED_ENTITY").sum() lfm_cnt = (df_17k.get("catalog_intelligence_tier") == "TIER_3_LASTFM_VERIFIED_ENTITY").sum()
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #38bdf8;">Last.fm Verified</div><div class="kpi-value" style="color: #38bdf8;">{lfm_cnt:,}</div><div class="kpi-subtitle">Active Platform Entities</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #38bdf8;">Last.fm Verified</div><div class="kpi-value" style="color: #38bdf8;">{lfm_cnt:,}</div><div class="kpi-subtitle">Active Platform Entities</div></div>', unsafe_allow_html=True)
with mk5: with mk5:
unver_cnt = len(df_17k) - ver_cnt unver_cnt = len(df_17k) - ver_cnt
@@ -393,7 +310,7 @@ if "Global Master Catalog" in catalog_source:
st.dataframe( st.dataframe(
filt_17k[disp_cols].reset_index(drop=True), filt_17k[disp_cols].reset_index(drop=True),
use_container_width=True, width="stretch",
height=560, height=560,
column_config={ column_config={
"catalog_rank": st.column_config.NumberColumn("Global Rank", format="#%d", width="small"), "catalog_rank": st.column_config.NumberColumn("Global Rank", format="#%d", width="small"),
@@ -408,455 +325,220 @@ if "Global Master Catalog" in catalog_source:
) )
with st.sidebar: with st.sidebar:
st.markdown("##### 💾 17k Data Export") st.markdown("##### 17k Data Export")
csv_17k = filt_17k.to_csv(index=False).encode("utf-8") csv_17k = filt_17k.to_csv(index=False).encode("utf-8")
st.download_button("📥 Export 17k Table CSV", data=csv_17k, file_name="master_17k_catalog.csv", mime="text/csv", use_container_width=True) st.download_button("Export 17k Table CSV", data=csv_17k, file_name="master_17k_catalog.csv", mime="text/csv", width="stretch")
# =====================================================================
# MODULE 2: HIFFI HIP-HOP 500 (EXECUTIVE DASHBOARD)
# =====================================================================
else: else:
df = df_500 df = df_500
if df.empty: if df.empty:
st.error("Hip-Hop 500 dataset not found. Please verify data/hiffi_catalog.db exists.") st.error("Hip-Hop 500 dataset not found in data/hiffi_catalog.db")
st.stop() st.stop()
with st.sidebar: with st.sidebar:
view_mode = st.radio( view_mode = st.radio(
"Leaderboard Perspective:", "Leaderboard Perspective:",
[ [
"🏆 Published Multi-Platform Rank", "Published Multi-Platform Rank",
"🚀 Breakout Velocity (BVI)", "Breakout Velocity (BVI)",
"💿 Record Sales (CSPC Methodology)", "Full Cohort Audit Grid",
"🧪 Commercial Simulation Engine (CPI)",
"🔍 Full Cohort Audit Grid",
], ],
index=0, index=0,
) )
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
catalog_filter = "All Monitored Artists (500)" st.markdown("##### Cohort Filters")
if view_mode in ("💿 Record Sales (CSPC Methodology)", "🧪 Commercial Simulation Engine (CPI)"):
st.markdown("##### 🔍 Sales Catalog Scope")
catalog_filter = st.radio(
"Filter Rows by Sales Telemetry:",
["All Monitored Artists (500)", "Verified Only (321)", "Pure CSPC Only (62)"],
index=0,
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
sim_weight = 0.15
sim_mode = "matched_only"
if view_mode == "🧪 Commercial Simulation Engine (CPI)" and engine is not None:
st.markdown("##### 🧪 Simulation Parameters")
sim_mode_choice = st.radio(
"Evaluation Scope:",
["🎯 Matched Cohort Only", "🌐 Global Cohort (N=500, Weight Redistribution)"],
index=1 if catalog_filter == "All Monitored Artists (500)" else 0,
)
sim_mode = "matched_only" if "Matched" in sim_mode_choice else "global_redistribution"
sim_weight = st.select_slider(
"Commercial Signal Weight:",
options=[0.0, 0.05, 0.10, 0.15, 0.20],
value=0.15,
format_func=lambda x: f"{int(x*100)}% Commercial",
)
st.caption(f"Baseline Score Weight: **{int((1.0 - sim_weight)*100)}%**")
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("##### 🛡️ Cohort Filters")
all_verticals = sorted(df["subgenre"].dropna().unique().tolist()) all_verticals = sorted(df["subgenre"].dropna().unique().tolist())
selected_verticals = st.multiselect("Vertical", options=all_verticals, default=all_verticals) selected_verticals = st.multiselect("Vertical", options=all_verticals, default=all_verticals, key="v_sel_pure")
tier_options = sorted(df["platform_evidence_tier"].dropna().unique().astype(int).tolist(), reverse=True) tier_options = sorted(df["platform_evidence_tier"].dropna().unique().astype(int).tolist(), reverse=True)
selected_tiers = st.multiselect("Evidence Tier", options=tier_options, default=tier_options) selected_tiers = st.multiselect("Evidence Tier", options=tier_options, default=tier_options, key="t_sel_pure")
min_xp = st.slider("Min HIFFI-XP Score", 0.0, 100.0, 0.0, 5.0) min_xp = st.slider("Min HIFFI-XP Score", 0.0, 100.0, 0.0, 5.0)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("##### Export")
csv_payload = df.to_csv(index=False).encode("utf-8")
st.download_button(
"Download Top 500 CSV",
data=csv_payload,
file_name="hiffi_top500_telemetry.csv",
mime="text/csv",
width="stretch",
)
st.caption("Telemetry Engine: 45% Spotify - 35% YouTube - 20% Instagram")
filtered_df = df[ filtered_df = df[
(df["subgenre"].isin(selected_verticals)) (df["subgenre"].isin(selected_verticals))
& (df["platform_evidence_tier"].isin(selected_tiers)) & (df["platform_evidence_tier"].isin(selected_tiers))
& (df["hiffi_xp_score"].fillna(0.0) >= min_xp) & (df["hiffi_xp_score"].fillna(0.0) >= min_xp)
].copy() ].copy()
if "sales_data_status" in filtered_df.columns:
if catalog_filter == "Pure CSPC Only (62)":
filtered_df = filtered_df[filtered_df["sales_data_status"] == "VERIFIED_CSPC"].copy()
elif catalog_filter == "Verified Only (321)":
filtered_df = filtered_df[filtered_df["sales_data_status"].isin(["VERIFIED_CSPC", "VERIFIED_RIAA"])].copy()
sim_metrics = {}
if view_mode == "🧪 Commercial Simulation Engine (CPI)" and engine is not None:
try:
filtered_df = engine.simulate_ranking(filtered_df, commercial_weight=sim_weight, mode=sim_mode)
sim_metrics = engine.compute_simulation_metrics(filtered_df)
except Exception:
pass
with st.sidebar:
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("##### 💾 Unified Audit Exports")
if generate_executive_sales_workbook is not None:
try:
excel_workbook_bytes = generate_executive_sales_workbook(filtered_df, commercial_weight=sim_weight, mode=sim_mode)
st.download_button(
"📊 Multi-Tab Audit Excel (.xlsx)",
data=excel_workbook_bytes,
file_name="hiffi_executive_sales_audit.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True,
)
except Exception:
pass
csv_payload = filtered_df.to_csv(index=False).encode("utf-8")
st.download_button(
"📥 Filtered Table CSV",
data=csv_payload,
file_name="hiffi_multiplatform_filtered.csv",
mime="text/csv",
use_container_width=True,
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.caption("Governance: CSPC Methodology • EAS Units • Zero-Imputation Compliant")
head_col1, head_col2 = st.columns([3, 1]) head_col1, head_col2 = st.columns([3, 1])
with head_col1: with head_col1:
st.markdown("<h2 style='margin-bottom: 2px;'>HIFFI Hip-Hop 500 Intelligence Hub</h2>", unsafe_allow_html=True) st.markdown("<h2 style='margin-bottom: 2px;'>HIFFI Hip-Hop 500 Intelligence Hub</h2>", unsafe_allow_html=True)
st.markdown("<p style='color: #9ca3af; font-size: 0.92rem; margin-top: 0;'>Multi-platform composite rankings with zero-imputation policy, dynamic weight redistribution, and verified record sales.</p>", unsafe_allow_html=True) st.markdown("<p style='color: #9ca3af; font-size: 0.90rem; margin-top: 0;'>Pure multi-platform composite rankings (45% Spotify, 35% YouTube, 20% Instagram) with zero-imputation policy.</p>", unsafe_allow_html=True)
with head_col2: with head_col2:
st.markdown( st.markdown(
f"<div style='text-align: right; color: #6b7280; font-size: 0.8rem; padding-top: 10px;'>Filtered Cohort: <b>{len(filtered_df)} / {len(df)}</b> artists</div>", f"<div style='text-align: right; color: #6b7280; font-size: 0.85rem; padding-top: 10px;'>Active Cohort: <b>{len(filtered_df)} / {len(df)}</b> artists</div>",
unsafe_allow_html=True, unsafe_allow_html=True,
) )
# 500 Macro KPI Cards (6 Columns with YouTube Telemetry) k1, k2, k3, k4, k5 = st.columns(5)
k1, k2, k3, k4, k5, k6 = st.columns(6)
with k1: with k1:
st.markdown(f'<div class="kpi-box"><div class="kpi-title">Tracked Artists</div><div class="kpi-value">{len(df)}</div><div class="kpi-subtitle">Monitored Cohort</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title">Tracked Artists</div><div class="kpi-value">{len(df)}</div><div class="kpi-subtitle">Monitored Cohort</div></div>', unsafe_allow_html=True)
with k2: with k2:
t3_count = len(df[df["platform_evidence_tier"] == 3]) t3_count = int((df["platform_evidence_tier"] == 3).sum())
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #10b981;">Tier 3 (Complete)</div><div class="kpi-value" style="color: #10b981;">{t3_count}</div><div class="kpi-subtitle">YouTube + Spotify + Instagram</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #10b981;">Tier 3 (Complete)</div><div class="kpi-value" style="color: #10b981;">{t3_count}</div><div class="kpi-subtitle">Spotify + YT + IG</div></div>', unsafe_allow_html=True)
with k3: with k3:
yt_count = df["subscriber_count"].fillna(0).gt(0).sum() if "subscriber_count" in df.columns else len(df) yt_count = int(df["subscriber_count"].fillna(0).gt(0).sum())
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #ef4444;">YouTube Telemetry</div><div class="kpi-value" style="color: #ef4444;">{yt_count}</div><div class="kpi-subtitle">Verified video channels</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #ef4444;">YouTube Telemetry</div><div class="kpi-value" style="color: #ef4444;">{yt_count}</div><div class="kpi-subtitle">Verified video channels</div></div>', unsafe_allow_html=True)
with k4: with k4:
sp_count = df["spotify_monthly_listeners"].fillna(0).gt(0).sum() if "spotify_monthly_listeners" in df.columns else len(df) sp_count = int(df["spotify_monthly_listeners"].fillna(0).gt(0).sum())
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #38bdf8;">Spotify Telemetry</div><div class="kpi-value" style="color: #38bdf8;">{sp_count}</div><div class="kpi-subtitle">Verified monthly listeners</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #38bdf8;">Spotify Telemetry</div><div class="kpi-value" style="color: #38bdf8;">{sp_count}</div><div class="kpi-subtitle">Verified monthly listeners</div></div>', unsafe_allow_html=True)
with k5: with k5:
ig_count = df["instagram_followers"].fillna(0).gt(0).sum() if "instagram_followers" in df.columns else len(df) ig_count = int(df["instagram_followers"].fillna(0).gt(0).sum())
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #f59e0b;">Instagram Audience</div><div class="kpi-value" style="color: #f59e0b;">{ig_count}</div><div class="kpi-subtitle">Verified public followings</div></div>', unsafe_allow_html=True) st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #f59e0b;">Instagram Audience</div><div class="kpi-value" style="color: #f59e0b;">{ig_count}</div><div class="kpi-subtitle">Verified public followings</div></div>', unsafe_allow_html=True)
with k6:
cataloged_count = df["total_eas_cspc"].fillna(0).gt(0).sum() if "total_eas_cspc" in df.columns else 0
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #ec4899;">Catalog Coverage</div><div class="kpi-value" style="color: #ec4899;">{cataloged_count}</div><div class="kpi-subtitle">Commercial Units Indexed</div></div>', unsafe_allow_html=True)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
if view_mode in ("💿 Record Sales (CSPC Methodology)", "🧪 Commercial Simulation Engine (CPI)") and "sales_data_status" in df.columns: top3 = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).head(3)
verified_cspc_cnt = int((df["sales_data_status"] == "VERIFIED_CSPC").sum()) if len(top3) >= 3:
verified_riaa_cnt = int((df["sales_data_status"] == "VERIFIED_RIAA").sum()) st.markdown("#### Market Leaders (Audited Multi-Platform Baseline)")
estimated_sea_cnt = int((df["sales_data_status"] == "ESTIMATED_SEA").sum()) p1, p2, p3 = st.columns(3)
total_active_coverage = verified_cspc_cnt + verified_riaa_cnt + estimated_sea_cnt podium_data = [
coverage_pct = (total_active_coverage / len(df)) * 100.0 if len(df) > 0 else 0.0 (p1, top3.iloc[0], "PUBLISHED #1", "podium-gold", "#f59e0b"),
(p2, top3.iloc[1], "PUBLISHED #2", "podium-silver", "#94a3b8"),
st.markdown( (p3, top3.iloc[2], "PUBLISHED #3", "podium-bronze", "#f97316"),
f"""
<div class="coverage-card">
<div>
<span style="font-weight: 800; color: #f9fafb; font-size: 0.92rem;">📡 Commercial Telemetry Coverage Progress:</span>
<span style="color: #10b981; font-weight: 700; margin-left: 8px;">{verified_cspc_cnt} CSPC Verified</span>
<span style="color: #64748b; margin: 0 6px;">•</span>
<span style="color: #38bdf8; font-weight: 700;">{verified_riaa_cnt} RIAA Certified</span>
<span style="color: #64748b; margin: 0 6px;">•</span>
<span style="color: #a855f7; font-weight: 600;">{estimated_sea_cnt} Streaming SEA</span>
</div>
<div style="font-size: 0.85rem; color: #94a3b8;">
Active Commercial Coverage: <strong style="color: #10b981;">{coverage_pct:.1f}%</strong> ({total_active_coverage}/{len(df)})
</div>
</div>
""",
unsafe_allow_html=True,
)
if view_mode == "💿 Record Sales (CSPC Methodology)":
st.markdown("### 💿 Global Record Sales & Lifetime Commercial Ranking")
st.caption("Standardized on ChartMasters CSPC, verified RIAA certifications, and video/audio streaming equivalent units.")
sc1, sc2, sc3, sc4 = st.columns(4)
total_sales_volume = filtered_df["total_eas_cspc"].fillna(0).sum() if "total_eas_cspc" in filtered_df.columns else 0
cspc_count = (filtered_df["sales_data_status"] == "VERIFIED_CSPC").sum() if "sales_data_status" in filtered_df.columns else 0
riaa_count = (filtered_df["sales_data_status"] == "VERIFIED_RIAA").sum() if "sales_data_status" in filtered_df.columns else 0
sea_count = (filtered_df["sales_data_status"] == "ESTIMATED_SEA").sum() if "sales_data_status" in filtered_df.columns else 0
with sc1:
st.markdown(f'<div class="kpi-box"><div class="kpi-title">Tracked Catalog Volume</div><div class="kpi-value">{total_sales_volume/1e6:.1f}M</div><div class="kpi-subtitle">Total Equivalent Album Sales (EAS)</div></div>', unsafe_allow_html=True)
with sc2:
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #10b981;">CSPC Verified</div><div class="kpi-value" style="color: #10b981;">{cspc_count}</div><div class="kpi-subtitle">ChartMasters Full Dossiers</div></div>', unsafe_allow_html=True)
with sc3:
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #38bdf8;">RIAA Certified</div><div class="kpi-value" style="color: #38bdf8;">{riaa_count}</div><div class="kpi-subtitle">Domestic Certifications</div></div>', unsafe_allow_html=True)
with sc4:
st.markdown(f'<div class="kpi-box"><div class="kpi-title" style="color: #a855f7;">Streaming SEA</div><div class="kpi-value" style="color: #a855f7;">{sea_count}</div><div class="kpi-subtitle">Luminate/Billboard Conversion</div></div>', unsafe_allow_html=True)
st.markdown("#### 🏆 All-Time Global Sales Leaderboard")
sales_table_df = filtered_df.sort_values(by="total_eas_cspc", ascending=False).copy() if "total_eas_cspc" in filtered_df.columns else filtered_df.copy()
if "sales_data_status" in sales_table_df.columns:
sales_table_df["provenance_badge"] = sales_table_df["sales_data_status"].apply(get_provenance_badge)
sales_cols = [
"cohort_sales_rank", "chartmasters_sales_rank", "name", "provenance_badge",
"total_eas_cspc", "pure_album_sales", "streaming_eas", "physical_singles_sales",
"digital_singles_sales", "commercial_power_index", "hiffi_xp_published_rank"
] ]
render_s_cols = [c for c in sales_cols if c in sales_table_df.columns] for col, row_data, rank_label, css_class, accent_color in podium_data:
tier_val = int(row_data.get("platform_evidence_tier", 3))
sp_num = int(row_data.get("spotify_monthly_listeners") or 0)
yt_num = int(row_data.get("subscriber_count") or 0)
ig_num = int(row_data.get("instagram_followers") or 0)
xp_val = float(row_data.get("hiffi_xp_score") or 0.0)
artist_name = str(row_data.get("name", "Unknown"))
st.dataframe( podium_html = (
sales_table_df[render_s_cols].reset_index(drop=True), f'<div class="podium-tile {css_class}">'
use_container_width=True, f'<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px;">'
height=380, f'<span style="font-weight: 800; color: {accent_color}; font-size: 0.85rem;">{rank_label}</span>'
column_config={ f'<span class="badge badge-tier{tier_val}">Tier {tier_val} Evidence</span>'
"cohort_sales_rank": st.column_config.NumberColumn("Cohort Sales Rank", format="#%d", width="small"), f'</div>'
"chartmasters_sales_rank": st.column_config.NumberColumn("All-Time Global Rank", format="#%d", width="small"), f'<div style="font-size: 1.45rem; font-weight: 800; color: #ffffff; margin-bottom: 14px;">{artist_name}</div>'
"name": st.column_config.TextColumn("Artist", width="medium"), f'<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; background: #0b0f17; padding: 10px; border-radius: 8px; text-align: center;">'
"provenance_badge": st.column_config.TextColumn("Audit Provenance", width="medium"), f'<div><div style="font-size: 0.64rem; color: #9ca3af;">HIFFI-XP</div><div style="font-size: 1.12rem; font-weight: 800; color: {accent_color};">{xp_val:.2f}</div></div>'
"total_eas_cspc": st.column_config.NumberColumn("Total EAS Units", format="%,d", width="medium"), f'<div><div style="font-size: 0.64rem; color: #9ca3af;">Spotify</div><div style="font-size: 0.90rem; font-weight: 700; color: #38bdf8;">{sp_num:,}</div></div>'
"pure_album_sales": st.column_config.NumberColumn("Pure Studio Albums", format="%,d", width="medium"), f'<div><div style="font-size: 0.64rem; color: #9ca3af;">YT Subs</div><div style="font-size: 0.90rem; font-weight: 700; color: #ef4444;">{yt_num:,}</div></div>'
"streaming_eas": st.column_config.NumberColumn("Streaming EAS", format="%,d", width="medium"), f'<div><div style="font-size: 0.64rem; color: #9ca3af;">IG Follow</div><div style="font-size: 0.90rem; font-weight: 700; color: #e5e7eb;">{ig_num:,}</div></div>'
"physical_singles_sales": st.column_config.NumberColumn("Physical Singles", format="%,d", width="small"), f'</div>'
"digital_singles_sales": st.column_config.NumberColumn("Digital Downloads", format="%,d", width="small"), f'</div>'
"commercial_power_index": st.column_config.ProgressColumn("CPI Score", min_value=0, max_value=100, format="%.1f", width="small"), )
"hiffi_xp_published_rank": st.column_config.NumberColumn("HIFFI Base Rank", format="#%d", width="small"), with col:
}, st.markdown(podium_html, unsafe_allow_html=True)
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
c_s1, c_s2 = st.columns(2)
with c_s1:
if "total_eas_cspc" in sales_table_df.columns:
top_sellers = sales_table_df[sales_table_df["total_eas_cspc"].fillna(0) > 0].head(12)
fig_format = go.Figure()
fig_format.add_trace(go.Bar(name="Traditional Albums", x=top_sellers["name"], y=top_sellers.get("pure_album_sales", 0), marker_color="#8b5cf6"))
fig_format.add_trace(go.Bar(name="Digital Singles", x=top_sellers["name"], y=top_sellers.get("digital_singles_sales", 0), marker_color="#38bdf8"))
fig_format.add_trace(go.Bar(name="Streaming EAS", x=top_sellers["name"], y=top_sellers.get("streaming_eas", 0), marker_color="#10b981"))
fig_format.update_layout(
barmode="stack", title="Format Contribution: Pure Albums vs Downloads vs Streaming EAS",
template="plotly_dark", paper_bgcolor="#111827", plot_bgcolor="#111827",
font=dict(color="#9ca3af"), height=360, margin=dict(l=20, r=20, t=50, b=20),
legend=dict(orientation="h", yanchor="bottom", y=1.02, xanchor="right", x=1.0),
)
st.plotly_chart(fig_format, use_container_width=True)
with c_s2: st.markdown(f"#### {view_mode}")
if "total_eas_cspc" in sales_table_df.columns and "hiffi_xp_score" in sales_table_df.columns:
scatter_data = sales_table_df.dropna(subset=["total_eas_cspc", "hiffi_xp_score"]).copy()
if not scatter_data.empty:
fig_quad = px.scatter(
scatter_data, x="hiffi_xp_score", y="total_eas_cspc", size="total_eas_cspc",
color="provenance_badge" if "provenance_badge" in scatter_data.columns else None,
hover_name="name", log_y=True,
title="Legacy vs. Velocity Quadrant (All-Time Sales vs HIFFI-XP)",
labels={"hiffi_xp_score": "Current Velocity (HIFFI-XP)", "total_eas_cspc": "Total Lifetime EAS Units (Log Scale)"},
template="plotly_dark",
)
fig_quad.update_layout(paper_bgcolor="#111827", plot_bgcolor="#111827", font=dict(color="#9ca3af"), height=360, margin=dict(l=20, r=20, t=50, b=20))
st.plotly_chart(fig_quad, use_container_width=True)
if view_mode == "Published Multi-Platform Rank":
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
cols = [
"hiffi_xp_published_rank", "name", "platform_evidence_tier",
"hiffi_xp_score", "spotify_monthly_listeners", "subscriber_count",
"instagram_followers", "effective_weights", "anomaly_flags"
]
elif view_mode == "Breakout Velocity (BVI)":
bvi_series = filtered_df.get("breakout_velocity_index", pd.Series(0.0, index=filtered_df.index)).fillna(0.0)
table_df = filtered_df.copy()
table_df["breakout_velocity_index"] = bvi_series
table_df = table_df.sort_values(by="breakout_velocity_index", ascending=False)
cols = ["name", "breakout_velocity_index", "hiffi_xp_published_rank", "daily_view_velocity", "momentum_30d", "subscriber_count", "subgenre"]
else: else:
top3 = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).head(3) table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
if len(top3) >= 3 and view_mode != "🧪 Commercial Simulation Engine (CPI)": cols = ["hiffi_xp_published_rank", "name", "platform_evidence_tier", "hiffi_xp_score", "spotify_monthly_listeners", "subscriber_count", "instagram_followers", "daily_view_velocity", "anomaly_flags"]
st.markdown("#### 👑 Market Leaders (Published Baseline Rankings)")
p1, p2, p3 = st.columns(3)
podium_data = [
(p1, top3.iloc[0], "🥇 PUBLISHED #1", "podium-gold", "#f59e0b"),
(p2, top3.iloc[1], "🥈 PUBLISHED #2", "podium-silver", "#94a3b8"),
(p3, top3.iloc[2], "🥉 PUBLISHED #3", "podium-bronze", "#f97316"),
]
for col, row_data, rank_label, css_class, accent_color in podium_data:
tier_val = int(row_data.get("platform_evidence_tier", 1))
yt_str = f"{int(row_data['subscriber_count']):,}" if pd.notna(row_data.get("subscriber_count")) else "NaN"
sp_str = f"{int(row_data['spotify_monthly_listeners']):,}" if pd.notna(row_data.get("spotify_monthly_listeners")) else "NaN"
ig_str = f"{int(row_data['instagram_followers']):,}" if pd.notna(row_data.get("instagram_followers")) else "NaN"
with col:
st.markdown(
f"""
<div class="podium-tile {css_class}">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
<span style="font-weight: 800; color: {accent_color}; font-size: 0.85rem;">{rank_label}</span>
<span class="badge badge-tier{tier_val}">Tier {tier_val} Evidence</span>
</div>
<div style="font-size: 1.35rem; font-weight: 800; color: #ffffff; margin-bottom: 2px;">{row_data['name']}</div>
<div style="font-size: 0.8rem; color: #6b7280; margin-bottom: 12px;">{row_data.get('channel_title', '')}</div>
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; background: #0b0f17; padding: 10px 10px; border-radius: 8px;">
<div><div style="font-size: 0.64rem; color: #9ca3af;">HIFFI-XP</div><div style="font-size: 1.15rem; font-weight: 800; color: {accent_color};">{row_data.get('hiffi_xp_score', 0.0):.2f}</div></div>
<div><div style="font-size: 0.64rem; color: #9ca3af;">YT Subs</div><div style="font-size: 0.95rem; font-weight: 700; color: #ef4444;">{yt_str}</div></div>
<div><div style="font-size: 0.64rem; color: #9ca3af;">Spotify</div><div style="font-size: 0.95rem; font-weight: 700; color: #38bdf8;">{sp_str}</div></div>
<div><div style="font-size: 0.64rem; color: #9ca3af;">IG Follow</div><div style="font-size: 0.95rem; font-weight: 700; color: #e5e7eb;">{ig_str}</div></div>
</div>
</div>
""",
unsafe_allow_html=True,
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
if view_mode == "🧪 Commercial Simulation Engine (CPI)": display_table = table_df.copy()
st.markdown("#### 🧪 Simulation Impact & Decision Summary") render_cols = [c for c in cols if c in display_table.columns]
sm1, sm2, sm3, sm4 = st.columns(4)
g_delta = f"+{sim_metrics.get('top_gainer_delta', 0)}" if sim_metrics.get('top_gainer_delta', 0) > 0 else f"{sim_metrics.get('top_gainer_delta', 0)}" st.dataframe(
l_delta = f"{sim_metrics.get('top_loser_delta', 0)}" display_table[render_cols].reset_index(drop=True),
width="stretch",
with sm1: height=420,
st.markdown(f'<div class="sim-kpi-card"><div class="sim-kpi-title">Top Gainer</div><div class="sim-kpi-val" style="color: #10b981;">{sim_metrics.get("top_gainer_name", "None")} <span style="font-size: 0.85rem;">(▲ {g_delta})</span></div></div>', unsafe_allow_html=True) column_config={
with sm2: "hiffi_xp_published_rank": st.column_config.NumberColumn("Rank", format="#%d", width="small"),
st.markdown(f'<div class="sim-kpi-card"><div class="sim-kpi-title">Top Loser</div><div class="sim-kpi-val" style="color: #ef4444;">{sim_metrics.get("top_loser_name", "None")} <span style="font-size: 0.85rem;">(▼ {l_delta})</span></div></div>', unsafe_allow_html=True) "name": st.column_config.TextColumn("Artist", width="medium"),
with sm3: "platform_evidence_tier": st.column_config.NumberColumn("Evidence Tier", format="Tier %d", width="small"),
st.markdown(f'<div class="sim-kpi-card"><div class="sim-kpi-title">Mean Absolute Shift</div><div class="sim-kpi-val" style="color: #38bdf8;">± {sim_metrics.get("mean_abs_delta", 0.0)} <span style="font-size: 0.85rem; color: #94a3b8;">positions</span></div></div>', unsafe_allow_html=True) "hiffi_xp_score": st.column_config.ProgressColumn("HIFFI-XP Score", min_value=0, max_value=100, format="%.2f", width="medium"),
with sm4: "spotify_monthly_listeners": st.column_config.NumberColumn("Spotify Listeners", format="%,d", width="medium"),
st.markdown(f'<div class="sim-kpi-card"><div class="sim-kpi-title">Simulation Scope</div><div class="sim-kpi-val" style="color: #f59e0b;">{sim_metrics.get("matched_coverage", 0)} / {len(filtered_df)} <span style="font-size: 0.85rem; color: #94a3b8;">({sim_mode})</span></div></div>', unsafe_allow_html=True) "subscriber_count": st.column_config.NumberColumn("YouTube Subscribers", format="%,d", width="medium"),
"instagram_followers": st.column_config.NumberColumn("Instagram Followers", format="%,d", width="medium"),
st.caption(f"💡 **Executive Takeaway:** At **{int(sim_weight*100)}% commercial weight**, the average cataloged artist moves **±{sim_metrics.get('mean_abs_delta', 0.0)} positions**.") "effective_weights": st.column_config.TextColumn("Weights Distribution", width="medium"),
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True) "anomaly_flags": st.column_config.TextColumn("Integrity Audit", width="small"),
"breakout_velocity_index": st.column_config.ProgressColumn("Breakout Velocity Index", format="%.2f", min_value=0, max_value=100, width="medium"),
st.markdown(f"#### 📋 {view_mode}") "daily_view_velocity": st.column_config.NumberColumn("Daily Video Velocity", format="%,d", width="small"),
},
if view_mode == "🏆 Published Multi-Platform Rank":
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
# Explicitly placing subscriber_count right after HIFFI-XP Score
cols = [
"hiffi_xp_published_rank", "hiffi_xp_rank", "name",
"platform_evidence_tier", "hiffi_xp_score", "subscriber_count",
"spotify_monthly_listeners", "instagram_followers",
"effective_weights", "anomaly_flags"
]
elif view_mode == "🚀 Breakout Velocity (BVI)":
bvi_series = filtered_df.get("breakout_velocity_index", pd.Series(0.0, index=filtered_df.index)).fillna(0.0)
table_df = filtered_df[bvi_series.notna()].copy()
table_df["breakout_velocity_index"] = bvi_series
table_df = table_df.sort_values(by="breakout_velocity_index", ascending=False)
cols = ["name", "breakout_velocity_index", "hiffi_xp_published_rank", "daily_view_velocity", "momentum_30d", "subscriber_count", "subgenre"]
elif view_mode == "🧪 Commercial Simulation Engine (CPI)":
table_df = filtered_df.sort_values(by="simulated_rank", ascending=True).copy() if "simulated_rank" in filtered_df.columns else filtered_df.copy()
def format_rank_delta(val):
if pd.isna(val):
return "N/A"
ival = int(val)
if ival > 0:
return f"▲ +{ival} (Up)"
elif ival < 0:
return f"▼ {ival} (Down)"
else:
return "— 0 (Equal)"
if "simulation_rank_delta" in table_df.columns:
table_df["rank_shift_indicator"] = table_df["simulation_rank_delta"].apply(format_rank_delta)
else:
table_df["rank_shift_indicator"] = "— 0"
comp_rank = "baseline_matched_rank" if sim_mode == "matched_only" else "hiffi_xp_published_rank"
cols = [
"simulated_rank", comp_rank, "rank_shift_indicator", "name",
"simulated_hiffi_score", "hiffi_xp_score", "commercial_power_index",
"total_eas_cspc", "sales_data_status",
]
else:
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
cols = ["hiffi_xp_published_rank", "name", "production_hiffi_rank", "hiffi_xp_rank", "rank_delta", "platform_evidence_tier", "hiffi_xp_score", "subscriber_count", "spotify_monthly_listeners", "instagram_followers", "total_eas_cspc", "sales_data_status", "evidence_exclusions", "anomaly_flags"]
display_table = table_df.copy()
if "sales_data_status" in display_table.columns:
display_table["sales_data_status"] = display_table["sales_data_status"].apply(get_provenance_badge)
render_cols = [c for c in cols if c in display_table.columns]
st.dataframe(
display_table[render_cols].reset_index(drop=True),
use_container_width=True,
height=340,
column_config={
"hiffi_xp_published_rank": st.column_config.NumberColumn("Published Rank", format="%d", width="small"),
"hiffi_xp_rank": st.column_config.NumberColumn("Raw Rank", format="%d", width="small"),
"name": st.column_config.TextColumn("Artist", width="medium"),
"platform_evidence_tier": st.column_config.NumberColumn("Tier", format="Tier %d", width="small"),
"baseline_matched_rank": st.column_config.NumberColumn("Baseline Match Rank", format="%d", width="small"),
"simulated_rank": st.column_config.NumberColumn("Simulated Rank", format="%d", width="small"),
"rank_shift_indicator": st.column_config.TextColumn("Simulated Δ (Movement)", width="medium"),
"simulated_hiffi_score": st.column_config.ProgressColumn("Simulated Score", format="%.2f", min_value=0, max_value=100, width="medium"),
"hiffi_xp_score": st.column_config.ProgressColumn("HIFFI-XP Score", format="%.2f", min_value=0, max_value=100, width="medium"),
"subscriber_count": st.column_config.NumberColumn("YouTube Subscribers", format="%,d", width="medium"),
"spotify_monthly_listeners": st.column_config.NumberColumn("Spotify Monthly Listeners", format="%,d", width="medium"),
"instagram_followers": st.column_config.NumberColumn("Instagram Followers", format="%,d", width="medium"),
"breakout_velocity_index": st.column_config.ProgressColumn("Breakout Velocity Index", format="%.2f", min_value=0, max_value=100, width="medium"),
"commercial_power_index": st.column_config.ProgressColumn("Commercial Power Index", format="%.1f", min_value=0, max_value=100, width="medium"),
"total_eas_cspc": st.column_config.NumberColumn("Total Equivalent Album Sales", format="%,d", width="medium"),
"pure_album_sales": st.column_config.NumberColumn("Traditional Album Sales", format="%,d", width="medium"),
"streaming_eas": st.column_config.NumberColumn("Streaming-Equivalent Album Sales", format="%,d", width="medium"),
"sales_data_status": st.column_config.TextColumn("Sales Audit Status", width="medium"),
},
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
c_left, c_right = st.columns(2)
with c_left:
chart_df = filtered_df.dropna(subset=["spotify_monthly_listeners", "instagram_followers", "hiffi_xp_score"]).copy() if all(c in filtered_df.columns for c in ["spotify_monthly_listeners", "instagram_followers", "hiffi_xp_score"]) else pd.DataFrame()
if not chart_df.empty:
fig_scatter = px.scatter(
chart_df, x="spotify_monthly_listeners", y="instagram_followers",
size="hiffi_xp_score", color="platform_evidence_tier", hover_name="name",
log_x=True, log_y=True, title="Spotify Monthly Listeners vs. Instagram Followers",
labels={"spotify_monthly_listeners": "Spotify Monthly Listeners (Log Scale)", "instagram_followers": "Instagram Followers (Log Scale)", "platform_evidence_tier": "Tier"},
template="plotly_dark",
)
fig_scatter.update_layout(paper_bgcolor="#111827", plot_bgcolor="#111827", font=dict(color="#9ca3af"), height=320, margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_scatter, use_container_width=True)
with c_right:
if "platform_evidence_tier" in filtered_df.columns:
tier_counts = filtered_df["platform_evidence_tier"].value_counts().reset_index()
tier_counts.columns = ["Tier", "Count"]
fig_pie = px.pie(tier_counts, values="Count", names="Tier", title="Evidence Tier Cohort Distribution", template="plotly_dark", color="Tier", color_discrete_map={3: "#10b981", 2: "#38bdf8", 1: "#f59e0b", 0: "#ef4444"})
fig_pie.update_layout(paper_bgcolor="#111827", plot_bgcolor="#111827", font=dict(color="#9ca3af"), height=320, margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_pie, use_container_width=True)
# Dossier for 500
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("#### 🔎 Artist Dossier")
selected_name = st.selectbox("Inspect Artist Details:", df["name"].unique(), label_visibility="collapsed")
row = df[df["name"] == selected_name].iloc[0]
d1, d2, d3, d4, d5, d6 = st.columns(6)
with d1:
h_val = row.get("hiffi_xp_score")
st.metric("HIFFI-XP Score", f"{h_val:.2f}" if pd.notna(h_val) else "N/A")
with d2:
pub_rank = row.get("hiffi_xp_published_rank")
st.metric("Published Rank", f"#{int(pub_rank)}" if pd.notna(pub_rank) else "Unranked")
with d3:
yt_s = row.get("subscriber_count")
st.metric("YouTube Subscribers", f"{int(yt_s):,}" if pd.notna(yt_s) else "Unindexed (NaN)")
with d4:
sp_l = row.get("spotify_monthly_listeners")
st.metric("Spotify Listeners", f"{int(sp_l):,}" if pd.notna(sp_l) else "Unindexed (NaN)")
with d5:
ig_f = row.get("instagram_followers")
st.metric("Instagram Followers", f"{int(ig_f):,}" if pd.notna(ig_f) else "Unindexed (NaN)")
with d6:
sales_val = row.get("total_eas_cspc")
st.metric("Total EAS Sales", f"{int(sales_val):,}" if pd.notna(sales_val) else "Awaiting Ingestion")
st.markdown(
f"""
<div style="background: #111827; padding: 14px 18px; border-radius: 8px; border: 1px solid #1f2937; margin-top: 10px; font-size: 0.85rem;">
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
<div><b>Artist:</b> {row['name']}</div>
<div><b>Evidence Tier:</b> Tier {int(row.get('platform_evidence_tier', 1))}</div>
<div><b>YouTube Subscribers:</b> {f"{int(row.get('subscriber_count')):,}" if pd.notna(row.get('subscriber_count')) else "N/A"}</div>
<div><b>Global Sales Rank:</b> {f"#{int(row.get('chartmasters_sales_rank'))}" if pd.notna(row.get('chartmasters_sales_rank')) else "Unranked"}</div>
<div><b>Cohort Sales Rank:</b> {f"#{int(row.get('cohort_sales_rank'))}" if pd.notna(row.get('cohort_sales_rank')) else "Uncataloged"}</div>
<div><b>Commercial Power Index:</b> {f"{row.get('commercial_power_index'):.1f}" if pd.notna(row.get('commercial_power_index')) else "Awaiting Audit"}</div>
<div><b>Traditional Albums:</b> {f"{int(row.get('pure_album_sales')):,}" if pd.notna(row.get('pure_album_sales')) else "N/A"}</div>
<div><b>Streaming-Equivalent EAS:</b> {f"{int(row.get('streaming_eas')):,}" if pd.notna(row.get('streaming_eas')) else "N/A"}</div>
<div><b>Sales Status:</b> {get_provenance_badge(row.get('sales_data_status', ''))}</div>
</div>
</div>
""",
unsafe_allow_html=True,
) )
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
c_left, c_right = st.columns(2)
with c_left:
chart_df = filtered_df.dropna(subset=["spotify_monthly_listeners", "instagram_followers", "hiffi_xp_score"]).copy() if all(c in filtered_df.columns for c in ["spotify_monthly_listeners", "instagram_followers", "hiffi_xp_score"]) else pd.DataFrame()
if not chart_df.empty:
fig_scatter = px.scatter(
chart_df, x="spotify_monthly_listeners", y="instagram_followers",
size="hiffi_xp_score", color="platform_evidence_tier", hover_name="name",
log_x=True, log_y=True, title="Spotify Monthly Listeners vs. Instagram Followers",
labels={"spotify_monthly_listeners": "Spotify Listeners (Log)", "instagram_followers": "Instagram Followers (Log)", "platform_evidence_tier": "Tier"},
template="plotly_dark",
)
fig_scatter.update_layout(paper_bgcolor="#111827", plot_bgcolor="#111827", font=dict(color="#9ca3af"), height=320, margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_scatter, width="stretch")
with c_right:
if "platform_evidence_tier" in filtered_df.columns:
tier_counts = filtered_df["platform_evidence_tier"].value_counts().reset_index()
tier_counts.columns = ["Tier", "Count"]
fig_pie = px.pie(tier_counts, values="Count", names="Tier", title="Evidence Tier Cohort Distribution", template="plotly_dark", color="Tier", color_discrete_map={3: "#10b981", 2: "#38bdf8", 1: "#f59e0b", 0: "#ef4444"})
fig_pie.update_layout(paper_bgcolor="#111827", plot_bgcolor="#111827", font=dict(color="#9ca3af"), height=320, margin=dict(l=20, r=20, t=40, b=20))
st.plotly_chart(fig_pie, width="stretch")
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("#### Artist Dossier")
if "name" in df.columns and len(df["name"].unique()) > 0:
selected_name = st.selectbox("Inspect Artist Details:", df["name"].unique(), label_visibility="collapsed")
matched_rows = df[df["name"] == selected_name]
if not matched_rows.empty:
row = matched_rows.iloc[0]
d1, d2, d3, d4, d5 = st.columns(5)
with d1:
st.metric("HIFFI-XP Score", f"{float(row.get('hiffi_xp_score') or 0.0):.2f}")
with d2:
pub_rank = row.get("hiffi_xp_published_rank")
st.metric("Published Rank", f"#{int(pub_rank)}" if pd.notna(pub_rank) else "Unranked")
with d3:
sp_l = row.get("spotify_monthly_listeners")
st.metric("Spotify Listeners", f"{int(sp_l):,}" if pd.notna(sp_l) else "0")
with d4:
yt_s = row.get("subscriber_count")
st.metric("YouTube Subscribers", f"{int(yt_s):,}" if pd.notna(yt_s) else "0")
with d5:
ig_f = row.get("instagram_followers")
st.metric("Instagram Followers", f"{int(ig_f):,}" if pd.notna(ig_f) else "0")
artist_disp_name = str(row["name"])
tier_disp_num = int(row.get("platform_evidence_tier", 1))
bvi_val = float(row.get("breakout_velocity_index") or 0.0)
weights_disp = str(row.get("effective_weights", "sp:45.0%,yt:35.0%,ig:20.0%"))
dossier_html = (
f'<div style="background: #111827; padding: 14px 18px; border-radius: 8px; border: 1px solid #1f2937; margin-top: 10px; font-size: 0.85rem;">'
f'<div style="display: flex; gap: 24px; flex-wrap: wrap;">'
f'<div><b>Artist:</b> {artist_disp_name}</div>'
f'<div><b>Evidence Tier:</b> Tier {tier_disp_num}</div>'
f'<div><b>Breakout Velocity Index:</b> {bvi_val:.2f}</div>'
f'<div><b>Telemetry Weighting:</b> {weights_disp}</div>'
f'<div><b>Integrity Audit:</b> {row.get("anomaly_flags", "CLEAN")}</div>'
f'</div>'
f'</div>'
)
st.markdown(dossier_html, unsafe_allow_html=True)
+201 -401
View File
@@ -1,457 +1,257 @@
""" import math
core/ranking_engine.py from datetime import datetime, timezone
Ranks artists according to HIFFI formula stages: from typing import Any, Dict, Optional, Tuple, Union
- YouTube Production Baseline: Momentum (35%), Velocity (30%), Reach (20%), Consistency (15%)
- Breakout Velocity Index (BVI, 0-100) with 10k subscriber floor
- Multi-Platform HIFFI-XP Composite (0-100):
* YouTube Production Score (50%)
* Spotify Monthly Listeners (25%)
* Spotify Daily Change / Velocity (5%)
* Instagram Followers (20%)
With zero-imputation compliance, proportional weight redistribution,
and effective weight serialization.
- Publication Eligibility Gates: Decoupled public chart assigning consecutive ranks
while enforcing multi-platform evidence tiers (Top 10 = Tier 3, Top 50 = Tier >= 2)
conditioned on strict identity verification, status_flag validation, and UTC data freshness thresholds.
Fail-closed on missing timestamp columns, failed lookups, or unverified statuses.
"""
from typing import Optional, Tuple, Union
import numpy as np import numpy as np
import pandas as pd import pandas as pd
class HIFFIRankingEngine: class HIFFIRankingEngine:
"""
HIFFI-XP Core Ranking Engine & Governance Suite.
- 45/35/20 Bounded log-scaled scoring with dynamic weight redistribution.
- Publication eligibility gates (timestamp freshness, identity verification, status flags).
- Statistical anomaly detection for ratio spikes and unnatural patterns.
"""
@staticmethod def __init__(self):
def _log_scale(series: pd.Series) -> pd.Series: self.MAX_SPOTIFY = 120_000_000.0
valid = series.dropna() self.MAX_YT_SUBS = 70_000_000.0
log_vals = np.log1p(np.maximum(valid.astype(float), 0.0)) self.MAX_YT_VELOCITY = 5_000_000.0
s_min, s_max = log_vals.min(), log_vals.max() self.MAX_INSTAGRAM = 150_000_000.0
if np.isclose(s_max, s_min) or pd.isna(s_min):
return pd.Series(0.0, index=series.index)
scaled = (log_vals - s_min) / (s_max - s_min + 1e-9)
return scaled.reindex(series.index)
@staticmethod self.w_sp = 0.45
def _min_max_scale(series: pd.Series) -> pd.Series: self.w_yt = 0.35
valid = series.dropna() self.w_ig = 0.20
s_min, s_max = valid.min(), valid.max()
if np.isclose(s_max, s_min) or pd.isna(s_min):
return pd.Series(0.0, index=series.index)
scaled = (valid - s_min) / (s_max - s_min + 1e-9)
return scaled.reindex(series.index)
@staticmethod def _log_scale(self, val: float, max_bound: float) -> float:
def _is_signal_fresh( if pd.isna(val) or val <= 0:
timestamp_val: object, return 0.0
max_days: float, s = math.log10(float(val) + 1.0)
reference_time: Optional[pd.Timestamp] = None mx = math.log10(float(max_bound) + 1.0)
) -> bool: return float(np.clip((s / mx) * 100.0, 0.0, 100.0))
"""
Parses UTC ISO 8601 string or pd.Timestamp and checks staleness.
Fails closed (returns False) on missing, null, or unparseable timestamps.
"""
if pd.isna(timestamp_val):
return False
try:
ts = pd.to_datetime(timestamp_val, utc=True)
ref = reference_time or pd.Timestamp.now(tz="UTC")
age_days = (ref - ts).total_seconds() / 86400.0
return 0.0 <= age_days <= max_days
except Exception:
return False
def calculate_breakout_velocity(self, df: pd.DataFrame) -> pd.Series: def calculate_breakout_velocity(self, df: pd.DataFrame) -> pd.Series:
""" if df.empty:
Computes the Breakout Velocity Index (BVI, 0 - 100). return pd.Series(dtype=float)
Requires at least 10,000 subscribers and positive velocity to qualify. growth_7d = pd.to_numeric(df.get("growth_velocity_7d", 0.0), errors="coerce").fillna(0.0)
""" growth_30d = pd.to_numeric(df.get("growth_velocity_30d", 0.0), errors="coerce").fillna(0.0)
out_bvi = pd.Series(np.nan, index=df.index, dtype=float) raw_bvi = (growth_7d * 0.6 + growth_30d * 0.4) * 10.0
return pd.Series(np.clip(raw_bvi, 0.0, 100.0), index=df.index).round(2)
status_col = df["status_flag"] if "status_flag" in df.columns else pd.Series("COMPLETE", index=df.index)
valid_mask = (
df["subscriber_count"].notna()
& (df["subscriber_count"] >= 10000)
& df["daily_view_velocity"].notna()
& (df["daily_view_velocity"] > 0.0)
& (status_col != "INACTIVE_OR_DORMANT")
& (status_col != "FAILED_LOOKUP")
)
if not valid_mask.any():
return out_bvi
subset = df[valid_mask]
subs = subset["subscriber_count"].astype(float)
daily_vel = subset["daily_view_velocity"].astype(float)
momentum = subset["momentum_30d"].fillna(0.0).clip(lower=-100.0, upper=300.0)
v_aud = daily_vel / np.maximum(subs, 1000.0)
m_signal = (momentum + 100.0) / 400.0
log_v_aud = np.log10(1.0 + v_aud)
w_emerge = 1.0 / (1.0 + np.log10(1.0 + np.maximum(subs, 1.0)))
raw_bvi = (0.65 * log_v_aud + 0.35 * m_signal) * w_emerge
bvi_min = raw_bvi.min()
bvi_max = raw_bvi.max()
if np.isclose(bvi_max, bvi_min):
scaled_bvi = pd.Series(50.0, index=subset.index)
else:
scaled_bvi = ((raw_bvi - bvi_min) / (bvi_max - bvi_min + 1e-9)) * 100.0
out_bvi.loc[valid_mask] = scaled_bvi.round(2)
return out_bvi
def score_and_rank(
self,
df: pd.DataFrame,
min_confidence: float = 0.50,
) -> pd.DataFrame:
"""Computes baseline YouTube production ranking."""
out = df.copy()
if "confidence_score" in out.columns:
out["confidence_score"] = pd.to_numeric(
out["confidence_score"].apply(
lambda x: x.get("composite_confidence", 0.0) if isinstance(x, dict) else x
),
errors="coerce"
).fillna(0.0)
out["breakout_velocity_index"] = self.calculate_breakout_velocity(out)
status_col = out["status_flag"] if "status_flag" in out.columns else pd.Series("COMPLETE", index=out.index)
has_baseline = (
out["subscriber_count"].notna()
& out["lifetime_views"].notna()
& (status_col != "FAILED_LOOKUP")
)
out["is_rank_eligible"] = (
has_baseline
& (out.get("confidence_score", pd.Series(1.0, index=out.index)) >= min_confidence)
& (status_col.isin(["COMPLETE", "CATALOG_ACTIVE_NO_RECENT_UPLOAD"]))
)
filled_momentum = out["momentum_30d"].fillna(0.0).clip(lower=-100.0, upper=300.0) if "momentum_30d" in out.columns else pd.Series(0.0, index=out.index)
score_momentum = self._min_max_scale(filled_momentum)
filled_velocity = out["daily_view_velocity"].fillna(0.0) if "daily_view_velocity" in out.columns else pd.Series(0.0, index=out.index)
score_velocity = self._log_scale(filled_velocity)
log_subs = self._log_scale(out["subscriber_count"].fillna(0.0)) if "subscriber_count" in out.columns else pd.Series(0.0, index=out.index)
log_lifetime = self._log_scale(out["lifetime_views"].fillna(0.0)) if "lifetime_views" in out.columns else pd.Series(0.0, index=out.index)
score_reach = 0.60 * log_subs + 0.40 * log_lifetime
score_consistency = self._log_scale(out["video_count"].fillna(0.0)) if "video_count" in out.columns else pd.Series(0.0, index=out.index)
hiffi_raw = (
0.35 * score_momentum
+ 0.30 * score_velocity
+ 0.20 * score_reach
+ 0.15 * score_consistency
) * 100.0
conf_multiplier = 0.8 + 0.2 * out.get("confidence_score", pd.Series(1.0, index=out.index)).clip(0.0, 1.0)
out["production_hiffi_score"] = (hiffi_raw * conf_multiplier).round(2)
eligible_mask = out["is_rank_eligible"]
out["production_hiffi_rank"] = pd.NA
out.loc[eligible_mask, "production_hiffi_rank"] = (
out.loc[eligible_mask, "production_hiffi_score"]
.rank(ascending=False, method="min")
.astype(int)
)
bvi_eligible_mask = eligible_mask & out["breakout_velocity_index"].notna()
out["breakout_rank"] = pd.NA
out.loc[bvi_eligible_mask, "breakout_rank"] = (
out.loc[bvi_eligible_mask, "breakout_velocity_index"]
.rank(ascending=False, method="min")
.astype(int)
)
return out
def calculate_hiffi_xp(self, df: pd.DataFrame) -> pd.DataFrame: def calculate_hiffi_xp(self, df: pd.DataFrame) -> pd.DataFrame:
"""
Calculates HIFFI-XP composite multi-platform score, raw rank, and effective weights.
Enforces proportional weight redistribution when signals are missing (NaN).
Weights: YouTube (0.50), Spotify Monthly (0.25), Spotify Velocity (0.05), Instagram (0.20).
"""
out = df.copy() out = df.copy()
# Schema aliasing for raw input fallbacks has_sp = out.get("spotify_monthly_listeners", pd.Series(0, index=out.index)).fillna(0).gt(0).astype(int)
if "production_hiffi_score" not in out.columns and "hiffi_score" in out.columns: has_yt = out.get("subscriber_count", pd.Series(0, index=out.index)).fillna(0).gt(0).astype(int)
out["production_hiffi_score"] = out["hiffi_score"] has_ig = out.get("instagram_followers", pd.Series(0, index=out.index)).fillna(0).gt(0).astype(int)
if "production_hiffi_rank" not in out.columns and "hiffi_rank" in out.columns:
out["production_hiffi_rank"] = out["hiffi_rank"]
base_weights = { if "subscriber_count" not in out.columns:
"yt": 0.50, has_yt = out.get("lifetime_views", pd.Series(0, index=out.index)).fillna(0).gt(0).astype(int)
"sp_monthly": 0.25, if has_yt.sum() == 0 and "status_flag" in out.columns:
"sp_velocity": 0.05, has_yt = (out["status_flag"] == "COMPLETE").astype(int)
"ig": 0.20
}
def log_scale_vector(series: pd.Series) -> pd.Series: out["platform_evidence_tier"] = has_sp + has_yt + has_ig
valid = series.dropna()
if valid.empty:
return pd.Series(np.nan, index=series.index)
log_val = np.log1p(np.maximum(0.0, valid.astype(float)))
min_v, max_v = log_val.min(), log_val.max()
if np.isclose(max_v, min_v):
return pd.Series(1.0, index=valid.index)
scaled = (log_val - min_v) / (max_v - min_v + 1e-9)
return scaled.reindex(series.index)
def linear_scale_vector(series: pd.Series) -> pd.Series: confidence_map = {3: 1.00, 2: 0.85, 1: 0.65, 0: 0.50}
valid = series.dropna() out["confidence_score"] = out["platform_evidence_tier"].map(confidence_map).fillna(1.0)
if valid.empty:
return pd.Series(np.nan, index=series.index)
min_v, max_v = valid.min(), valid.max()
if np.isclose(max_v, min_v):
return pd.Series(0.5, index=valid.index)
scaled = (valid - min_v) / (max_v - min_v + 1e-9)
return scaled.reindex(series.index)
s_yt = log_scale_vector(out["production_hiffi_score"]) if "production_hiffi_score" in out.columns else pd.Series(np.nan, index=out.index) scores = []
s_sp_m = log_scale_vector(out["spotify_monthly_listeners"]) if "spotify_monthly_listeners" in out.columns else pd.Series(np.nan, index=out.index) effective_weight_strings = []
s_sp_v = linear_scale_vector(out["spotify_daily_change"]) if "spotify_daily_change" in out.columns else pd.Series(np.nan, index=out.index)
s_ig = log_scale_vector(out["instagram_followers"]) if "instagram_followers" in out.columns else pd.Series(np.nan, index=out.index)
signals = pd.DataFrame({ for _, row in out.iterrows():
"yt": s_yt, sp_val = row.get("spotify_monthly_listeners")
"sp_monthly": s_sp_m, sp_clean = float(sp_val) if pd.notna(sp_val) and float(sp_val) > 0 else 0.0
"sp_velocity": s_sp_v,
"ig": s_ig
}, index=out.index)
# Proportional Dynamic Weight Redistribution Matrix yt_sub = row.get("subscriber_count")
weight_mask = signals.notna() yt_sub_clean = float(yt_sub) if pd.notna(yt_sub) and float(yt_sub) > 0 else 0.0
allocated_weights = weight_mask.multiply(pd.Series(base_weights), axis=1)
weight_sum = allocated_weights.sum(axis=1)
weight_sum = weight_sum.replace(0.0, np.nan) yt_vel = row.get("daily_view_velocity")
normalized_weights = allocated_weights.divide(weight_sum, axis=0) yt_vel_clean = float(yt_vel) if pd.notna(yt_vel) and float(yt_vel) > 0 else 0.0
# Explainability: Format explicit percentage breakdown per artist ig_val = row.get("instagram_followers")
def format_weights(row: pd.Series) -> str: ig_clean = float(ig_val) if pd.notna(ig_val) and float(ig_val) > 0 else 0.0
parts = [f"{col}:{val*100.0:.1f}%" for col, val in row.items() if val > 0]
return ",".join(parts) if parts else "NONE"
out["effective_weights"] = normalized_weights.apply(format_weights, axis=1) s_sp = self._log_scale(sp_clean, self.MAX_SPOTIFY)
s_yt = self._log_scale(yt_sub_clean, self.MAX_YT_SUBS) * 0.70 + self._log_scale(yt_vel_clean, self.MAX_YT_VELOCITY) * 0.30
s_ig = self._log_scale(ig_clean, self.MAX_INSTAGRAM)
component_scores = (signals.fillna(0.0) * normalized_weights).sum(axis=1) * 100.0 active_weights = {}
out["hiffi_xp_score"] = component_scores.round(2) if sp_clean > 0:
out.loc[weight_sum.isna(), "hiffi_xp_score"] = np.nan active_weights["sp"] = self.w_sp
if yt_sub_clean > 0 or yt_vel_clean > 0:
active_weights["yt"] = self.w_yt
if ig_clean > 0:
active_weights["ig"] = self.w_ig
# Deterministic tie-breaking for raw continuous ranking total_active = sum(active_weights.values())
sort_cols = ["hiffi_xp_score", "production_hiffi_score", "spotify_monthly_listeners"]
present_sort = [c for c in sort_cols if c in out.columns]
valid_mask = out["hiffi_xp_score"].notna() if total_active > 0:
sorted_indices = ( norm_sp = active_weights.get("sp", 0.0) / total_active
out[valid_mask] norm_yt = active_weights.get("yt", 0.0) / total_active
.sort_values(by=present_sort, ascending=[False] * len(present_sort)) norm_ig = active_weights.get("ig", 0.0) / total_active
.index
)
out["hiffi_xp_rank"] = pd.NA raw_score = (s_sp * norm_sp) + (s_yt * norm_yt) + (s_ig * norm_ig)
out.loc[sorted_indices, "hiffi_xp_rank"] = range(1, len(sorted_indices) + 1) conf = confidence_map.get(row.get("platform_evidence_tier", 3), 1.0)
out.loc[valid_mask, "hiffi_xp_rank"] = out.loc[valid_mask, "hiffi_xp_rank"].astype(int) final_score = float(round(raw_score * conf, 2))
wt_str = f"sp:{round(norm_sp*100,1)}%,yt:{round(norm_yt*100,1)}%,ig:{round(norm_ig*100,1)}%"
else:
prod_score = row.get("production_hiffi_score")
if pd.notna(prod_score):
final_score = float(round(float(prod_score), 2))
else:
final_score = 0.0
wt_str = "unindexed"
if "production_hiffi_rank" in out.columns: scores.append(final_score)
out["rank_delta"] = pd.to_numeric(out["production_hiffi_rank"], errors="coerce") - pd.to_numeric(out["hiffi_xp_rank"], errors="coerce") effective_weight_strings.append(wt_str)
else:
out["rank_delta"] = np.nan out["hiffi_xp_score"] = scores
out["effective_weights"] = effective_weight_strings
if "catalog_rank" not in out.columns or out["catalog_rank"].isna().any():
out["catalog_rank"] = out["hiffi_xp_score"].rank(ascending=False, method="min").astype(int)
out["hiffi_xp_published_rank"] = out["catalog_rank"]
return out return out
def detect_metric_anomalies(self, df: pd.DataFrame) -> pd.DataFrame: def score_and_rank(self, df: pd.DataFrame) -> pd.DataFrame:
""" return self.calculate_hiffi_xp(df)
Flags suspicious metric patterns without altering raw scores:
- SUSPICIOUS_IG_TO_YT_RATIO: IG followers > 10x YouTube lifetime views.
- EXTREME_SPOTIFY_VELOCITY_RATIO: Spotify daily change > 25% of monthly listeners.
- Populates 'anomaly_flags' column (semicolon-delimited or 'CLEAN').
"""
out = df.copy()
flags = [[] for _ in range(len(out))]
# 1. Instagram to YouTube Lifetime Ratio Check
if "instagram_followers" in out.columns and "lifetime_views" in out.columns:
ig = pd.to_numeric(out["instagram_followers"], errors="coerce")
yt_views = pd.to_numeric(out["lifetime_views"], errors="coerce")
suspicious_ratio = (ig.notna() & yt_views.notna() & (yt_views > 0) & (ig > 10.0 * yt_views))
for idx, is_suspicious in enumerate(suspicious_ratio):
if is_suspicious:
flags[idx].append("SUSPICIOUS_IG_TO_YT_RATIO")
# 2. Spotify Velocity to Monthly Listeners Check
if "spotify_daily_change" in out.columns and "spotify_monthly_listeners" in out.columns:
sp_change = pd.to_numeric(out["spotify_daily_change"], errors="coerce")
sp_monthly = pd.to_numeric(out["spotify_monthly_listeners"], errors="coerce")
extreme_velocity = (
sp_change.notna()
& sp_monthly.notna()
& (sp_monthly > 1000)
& (sp_change.abs() > 0.25 * sp_monthly)
)
for idx, is_extreme in enumerate(extreme_velocity):
if is_extreme:
flags[idx].append("EXTREME_SPOTIFY_VELOCITY_RATIO")
out["anomaly_flags"] = [";".join(f) if f else "CLEAN" for f in flags]
return out
def apply_publication_eligibility_gates( def apply_publication_eligibility_gates(
self, self,
df: pd.DataFrame, df: pd.DataFrame,
reference_time: Optional[pd.Timestamp] = None, reference_time: Optional[Any] = None,
return_metadata: bool = False return_metadata: bool = False
) -> Union[pd.DataFrame, Tuple[pd.DataFrame, dict]]: ) -> Union[pd.DataFrame, Tuple[pd.DataFrame, Dict[str, Any]]]:
"""
Constructs a publication ranking layer decoupled from raw scores:
- Top 10 requires all 3 platforms (Tier 3), strictly verified and fresh.
- Top 50 requires at least 2 platforms (Tier >= 2), strictly verified and fresh.
- Freshness Thresholds: YouTube <= 7d, Spotify <= 7d, Instagram <= 14d.
- Integrity Gate: Artists with status_flag == 'FAILED_LOOKUP' are disqualified from YouTube credit.
- Fail-closed: missing timestamp columns, stale records, or unverified statuses evaluate
strictly to False and are logged to evidence_exclusions.
- Preserves raw hiffi_xp_score and raw hiffi_xp_rank completely unchanged.
- Produces consecutive, unique ranks in 'hiffi_xp_published_rank' (1 to N).
"""
out = df.copy() out = df.copy()
ref_time = reference_time or pd.Timestamp.now(tz="UTC")
exclusions = [[] for _ in range(len(out))] if reference_time is None:
ref_dt = datetime.now(timezone.utc)
# 1. YouTube Validation (Score present + status not FAILED_LOOKUP + Freshness <= 7 days) elif isinstance(reference_time, datetime):
status_col = out["status_flag"] if "status_flag" in out.columns else pd.Series("COMPLETE", index=out.index) ref_dt = reference_time if reference_time.tzinfo else reference_time.replace(tzinfo=timezone.utc)
has_yt_val = out["production_hiffi_score"].notna() if "production_hiffi_score" in out.columns else pd.Series(False, index=out.index)
is_lookup_failed = status_col == "FAILED_LOOKUP"
if "youtube_updated_at" in out.columns:
yt_fresh = out["youtube_updated_at"].apply(
lambda x: self._is_signal_fresh(x, max_days=7.0, reference_time=ref_time)
)
else: else:
yt_fresh = pd.Series(False, index=out.index) ref_dt = pd.to_datetime(reference_time, utc=True).to_pydatetime()
for idx, (val, failed, fresh) in enumerate(zip(has_yt_val, is_lookup_failed, yt_fresh)): max_staleness_days = 7.0
if failed:
exclusions[idx].append("FAILED_YOUTUBE_LOOKUP")
elif val and not fresh:
exclusions[idx].append("STALE_OR_MISSING_YOUTUBE_TIMESTAMP")
valid_yt = has_yt_val & (~is_lookup_failed) & yt_fresh def is_fresh(ts_val: Any) -> bool:
if pd.isna(ts_val) or ts_val is None:
return False
try:
dt = pd.to_datetime(ts_val, utc=True).to_pydatetime()
diff = (ref_dt - dt).total_seconds() / 86400.0
return 0.0 <= diff <= max_staleness_days
except Exception:
return False
# 2. Spotify Validation (Telemetry present + Freshness <= 7 days) has_yt_col = "youtube_updated_at" in out.columns
has_sp_val = out["spotify_monthly_listeners"].notna() if "spotify_monthly_listeners" in out.columns else pd.Series(False, index=out.index) has_sp_col = "spotify_updated_at" in out.columns
if "spotify_updated_at" in out.columns: has_ig_col = "instagram_updated_at" in out.columns
sp_fresh = out["spotify_updated_at"].apply( has_ver_col = "instagram_verification_status" in out.columns
lambda x: self._is_signal_fresh(x, max_days=7.0, reference_time=ref_time)
)
else:
sp_fresh = pd.Series(False, index=out.index)
for idx, (val, fresh) in enumerate(zip(has_sp_val, sp_fresh)): yt_credits = []
if val and not fresh: sp_credits = []
exclusions[idx].append("STALE_OR_MISSING_SPOTIFY_TIMESTAMP") ig_credits = []
valid_sp = has_sp_val & sp_fresh exclusions_list = []
# 3. Instagram Validation (Followers present + Approved Status + Freshness <= 14 days) for _, row in out.iterrows():
has_ig_val = out["instagram_followers"].notna() if "instagram_followers" in out.columns else pd.Series(False, index=out.index) row_exclusions = []
verified_statuses = {"AUTOMATED_VERIFIED", "MANUAL_VERIFIED"}
if "instagram_verification_status" in out.columns: # 1. YouTube Evaluation
ig_verified = out["instagram_verification_status"].isin(verified_statuses) status_flag = str(row.get("status_flag", "COMPLETE")).upper()
else: if status_flag == "FAILED_LOOKUP":
ig_verified = pd.Series(False, index=out.index) yt_ok = False
row_exclusions.append("FAILED_YOUTUBE_LOOKUP")
elif not has_yt_col:
yt_ok = False
row_exclusions.append("STALE_OR_MISSING_YOUTUBE_TIMESTAMP")
else:
yt_ok = is_fresh(row.get("youtube_updated_at"))
if not yt_ok:
row_exclusions.append("STALE_OR_MISSING_YOUTUBE_TIMESTAMP")
yt_credits.append(int(yt_ok))
for idx, (val, ver) in enumerate(zip(has_ig_val, ig_verified)): # 2. Spotify Evaluation
if val and not ver: sp_val = row.get("spotify_monthly_listeners")
exclusions[idx].append("UNVERIFIED_INSTAGRAM_IDENTITY") has_sp_val = pd.notna(sp_val) and float(sp_val) > 0
if not has_sp_col:
sp_ok = False
row_exclusions.append("STALE_OR_MISSING_SPOTIFY_TIMESTAMP")
else:
sp_ok = has_sp_val and is_fresh(row.get("spotify_updated_at"))
if not sp_ok:
row_exclusions.append("STALE_OR_MISSING_SPOTIFY_TIMESTAMP")
sp_credits.append(int(sp_ok))
if "instagram_updated_at" in out.columns: # 3. Instagram Evaluation
ig_fresh = out["instagram_updated_at"].apply( ig_val = row.get("instagram_followers")
lambda x: self._is_signal_fresh(x, max_days=14.0, reference_time=ref_time) has_ig_val = pd.notna(ig_val) and float(ig_val) > 0
)
else:
ig_fresh = pd.Series(False, index=out.index)
for idx, (val, ver, fresh) in enumerate(zip(has_ig_val, ig_verified, ig_fresh)): if not has_ver_col:
if val and ver and not fresh: ig_ver_ok = False
exclusions[idx].append("STALE_OR_MISSING_INSTAGRAM_TIMESTAMP") else:
ver_status = str(row.get("instagram_verification_status", "")).upper()
ig_ver_ok = ver_status in ("AUTOMATED_VERIFIED", "MANUAL_VERIFIED", "VERIFIED")
valid_ig = has_ig_val & ig_verified & ig_fresh if not has_ig_col:
ig_fresh = False
row_exclusions.append("STALE_OR_MISSING_INSTAGRAM_TIMESTAMP")
else:
ig_fresh = is_fresh(row.get("instagram_updated_at"))
if not ig_fresh:
row_exclusions.append("STALE_OR_MISSING_INSTAGRAM_TIMESTAMP")
out["platform_evidence_tier"] = ( ig_ok = has_ig_val and ig_ver_ok and ig_fresh
valid_yt.astype(int) + valid_sp.astype(int) + valid_ig.astype(int) ig_credits.append(int(ig_ok))
)
out["evidence_exclusions"] = [";".join(e) if e else "NONE" for e in exclusions]
# 4. Deterministic sorting by raw composite score exclusions_list.append(";".join(row_exclusions) if row_exclusions else "NONE")
sort_cols = ["hiffi_xp_score", "production_hiffi_score", "spotify_monthly_listeners"]
present_sort_cols = [c for c in sort_cols if c in out.columns]
pool = out.sort_values(
by=present_sort_cols,
ascending=[False] * len(present_sort_cols)
).copy()
assigned_indices = [] out["gated_yt_credit"] = yt_credits
shortfall_audit = {"top_10_shortfall": False, "top_50_shortfall": False} out["gated_sp_credit"] = sp_credits
out["gated_ig_credit"] = ig_credits
out["platform_evidence_tier"] = [y + s + i for y, s, i in zip(yt_credits, sp_credits, ig_credits)]
out["evidence_exclusions"] = exclusions_list
# Pass 1: Select Top 10 from Tier 3 candidates if "hiffi_xp_published_rank" not in out.columns:
tier3_candidates = pool[pool["platform_evidence_tier"] == 3] if "hiffi_xp_score" in out.columns:
top_10_indices = tier3_candidates.head(10).index.tolist() out["hiffi_xp_published_rank"] = out["hiffi_xp_score"].rank(ascending=False, method="min").astype(int)
assigned_indices.extend(top_10_indices) else:
out["hiffi_xp_published_rank"] = list(range(1, len(out) + 1))
if len(assigned_indices) < min(10, len(pool)):
shortfall_audit["top_10_shortfall"] = True
remaining_pool = pool.drop(index=assigned_indices)
shortfall = min(10 - len(assigned_indices), len(remaining_pool))
backfill = remaining_pool[remaining_pool["platform_evidence_tier"] >= 2].head(shortfall)
assigned_indices.extend(backfill.index.tolist())
if len(assigned_indices) < min(10, len(pool)):
remaining_pool = pool.drop(index=assigned_indices)
shortfall = min(10 - len(assigned_indices), len(remaining_pool))
assigned_indices.extend(remaining_pool.head(shortfall).index.tolist())
# Pass 2: Select Positions 11 to 50 from Tier >= 2 candidates
remaining_pool = pool.drop(index=assigned_indices)
needed_for_50 = min(40, len(pool) - len(assigned_indices))
tier2_plus_candidates = remaining_pool[remaining_pool["platform_evidence_tier"] >= 2]
top_50_fill = tier2_plus_candidates.head(needed_for_50).index.tolist()
assigned_indices.extend(top_50_fill)
if len(assigned_indices) < min(50, len(pool)):
shortfall_audit["top_50_shortfall"] = True
remaining_pool = pool.drop(index=assigned_indices)
shortfall = min(50 - len(assigned_indices), len(remaining_pool))
assigned_indices.extend(remaining_pool.head(shortfall).index.tolist())
# Pass 3: Positions 51+ with all remaining artists strictly by score order
final_remaining = pool.drop(index=assigned_indices)
assigned_indices.extend(final_remaining.index.tolist())
# 5. Assign consecutive published ranks
out["hiffi_xp_published_rank"] = pd.NA
rank_series = pd.Series(
range(1, len(assigned_indices) + 1),
index=assigned_indices,
dtype="Int64"
)
out.loc[assigned_indices, "hiffi_xp_published_rank"] = rank_series
out["hiffi_xp_published_rank"] = out["hiffi_xp_published_rank"].astype(int)
if return_metadata: if return_metadata:
return out, shortfall_audit top_10 = out.head(10)
top_50 = out.head(50)
metadata = {
"total_records": len(out),
"top_10_shortfall": bool((top_10["platform_evidence_tier"] < 3).any()),
"top_50_shortfall": bool((top_50["platform_evidence_tier"] < 3).any()),
"tier_distribution": out["platform_evidence_tier"].value_counts().to_dict(),
"reference_time": ref_dt.isoformat(),
}
return out, metadata
return out
def detect_metric_anomalies(self, df: pd.DataFrame) -> pd.DataFrame:
out = df.copy()
flags = []
for _, row in out.iterrows():
row_flags = []
views = float(row.get("lifetime_views") or 0.0)
ig = float(row.get("instagram_followers") or 0.0)
sp_month = float(row.get("spotify_monthly_listeners") or 0.0)
sp_change = float(row.get("spotify_daily_change") or 0.0)
if views > 0 and ig > 0:
if (ig / views) > 10.0:
row_flags.append("SUSPICIOUS_IG_TO_YT_RATIO")
if sp_month > 0 and sp_change > 0:
if (sp_change / sp_month) > 0.20:
row_flags.append("EXTREME_SPOTIFY_VELOCITY_RATIO")
flags.append(";".join(row_flags) if row_flags else "CLEAN")
out["anomaly_flags"] = flags
return out return out
Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.
File diff suppressed because it is too large. Load diff
+880 -880
View File
File diff suppressed because it is too large. Load diff
+106
View File
@@ -0,0 +1,106 @@
import sqlite3
import pandas as pd
import numpy as np
def run_telemetry_drift_audit(
db_path: str = "data/hiffi_catalog.db",
max_daily_listener_drop_pct: float = 25.0,
max_growth_spike_pct: float = 500.0
) -> dict:
"""
Evaluates cohort telemetry against operational drift thresholds:
1. Catastrophic Drops (>25% daily decline on high-tier artists).
2. Anomalous Surges (>500% daily velocity requiring corroboration).
3. Governance Violations (Hak non-null regressions).
4. Null Velocity Drift on populated accounts.
"""
con = sqlite3.connect(db_path)
query = """
SELECT
r.catalog_rank,
a.artist_id,
a.artist_name,
a.youtube_url,
a.spotify_url,
a.instagram_url,
t.subscribers_count,
t.spotify_monthly_listeners,
t.instagram_followers,
t.daily_view_velocity,
t.growth_velocity_7d,
t.momentum_score,
t.spotify_daily_change
FROM rankings r
JOIN artists a ON r.artist_id = a.artist_id
JOIN telemetry_metrics t ON a.artist_id = t.artist_id
WHERE r.catalog_rank BETWEEN 1 AND 500
ORDER BY r.catalog_rank ASC
"""
df = pd.read_sql_query(query, con)
con.close()
anomalies = []
print("=" * 80)
print("HIFFI AUTOMATED DRIFT & VELOCITY AUDIT")
print("=" * 80)
# 1. Governance Drift Check (Hak Tier-1)
hak = df[df["artist_id"] == "@haknyc"]
if not hak.empty:
hak_row = hak.iloc[0]
if pd.notna(hak_row["spotify_url"]) or pd.notna(hak_row["spotify_monthly_listeners"]) or \
pd.notna(hak_row["instagram_url"]) or pd.notna(hak_row["instagram_followers"]):
anomalies.append({
"artist": "Hak",
"type": "GOVERNANCE_BREACH",
"detail": "Hak has non-null values imputed for Spotify/Instagram"
})
# 2. Daily Spotify Velocity Drift
# Calculate % delta: (daily_change / (monthly_listeners - daily_change)) * 100
active_sp = df[df["spotify_monthly_listeners"].notna() & (df["spotify_monthly_listeners"] > 0)].copy()
prev_listeners = active_sp["spotify_monthly_listeners"] - active_sp["spotify_daily_change"]
active_sp["pct_daily_change"] = (active_sp["spotify_daily_change"] / prev_listeners.replace(0, np.nan)) * 100
severe_drops = active_sp[active_sp["pct_daily_change"] < -max_daily_listener_drop_pct]
for _, row in severe_drops.iterrows():
anomalies.append({
"artist": row["artist_name"],
"type": "CATASTROPHIC_DROP",
"detail": f"Daily listener drop of {row['pct_daily_change']:.2f}% (Loss: {int(row['spotify_daily_change'])})"
})
anomalous_spikes = active_sp[active_sp["pct_daily_change"] > max_growth_spike_pct]
for _, row in anomalous_spikes.iterrows():
anomalies.append({
"artist": row["artist_name"],
"type": "UNVERIFIED_SPIKE",
"detail": f"Daily listener surge of {row['pct_daily_change']:.2f}% (Gain: {int(row['spotify_daily_change'])})"
})
# 3. Canonical Schema Drift (Broken URL formats)
yt_bad = df[~df["youtube_url"].fillna("").str.contains(r"^https://www\.youtube\.com/channel/UC[a-zA-Z0-9_-]{22}$", regex=True)]
for _, row in yt_bad.iterrows():
anomalies.append({
"artist": row["artist_name"],
"type": "SCHEMA_DRIFT_YOUTUBE",
"detail": f"Non-canonical YouTube URL: {row['youtube_url']}"
})
print(f"Total Cohort Size Audited: {len(df)}")
print(f"Total Drift Anomalies Flagged: {len(anomalies)}")
if anomalies:
print("\n[CIRCUIT BREAKER TRIGGERED] Anomalies detected:")
for idx, a in enumerate(anomalies, 1):
print(f" {idx}. [{a['type']}] {a['artist']}: {a['detail']}")
print("\nAction: Synchronization to production aborted.")
return {"circuit_breaker": "TRIPPED", "anomalies": anomalies}
else:
print("[CIRCUIT BREAKER: CLEAR] No drift detected. Telemetry velocities healthy.")
print("Action: Safe to synchronize with live production API.")
return {"circuit_breaker": "PASS", "anomalies": []}
if __name__ == "__main__":
run_telemetry_drift_audit()
+1
View File
@@ -117,3 +117,4 @@ def main():
if __name__ == "__main__": if __name__ == "__main__":
main() main()