""" core/confidence_scorer.py Computes composite authenticity confidence scores (0.0 - 1.0) across YouTube channel titles, MusicBrainz, and Wikidata with noise-cleaning and cross-referencing. """ import re from typing import Dict, Any, Optional class ConfidenceScorer: """ Evaluates multi-source identity alignment across YouTube, MusicBrainz, and Wikidata. """ def __init__( self, yt_weight: float = 0.40, mb_weight: float = 0.35, wd_weight: float = 0.25, ): self.yt_weight = yt_weight self.mb_weight = mb_weight self.wd_weight = wd_weight @staticmethod def _clean_string(text: Optional[str]) -> str: """Strips common artist channel noise tokens and punctuation.""" if not text: return "" s = text.lower() # Remove suffixes like VEVO, Official, Records, Music, Topic noise_tokens = [ r"\bvevo\b", r"\bofficial\b", r"\btopic\b", r"\bmusic\b", r"\brecords\b", r"\btv\b", r"\bchannel\b", ] for token in noise_tokens: s = re.sub(token, "", s) # Remove all non-alphanumeric characters return re.sub(r"[^a-z0-9]", "", s) def _match_youtube(self, artist_name: str, yt_title: Optional[str]) -> float: """Evaluates whether the YouTube channel title matches the artist.""" if not yt_title or not artist_name: return 0.0 clean_artist = self._clean_string(artist_name) clean_title = self._clean_string(yt_title) if not clean_artist or not clean_title: return 0.0 # Exact match after cleaning (e.g. 'KendrickLamarVEVO' -> 'kendricklamar' == 'kendricklamar') if clean_artist == clean_title: return 1.0 # Substring containment if clean_artist in clean_title or clean_title in clean_artist: return 0.85 return 0.0 def calculate_score( self, yt_metrics: Dict[str, Any], mb_result: Dict[str, Any], wd_result: Dict[str, Any], ) -> Dict[str, Any]: """ Computes composite confidence based on: - YouTube channel name alignment (40%) - MusicBrainz match verification (35%) - Wikidata entity validation (25%) - Cross-referencing bonus: MBID linked directly inside Wikidata """ artist_name = yt_metrics.get("channel_title") or "" # If artist name was provided separately in the identifier dict, we compare against channel_title yt_title = yt_metrics.get("channel_title") raw_name = yt_metrics.get("name") or yt_title # 1. YouTube Title Alignment Score yt_score = self._match_youtube(raw_name, yt_title) # 2. MusicBrainz Score mb_match = mb_result.get("verified", False) if isinstance(mb_result, dict) else bool(mb_result) mb_score = 1.0 if mb_match else 0.0 # 3. Wikidata Score wd_match = wd_result.get("verified", False) if isinstance(wd_result, dict) else bool(wd_result) wd_score = 1.0 if wd_match else 0.0 # 4. Cross-Referencing Bonus: Wikidata linked to MusicBrainz ID cross_ref_bonus = 0.0 if isinstance(wd_result, dict) and isinstance(mb_result, dict): wd_mbid = wd_result.get("musicbrainz_id") actual_mbid = mb_result.get("mbid") if wd_mbid and actual_mbid and wd_mbid == actual_mbid: cross_ref_bonus = 0.05 # Extra 5% confidence for direct KG linkage # Composite calculation composite = ( self.yt_weight * yt_score + self.mb_weight * mb_score + self.wd_weight * wd_score + cross_ref_bonus ) composite = round(min(composite, 1.0), 2) # Tier classification if composite >= 0.80: tier = "High Confidence" elif composite >= 0.50: tier = "Medium Confidence" else: tier = "Low / Emerging" return { "composite_confidence": composite, "confidence_tier": tier, "sub_scores": { "youtube": yt_score, "musicbrainz": mb_score, "wikidata": wd_score, "cross_referenced": cross_ref_bonus > 0.0, }, }