fix: resolve governance gaps, patch credential exposure, and connect multi-platform dashboard
HIFFI Multi-Platform CI / test (3.12) (push) Successful in 33s
HIFFI Multi-Platform CI / test (3.11) (push) Successful in 40s

This commit is contained in:
Srilekha committed 2026-10-27 10:23:02 +05:30
1 parent 6f2e8e0c9d
commit 7fb7d775d8
5 files changed
+322 -385

No files matched your search

+186 -263
View File
@@ -1,30 +1,30 @@
"""
app.py
HIFFI Artist Intelligence - Executive Dashboard
Clean dark-themed design with aligned dataframes, responsive cards, and scouting dossier.
HIFFI Artist Intelligence - Multi-Platform Executive Dashboard
Clean dark-themed design with multi-platform evidence tiers, Spotify & Instagram telemetry,
breakout velocity index, and deep-dive scouting dossier.
"""
import io
import os
from pathlib import Path
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st
st.set_page_config(
page_title="HIFFI | Artist Intelligence",
page_title="HIFFI | Multi-Platform Intelligence",
page_icon="⚡",
layout="wide",
initial_sidebar_state="expanded",
)
DATA_PATH = "enriched_artists.csv"
DATA_PATH = Path("data/hiffi_multiplatform_500.csv")
FALLBACK_DATA_PATH = Path("enriched_artists.csv")
# Global CSS Polish
st.markdown(
"""
<style>
/* Global Container Spacing */
.block-container {
padding-top: 1.8rem;
padding-bottom: 2.5rem;
@@ -71,7 +71,7 @@ st.markdown(
.podium-silver { border-top: 4px solid #94a3b8; }
.podium-bronze { border-top: 4px solid #f97316; }
/* Health Badges */
/* Health & Tier Badges */
.badge {
display: inline-block;
padding: 3px 8px;
@@ -80,11 +80,11 @@ st.markdown(
font-weight: 700;
text-transform: uppercase;
}
.badge-active { background: rgba(16, 185, 129, 0.15); color: #10b981; }
.badge-evergreen { background: rgba(56, 189, 248, 0.15); color: #38bdf8; }
.badge-dormant { background: rgba(239, 68, 68, 0.15); color: #ef4444; }
.badge-tier3 { background: rgba(16, 185, 129, 0.2); color: #10b981; }
.badge-tier2 { background: rgba(56, 189, 248, 0.2); color: #38bdf8; }
.badge-tier1 { background: rgba(245, 158, 11, 0.2); color: #f59e0b; }
.badge-dormant { background: rgba(239, 68, 68, 0.2); color: #ef4444; }
/* Clean Section Dividers */
.custom-divider {
height: 1px;
background: #1f2937;
@@ -98,24 +98,44 @@ st.markdown(
@st.cache_data(ttl=60)
def load_data():
if not os.path.exists(DATA_PATH):
target = DATA_PATH if DATA_PATH.exists() else FALLBACK_DATA_PATH
if not target.exists():
return pd.DataFrame()
df = pd.read_csv(DATA_PATH)
df = pd.read_csv(target)
# Normalize primary score & rank columns if running from legacy source
if "hiffi_xp_published_rank" not in df.columns:
df["hiffi_xp_published_rank"] = df.get("hiffi_xp_rank", df.get("hiffi_rank", df.index + 1))
if "hiffi_xp_score" not in df.columns:
df["hiffi_xp_score"] = df.get("production_hiffi_score", df.get("hiffi_score", 0.0))
if "platform_evidence_tier" not in df.columns:
has_yt = df.get("production_hiffi_score", df.get("hiffi_score")).notna().astype(int)
has_sp = df.get("spotify_monthly_listeners", pd.Series(index=df.index)).notna().astype(int)
has_ig = df.get("instagram_followers", pd.Series(index=df.index)).notna().astype(int)
df["platform_evidence_tier"] = has_yt + has_sp + has_ig
numeric_cols = [
"hiffi_score",
"hiffi_xp_published_rank",
"hiffi_xp_rank",
"hiffi_xp_score",
"production_hiffi_rank",
"production_hiffi_score",
"platform_evidence_tier",
"breakout_velocity_index",
"subscriber_count",
"daily_view_velocity",
"momentum_30d",
"spotify_monthly_listeners",
"spotify_daily_change",
"instagram_followers",
"confidence_score",
"hiffi_rank",
"breakout_rank",
"breakout_rank"
]
for col in numeric_cols:
if col in df.columns:
df[col] = pd.to_numeric(df[col], errors="coerce")
# Fallback assignment for subgenre if not yet computed in pipeline
if "subgenre" not in df.columns:
def infer_subgenre(r):
subs = r.get("subscriber_count", 0) or 0
@@ -132,7 +152,6 @@ def load_data():
def to_excel_bytes(dataframe: pd.DataFrame) -> bytes:
"""Converts dataframe to downloadable in-memory Excel spreadsheet."""
output = io.BytesIO()
with pd.ExcelWriter(output, engine="openpyxl") as writer:
dataframe.to_excel(writer, index=False, sheet_name="HIFFI_Cohort")
@@ -143,92 +162,71 @@ df = load_data()
# ----------------- Sidebar Controls -----------------
with st.sidebar:
st.markdown("### ⚡ HIFFI Intelligence")
st.caption("Hip-Hop Market Scoring & Breakout Detection")
st.markdown("### ⚡ HIFFI Multi-Platform")
st.caption("YouTube • Spotify • Instagram Analytics")
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
if df.empty:
st.error(f"'{DATA_PATH}' not found. Please run 'python main.py' first!")
st.error("No dataset found. Run 'python run_pipeline.py' to generate master data.")
st.stop()
view_mode = st.radio(
"Leaderboard Perspective:",
["🏆 Overall HIFFI Rank", "🚀 Breakout Velocity (BVI)", "🔍 Master Audit Grid"],
["🏆 Published Multi-Platform Rank", "🚀 Breakout Velocity (BVI)", "🔍 Full Cohort Audit Grid"],
index=0,
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("##### 🛡️ Filters")
st.markdown("##### 🛡️ Cohort Filters")
# Subgenre / Vertical Filter
all_verticals = sorted(df["subgenre"].dropna().unique().tolist())
target_presets = ["Legends", "Trap & Street", "UK Drill", "Lyricists", "Breakout / Emerging"]
vertical_choices = [v for v in target_presets if v in all_verticals] + [v for v in all_verticals if v not in target_presets]
selected_verticals = st.multiselect("Vertical", options=all_verticals, default=all_verticals)
selected_verticals = st.multiselect(
"Hip-Hop Vertical",
options=vertical_choices,
default=vertical_choices,
)
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)
available_statuses = df["health_status"].dropna().unique().tolist()
selected_statuses = st.multiselect(
"Channel Status",
options=available_statuses,
default=available_statuses,
)
min_xp = st.slider("Min HIFFI-XP Score", 0.0, 100.0, 0.0, 5.0)
min_conf = st.slider(
"Min Confidence",
min_value=0.0,
max_value=1.0,
value=0.50,
step=0.05,
)
# Filter logic
filtered_df = df[
(df["health_status"].isin(selected_statuses))
& (df["subgenre"].isin(selected_verticals))
& (df["confidence_score"] >= min_conf)
(df["subgenre"].isin(selected_verticals))
& (df["platform_evidence_tier"].isin(selected_tiers))
& (df["hiffi_xp_score"].fillna(0.0) >= min_xp)
].copy()
# Sidebar Data Export Buttons (Filters applied)
with st.sidebar:
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.markdown("##### 💾 Export Filtered Cohort")
col_csv, col_xlsx = st.columns(2)
csv_payload = filtered_df.to_csv(index=False).encode("utf-8")
col_csv.download_button(
label="📥 CSV",
"📥 CSV",
data=csv_payload,
file_name="hiffi_filtered_cohort.csv",
file_name="hiffi_multiplatform_filtered.csv",
mime="text/csv",
use_container_width=True,
width="stretch",
)
excel_payload = to_excel_bytes(filtered_df)
col_xlsx.download_button(
label="📊 Excel",
"📊 Excel",
data=excel_payload,
file_name="hiffi_filtered_cohort.xlsx",
file_name="hiffi_multiplatform_filtered.xlsx",
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
use_container_width=True,
width="stretch",
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.caption("Momentum (35%) • Velocity (30%) • Reach (20%) • Cadence (15%)")
st.caption("Weights: YouTube (50%) • Spotify (30%) • Instagram (20%)")
# ----------------- Main Header -----------------
head_col1, head_col2 = st.columns([3, 1])
with head_col1:
st.markdown("<h2 style='margin-bottom: 2px;'>HIFFI Artist Intelligence Hub</h2>", unsafe_allow_html=True)
st.markdown("<p style='color: #9ca3af; font-size: 0.92rem; margin-top: 0;'>Cross-platform tracking of catalog sustainability and audience breakout velocity.</p>", unsafe_allow_html=True)
st.markdown("<h2 style='margin-bottom: 2px;'>HIFFI Multi-Platform 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 and dynamic weight redistribution.</p>", unsafe_allow_html=True)
with head_col2:
st.markdown(
f"<div style='text-align: right; color: #6b7280; font-size: 0.8rem; padding-top: 10px;'>Active Cohort: <b>{len(filtered_df)} / {len(df)}</b> artists</div>",
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>",
unsafe_allow_html=True,
)
@@ -241,46 +239,46 @@ with k1:
<div class="kpi-box">
<div class="kpi-title">Tracked Artists</div>
<div class="kpi-value">{len(df)}</div>
<div class="kpi-subtitle">Monitored pipeline size</div>
<div class="kpi-subtitle">Monitored Cohort</div>
</div>
""",
unsafe_allow_html=True,
)
with k2:
active_count = len(df[df["health_status"] == "ACTIVE_HEALTHY"])
t3_count = len(df[df["platform_evidence_tier"] == 3])
st.markdown(
f"""
<div class="kpi-box">
<div class="kpi-title" style="color: #10b981;">Active Healthy</div>
<div class="kpi-value" style="color: #10b981;">{active_count}</div>
<div class="kpi-subtitle">Recent uploads & steady traction</div>
<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,
)
with k3:
evergreen_count = len(df[df["health_status"] == "CATALOG_EVERGREEN"])
sp_count = df["spotify_monthly_listeners"].notna().sum()
st.markdown(
f"""
<div class="kpi-box">
<div class="kpi-title" style="color: #38bdf8;">Catalog Evergreen</div>
<div class="kpi-value" style="color: #38bdf8;">{evergreen_count}</div>
<div class="kpi-subtitle">Strong passive catalog streams</div>
<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 k4:
zombies = len(df[df["health_status"] == "DORMANT_CATALOG"])
ig_count = df["instagram_followers"].notna().sum()
st.markdown(
f"""
<div class="kpi-box">
<div class="kpi-title" style="color: #ef4444;">Dormant / Excluded</div>
<div class="kpi-value" style="color: #ef4444;">{zombies}</div>
<div class="kpi-subtitle">Disqualified (&lt;NA&gt; Rank)</div>
<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,
@@ -288,31 +286,24 @@ with k4:
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
# ----------------- Top 3 Leaders Podium -----------------
top3 = (
filtered_df[filtered_df["is_rank_eligible"] == True]
.sort_values(by="hiffi_rank", ascending=True)
.head(3)
)
# ----------------- Podium -----------------
top3 = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).head(3)
if len(top3) >= 3:
st.markdown("#### 👑 Market Leaders")
st.markdown("#### 👑 Market Leaders (Published Rankings)")
p1, p2, p3 = st.columns(3)
podium_data = [
(p1, top3.iloc[0], "🥇 RANK #1", "podium-gold", "#f59e0b"),
(p2, top3.iloc[1], "🥈 RANK #2", "podium-silver", "#94a3b8"),
(p3, top3.iloc[2], "🥉 RANK #3", "podium-bronze", "#f97316"),
(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:
badge_cls = (
"badge-active"
if row_data["health_status"] == "ACTIVE_HEALTHY"
else "badge-evergreen"
)
bvi_str = f"{row_data['breakout_velocity_index']:.1f}" if pd.notna(row_data.get("breakout_velocity_index")) else "N/A"
vel_str = f"{int(row_data['daily_view_velocity']):,}" if pd.notna(row_data.get("daily_view_velocity")) else "N/A"
tier_val = int(row_data.get("platform_evidence_tier", 1))
badge_cls = f"badge-tier{tier_val}"
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(
@@ -320,22 +311,22 @@ if len(top3) >= 3:
<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_cls}">{row_data['health_status'].replace('_', ' ')}</span>
<span class="badge {badge_cls}">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: flex; justify-content: space-between; background: #0b0f17; padding: 10px 14px; border-radius: 8px;">
<div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">HIFFI Score</div>
<div style="font-size: 1.25rem; font-weight: 800; color: {accent_color};">{row_data['hiffi_score']:.1f}</div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">HIFFI-XP</div>
<div style="font-size: 1.25rem; font-weight: 800; color: {accent_color};">{row_data['hiffi_xp_score']:.2f}</div>
</div>
<div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">Breakout Index</div>
<div style="font-size: 1.25rem; font-weight: 800; color: #a855f7;">{bvi_str}</div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">Spotify Monthly</div>
<div style="font-size: 1.05rem; font-weight: 700; color: #38bdf8;">{sp_str}</div>
</div>
<div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">Daily Views</div>
<div style="font-size: 1.05rem; font-weight: 700; color: #e5e7eb;">{vel_str}</div>
<div style="font-size: 0.68rem; color: #9ca3af; text-transform: uppercase;">IG Followers</div>
<div style="font-size: 1.05rem; font-weight: 700; color: #e5e7eb;">{ig_str}</div>
</div>
</div>
</div>
@@ -348,233 +339,165 @@ if len(top3) >= 3:
# ----------------- Leaderboard Grid -----------------
st.markdown(f"#### 📋 {view_mode}")
if view_mode == "🏆 Overall HIFFI Rank":
table_df = (
filtered_df[filtered_df["is_rank_eligible"] == True]
.sort_values(by="hiffi_rank", ascending=True)
.copy()
)
if view_mode == "🏆 Published Multi-Platform Rank":
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
cols = [
"hiffi_rank",
"hiffi_xp_published_rank",
"hiffi_xp_rank",
"name",
"subgenre",
"hiffi_score",
"breakout_rank",
"breakout_velocity_index",
"daily_view_velocity",
"momentum_30d",
"health_status",
"platform_evidence_tier",
"hiffi_xp_score",
"spotify_monthly_listeners",
"instagram_followers",
"effective_weights",
"anomaly_flags"
]
elif view_mode == "🚀 Breakout Velocity (BVI)":
table_df = (
filtered_df[
(filtered_df["is_rank_eligible"] == True)
& (filtered_df["breakout_rank"].notna())
]
.sort_values(by="breakout_rank", ascending=True)
filtered_df[filtered_df["breakout_velocity_index"].notna()]
.sort_values(by="breakout_velocity_index", ascending=False)
.copy()
)
cols = [
"breakout_rank",
"name",
"subgenre",
"breakout_velocity_index",
"hiffi_rank",
"hiffi_score",
"hiffi_xp_published_rank",
"daily_view_velocity",
"momentum_30d",
"subscriber_count",
"health_status",
"subgenre"
]
else:
table_df = filtered_df.sort_values(by=["is_rank_eligible", "hiffi_score"], ascending=[False, False]).copy()
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
cols = [
"hiffi_xp_published_rank",
"name",
"subgenre",
"hiffi_score",
"breakout_velocity_index",
"daily_view_velocity",
"subscriber_count",
"health_status",
"confidence_score",
"is_rank_eligible",
"production_hiffi_rank",
"hiffi_xp_rank",
"rank_delta",
"platform_evidence_tier",
"hiffi_xp_score",
"spotify_monthly_listeners",
"instagram_followers",
"evidence_exclusions",
"anomaly_flags"
]
# Keep only columns that exist in DataFrame to prevent KeyError
render_cols = [c for c in cols if c in table_df.columns]
st.dataframe(
table_df[render_cols].reset_index(drop=True),
use_container_width=True,
height=290,
width="stretch",
height=320,
column_config={
"hiffi_rank": st.column_config.NumberColumn("Rank", format="%d", width="small"),
"breakout_rank": st.column_config.NumberColumn("BVI Rank", format="%d", width="small"),
"hiffi_xp_published_rank": st.column_config.NumberColumn("Pub Rank", format="%d", width="small"),
"hiffi_xp_rank": st.column_config.NumberColumn("Raw XP Rank", format="%d", width="small"),
"production_hiffi_rank": st.column_config.NumberColumn("YT Rank", format="%d", width="small"),
"platform_evidence_tier": st.column_config.NumberColumn("Tier", format="%d", width="small"),
"name": st.column_config.TextColumn("Artist", width="medium"),
"subgenre": st.column_config.TextColumn("Vertical", width="medium"),
"hiffi_score": st.column_config.ProgressColumn(
"HIFFI Score",
format="%.1f",
min_value=0,
max_value=100,
width="medium",
),
"breakout_velocity_index": st.column_config.ProgressColumn(
"Breakout (BVI)",
format="%.1f",
min_value=0,
max_value=100,
width="medium",
),
"daily_view_velocity": st.column_config.NumberColumn(
"Daily Views / Day",
format="%,d",
width="medium",
),
"momentum_30d": st.column_config.NumberColumn(
"30d Momentum",
format="%.1f%%",
width="small",
),
"subscriber_count": st.column_config.NumberColumn(
"Subscribers",
format="%,d",
width="medium",
),
"health_status": st.column_config.TextColumn("Status", width="medium"),
"confidence_score": st.column_config.NumberColumn("Confidence", format="%.2f", width="small"),
"is_rank_eligible": st.column_config.CheckboxColumn("Eligible", width="small"),
"hiffi_xp_score": st.column_config.ProgressColumn("HIFFI-XP Score", format="%.2f", min_value=0, max_value=100, width="medium"),
"breakout_velocity_index": st.column_config.ProgressColumn("BVI", format="%.1f", min_value=0, max_value=100, width="small"),
"spotify_monthly_listeners": st.column_config.NumberColumn("Spotify Monthly", format="%,d", width="medium"),
"instagram_followers": st.column_config.NumberColumn("IG Followers", format="%,d", width="medium"),
"effective_weights": st.column_config.TextColumn("Dynamic Redistribution", width="large"),
"anomaly_flags": st.column_config.TextColumn("Anomaly Status", width="medium"),
"evidence_exclusions": st.column_config.TextColumn("Exclusions", width="large"),
},
)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
# ----------------- Visual Insights Charts -----------------
st.markdown("#### 📊 Market Landscape Visualizer")
st.markdown("#### 📊 Multi-Platform Analytics")
c_left, c_right = st.columns(2)
with c_left:
scatter_df = filtered_df[filtered_df["is_rank_eligible"] == True].copy()
fig_scatter = px.scatter(
scatter_df,
x="daily_view_velocity",
y="momentum_30d",
size="subscriber_count",
color="health_status",
color_discrete_map={
"ACTIVE_HEALTHY": "#10b981",
"CATALOG_EVERGREEN": "#38bdf8",
"DORMANT_CATALOG": "#ef4444",
},
hover_name="name",
hover_data={"daily_view_velocity": ":,d", "momentum_30d": ":.1f%"},
log_x=True,
title="View Velocity vs. 30-Day Momentum",
labels={
"daily_view_velocity": "Daily Views / Day (Log Scale)",
"momentum_30d": "30-Day Growth (%)",
"health_status": "Vitality",
},
template="plotly_dark",
)
fig_scatter.update_layout(
paper_bgcolor="#111827",
plot_bgcolor="#111827",
font=dict(color="#9ca3af"),
margin=dict(l=20, r=20, t=40, b=20),
height=320,
)
st.plotly_chart(fig_scatter, use_container_width=True)
chart_df = filtered_df.dropna(subset=["spotify_monthly_listeners", "instagram_followers", "hiffi_xp_score"]).copy()
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"),
margin=dict(l=20, r=20, t=40, b=20),
height=320,
)
st.plotly_chart(fig_scatter, width="stretch")
else:
st.info("Insufficient complete data points to render multi-platform scatter plot.")
with c_right:
bvi_df = (
filtered_df[filtered_df["breakout_velocity_index"].notna()]
.sort_values(by="breakout_velocity_index", ascending=True)
)
fig_bar = px.bar(
bvi_df,
x="breakout_velocity_index",
y="name",
orientation="h",
color="breakout_velocity_index",
color_continuous_scale="Purples",
title="Breakout Velocity Index (BVI)",
labels={"breakout_velocity_index": "BVI Score", "name": "Artist"},
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_bar.update_layout(
fig_pie.update_layout(
paper_bgcolor="#111827",
plot_bgcolor="#111827",
font=dict(color="#9ca3af"),
coloraxis_showscale=False,
margin=dict(l=20, r=20, t=40, b=20),
height=320,
)
st.plotly_chart(fig_bar, use_container_width=True)
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
st.plotly_chart(fig_pie, width="stretch")
# ----------------- Deep Dive Scouting Dossier -----------------
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]
# Resolve YouTube channel direct link
yt_url = str(row.get("youtube_url", "")).strip()
yt_id = str(row.get("channel_id", row.get("youtube_channel_id", ""))).strip()
if (not yt_url or yt_url == "nan") and yt_id and yt_id != "nan":
if yt_id.startswith("UC"):
yt_url = f"https://www.youtube.com/channel/{yt_id}"
elif yt_id.startswith("@"):
yt_url = f"https://www.youtube.com/{yt_id}"
else:
yt_url = f"https://www.youtube.com/@{yt_id}"
d1, d2, d3, d4 = st.columns(4)
with d1:
h_val = row.get("hiffi_score")
st.metric("HIFFI Score", f"{h_val:.1f}" if pd.notna(h_val) else "N/A")
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:
b_val = row.get("breakout_velocity_index")
st.metric("Breakout Index (BVI)", f"{b_val:.1f}" if pd.notna(b_val) else "N/A")
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:
s_val = row.get("subscriber_count")
st.metric("Subscribers", f"{int(s_val):,}" if pd.notna(s_val) else "N/A")
sp_l = row.get("spotify_monthly_listeners")
st.metric("Spotify Listeners", f"{int(sp_l):,}" if pd.notna(sp_l) else "Unindexed (NaN)")
with d4:
conf = row.get("confidence_score", 0.0)
tier = row.get("confidence_tier", "Standard")
st.metric("Entity Confidence", f"{conf * 100:.0f}%", delta=tier, delta_color="normal")
ig_f = row.get("instagram_followers")
st.metric("Instagram Followers", f"{int(ig_f):,}" if pd.notna(ig_f) else "Unindexed (NaN)")
badge_cls = (
"badge-active"
if row.get("health_status") == "ACTIVE_HEALTHY"
else "badge-evergreen"
if row.get("health_status") == "CATALOG_EVERGREEN"
else "badge-dormant"
)
# Dossier info box with integrated Official YouTube Button
st.markdown(
f"""
<div style="background: #111827; padding: 14px 18px; border-radius: 8px; border: 1px solid #1f2937; margin-top: 10px; display: flex; justify-content: space-between; align-items: center; font-size: 0.85rem;">
<div style="display: flex; gap: 24px; align-items: center;">
<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>Vertical:</b> <span style="color: #a855f7;">{row.get('subgenre', 'N/A')}</span></div>
<div><b>Channel:</b> {row.get('channel_title', 'Unknown')}</div>
<div><b>Status:</b> <span class="badge {badge_cls}">{row.get('health_status', 'UNKNOWN')}</span></div>
<div><b>Zombie Flag:</b> {'⚠️ Yes' if row.get('is_zombie') else '✅ Clean'}</div>
<div><b>Eligible for Rank:</b> {'✅ Yes' if row.get('is_rank_eligible') else '❌ Disqualified'}</div>
<div><b>Evidence Tier:</b> Tier {int(row.get('platform_evidence_tier', 1))}</div>
<div><b>Instagram Status:</b> {row.get('instagram_verification_status', 'N/A')}</div>
<div><b>Spotify Source:</b> {row.get('spotify_data_source', 'N/A')}</div>
<div><b>Effective Weights:</b> {row.get('effective_weights', 'N/A')}</div>
<div><b>Anomaly Flags:</b> {row.get('anomaly_flags', 'CLEAN')}</div>
</div>
</div>
""",
unsafe_allow_html=True,
)
st.write("")
if yt_url and yt_url != "nan":
st.link_button("▶ Open Official YouTube Channel", yt_url, use_container_width=False)
else:
st.caption("⚠️ No verified YouTube channel link available for this artist.")
+20 -4
View File
@@ -4,16 +4,28 @@ Direct diagnostic to test Spotify OAuth2 Client Credentials flow
and probe endpoint permissions for Artist data, Top Tracks, and Search.
"""
import os
import requests
import base64
import os
from pathlib import Path
import sys
import requests
from dotenv import load_dotenv
CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID", "a4a008ec790d45fba6a37315a1a3d847")
CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET", "a5c67d9a63d340b6ae4a5bec83bca241")
PROJECT_ROOT = Path(__file__).resolve().parent.parent
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
load_dotenv(PROJECT_ROOT / ".env")
CLIENT_ID = os.getenv("SPOTIFY_CLIENT_ID", "").strip()
CLIENT_SECRET = os.getenv("SPOTIFY_CLIENT_SECRET", "").strip()
def get_token(client_id: str, client_secret: str):
"""Requests a bearer token via standard OAuth2 Client Credentials."""
if not client_id or not client_secret:
return 0, {"error": "SPOTIFY_CLIENT_ID or SPOTIFY_CLIENT_SECRET not configured"}
auth_header = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
url = "https://accounts.spotify.com/api/token"
res = requests.post(
@@ -50,6 +62,10 @@ if __name__ == "__main__":
print("SPOTIFY API AUTHENTICATION & ENDPOINT PERMISSION DIAGNOSTIC")
print("=" * 80)
if not CLIENT_ID or not CLIENT_SECRET:
print("Error: Missing credentials in environment. Set SPOTIFY_CLIENT_ID and SPOTIFY_CLIENT_SECRET in .env.")
sys.exit(1)
status, token_resp = get_token(CLIENT_ID, CLIENT_SECRET)
print(f"Token Endpoint Status: {status}")
+39 -22
View File
@@ -12,8 +12,8 @@ Ranks artists according to HIFFI formula stages:
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 and UTC data freshness thresholds.
Fail-closed on missing timestamp columns or unverified statuses.
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
@@ -26,7 +26,7 @@ class HIFFIRankingEngine:
@staticmethod
def _log_scale(series: pd.Series) -> pd.Series:
valid = series.dropna()
log_vals = np.log10(1.0 + np.maximum(valid, 0.0))
log_vals = np.log1p(np.maximum(valid.astype(float), 0.0))
s_min, s_max = log_vals.min(), log_vals.max()
if np.isclose(s_max, s_min) or pd.isna(s_min):
return pd.Series(0.0, index=series.index)
@@ -70,13 +70,15 @@ class HIFFIRankingEngine:
"""
out_bvi = pd.Series(np.nan, index=df.index, dtype=float)
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)
& (df["status_flag"] != "INACTIVE_OR_DORMANT")
& (df["status_flag"] != "FAILED_LOOKUP")
& (status_col != "INACTIVE_OR_DORMANT")
& (status_col != "FAILED_LOOKUP")
)
if not valid_mask.any():
@@ -123,29 +125,31 @@ class HIFFIRankingEngine:
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()
& (out["status_flag"] != "FAILED_LOOKUP")
& (status_col != "FAILED_LOOKUP")
)
out["is_rank_eligible"] = (
has_baseline
& (out["confidence_score"] >= min_confidence)
& (out["status_flag"].isin(["COMPLETE", "CATALOG_ACTIVE_NO_RECENT_UPLOAD"]))
& (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)
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)
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))
log_lifetime = self._log_scale(out["lifetime_views"].fillna(0.0))
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))
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
@@ -154,7 +158,7 @@ class HIFFIRankingEngine:
+ 0.15 * score_consistency
) * 100.0
conf_multiplier = 0.8 + 0.2 * out["confidence_score"].clip(0.0, 1.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"]
@@ -183,6 +187,12 @@ class HIFFIRankingEngine:
"""
out = df.copy()
# Schema aliasing for raw input fallbacks
if "production_hiffi_score" not in out.columns and "hiffi_score" in out.columns:
out["production_hiffi_score"] = out["hiffi_score"]
if "production_hiffi_rank" not in out.columns and "hiffi_rank" in out.columns:
out["production_hiffi_rank"] = out["hiffi_rank"]
base_weights = {
"yt": 0.50,
"sp_monthly": 0.25,
@@ -311,7 +321,8 @@ class HIFFIRankingEngine:
- 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.
- Fail-closed: missing timestamp columns or unverified statuses evaluate
- 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).
@@ -321,8 +332,11 @@ class HIFFIRankingEngine:
exclusions = [[] for _ in range(len(out))]
# 1. YouTube Validation (Score present + Freshness <= 7 days)
has_yt_val = out["production_hiffi_score"].notna()
# 1. YouTube Validation (Score present + status not FAILED_LOOKUP + Freshness <= 7 days)
status_col = out["status_flag"] if "status_flag" in out.columns else pd.Series("COMPLETE", index=out.index)
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)
@@ -330,13 +344,16 @@ class HIFFIRankingEngine:
else:
yt_fresh = pd.Series(False, index=out.index)
for idx, (val, fresh) in enumerate(zip(has_yt_val, yt_fresh)):
if val and not fresh:
for idx, (val, failed, fresh) in enumerate(zip(has_yt_val, is_lookup_failed, yt_fresh)):
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 & yt_fresh
valid_yt = has_yt_val & (~is_lookup_failed) & yt_fresh
# 2. Spotify Validation (Telemetry present + Freshness <= 7 days)
has_sp_val = out["spotify_monthly_listeners"].notna()
has_sp_val = out["spotify_monthly_listeners"].notna() if "spotify_monthly_listeners" in out.columns else pd.Series(False, index=out.index)
if "spotify_updated_at" in out.columns:
sp_fresh = out["spotify_updated_at"].apply(
lambda x: self._is_signal_fresh(x, max_days=7.0, reference_time=ref_time)
@@ -350,7 +367,7 @@ class HIFFIRankingEngine:
valid_sp = has_sp_val & sp_fresh
# 3. Instagram Validation (Followers present + Approved Status + Freshness <= 14 days)
has_ig_val = out["instagram_followers"].notna()
has_ig_val = out["instagram_followers"].notna() if "instagram_followers" in out.columns else pd.Series(False, index=out.index)
verified_statuses = {"AUTOMATED_VERIFIED", "MANUAL_VERIFIED"}
if "instagram_verification_status" in out.columns:
+18 -66
View File
@@ -11,7 +11,6 @@ from pathlib import Path
import sys
from typing import Dict, Optional
import numpy as np
import pandas as pd
PROJECT_ROOT = Path(__file__).resolve().parent.parent
@@ -22,6 +21,7 @@ from collectors.instagram_verifier import InstagramCandidateVerifier
from collectors.kworb_spotify_collector import KworbStreamingCollector
from collectors.spotify_api_collector import SpotifyAPICollector
from collectors.spotify_fallback_collector import SpotifyFallbackCollector
from core.ranking_engine import HIFFIRankingEngine
DATA_DIR = PROJECT_ROOT / "data"
BASE_DATASET = DATA_DIR / "hiffi_multiplatform_500.csv"
@@ -39,70 +39,16 @@ def load_handle_overrides() -> Dict[str, object]:
return {}
def _normalize_metric(series: pd.Series) -> pd.Series:
"""
Direct array-based log-scaling and min-max normalization to [0.0, 1.0].
Preserves index alignment and avoids dictionary/map collisions.
"""
clean_series = pd.to_numeric(series, errors="coerce")
out = pd.Series(0.0, index=series.index, dtype=float)
valid_mask = clean_series.notna() & (clean_series > 0)
if not valid_mask.any():
return out
log_vals = np.log1p(clean_series[valid_mask])
min_v, max_v = log_vals.min(), log_vals.max()
if max_v > min_v:
out.loc[valid_mask] = (log_vals - min_v) / (max_v - min_v)
else:
out.loc[valid_mask] = 1.0
return out
def calculate_hiffi_xp_scores(df: pd.DataFrame) -> pd.DataFrame:
"""
Computes experimental multi-platform HIFFI-XP scores and ranks.
Strictly preserves production_hiffi_rank and production_hiffi_score.
Weights: 50% YouTube production baseline, 30% Spotify telemetry, 20% Instagram reach.
Delegates to the canonical HIFFIRankingEngine to enforce zero-imputation,
proportional dynamic redistribution, anomaly detection, and publication eligibility gates.
"""
out = df.copy()
# Ensure baseline columns exist
if "production_hiffi_score" not in out.columns and "hiffi_score" in out.columns:
out["production_hiffi_score"] = out["hiffi_score"]
if "production_hiffi_rank" not in out.columns and "hiffi_rank" in out.columns:
out["production_hiffi_rank"] = out["hiffi_rank"]
# 1. YouTube production baseline signal (0.0 to 1.0)
yt_score = pd.to_numeric(out.get("production_hiffi_score", out.get("hiffi_score", 0)), errors="coerce").fillna(0.0)
# If production scores are already on a 0-100 scale, normalize directly
if yt_score.max() > 1.0:
yt_norm = (yt_score / 100.0).clip(0.0, 1.0)
else:
yt_norm = _normalize_metric(yt_score)
# 2. Spotify streaming signal (0.0 to 1.0)
sp_norm = _normalize_metric(out.get("spotify_monthly_listeners"))
# 3. Instagram reach signal (0.0 to 1.0)
ig_norm = _normalize_metric(out.get("instagram_followers"))
# Multi-platform composite weighting: 50% YT + 30% SP + 20% IG
xp_composite = (0.50 * yt_norm + 0.30 * sp_norm + 0.20 * ig_norm) * 100.0
out["hiffi_xp_score"] = xp_composite.clip(lower=0.0, upper=100.0).round(2)
# Monotonic ranking (higher score -> lower rank integer)
out["hiffi_xp_rank"] = out["hiffi_xp_score"].rank(ascending=False, method="min").astype(int)
# Calculate rank delta compared to YouTube production baseline
if "production_hiffi_rank" in out.columns:
prod_rank = pd.to_numeric(out["production_hiffi_rank"], errors="coerce")
out["rank_delta"] = prod_rank - out["hiffi_xp_rank"]
return out
engine = HIFFIRankingEngine()
scored = engine.calculate_hiffi_xp(df)
flagged = engine.detect_metric_anomalies(scored)
gated = engine.apply_publication_eligibility_gates(flagged)
return gated
def run_multiplatform_pipeline(
@@ -125,6 +71,12 @@ def run_multiplatform_pipeline(
initial_ig_verified = df["instagram_followers"].notna().sum() if "instagram_followers" in df.columns else 0
print(f"[Loader] Loaded {len(df)} checkpoints ({initial_ig_verified} verified follower records).")
now_utc = datetime.now(timezone.utc).isoformat()
# Ensure baseline YouTube timestamps exist for freshness validation
if "youtube_updated_at" not in df.columns or df["youtube_updated_at"].isna().all():
df["youtube_updated_at"] = df.get("updated_at", now_utc)
# 1. Instagram Candidate Verification & Overrides Ingestion
print("\n[Phase 1] Executing Automated Instagram Verification & Provenance...")
overrides = load_handle_overrides()
@@ -137,8 +89,6 @@ def run_multiplatform_pipeline(
ig_evidence = []
ig_dates = []
now_utc = datetime.now(timezone.utc).isoformat()
for _, row in df.iterrows():
name = str(row["name"]).strip()
override = overrides.get(name)
@@ -169,7 +119,7 @@ def run_multiplatform_pipeline(
ver_res = verifier.evaluate_candidate(
artist_name=name,
candidate_handle=cand_handle,
follower_count=cand_followers if pd.notna(cand_followers) else 0
follower_count=cand_followers if pd.notna(cand_followers) else None
)
elif hasattr(verifier, "verify_candidate"):
ver_res = verifier.verify_candidate(
@@ -199,6 +149,7 @@ def run_multiplatform_pipeline(
df["instagram_verification_status"] = ig_status
df["instagram_audit_evidence"] = ig_evidence
df["instagram_collected_at"] = ig_dates
df["instagram_updated_at"] = ig_dates
# 2. Spotify Multi-Tier Telemetry Ingestion (Tier 1: Kworb -> Tier 2: Official API -> Tier 3: Embed Fallback)
print("\n[Phase 2] Ingesting Spotify Telemetry (Tier 1: Kworb -> Tier 2: Official API -> Tier 3: Embed Fallback)...")
@@ -264,8 +215,9 @@ def run_multiplatform_pipeline(
df["spotify_data_source"] = sp_source
df["spotify_status"] = sp_status
df["spotify_collected_at"] = sp_dates
df["spotify_updated_at"] = sp_dates
# 3. HIFFI-XP Score Normalization & Monotonic Ranking
# 3. Canonical Scoring & Governance
scored_df = calculate_hiffi_xp_scores(df)
# Save finalized cohort
+59 -30
View File
@@ -19,6 +19,7 @@ if str(PROJECT_ROOT) not in sys.path:
from core.ranking_engine import HIFFIRankingEngine
DATASET_PATH = PROJECT_ROOT / "data" / "hiffi_multiplatform_500.csv"
DATASET_ANCHOR_TIME = pd.Timestamp("2026-10-26T12:00:00Z")
@pytest.fixture(scope="module")
@@ -170,15 +171,22 @@ def test_instagram_verifier_suspicious_follower_count():
assert followers is None or status != "AUTOMATED_VERIFIED"
def test_spotify_fallback_null_safety():
def test_spotify_fallback_null_safety(monkeypatch):
"""Verify Spotify embed fallback safely returns structured null dictionary on unindexed items."""
from collectors.spotify_fallback_collector import SpotifyFallbackCollector
# Mock unindexed response to avoid brittle offline network failures
class MockResponse:
status_code = 404
text = "Not Found"
monkeypatch.setattr("requests.Session.get", lambda *args, **kwargs: MockResponse())
fallback = SpotifyFallbackCollector()
res = fallback.fetch_artist_fallback("Unknown Artist", "spotify:artist:invalid_or_missing_id_999999")
assert res["spotify_monthly_listeners"] is None
assert res["spotify_daily_change"] is None
assert "UNAVAILABLE" in str(res["spotify_status"]) or "EMBED" in str(res["spotify_status"])
assert "NOT_FOUND" in str(res["spotify_status"]) or "UNAVAILABLE" in str(res["spotify_status"])
def test_spotify_fallback_mocked_network_error(monkeypatch):
@@ -294,9 +302,7 @@ def test_score_dominance(dataset):
def test_kendall_tau_diagnostic(dataset):
"""
Evaluates rank correlation between baseline and composite rank as a diagnostic.
"""
"""Evaluates rank correlation between baseline and composite rank as a diagnostic."""
from scipy.stats import kendalltau
valid = dataset.dropna(subset=["production_hiffi_rank", "hiffi_xp_rank"]).copy()
@@ -316,10 +322,7 @@ def test_score_quantile_spread(dataset):
def test_raw_dataset_strict_nan_retention(dataset):
"""
Verifies that unindexed secondary fields preserve genuine nulls
and guards against synthetic zeroes or string-serialized nulls ('nan', 'none', 'null').
"""
"""Verifies that unindexed secondary fields preserve genuine nulls and guard against synthetic zeroes."""
secondary_metric_cols = [
"spotify_monthly_listeners",
"spotify_daily_change",
@@ -329,16 +332,13 @@ def test_raw_dataset_strict_nan_retention(dataset):
for col in secondary_metric_cols:
if col in dataset.columns:
series = dataset[col]
# 1. Ensure genuine NaN values exist for unindexed artists
assert series.isna().any(), f"Column '{col}' contains zero null values across cohort."
# 2. Guard against string serialization traps ('nan', 'None', 'null')
string_nulls = series.dropna().astype(str).str.strip().str.lower().isin(
["nan", "none", "null", "undefined", "n/a"]
)
assert not string_nulls.any(), f"Column '{col}' contains string-serialized null tokens."
# 3. Guard against numerical zero substitution
numeric_vals = pd.to_numeric(series.dropna(), errors="coerce")
if col in ["spotify_monthly_listeners", "instagram_followers"]:
zero_records = (numeric_vals == 0).sum()
@@ -349,9 +349,9 @@ def test_raw_dataset_strict_nan_retention(dataset):
def test_publication_eligibility_gates(dataset):
"""Verifies that publication gates enforce evidence tiers without altering raw scores."""
"""Verifies that publication gates enforce evidence tiers using anchored timestamps without mutating raw scores."""
engine = HIFFIRankingEngine()
gated = engine.apply_publication_eligibility_gates(dataset)
gated = engine.apply_publication_eligibility_gates(dataset, reference_time=DATASET_ANCHOR_TIME)
# 1. Raw scores must remain unchanged
assert (gated["hiffi_xp_score"] == dataset["hiffi_xp_score"]).all(), (
@@ -380,14 +380,44 @@ def test_publication_eligibility_gates(dataset):
assert ranks == expected_ranks, "Published ranks contain gaps or duplicates."
def test_publication_gate_blocks_failed_youtube_lookup():
"""Confirms artists with status_flag == 'FAILED_LOOKUP' are disqualified from YouTube evidence credit."""
engine = HIFFIRankingEngine()
now_iso = DATASET_ANCHOR_TIME.isoformat()
test_df = pd.DataFrame([
{
"name": "Failed Lookup Artist",
"status_flag": "FAILED_LOOKUP",
"production_hiffi_score": 7.98,
"youtube_updated_at": now_iso,
"spotify_monthly_listeners": 25_000_000,
"spotify_updated_at": now_iso,
"instagram_followers": 10_000_000,
"instagram_verification_status": "AUTOMATED_VERIFIED",
"instagram_updated_at": now_iso,
"hiffi_xp_score": 85.0,
"hiffi_xp_rank": 1
}
])
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=DATASET_ANCHOR_TIME)
row = gated.iloc[0]
# Blocked from YouTube -> only Spotify + IG valid -> Tier 2
assert row["platform_evidence_tier"] == 2
assert "FAILED_YOUTUBE_LOOKUP" in row["evidence_exclusions"]
def test_publication_gate_blocks_unverified_identity():
"""Confirms that UNCERTAIN_NEEDS_REVIEW status does not count toward evidence tiers."""
engine = HIFFIRankingEngine()
now_iso = pd.Timestamp.now(tz="UTC").isoformat()
now_iso = DATASET_ANCHOR_TIME.isoformat()
test_df = pd.DataFrame([
{
"name": "Artist Pending Verification",
"status_flag": "COMPLETE",
"production_hiffi_score": 95.0,
"youtube_updated_at": now_iso,
"spotify_monthly_listeners": 40_000_000,
@@ -400,6 +430,7 @@ def test_publication_gate_blocks_unverified_identity():
},
{
"name": "Artist Fully Verified",
"status_flag": "COMPLETE",
"production_hiffi_score": 90.0,
"youtube_updated_at": now_iso,
"spotify_monthly_listeners": 35_000_000,
@@ -412,7 +443,7 @@ def test_publication_gate_blocks_unverified_identity():
}
])
gated = engine.apply_publication_eligibility_gates(test_df)
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=DATASET_ANCHOR_TIME)
pending_row = gated[gated["name"] == "Artist Pending Verification"].iloc[0]
verified_row = gated[gated["name"] == "Artist Fully Verified"].iloc[0]
@@ -423,11 +454,12 @@ def test_publication_gate_blocks_unverified_identity():
def test_publication_gate_missing_column_fails_closed():
"""Confirms that omitting the verification column fails closed (gives 0 IG credit)."""
engine = HIFFIRankingEngine()
now_iso = pd.Timestamp.now(tz="UTC").isoformat()
now_iso = DATASET_ANCHOR_TIME.isoformat()
test_df = pd.DataFrame([
{
"name": "Artist No Status Column",
"status_flag": "COMPLETE",
"production_hiffi_score": 95.0,
"youtube_updated_at": now_iso,
"spotify_monthly_listeners": 40_000_000,
@@ -439,18 +471,19 @@ def test_publication_gate_missing_column_fails_closed():
}
])
gated = engine.apply_publication_eligibility_gates(test_df)
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=DATASET_ANCHOR_TIME)
assert gated.loc[0, "platform_evidence_tier"] == 2
def test_publication_gate_zero_multiplatform_cohort():
"""Confirms an entirely Tier 1 cohort produces consecutive ranks and flags an audited shortfall."""
engine = HIFFIRankingEngine()
now_iso = pd.Timestamp.now(tz="UTC").isoformat()
now_iso = DATASET_ANCHOR_TIME.isoformat()
records = [
{
"name": f"Solo Artist {i+1}",
"status_flag": "COMPLETE",
"production_hiffi_score": 85.0 - i,
"youtube_updated_at": now_iso,
"spotify_monthly_listeners": np.nan,
@@ -465,13 +498,10 @@ def test_publication_gate_zero_multiplatform_cohort():
]
tier1_df = pd.DataFrame(records)
gated, audit = engine.apply_publication_eligibility_gates(tier1_df, return_metadata=True)
gated, audit = engine.apply_publication_eligibility_gates(tier1_df, reference_time=DATASET_ANCHOR_TIME, return_metadata=True)
# Must retain unique contiguous ordering 1 to 25
assert gated["hiffi_xp_published_rank"].tolist() == list(range(1, 26))
# All records remain Tier 1
assert (gated["platform_evidence_tier"] == 1).all()
# Shortfall must be explicitly reported
assert audit["top_10_shortfall"] is True
assert audit["top_50_shortfall"] is True
@@ -479,11 +509,11 @@ def test_publication_gate_zero_multiplatform_cohort():
def test_freshness_gate_stale_timestamps_demote_tier():
"""Validates that metrics older than threshold lose evidence tier credit while retaining raw score."""
engine = HIFFIRankingEngine()
ref_time = pd.Timestamp("2026-10-26T12:00:00Z")
ref_time = DATASET_ANCHOR_TIME
# Artist has all metrics present, but Spotify is 10 days old (>7d) and IG is 20 days old (>14d)
test_df = pd.DataFrame([{
"name": "Artist Stale Telemetry",
"status_flag": "COMPLETE",
"production_hiffi_score": 90.0,
"youtube_updated_at": "2026-10-25T12:00:00Z", # 1 day old (Fresh)
"spotify_monthly_listeners": 25_000_000,
@@ -498,7 +528,6 @@ def test_freshness_gate_stale_timestamps_demote_tier():
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=ref_time)
row = gated.iloc[0]
# Only YouTube is fresh; Tier must demote to 1
assert row["platform_evidence_tier"] == 1
assert row["hiffi_xp_score"] == 88.5
assert "STALE_OR_MISSING_SPOTIFY_TIMESTAMP" in row["evidence_exclusions"]
@@ -508,10 +537,11 @@ def test_freshness_gate_stale_timestamps_demote_tier():
def test_freshness_gate_missing_or_malformed_timestamp():
"""Fails closed when timestamp string is empty, null, or malformed."""
engine = HIFFIRankingEngine()
ref_time = pd.Timestamp("2026-10-26T12:00:00Z")
ref_time = DATASET_ANCHOR_TIME
test_df = pd.DataFrame([{
"name": "Artist Malformed Timestamp",
"status_flag": "COMPLETE",
"production_hiffi_score": 85.0,
"youtube_updated_at": "invalid_date_format",
"spotify_monthly_listeners": 15_000_000,
@@ -526,7 +556,6 @@ def test_freshness_gate_missing_or_malformed_timestamp():
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=ref_time)
row = gated.iloc[0]
# Only Instagram is valid & fresh -> Tier 1
assert row["platform_evidence_tier"] == 1
assert "STALE_OR_MISSING_YOUTUBE_TIMESTAMP" in row["evidence_exclusions"]
assert "STALE_OR_MISSING_SPOTIFY_TIMESTAMP" in row["evidence_exclusions"]
@@ -538,6 +567,7 @@ def test_freshness_gate_missing_timestamp_columns_fail_closed():
test_df = pd.DataFrame([{
"name": "Artist No Timestamps",
"status_flag": "COMPLETE",
"production_hiffi_score": 90.0,
"spotify_monthly_listeners": 20_000_000,
"instagram_followers": 10_000_000,
@@ -546,7 +576,7 @@ def test_freshness_gate_missing_timestamp_columns_fail_closed():
"hiffi_xp_rank": 1
}])
gated = engine.apply_publication_eligibility_gates(test_df)
gated = engine.apply_publication_eligibility_gates(test_df, reference_time=DATASET_ANCHOR_TIME)
row = gated.iloc[0]
assert row["platform_evidence_tier"] == 0
@@ -591,5 +621,4 @@ def test_anomaly_detection_flags_suspicious_patterns():
assert flagged.loc[0, "anomaly_flags"] == "CLEAN"
assert "SUSPICIOUS_IG_TO_YT_RATIO" in flagged.loc[1, "anomaly_flags"]
assert "EXTREME_SPOTIFY_VELOCITY_RATIO" in flagged.loc[2, "anomaly_flags"]
# Raw scores must remain unaffected
assert flagged.loc[1, "hiffi_xp_score"] == 70.0