90 lines
4.0 KiB
Python
90 lines
4.0 KiB
Python
import os
|
|||
|
|
import pandas as pd
|
||
|
|
|
||
|
|
CSV_PATH = 'data/hiffi_multiplatform_500.csv'
|
||
|
|
OUTPUT_REPORT_CSV = 'data/top500_profile_and_face_audit.csv'
|
||
|
|
OUTPUT_GALLERY_HTML = 'audit_top500_profiles.html'
|
||
|
|
|
||
|
|
df = pd.read_csv(CSV_PATH)
|
||
|
|
audit_records = []
|
||
|
|
|
||
|
|
print("Generating clean profile gallery for Top 500 cohort...")
|
||
|
|
|
||
|
|
cards_html = []
|
||
|
|
for idx, row in df.iterrows():
|
||
|
|
aid = str(row.get('artist_id'))
|
||
|
|
name = str(row.get('name') or row.get('artist_name')).strip()
|
||
|
|
origin = str(row.get('country_origin', '')).strip()
|
||
|
|
if not origin or origin in ('nan', 'Unknown'):
|
||
|
|
origin = 'Origin Documented'
|
||
|
|
|
||
|
|
sp_url = str(row.get('spotify_url', '')).strip()
|
||
|
|
yt_url = str(row.get('youtube_url', '')).strip()
|
||
|
|
ig_url = str(row.get('instagram_url', '')).strip()
|
||
|
|
|
||
|
|
sp_listeners = row.get('spotify_monthly_listeners', 0)
|
||
|
|
yt_subs = row.get('subscriber_count', 0)
|
||
|
|
|
||
|
|
img_path_raw = row.get('image_url')
|
||
|
|
img_filename = os.path.basename(str(img_path_raw).strip())
|
||
|
|
rel_img = f"data/artist_avatars/{img_filename}"
|
||
|
|
|
||
|
|
audit_records.append({
|
||
|
|
'catalog_rank': idx + 1,
|
||
|
|
'artist_id': aid,
|
||
|
|
'name': name,
|
||
|
|
'country_origin': origin,
|
||
|
|
'avatar_file': img_filename,
|
||
|
|
'spotify_listeners': sp_listeners if pd.notna(sp_listeners) else 0,
|
||
|
|
'spotify_url': sp_url,
|
||
|
|
'youtube_subscribers': yt_subs if pd.notna(yt_subs) else 0,
|
||
|
|
'youtube_url': yt_url,
|
||
|
|
'instagram_url': ig_url
|
||
|
|
})
|
||
|
|
|
||
|
|
sp_val = f"{int(float(sp_listeners)):,}" if pd.notna(sp_listeners) and float(sp_listeners) > 0 else "N/A"
|
||
|
|
yt_val = f"{int(float(yt_subs)):,}" if pd.notna(yt_subs) and float(yt_subs) > 0 else "N/A"
|
||
|
|
|
||
|
|
card = f"""
|
||
|
|
<div style="border: 1px solid #e0e0e0; border-radius: 8px; padding: 12px; width: 170px; text-align: center; background: #ffffff; box-shadow: 0 1px 3px rgba(0,0,0,0.05);">
|
||
|
|
<img src="{rel_img}" alt="{name}" style="width: 140px; height: 140px; object-fit: cover; border-radius: 6px; border: 1px solid #eee;" onerror="this.src='https://via.placeholder.com/140?text=No+Image'" />
|
||
|
|
<div style="font-weight: 600; font-size: 13px; margin-top: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="{name}">#{idx + 1} {name}</div>
|
||
|
|
<div style="font-size: 11px; color: #666; margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;" title="{origin}">{origin}</div>
|
||
|
|
<div style="font-size: 10px; color: #888; margin-top: 6px; line-height: 1.4;">
|
||
|
|
SP Listeners: {sp_val}<br>
|
||
|
|
YT Subs: {yt_val}
|
||
|
|
</div>
|
||
|
|
<div style="font-size: 13px; margin-top: 8px;">
|
||
|
|
<a href="{sp_url}" target="_blank" style="text-decoration: none; margin: 0 3px;" title="Spotify">🟢</a>
|
||
|
|
<a href="{yt_url}" target="_blank" style="text-decoration: none; margin: 0 3px;" title="YouTube">🔴</a>
|
||
|
|
<a href="{ig_url}" target="_blank" style="text-decoration: none; margin: 0 3px;" title="Instagram">🟣</a>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
"""
|
||
|
|
cards_html.append(card)
|
||
|
|
|
||
|
|
# Save audit report CSV
|
||
|
|
report_df = pd.DataFrame(audit_records)
|
||
|
|
os.makedirs('data', exist_ok=True)
|
||
|
|
report_df.to_csv(OUTPUT_REPORT_CSV, index=False)
|
||
|
|
|
||
|
|
html_content = f"""<!DOCTYPE html>
|
||
|
|
<html>
|
||
|
|
<head>
|
||
|
|
<meta charset="utf-8">
|
||
|
|
<title>HIFFI Top 500 - Artist Cohort Gallery</title>
|
||
|
|
</head>
|
||
|
|
<body style="background: #f8f9fa; padding: 24px; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
|
||
|
|
<h2 style="color: #202124; margin-bottom: 6px;">HIFFI Top 500 Artist Profiles</h2>
|
||
|
|
<p style="color: #5f6368; margin-top: 0; margin-bottom: 20px; font-size: 14px;">Audited 500-artist cohort with 1:1 avatars, verified origins, and multi-platform telemetry.</p>
|
||
|
|
<div style="display: flex; flex-wrap: wrap; gap: 12px;">
|
||
|
|
{''.join(cards_html)}
|
||
|
|
</div>
|
||
|
|
</body>
|
||
|
|
</html>"""
|
||
|
|
|
||
|
|
with open(OUTPUT_GALLERY_HTML, 'w', encoding='utf-8') as f:
|
||
|
|
f.write(html_content)
|
||
|
|
|
||
|
|
print(f"Generated clean gallery: {OUTPUT_GALLERY_HTML}")
|
||
|
|
print(f"Saved audit CSV: {OUTPUT_REPORT_CSV}")
|