feat: complete multiplatform pipeline hardening, neutral anchor scoring, and automated health checks

This commit is contained in:
Srilekha committed 2026-10-24 09:48:37 +05:30
1 parent 56cbd08248
commit 66db687a58
11 files changed
+1097 -1005

No files matched your search

+75 -158
View File
@@ -1,174 +1,91 @@
"""
test_multiplatform_ranking.py
Simulates the expanded multi-platform HIFFI formula on the pilot cohort.
Merges YouTube metrics from enriched_artists.csv with Spotify and Instagram signals.
Automated Quality Assurance & Governance Test Suite for HIFFI Multi-Platform Pipeline.
"""
import pandas as pd
from pathlib import Path
import numpy as np
from collectors.browser_collector import MultiPlatformSignalCollector, sync_playwright
import pandas as pd
import pytest
PILOT_ARTISTS = [
{
"name": "Kendrick Lamar",
"spotify_url": "https://open.spotify.com/artist/2YZyLoL8N0Wb9xBt1NhZWg",
"ig_url": "https://www.instagram.com/kendricklamar/",
},
{
"name": "Drake",
"spotify_url": "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4",
"ig_url": "https://www.instagram.com/champagnepapi/",
},
{
"name": "Central Cee",
"spotify_url": "https://open.spotify.com/artist/5H4ikTs84ffUwPVEMOzPt0",
"ig_url": "https://www.instagram.com/centralcee/",
},
{
"name": "GloRilla",
"spotify_url": "https://open.spotify.com/artist/2qoQgPAilUQnvEBEnfZQI8",
"ig_url": "https://www.instagram.com/glorillapimp/",
},
{
"name": "Sexyy Red",
"spotify_url": "https://open.spotify.com/artist/1pPmIToKXyG75V4XZgqpvf",
"ig_url": "https://www.instagram.com/sexyyred/",
},
]
DATA_FILE = Path("data/hiffi_multiplatform_500.csv")
def min_max_normalize(series: pd.Series) -> pd.Series:
"""Scales numeric values to a 0-100 range."""
min_v = series.min()
max_v = series.max()
if max_v == min_v:
return pd.Series(50.0, index=series.index)
return ((series - min_v) / (max_v - min_v)) * 100.0
@pytest.fixture(scope="module")
def df():
assert DATA_FILE.exists(), f"Target dataset {DATA_FILE} does not exist."
return pd.read_csv(DATA_FILE)
def main():
print("=" * 80)
print("STEP 1: COLLECTING LIVE SPOTIFY & INSTAGRAM SIGNALS (PILOT)")
print("=" * 80)
def test_cohort_size(df):
"""Dataset must contain exactly 500 artist records."""
assert len(df) == 500, f"Expected 500 artists, found {len(df)}"
collector = MultiPlatformSignalCollector(headless=True)
collected_rows = []
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
context = browser.new_context(
viewport={"width": 1920, "height": 1080},
user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36",
locale="en-US",
)
page = context.new_page()
for a in PILOT_ARTISTS:
data = collector.collect_artist(a, page)
collected_rows.append(data)
sp_display = (
f"{data['spotify_monthly_listeners']:,}"
if data["spotify_monthly_listeners"] is not None
else "None"
)
ig_display = (
f"{data['instagram_followers']:,}"
if data["instagram_followers"] is not None
else "None"
)
print(f"Collected: {data['name']} -> Spotify: {sp_display}, IG: {ig_display}")
browser.close()
new_signals_df = pd.DataFrame(collected_rows)
print("\n" + "=" * 80)
print("STEP 2: MERGING WITH EXISTING YOUTUBE METRICS FROM enriched_artists.csv")
print("=" * 80)
df_existing = pd.read_csv("enriched_artists.csv")
df_existing["name_clean"] = df_existing["name"].str.strip().str.lower()
new_signals_df["name_clean"] = new_signals_df["name"].str.strip().str.lower()
merged_df = pd.merge(
new_signals_df,
df_existing,
on="name_clean",
how="inner",
suffixes=("", "_yt"),
def test_spotify_streaming_governance(df):
"""
Validates audited public streaming ingestion:
1. Populated listener counts must be positive integers (> 0).
2. Status must be explicit (either VERIFIED_PUBLIC_AGGREGATOR or UNAVAILABLE_IN_TOP_5000).
3. No zero-imputed values allowed.
"""
valid_statuses = {"VERIFIED_PUBLIC_AGGREGATOR", "UNAVAILABLE_IN_TOP_5000"}
assert set(df["spotify_status"].unique()).issubset(valid_statuses), (
f"Found invalid spotify_status values: {df['spotify_status'].unique()}"
)
if merged_df.empty:
matched_indices = []
for name in new_signals_df["name"]:
match = df_existing[df_existing["name"].str.contains(name, case=False, na=False)]
if not match.empty:
matched_indices.append(match.index[0])
merged_df = df_existing.loc[matched_indices].copy()
merged_df = merged_df.merge(new_signals_df, left_on="name", right_on="name")
valid_listeners = df["spotify_monthly_listeners"].dropna()
assert len(valid_listeners) > 0, "Expected verified streaming records to be populated."
assert (valid_listeners > 0).all(), "Found zero or negative listener values (must be positive integer or NaN)."
eval_cols = [
"name",
"subscriber_count",
"recent_avg_views",
"spotify_monthly_listeners",
"instagram_followers",
"hiffi_score",
def test_entity_resolution_completeness(df):
"""Top 50 established artists must have candidate Spotify IDs resolved (>= 90%)."""
top_50 = df[df["production_hiffi_rank"] <= 50]
top_50_resolved = (top_50["spotify_id"].notna()) & (top_50["spotify_id"] != "N/A")
assert top_50_resolved.mean() >= 0.90, (
f"Expected >= 90% of top 50 artists to have resolved candidate Spotify IDs, got {top_50_resolved.mean():.1%}"
)
valid_mask = (
df["spotify_id"].isna()
| (df["spotify_id"] == "N/A")
| (df["spotify_id"].astype(str).str.len() == 22)
)
assert valid_mask.all(), "Found malformed Spotify IDs."
def test_instagram_follower_validity(df):
"""Follower counts must be non-negative numbers or NaN."""
valid_followers = df["instagram_followers"].dropna()
assert (valid_followers > 0).all(), "Found zero or negative follower values."
assert np.isfinite(valid_followers).all(), "Found infinite values."
def test_score_normalization_bounds(df):
"""Production and experimental scores must strictly fall within [0.0, 100.0]."""
for col in ["production_hiffi_score", "hiffi_xp_score"]:
valid_scores = df[col].dropna()
assert (valid_scores >= 0.0).all(), f"Found scores < 0 in {col}"
assert (valid_scores <= 100.0).all(), f"Found scores > 100 in {col}"
def test_rank_monotonicity(df):
"""Higher or equal scores must produce better (lower numeric) or equal ranks."""
sorted_df = df.sort_values("hiffi_xp_rank", ascending=True).reset_index(drop=True)
scores = sorted_df["hiffi_xp_score"].tolist()
for i in range(len(scores) - 1):
assert scores[i] >= scores[i + 1], f"Rank inversion detected at row {i}"
def test_schema_provenance_columns(df):
"""Dataset must contain all mandatory provenance and audit timestamps."""
mandatory_cols = [
"name", "production_hiffi_rank", "production_hiffi_score",
"hiffi_xp_rank", "hiffi_xp_score", "wikidata_qid", "spotify_id",
"spotify_status", "spotify_monthly_listeners", "spotify_data_source",
"instagram_handle", "instagram_followers", "instagram_collected_at",
"youtube_data_source", "has_complete_multiplatform_signals"
]
pilot_view = merged_df[[c for c in eval_cols if c in merged_df.columns]].copy()
# Fill uncollected values with 0
pilot_view["spotify_monthly_listeners"] = pilot_view["spotify_monthly_listeners"].fillna(0)
pilot_view["instagram_followers"] = pilot_view["instagram_followers"].fillna(0)
print("\n" + "=" * 80)
print("STEP 3: COMPUTING NORMALIZED COMPOSITE MULTI-PLATFORM RANKING")
print("=" * 80)
# 1. Normalize individual signal channels (0 - 100)
pilot_view["norm_yt_views"] = min_max_normalize(pilot_view["recent_avg_views"])
pilot_view["norm_spotify"] = min_max_normalize(pilot_view["spotify_monthly_listeners"])
pilot_view["norm_instagram"] = min_max_normalize(pilot_view["instagram_followers"])
# 2. Formula weighting:
# 40% Spotify Streaming (Core Audio Demand)
# 35% YouTube Engagement (Video / Catalog Depth)
# 25% Instagram Reach (Social Following / Cultural Reach)
pilot_view["multi_platform_score"] = (
(0.40 * pilot_view["norm_spotify"])
+ (0.35 * pilot_view["norm_yt_views"])
+ (0.25 * pilot_view["norm_instagram"])
).round(2)
# 3. Compute Ranks
pilot_view["new_multi_rank"] = (
pilot_view["multi_platform_score"]
.rank(ascending=False, method="min")
.astype(int)
)
if "hiffi_score" in pilot_view.columns:
pilot_view["original_yt_rank"] = (
pilot_view["hiffi_score"].rank(ascending=False, method="min").astype(int)
)
display_cols = [
"name",
"original_yt_rank",
"new_multi_rank",
"multi_platform_score",
"spotify_monthly_listeners",
"instagram_followers",
]
print("\nComparison Table (Original YouTube Rank vs. Multi-Platform Rank):")
print(
pilot_view[[c for c in display_cols if c in pilot_view.columns]]
.sort_values("new_multi_rank")
.to_string(index=False)
)
print("=" * 80)
if __name__ == "__main__":
main()
for col in mandatory_cols:
assert col in df.columns, f"Mandatory column missing: {col}"