113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
"""
|
|
collectors/wikidata_resolver.py
|
|
Directly resolves canonical Wikidata Q-IDs, official Spotify Artist IDs (P1902),
|
|
and Instagram usernames (P2003) via Wikidata's public entity API.
|
|
"""
|
|
|
|
import requests
|
|
import pandas as pd
|
|
from typing import List, Dict, Any
|
|
|
|
WIKIDATA_SEARCH_URL = "https://www.wikidata.org/w/api.php"
|
|
HEADERS = {
|
|
"User-Agent": "HIFFIArtistIntelligence/1.0 (sri@superlabs.ai) python-requests"
|
|
}
|
|
|
|
|
|
def search_wikidata_qid(artist_name: str) -> str:
|
|
"""Finds the most relevant Wikidata Q-identifier for a music artist."""
|
|
params = {
|
|
"action": "wbsearchentities",
|
|
"format": "json",
|
|
"language": "en",
|
|
"type": "item",
|
|
"search": artist_name,
|
|
"limit": 5,
|
|
}
|
|
try:
|
|
res = requests.get(WIKIDATA_SEARCH_URL, params=params, headers=HEADERS, timeout=10)
|
|
if res.status_code == 200:
|
|
data = res.json().get("search", [])
|
|
for item in data:
|
|
desc = item.get("description", "").lower()
|
|
if any(k in desc for k in ["rapper", "singer", "musician", "artist", "record producer", "songwriter", "drill"]):
|
|
return item.get("id")
|
|
if data:
|
|
return data[0].get("id")
|
|
except Exception:
|
|
pass
|
|
return ""
|
|
|
|
|
|
def get_identifiers_for_qid(qid: str) -> Dict[str, str]:
|
|
"""
|
|
Extracts P1902 (Spotify ID) and P2003 (Instagram username) directly
|
|
from the Wikidata item JSON claim tree.
|
|
"""
|
|
if not qid:
|
|
return {"spotify_id": "N/A", "instagram_handle": "N/A"}
|
|
|
|
url = f"https://www.wikidata.org/wiki/Special:EntityData/{qid}.json"
|
|
try:
|
|
res = requests.get(url, headers=HEADERS, timeout=10)
|
|
if res.status_code == 200:
|
|
entities = res.json().get("entities", {})
|
|
entity = entities.get(qid, {})
|
|
claims = entity.get("claims", {})
|
|
|
|
# P1902: Spotify artist ID
|
|
spotify_id = "N/A"
|
|
if "P1902" in claims:
|
|
for statement in claims["P1902"]:
|
|
mainsnak = statement.get("mainsnak", {})
|
|
datavalue = mainsnak.get("datavalue", {})
|
|
val = datavalue.get("value")
|
|
if val and isinstance(val, str):
|
|
spotify_id = val
|
|
break
|
|
|
|
# P2003: Instagram username
|
|
instagram_handle = "N/A"
|
|
if "P2003" in claims:
|
|
for statement in claims["P2003"]:
|
|
mainsnak = statement.get("mainsnak", {})
|
|
datavalue = mainsnak.get("datavalue", {})
|
|
val = datavalue.get("value")
|
|
if val and isinstance(val, str):
|
|
instagram_handle = val
|
|
break
|
|
|
|
return {
|
|
"spotify_id": spotify_id,
|
|
"instagram_handle": instagram_handle
|
|
}
|
|
except Exception:
|
|
pass
|
|
|
|
return {"spotify_id": "N/A", "instagram_handle": "N/A"}
|
|
|
|
|
|
def resolve_pilot_cohort(artist_names: List[str]) -> pd.DataFrame:
|
|
rows = []
|
|
for name in artist_names:
|
|
qid = search_wikidata_qid(name)
|
|
if qid:
|
|
ids = get_identifiers_for_qid(qid)
|
|
rows.append({
|
|
"name": name,
|
|
"wikidata_qid": qid,
|
|
"wikidata_uri": f"https://www.wikidata.org/wiki/{qid}",
|
|
"spotify_id": ids["spotify_id"],
|
|
"instagram_handle": ids["instagram_handle"],
|
|
"status": "RESOLVED"
|
|
})
|
|
else:
|
|
rows.append({
|
|
"name": name,
|
|
"wikidata_qid": "N/A",
|
|
"wikidata_uri": "N/A",
|
|
"spotify_id": "N/A",
|
|
"instagram_handle": "N/A",
|
|
"status": "NOT_FOUND"
|
|
})
|
|
return pd.DataFrame(rows) |