Files
Srilekha c2962e3bc2
HIFFI Multi-Platform CI / test (3.11) (push) Failing after 42s
HIFFI Multi-Platform CI / test (3.12) (push) Canceled after 44s
feat(cron): finalize top 500 cohort weekly ranking pipeline with delta tracking
2026-11-09 12:17:51 +05:30

175 lines
6.7 KiB
Python

import os
import sys
import sqlite3
import logging
from datetime import datetime, timezone
import pandas as pd
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if BASE_DIR not in sys.path:
sys.path.insert(0, BASE_DIR)
from core.ranking_engine import HIFFIRankingEngine
DB_PATH = os.path.join(BASE_DIR, "data", "hiffi_catalog.db")
CSV_PATH = os.path.join(BASE_DIR, "data", "hiffi_multiplatform_500.csv")
LOG_PATH = os.path.join(BASE_DIR, "cron_ranking.log")
logging.basicConfig(
filename=LOG_PATH,
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("HIFFI_CRON")
def run_weekly_ranking_pipeline():
start_time = datetime.now(timezone.utc)
print("\n========================================================")
print(" HIFFI AUTOMATED WEEKLY RANKING PIPELINE (TOP 500 ONLY)")
print(f" Run Timestamp (UTC): {start_time.isoformat()}")
print("========================================================\n")
logger.info("Starting automated 500-cohort weekly ranking pipeline...")
if not os.path.exists(DB_PATH):
err = f"Database not found at {DB_PATH}"
logger.error(err)
print(f"ERROR: {err}")
return False
if not os.path.exists(CSV_PATH):
err = f"Cohort CSV not found at {CSV_PATH}"
logger.error(err)
print(f"ERROR: {err}")
return False
conn = sqlite3.connect(DB_PATH)
cur = conn.cursor()
try:
# Read the verified 500 cohort IDs directly from the baseline CSV
base_cohort_df = pd.read_csv(CSV_PATH)
cohort_ids = [str(x) for x in base_cohort_df['artist_id'].dropna().tolist()][:500]
placeholders = ",".join([f"'{aid}'" for aid in cohort_ids])
# 1. Fetch telemetry strictly for the 500 cohort
query = f"""
WITH latest_telemetry AS (
SELECT t.*, ROW_NUMBER() OVER (
PARTITION BY t.artist_id
ORDER BY t.last_synced_at DESC, t.telemetry_id DESC
) as rn
FROM telemetry_metrics t
WHERE t.artist_id IN ({placeholders})
)
SELECT
a.artist_id,
a.artist_name,
a.country_origin,
a.image_url,
a.spotify_url,
a.youtube_url,
a.instagram_url,
COALESCE(t.spotify_monthly_listeners, 0) as spotify_monthly_listeners,
COALESCE(t.subscribers_count, 0) as subscriber_count,
COALESCE(t.daily_view_velocity, 0) as daily_view_velocity,
COALESCE(t.instagram_followers, 0) as instagram_followers,
r.catalog_rank as previous_rank,
r.catalog_intelligence_tier as current_tier
FROM artists a
LEFT JOIN latest_telemetry t ON a.artist_id = t.artist_id AND t.rn = 1
LEFT JOIN rankings r ON a.artist_id = r.artist_id
WHERE a.artist_id IN ({placeholders});
"""
df = pd.read_sql_query(query, conn)
total_artists = len(df)
print(f"Loaded verified cohort: {total_artists} artists.")
logger.info(f"Loaded {total_artists} cohort artists for recalculation.")
# 2. Run Core Scoring Engine (45% SP / 35% YT / 20% IG)
engine = HIFFIRankingEngine()
ranked_df = engine.calculate_hiffi_xp(df)
if 'hiffi_xp_score' in ranked_df.columns:
score_col = 'hiffi_xp_score'
elif 'score' in ranked_df.columns:
score_col = 'score'
elif 'production_hiffi_score' in ranked_df.columns:
score_col = 'production_hiffi_score'
else:
numeric_cols = ranked_df.select_dtypes(include=['float64', 'float32']).columns
score_col = numeric_cols[0]
# Sort descending by score
ranked_df = ranked_df.sort_values(by=score_col, ascending=False).reset_index(drop=True)
ranked_df['new_rank'] = ranked_df.index + 1
# Track rank deltas
ranked_df['rank_delta'] = ranked_df.apply(
lambda row: int(row['previous_rank'] - row['new_rank'])
if pd.notna(row['previous_rank']) and row['previous_rank'] > 0
else 0,
axis=1
)
def compute_tier(rank_val):
if rank_val <= 50:
return "TIER_1_ICON"
elif rank_val <= 200:
return "TIER_2_MAINSTREAM"
else:
return "TIER_3_RISING"
ranked_df['tier_rank'] = ranked_df['new_rank']
ranked_df['catalog_intelligence_tier'] = ranked_df['new_rank'].apply(compute_tier)
# 3. Update SQLite database rankings table for the 500 cohort
print("Committing 500 cohort ranks to SQLite database...")
update_data = []
for _, row in ranked_df.iterrows():
aid = str(row['artist_id'])
c_rank = int(row['new_rank'])
t_rank = int(row['tier_rank'])
tier_str = str(row['catalog_intelligence_tier'])
update_data.append((c_rank, t_rank, tier_str, aid))
cur.executemany("""
UPDATE rankings
SET catalog_rank = ?,
tier_rank = ?,
catalog_intelligence_tier = ?
WHERE artist_id = ?;
""", update_data)
conn.commit()
print(f"Updated database ranking records for {len(update_data)} artists.")
# 4. Save updated 500 cohort CSV
ranked_df['catalog_rank'] = ranked_df['new_rank']
if 'name' not in ranked_df.columns and 'artist_name' in ranked_df.columns:
ranked_df['name'] = ranked_df['artist_name']
ranked_df.to_csv(CSV_PATH, index=False)
print(f"Exported Top 500 cohort CSV to: {CSV_PATH}")
# Telemetry summary
print("\n--- TOP 5 RANKING SNAPSHOT ---")
for _, r in ranked_df.head(5).iterrows():
delta_str = f"+{r['rank_delta']}" if r['rank_delta'] > 0 else str(r['rank_delta'])
print(f"#{r['new_rank']} {r.get('name', r.get('artist_name'))} | Score: {r[score_col]:.2f} | Tier: {r['catalog_intelligence_tier']} | Delta: {delta_str}")
elapsed = (datetime.now(timezone.utc) - start_time).total_seconds()
print(f"\nWeekly ranking pipeline completed successfully in {elapsed:.2f}s.")
logger.info(f"Weekly ranking pipeline finished successfully in {elapsed:.2f}s.")
return True
except Exception as e:
conn.rollback()
logger.error(f"Weekly ranking run failed: {str(e)}", exc_info=True)
print(f"\nERROR running ranking pipeline: {e}")
return False
finally:
conn.close()
if __name__ == "__main__":
success = run_weekly_ranking_pipeline()
sys.exit(0 if success else 1)