feat: complete Day 2 handoff with API service, PM2 process daemons, and test suites
This commit is contained in:
1 parent
564262340a
commit
12f0786be9
27 files changed
+3234
-1013
No files matched your search
@@ -2,16 +2,19 @@
|
||||
app.py
|
||||
HIFFI Artist Intelligence - Multi-Platform Executive Dashboard
|
||||
Clean dark-themed design with multi-platform evidence tiers, Spotify & Instagram telemetry,
|
||||
ChartMasters record sales (CSPC/EAS), breakout velocity index, and deep-dive scouting dossier.
|
||||
ChartMasters record sales (CSPC/EAS), breakout velocity index, deep-dive scouting dossier,
|
||||
and experimental Commercial Power Index (CPI) simulation engine with sensitivity analysis.
|
||||
"""
|
||||
|
||||
import io
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
import plotly.graph_objects as go
|
||||
import streamlit as st
|
||||
|
||||
from core.cpi_engine import CommercialPowerEngine
|
||||
from exporters.excel_workbook import generate_executive_sales_workbook
|
||||
|
||||
st.set_page_config(
|
||||
page_title="HIFFI | Multi-Platform Intelligence",
|
||||
page_icon="⚡",
|
||||
@@ -22,7 +25,7 @@ st.set_page_config(
|
||||
DATA_PATH = Path("data/hiffi_multiplatform_500.csv")
|
||||
FALLBACK_DATA_PATH = Path("enriched_artists.csv")
|
||||
|
||||
# Global CSS Polish
|
||||
# Global CSS Styling
|
||||
st.markdown(
|
||||
"""
|
||||
<style>
|
||||
@@ -60,6 +63,39 @@ st.markdown(
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
/* Telemetry Coverage Banner */
|
||||
.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;
|
||||
}
|
||||
|
||||
/* Simulation Micro-Cards */
|
||||
.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 Cards */
|
||||
.podium-tile {
|
||||
background: #111827;
|
||||
@@ -72,7 +108,7 @@ st.markdown(
|
||||
.podium-silver { border-top: 4px solid #94a3b8; }
|
||||
.podium-bronze { border-top: 4px solid #f97316; }
|
||||
|
||||
/* Health & Tier Badges */
|
||||
/* Badges */
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 3px 8px;
|
||||
@@ -105,7 +141,6 @@ def load_data():
|
||||
|
||||
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:
|
||||
@@ -139,11 +174,17 @@ def load_data():
|
||||
"digital_singles_sales",
|
||||
"streaming_eas",
|
||||
"total_eas_cspc",
|
||||
"riaa_certified_album_units",
|
||||
"riaa_certified_singles_units",
|
||||
]
|
||||
for col in numeric_cols:
|
||||
if col in df.columns:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
# Commercial Power Engine
|
||||
engine = CommercialPowerEngine()
|
||||
df["commercial_power_index"] = engine.calculate_cpi(df)
|
||||
|
||||
if "subgenre" not in df.columns:
|
||||
def infer_subgenre(r):
|
||||
subs = r.get("subscriber_count", 0) or 0
|
||||
@@ -159,23 +200,30 @@ def load_data():
|
||||
return df
|
||||
|
||||
|
||||
def to_excel_bytes(dataframe: pd.DataFrame) -> bytes:
|
||||
output = io.BytesIO()
|
||||
with pd.ExcelWriter(output, engine="openpyxl") as writer:
|
||||
dataframe.to_excel(writer, index=False, sheet_name="HIFFI_Cohort")
|
||||
return output.getvalue()
|
||||
|
||||
|
||||
df = load_data()
|
||||
engine = CommercialPowerEngine()
|
||||
|
||||
|
||||
def get_provenance_badge(status: str) -> str:
|
||||
"""Format status values into readable provenance labels."""
|
||||
s = str(status).strip()
|
||||
if s == "VERIFIED_CSPC":
|
||||
return "✅ VERIFIED (CSPC)"
|
||||
elif s == "VERIFIED_RIAA":
|
||||
return "🏅 VERIFIED (RIAA)"
|
||||
elif s == "ESTIMATED_SEA":
|
||||
return "📊 ESTIMATED (SEA)"
|
||||
return "⏳ PENDING_INGESTION"
|
||||
|
||||
|
||||
# ----------------- Sidebar Controls -----------------
|
||||
with st.sidebar:
|
||||
st.markdown("### ⚡ HIFFI Multi-Platform")
|
||||
st.caption("YouTube • Spotify • Instagram • Sales Analytics")
|
||||
st.caption("YouTube • Spotify • Instagram • Sales Telemetry")
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
|
||||
if df.empty:
|
||||
st.error("No dataset found. Run 'python run_pipeline.py' to generate master data.")
|
||||
st.error("No dataset found. Run pipeline to generate master data.")
|
||||
st.stop()
|
||||
|
||||
view_mode = st.radio(
|
||||
@@ -183,15 +231,49 @@ with st.sidebar:
|
||||
[
|
||||
"🏆 Published Multi-Platform Rank",
|
||||
"🚀 Breakout Velocity (BVI)",
|
||||
"💿 Record Sales & CSPC (ChartMasters)",
|
||||
"💿 Record Sales (CSPC Methodology)",
|
||||
"🧪 Commercial Simulation Engine (CPI)",
|
||||
"🔍 Full Cohort Audit Grid",
|
||||
],
|
||||
index=0,
|
||||
)
|
||||
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
st.markdown("##### 🛡️ Cohort Filters")
|
||||
|
||||
catalog_filter = "All Monitored Artists (500)"
|
||||
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,
|
||||
help="Toggle between viewing the full cohort, verified certifications, or only ChartMasters studies.",
|
||||
)
|
||||
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)":
|
||||
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,
|
||||
help="Matched Cohort evaluates cataloged artists in an isolated leaderboard. Global Cohort keeps all artists with normalized weights.",
|
||||
)
|
||||
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",
|
||||
help="Blends Commercial Power Index (CPI) with baseline production score.",
|
||||
)
|
||||
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())
|
||||
selected_verticals = st.multiselect("Vertical", options=all_verticals, default=all_verticals)
|
||||
|
||||
@@ -200,37 +282,48 @@ with st.sidebar:
|
||||
|
||||
min_xp = st.slider("Min HIFFI-XP Score", 0.0, 100.0, 0.0, 5.0)
|
||||
|
||||
# Filter logic
|
||||
filtered_df = df[
|
||||
(df["subgenre"].isin(selected_verticals))
|
||||
& (df["platform_evidence_tier"].isin(selected_tiers))
|
||||
& (df["hiffi_xp_score"].fillna(0.0) >= min_xp)
|
||||
].copy()
|
||||
|
||||
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()
|
||||
|
||||
# Execute simulation calculations if chosen
|
||||
sim_metrics = {}
|
||||
if view_mode == "🧪 Commercial Simulation Engine (CPI)":
|
||||
filtered_df = engine.simulate_ranking(filtered_df, commercial_weight=sim_weight, mode=sim_mode)
|
||||
sim_metrics = engine.compute_simulation_metrics(filtered_df)
|
||||
|
||||
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)
|
||||
st.markdown("##### 💾 Unified Audit Exports")
|
||||
|
||||
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",
|
||||
width="stretch",
|
||||
)
|
||||
|
||||
csv_payload = filtered_df.to_csv(index=False).encode("utf-8")
|
||||
col_csv.download_button(
|
||||
"📥 CSV",
|
||||
st.download_button(
|
||||
"📥 Filtered Table CSV",
|
||||
data=csv_payload,
|
||||
file_name="hiffi_multiplatform_filtered.csv",
|
||||
mime="text/csv",
|
||||
width="stretch",
|
||||
)
|
||||
|
||||
excel_payload = to_excel_bytes(filtered_df)
|
||||
col_xlsx.download_button(
|
||||
"📊 Excel",
|
||||
data=excel_payload,
|
||||
file_name="hiffi_multiplatform_filtered.xlsx",
|
||||
mime="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
width="stretch",
|
||||
)
|
||||
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
st.caption("Governance: Multi-Platform Signals + Verified ChartMasters Provenance")
|
||||
st.caption("Governance: CSPC Methodology • EAS Units • Zero-Imputation Compliant")
|
||||
|
||||
# ----------------- Main Header -----------------
|
||||
head_col1, head_col2 = st.columns([3, 1])
|
||||
@@ -243,368 +336,361 @@ with head_col2:
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
|
||||
# ----------------- KPI Cards Row (5 Columns) -----------------
|
||||
# ----------------- Macro KPI Cards -----------------
|
||||
k1, k2, k3, k4, k5 = st.columns(5)
|
||||
|
||||
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:
|
||||
t3_count = len(df[df["platform_evidence_tier"] == 3])
|
||||
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">YouTube + Spotify + Instagram</div></div>', unsafe_allow_html=True)
|
||||
with k3:
|
||||
sp_count = df["spotify_monthly_listeners"].notna().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 k4:
|
||||
ig_count = df["instagram_followers"].notna().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 k5:
|
||||
sales_indexed = df["total_eas_cspc"].notna().sum() if "total_eas_cspc" in df.columns else 0
|
||||
st.markdown(
|
||||
f"""
|
||||
<div class="kpi-box">
|
||||
<div class="kpi-title" style="color: #ec4899;">Record Sales</div>
|
||||
<div class="kpi-value" style="color: #ec4899;">{sales_indexed}</div>
|
||||
<div class="kpi-subtitle">ChartMasters CSPC records</div>
|
||||
</div>
|
||||
""",
|
||||
unsafe_allow_html=True,
|
||||
)
|
||||
cataloged_count = df["total_eas_cspc"].fillna(0).gt(0).sum()
|
||||
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">100% Cohort Cataloged</div></div>', unsafe_allow_html=True)
|
||||
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
|
||||
# ----------------- Podium -----------------
|
||||
top3 = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).head(3)
|
||||
# ----------------- Dynamic Telemetry Coverage Banner -----------------
|
||||
if view_mode in ("💿 Record Sales (CSPC Methodology)", "🧪 Commercial Simulation Engine (CPI)"):
|
||||
verified_cspc_cnt = int((df["sales_data_status"] == "VERIFIED_CSPC").sum())
|
||||
verified_riaa_cnt = int((df["sales_data_status"] == "VERIFIED_RIAA").sum())
|
||||
estimated_sea_cnt = int((df["sales_data_status"] == "ESTIMATED_SEA").sum())
|
||||
total_active_coverage = verified_cspc_cnt + verified_riaa_cnt + estimated_sea_cnt
|
||||
coverage_pct = (total_active_coverage / len(df)) * 100.0
|
||||
|
||||
if len(top3) >= 3:
|
||||
st.markdown("#### 👑 Market Leaders (Published 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))
|
||||
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(
|
||||
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_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-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;">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;">IG Followers</div>
|
||||
<div style="font-size: 1.05rem; 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)
|
||||
|
||||
# ----------------- Leaderboard Grid -----------------
|
||||
st.markdown(f"#### 📋 {view_mode}")
|
||||
|
||||
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",
|
||||
"hiffi_xp_rank",
|
||||
"name",
|
||||
"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["breakout_velocity_index"].notna()]
|
||||
.sort_values(by="breakout_velocity_index", ascending=False)
|
||||
.copy()
|
||||
st.markdown(
|
||||
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,
|
||||
)
|
||||
cols = [
|
||||
"name",
|
||||
"breakout_velocity_index",
|
||||
"hiffi_xp_published_rank",
|
||||
"daily_view_velocity",
|
||||
"momentum_30d",
|
||||
"subscriber_count",
|
||||
"subgenre"
|
||||
]
|
||||
elif view_mode == "💿 Record Sales & CSPC (ChartMasters)":
|
||||
table_df = (
|
||||
filtered_df[filtered_df["total_eas_cspc"].notna()]
|
||||
.sort_values(by="total_eas_cspc", ascending=False)
|
||||
.copy() if "total_eas_cspc" in filtered_df.columns else filtered_df.copy()
|
||||
)
|
||||
cols = [
|
||||
|
||||
# ----------------- Standalone Module Views -----------------
|
||||
|
||||
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()
|
||||
cspc_count = (filtered_df["sales_data_status"] == "VERIFIED_CSPC").sum()
|
||||
riaa_count = (filtered_df["sales_data_status"] == "VERIFIED_RIAA").sum()
|
||||
sea_count = (filtered_df["sales_data_status"] == "ESTIMATED_SEA").sum()
|
||||
|
||||
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()
|
||||
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",
|
||||
"sales_data_status",
|
||||
"sales_collected_at"
|
||||
]
|
||||
else:
|
||||
table_df = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).copy()
|
||||
cols = [
|
||||
"commercial_power_index",
|
||||
"hiffi_xp_published_rank",
|
||||
"name",
|
||||
"production_hiffi_rank",
|
||||
"hiffi_xp_rank",
|
||||
"rank_delta",
|
||||
"platform_evidence_tier",
|
||||
"hiffi_xp_score",
|
||||
"spotify_monthly_listeners",
|
||||
"instagram_followers",
|
||||
"total_eas_cspc",
|
||||
"sales_data_status",
|
||||
"evidence_exclusions",
|
||||
"anomaly_flags"
|
||||
]
|
||||
render_s_cols = [c for c in sales_cols if c in sales_table_df.columns]
|
||||
|
||||
render_cols = [c for c in cols if c in table_df.columns]
|
||||
st.dataframe(
|
||||
sales_table_df[render_s_cols].reset_index(drop=True),
|
||||
width="stretch",
|
||||
height=380,
|
||||
column_config={
|
||||
"cohort_sales_rank": st.column_config.NumberColumn("Cohort Sales Rank", format="#%d", width="small"),
|
||||
"chartmasters_sales_rank": st.column_config.NumberColumn("All-Time Global Rank", format="#%d", width="small"),
|
||||
"name": st.column_config.TextColumn("Artist", width="medium"),
|
||||
"provenance_badge": st.column_config.TextColumn("Audit Provenance", width="medium"),
|
||||
"total_eas_cspc": st.column_config.NumberColumn("Total EAS Units", format="%,d", width="medium"),
|
||||
"pure_album_sales": st.column_config.NumberColumn("Pure Studio Albums", format="%,d", width="medium"),
|
||||
"streaming_eas": st.column_config.NumberColumn("Streaming EAS", format="%,d", width="medium"),
|
||||
"physical_singles_sales": st.column_config.NumberColumn("Physical Singles", format="%,d", width="small"),
|
||||
"digital_singles_sales": st.column_config.NumberColumn("Digital Downloads", format="%,d", width="small"),
|
||||
"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"),
|
||||
},
|
||||
)
|
||||
|
||||
st.dataframe(
|
||||
table_df[render_cols].reset_index(drop=True),
|
||||
width="stretch",
|
||||
height=330,
|
||||
column_config={
|
||||
"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"),
|
||||
"cohort_sales_rank": st.column_config.NumberColumn("Cohort #", format="#%d", width="small"),
|
||||
"chartmasters_sales_rank": st.column_config.NumberColumn("Global #", format="#%d", width="small"),
|
||||
"platform_evidence_tier": st.column_config.NumberColumn("Tier", format="%d", width="small"),
|
||||
"name": st.column_config.TextColumn("Artist", width="medium"),
|
||||
"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"),
|
||||
"total_eas_cspc": st.column_config.NumberColumn("Total EAS (CSPC)", format="%,d", width="medium"),
|
||||
"pure_album_sales": st.column_config.NumberColumn("Pure Albums", format="%,d", width="medium"),
|
||||
"streaming_eas": st.column_config.NumberColumn("Streaming EAS", format="%,d", width="medium"),
|
||||
"physical_singles_sales": st.column_config.NumberColumn("Physical", format="%,d", width="small"),
|
||||
"digital_singles_sales": st.column_config.NumberColumn("Digital", format="%,d", width="small"),
|
||||
"sales_data_status": st.column_config.TextColumn("Status", width="medium"),
|
||||
"sales_collected_at": st.column_config.TextColumn("Collected Date", width="medium"),
|
||||
"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)
|
||||
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
|
||||
# ----------------- Visual Insights & Format Analysis -----------------
|
||||
st.markdown("#### 📊 Commercial & Multi-Platform Analytics")
|
||||
|
||||
if view_mode == "💿 Record Sales & CSPC (ChartMasters)":
|
||||
c_sales1, c_sales2 = st.columns(2)
|
||||
|
||||
with c_sales1:
|
||||
top_sales = (
|
||||
filtered_df[filtered_df["total_eas_cspc"].notna()]
|
||||
.sort_values(by="total_eas_cspc", ascending=False)
|
||||
.head(15)
|
||||
c_s1, c_s2 = st.columns(2)
|
||||
with c_s1:
|
||||
top_sellers = sales_table_df[sales_table_df["total_eas_cspc"] > 0].head(12)
|
||||
fig_format = go.Figure()
|
||||
fig_format.add_trace(go.Bar(name="Traditional Albums", x=top_sellers["name"], y=top_sellers["pure_album_sales"], marker_color="#8b5cf6"))
|
||||
fig_format.add_trace(go.Bar(name="Digital Singles", x=top_sellers["name"], y=top_sellers["digital_singles_sales"], marker_color="#38bdf8"))
|
||||
fig_format.add_trace(go.Bar(name="Streaming EAS", x=top_sellers["name"], y=top_sellers["streaming_eas"], 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),
|
||||
)
|
||||
if not top_sales.empty:
|
||||
fig_format = go.Figure()
|
||||
fig_format.add_trace(go.Bar(
|
||||
name="Pure Albums",
|
||||
x=top_sales["name"],
|
||||
y=top_sales["pure_album_sales"],
|
||||
marker_color="#8b5cf6"
|
||||
))
|
||||
fig_format.add_trace(go.Bar(
|
||||
name="Digital Singles",
|
||||
x=top_sales["name"],
|
||||
y=top_sales["digital_singles_sales"],
|
||||
marker_color="#38bdf8"
|
||||
))
|
||||
fig_format.add_trace(go.Bar(
|
||||
name="Streaming EAS",
|
||||
x=top_sales["name"],
|
||||
y=top_sales["streaming_eas"],
|
||||
marker_color="#10b981"
|
||||
))
|
||||
fig_format.update_layout(
|
||||
barmode="stack",
|
||||
title=dict(
|
||||
text="Format Breakdown: Pure Albums vs Singles vs Streaming (Top 15)",
|
||||
font=dict(size=14),
|
||||
),
|
||||
template="plotly_dark",
|
||||
paper_bgcolor="#111827",
|
||||
plot_bgcolor="#111827",
|
||||
font=dict(color="#9ca3af"),
|
||||
margin=dict(l=20, r=20, t=60, b=20),
|
||||
height=350,
|
||||
legend=dict(
|
||||
orientation="h",
|
||||
yanchor="bottom",
|
||||
y=1.04,
|
||||
xanchor="right",
|
||||
x=1.0,
|
||||
font=dict(size=11),
|
||||
),
|
||||
)
|
||||
st.plotly_chart(fig_format, width="stretch")
|
||||
else:
|
||||
st.info("No sales data available for current filter selection.")
|
||||
st.plotly_chart(fig_format, width="stretch")
|
||||
|
||||
with c_sales2:
|
||||
sales_scatter = filtered_df.dropna(subset=["pure_album_sales", "streaming_eas"]).copy()
|
||||
if not sales_scatter.empty:
|
||||
fig_scatter_sales = px.scatter(
|
||||
sales_scatter,
|
||||
x="pure_album_sales",
|
||||
y="streaming_eas",
|
||||
size="total_eas_cspc",
|
||||
hover_name="name",
|
||||
color="total_eas_cspc",
|
||||
color_continuous_scale="Viridis",
|
||||
log_x=True,
|
||||
log_y=True,
|
||||
title="Commercial Shift: Pure Album Sales vs Streaming Conversions",
|
||||
labels={
|
||||
"pure_album_sales": "Pure Album Sales (Log Scale)",
|
||||
"streaming_eas": "Streaming Equivalent Sales (Log Scale)",
|
||||
"total_eas_cspc": "Total EAS"
|
||||
},
|
||||
template="plotly_dark",
|
||||
)
|
||||
fig_scatter_sales.update_layout(
|
||||
paper_bgcolor="#111827",
|
||||
plot_bgcolor="#111827",
|
||||
font=dict(color="#9ca3af"),
|
||||
margin=dict(l=20, r=20, t=40, b=20),
|
||||
height=350,
|
||||
)
|
||||
st.plotly_chart(fig_scatter_sales, width="stretch")
|
||||
else:
|
||||
st.info("Insufficient records with both pure sales and streaming figures.")
|
||||
with c_s2:
|
||||
scatter_data = sales_table_df.dropna(subset=["total_eas_cspc", "hiffi_xp_score"]).copy()
|
||||
fig_quad = px.scatter(
|
||||
scatter_data,
|
||||
x="hiffi_xp_score",
|
||||
y="total_eas_cspc",
|
||||
size="total_eas_cspc",
|
||||
color="provenance_badge",
|
||||
hover_name="name",
|
||||
log_y=True,
|
||||
title="Legacy vs. Velocity Quadrant (All-Time Sales vs HIFFI-XP)",
|
||||
labels={"hiffi_xp_score": "Current Multi-Platform 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, width="stretch")
|
||||
|
||||
else:
|
||||
c_left, c_right = st.columns(2)
|
||||
top3 = filtered_df.sort_values(by="hiffi_xp_published_rank", ascending=True).head(3)
|
||||
if len(top3) >= 3 and view_mode != "🧪 Commercial Simulation Engine (CPI)":
|
||||
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))
|
||||
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: flex; justify-content: space-between; background: #0b0f17; padding: 10px 14px; border-radius: 8px;">
|
||||
<div><div style="font-size: 0.68rem; color: #9ca3af;">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;">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;">IG Followers</div><div style="font-size: 1.05rem; 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)
|
||||
|
||||
with c_left:
|
||||
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",
|
||||
},
|
||||
if view_mode == "🧪 Commercial Simulation Engine (CPI)":
|
||||
st.markdown("#### 🧪 Simulation Impact & Decision Summary")
|
||||
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)}"
|
||||
l_delta = f"{sim_metrics.get('top_loser_delta', 0)}"
|
||||
|
||||
with sm1:
|
||||
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)
|
||||
with sm2:
|
||||
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)
|
||||
with sm3:
|
||||
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)
|
||||
with sm4:
|
||||
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)
|
||||
|
||||
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**.")
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
|
||||
st.markdown(f"#### 📋 {view_mode}")
|
||||
|
||||
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", "hiffi_xp_rank", "name", "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["breakout_velocity_index"].notna()].sort_values(by="breakout_velocity_index", ascending=False).copy()
|
||||
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()
|
||||
|
||||
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)"
|
||||
|
||||
table_df["rank_shift_indicator"] = table_df["simulation_rank_delta"].apply(format_rank_delta)
|
||||
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", "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),
|
||||
width="stretch",
|
||||
height=340,
|
||||
column_config={
|
||||
"hiffi_xp_published_rank": st.column_config.NumberColumn("Published Rank", format="%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"),
|
||||
"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)
|
||||
|
||||
if view_mode == "🧪 Commercial Simulation Engine (CPI)":
|
||||
c_sim1, c_sim2 = st.columns(2)
|
||||
with c_sim1:
|
||||
sim_delta_df = filtered_df.dropna(subset=["simulation_rank_delta"]).sort_values(by="total_eas_cspc", ascending=False).head(15).copy()
|
||||
if not sim_delta_df.empty:
|
||||
fig_delta = px.bar(
|
||||
sim_delta_df,
|
||||
x="name",
|
||||
y="simulation_rank_delta",
|
||||
color="simulation_rank_delta",
|
||||
color_continuous_scale="RdYlGn",
|
||||
title=f"Simulated Rank Movement at {int(sim_weight*100)}% Commercial Weight (Top 15 Sellers)",
|
||||
labels={"simulation_rank_delta": "Rank Movement (▲ Positive / ▼ Negative)", "name": "Artist"},
|
||||
template="plotly_dark",
|
||||
)
|
||||
fig_delta.update_layout(
|
||||
paper_bgcolor="#111827",
|
||||
plot_bgcolor="#111827",
|
||||
font=dict(color="#9ca3af"),
|
||||
margin=dict(l=20, r=20, t=50, b=20),
|
||||
height=350,
|
||||
)
|
||||
st.plotly_chart(fig_delta, width="stretch")
|
||||
|
||||
with c_sim2:
|
||||
sensitivity_df = engine.run_sensitivity_matrix(filtered_df, mode=sim_mode).head(15)
|
||||
fig_sens = go.Figure()
|
||||
for idx, r in sensitivity_df.head(6).iterrows():
|
||||
fig_sens.add_trace(go.Scatter(
|
||||
x=["0%", "5%", "10%", "15%", "20%"],
|
||||
y=[r["rank_0pct"], r["rank_5pct"], r["rank_10pct"], r["rank_15pct"], r["rank_20pct"]],
|
||||
mode="lines+markers",
|
||||
name=r["name"],
|
||||
))
|
||||
fig_sens.update_layout(
|
||||
title="Weight Sensitivity: Rank Trajectories Across Weights (Top Volatile Acts)",
|
||||
yaxis=dict(autorange="reversed", title="Simulated Rank (Lower is Better)"),
|
||||
xaxis=dict(title="Commercial Signal Weight (%)"),
|
||||
template="plotly_dark",
|
||||
paper_bgcolor="#111827",
|
||||
plot_bgcolor="#111827",
|
||||
font=dict(color="#9ca3af"),
|
||||
margin=dict(l=20, r=20, t=50, b=20),
|
||||
height=350,
|
||||
)
|
||||
fig_scatter.update_layout(
|
||||
st.plotly_chart(fig_sens, width="stretch")
|
||||
else:
|
||||
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 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")
|
||||
with c_right:
|
||||
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"),
|
||||
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:
|
||||
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"),
|
||||
margin=dict(l=20, r=20, t=40, b=20),
|
||||
height=320,
|
||||
)
|
||||
st.plotly_chart(fig_pie, width="stretch")
|
||||
st.plotly_chart(fig_pie, width="stretch")
|
||||
|
||||
# ----------------- Deep Dive Scouting Dossier -----------------
|
||||
st.markdown("<div class='custom-divider'></div>", unsafe_allow_html=True)
|
||||
@@ -614,7 +700,6 @@ selected_name = st.selectbox("Inspect Artist Details:", df["name"].unique(), lab
|
||||
row = df[df["name"] == selected_name].iloc[0]
|
||||
|
||||
d1, d2, d3, d4, d5 = st.columns(5)
|
||||
|
||||
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")
|
||||
@@ -629,7 +714,7 @@ with d4:
|
||||
st.metric("Instagram Followers", f"{int(ig_f):,}" if pd.notna(ig_f) else "Unindexed (NaN)")
|
||||
with d5:
|
||||
sales_val = row.get("total_eas_cspc")
|
||||
st.metric("Total EAS Sales", f"{int(sales_val):,}" if pd.notna(sales_val) else "Uncataloged (NaN)")
|
||||
st.metric("Total EAS Sales", f"{int(sales_val):,}" if pd.notna(sales_val) else "Awaiting Ingestion")
|
||||
|
||||
st.markdown(
|
||||
f"""
|
||||
@@ -639,10 +724,10 @@ st.markdown(
|
||||
<div><b>Evidence Tier:</b> Tier {int(row.get('platform_evidence_tier', 1))}</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>Pure Albums:</b> {f"{int(row.get('pure_album_sales')):,}" if pd.notna(row.get('pure_album_sales')) else "N/A"}</div>
|
||||
<div><b>Streaming EAS:</b> {f"{int(row.get('streaming_eas')):,}" if pd.notna(row.get('streaming_eas')) else "N/A"}</div>
|
||||
<div><b>Sales Status:</b> {row.get('sales_data_status', 'NOT_CATALOGED_YET')}</div>
|
||||
<div><b>Provenance:</b> <a href="{row.get('sales_source_url', 'https://chartmasters.org')}" target="_blank" style="color: #38bdf8; text-decoration: underline;">ChartMasters Verification</a></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><b>Effective Weights:</b> {row.get('effective_weights', 'N/A')}</div>
|
||||
<div><b>Anomaly Flags:</b> {row.get('anomaly_flags', 'CLEAN')}</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user