Skip to content

Latest commit

 

History

History
199 lines (150 loc) · 6.55 KB

File metadata and controls

199 lines (150 loc) · 6.55 KB

Référence API Python

pip install unspsc-fr

Résolution de la base

UNSPSC() cherche la base SQLite dans cet ordre :

  1. UNSPSC(db_path=...) — chemin passé explicitement
  2. variable d'environnement UNSPSC_FR_DB
  3. cache local (~/.cache/unspsc-fr/<version>/unspsc-bilingue.sqlite), téléchargée depuis la release GitHub si absente (~88 Mo, une seule fois)
from unspsc_fr import UNSPSC

u = UNSPSC()                                    # cache/téléchargement automatique
u = UNSPSC(db_path="./unspsc-bilingue.sqlite")   # base locale explicite

UNSPSC peut s'utiliser comme gestionnaire de contexte pour fermer proprement la connexion :

with UNSPSC() as u:
    print(u.get(10101501).title)

Entry

Toutes les méthodes de lecture renvoient des Entry (dataclass immuable) :

@dataclass(frozen=True)
class Entry:
    code: int
    level: str                    # 'segment' | 'family' | 'class' | 'commodity'
    title_en: str
    title_fr: Optional[str]
    definition_en: Optional[str] = None

    @property
    def title(self) -> str: ...          # title_fr si présent, sinon title_en
    @property
    def is_translated(self) -> bool: ... # title_fr is not None
entry = u.get(10101501)
entry.title            # 'Chats'
entry.title_en         # 'Cats'
entry.is_translated     # True

entry = u.get(57000000)  # segment sans traduction française
entry.title             # 'Humanitarian Relief Items, Kits, or Accessories' (repli EN)
entry.is_translated     # False

UNSPSC.get(code, level=None)

Renvoie l'Entry pour un code, ou None s'il n'existe pas.

u.get(10101501)                        # → Entry(level='commodity', …)
u.get(99999999)                        # → None

Pour les rares codes existant à deux niveaux (voir classification.md), level lève l'ambiguïté ; sans lui, le niveau canonique (le plus haut) est retourné :

u.get(26132000)                        # → level='class' (canonique)
u.get(26132000, level="commodity")     # → level='commodity'

UNSPSC.children(code=None)

Enfants directs d'un code. Sans argument : les 58 segments racine. Sur un produit (niveau le plus profond) : liste vide.

u.children()              # les 58 segments
u.children(10100000)      # classes de la famille "Animaux vivants"
u.children(10101501)      # [] : un produit n'a pas d'enfants

UNSPSC.ancestors(code, level=None)

Chaîne des parents, du segment jusqu'au parent direct (exclut le code lui-même) :

[e.level for e in u.ancestors(10101501)]
# ['segment', 'family', 'class']

UNSPSC.search(query, lang=None, level=None, limit=20)

Recherche plein texte (SQLite FTS5), insensible aux accents et à la casse, classée par pertinence (bm25). Le dernier mot de query est traité en préfixe.

u.search("ciment", lang="fr")
u.search("electricite", lang="fr")        # sans accent : trouve "électricité"
u.search("cemen", lang="en", level="commodity", limit=5)   # préfixe + filtres

Fonctions utilitaires (sans base de données)

from unspsc_fr import level_of, parent_code

level_of(10101501)              # 'commodity' — déduit de la forme du code
level_of(10100000)              # 'family'

parent_code(10101501, "commodity")   # 10101500
parent_code(10101500, "class")       # 10100000
parent_code(10000000, "segment")     # None

Ces deux fonctions n'ouvrent aucune connexion : elles opèrent uniquement sur l'arithmétique du code (voir classification.md), utile pour valider ou trier des codes dans un pipeline sans dépendre de la base.

Recettes

Repérer des candidats de code depuis une description produit. search() fait une recherche plein texte à correspondance exacte sur tous les mots (logique ET) : une requête à plusieurs mots ne renvoie rien si un seul des mots est absent des libellés indexés. Une phrase telle que « sacs de ciment » échoue donc souvent telle quelle — les mots de liaison (« de », « pour », « et »…) n'apparaissent pas dans les libellés UNSPSC. Filtrer ces mots et retomber sur les termes les plus distinctifs donne de meilleurs candidats :

from unspsc_fr import UNSPSC

STOPWORDS = {"de", "du", "des", "le", "la", "les", "un", "une", "et", "pour", "en", "avec"}

def candidates(u: UNSPSC, description: str, limit: int = 3):
    tokens = [w for w in description.lower().split() if w not in STOPWORDS]
    for q in (" ".join(tokens), *tokens):   # phrase filtrée, puis mot par mot
        if q and (hits := u.search(q, lang="fr", level="commodity", limit=limit)):
            return hits
    return []

with UNSPSC() as u:
    for description in ["sacs de ciment", "ordinateur portable"]:
        hits = candidates(u, description)
        print(description, "→", [(e.code, e.title) for e in hits])

C'est une heuristique de repérage, pas un classifieur : elle renvoie des candidats plausibles par correspondance lexicale, sans compréhension sémantique — « service de conseil » ne trouvera pas la bonne classe si ses mots-clés n'apparaissent pas explicitement dans les libellés. Une classification fiable sur des descriptions libres demande des embeddings sémantiques (voir les pistes évoquées dans le README du dépôt).

Construire un fil d'Ariane (breadcrumb) pour un produit :

def breadcrumb(u: UNSPSC, code: int) -> str:
    chain = [*u.ancestors(code), u.get(code)]
    return " > ".join(e.title for e in chain if e)

breadcrumb(u, 10101501)
# 'Fournitures, accessoires et matériel pour plantes et animaux vivants > Animaux vivants > Bétail > Chats'

Exporter tous les produits traduits d'un segment en CSV :

import csv

with UNSPSC() as u, open("segment_10.csv", "w", newline="") as f:
    writer = csv.writer(f)
    for family in u.children(10000000):
        for klass in u.children(family.code):
            for product in u.children(klass.code):
                if product.is_translated:
                    writer.writerow([product.code, product.title_fr])

Publier une nouvelle version du package

La version est déclarée une seule fois, dans unspsc_fr.__version__ (python/src/unspsc_fr/__init__.py) — pyproject.toml la lit dynamiquement via tool.hatch.version.

# 1. modifier __version__ dans python/src/unspsc_fr/__init__.py
git commit -am "unspsc-fr 0.2.0"
git push
git tag py-v0.2.0
git push origin py-v0.2.0   # déclenche .github/workflows/publish-pypi.yml