SPB Git forge

spb/crea-ka

Public

Créa·Ka — annuaire public cross-plateforme des créateurs de contenu québécois (crea-ka.com)

52commits 1branches 0releases
11.3 MBsize
maindefault branch
19 days agolast push
Python 73.6% HTML 13.2% TypeScript 6% JavaScript 4.5% CSS 1.7% Dockerfile 0.6%
3.7 KB · 82 lines python
Raw Blame History
1# ==============================================================================2# Author: Simon-Pierre Boucher <contact@spboucher.ai>3# File:   creaka/identity.py4# Desc:   Résolution d'identité — rattacher les bons comptes à la bonne personne5#         avec un score de confiance par signal (CLAUDE.md §12.1)6# ==============================================================================7"""Résolution d'identité : LE cœur du projet.89Chaque rattachement de compte porte un score `confidence` selon le signal qui10l'a produit (du plus fort au plus faible, §12.1). Sous `REVIEW_THRESHOLD`, le11compte est marqué « à vérifier » et EXCLU de la fiche publique plutôt que12rattaché à tort — mieux vaut un lien vérifié de moins qu'un lien erroné.13"""14from __future__ import annotations1516from .schema import PlatformAccount1718# signaux §12.1, du plus fort au plus faible19SIGNAL_CONFIDENCE: dict[str, float] = {20    "profil_source": 0.98,   # le compte DONT la fiche est issue (attribution inhérente)21    "link_in_bio": 0.95,     # page Linktree/Beacons du créateur listant le compte22    "api_officielle": 0.95,  # confirmé par l'API officielle de la plateforme23    "cross_link": 0.90,      # un profil vérifié pointe vers l'autre (bio IG → TikTok)24    "listes_medias": 0.85,   # listes/palmarès médias croisés (plusieurs sources concordantes)25    "base_publique": 0.85,   # base de connaissances publique curée (Wikidata/Wikipédia)26    "handle_match": 0.75,    # même handle + même nom d'affichage sur 2 plateformes27    "mention": 0.60,         # mentions réciproques / mêmes liens externes28    "inconnu": 0.40,29}3031# sous ce seuil : « à vérifier », jamais publié comme rattaché32REVIEW_THRESHOLD = 0.5033# à partir de ce seuil : compte considéré fiable pour la déduplication (§12.2)34STRONG_THRESHOLD = 0.75353637def confidence_for(signal: str) -> float:38    return SIGNAL_CONFIDENCE.get(signal, SIGNAL_CONFIDENCE["inconnu"])394041def account(platform: str, handle: str, signal: str, *,42            followers: int | None = None, url: str = "",43            verified: bool | None = None) -> PlatformAccount:44    """Fabrique un compte avec la confiance du signal fourni (helper connecteurs)."""45    return PlatformAccount(platform=platform, handle=handle, url=url,46                           followers=followers, verified=verified,47                           confidence=confidence_for(signal), signal=signal)484950def needs_review(acc: PlatformAccount) -> bool:51    return (acc.confidence or 0.0) < REVIEW_THRESHOLD525354def is_strong(acc: PlatformAccount) -> bool:55    return (acc.confidence or 0.0) >= STRONG_THRESHOLD565758def merge_accounts(existing: list[PlatformAccount],59                   incoming: list[PlatformAccount]) -> list[PlatformAccount]:60    """Union de deux listes de comptes — on ne perd AUCUN lien vérifié (§12.2).6162    Même compte (plateforme+handle) : la meilleure confiance gagne ; les champs63    factuels (abonnés, badge) prennent la valeur la plus fraîche non nulle.64    """65    by_key: dict[str, PlatformAccount] = {a.key: a for a in existing}66    for acc in incoming:67        prev = by_key.get(acc.key)68        if prev is None:69            by_key[acc.key] = acc70            continue71        best = acc if (acc.confidence or 0) >= (prev.confidence or 0) else prev72        other = prev if best is acc else acc73        if best.followers is None:74            best.followers = other.followers75        if best.verified is None:76            best.verified = other.verified77        best.metrics = {**other.metrics, **best.metrics}78        best.last_checked = max(best.last_checked, other.last_checked)79        by_key[acc.key] = best80    return sorted(by_key.values(),81                  key=lambda a: (a.followers is None, -(a.followers or 0)))82