[DTP-Worker 20260730_085719] Auto exec · session 20260730_085719
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
CI / Contraintes NON-NÉGOCIABLES (CLAUDE.md) (push) Has been cancelled
CI / Validation JSON (schémas Faisabilité) (push) Has been cancelled
CI / Qualité documentaire (liens + 4Big) (push) Has been cancelled
CI / Publiciste · parser + schéma + generator (unittest) (push) Has been cancelled
CI / RBAC · 50 rôles + schéma (unittest) (push) Has been cancelled
CI / Faisabilité · générateur 4 volets + round-trip (unittest) (push) Has been cancelled
CI / RBAC · fixtures ERPNext (Role + Custom DocPerm) (push) Has been cancelled
CI / RBAC · plan User Permission (row-level) (push) Has been cancelled
CI / RBAC · Role Profile (bundles par portail) (push) Has been cancelled
CI / RBAC · run-book d'application unifié (agrégat 3 volets) (push) Has been cancelled
CI / Faisabilité · dossier bancable trilingue FR/EN/ES (push) Has been cancelled
CI / CRM · workflow vente ERPNext (lead → CONFOTUR) (push) Has been cancelled
CI / CRM · DocType porteur OTO Dossier Vente (push) Has been cancelled
CI / CRM · barème commissions vendeurs (push) Has been cancelled
CI / Fiscal · e-CF DGII (Compupar) (push) Has been cancelled
CI / Frontend · Workspaces 5 portails rôle (push) Has been cancelled
CI / Legal · DocType CONFOTUR Application (push) Has been cancelled
CI / QA · Audit 5D conformité (push) Has been cancelled
CI / SEO · mots-clés trilingues + schema.org + hreflang (push) Has been cancelled
CI / E2E baseline Playwright (manuel) (push) Has been cancelled
CI / Gate qualité (agrégat) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
"""Dependances partagees du generateur SEO · reutilisation stricte (#5).
|
||||
|
||||
On NE reimplemente rien qui existe deja dans le mandat :
|
||||
- `validator` (validateur JSON-Schema draft-07 maison, zero pip) et
|
||||
- `branding` (tokens de marque #4 + STATUTS_SANS_PRIX / devise #10)
|
||||
sont importes du livrable Publiciste, source unique de verite pour ces deux
|
||||
briques. Le contrat d'entree consomme est `projets_master.schema.json` (contrat
|
||||
Faisabilite<->Publiciste), lui aussi partage.
|
||||
|
||||
Fonctions locales minimales : chargement des fichiers + `slugify` deterministe
|
||||
(pas de dependance externe, pas d'horloge).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import unicodedata
|
||||
|
||||
_HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
_SEO = os.path.dirname(_HERE)
|
||||
_DELIVERABLES = os.path.normpath(os.path.join(_SEO, "..")) # 05_deliverables_mvp/
|
||||
_PUBLICISTE = os.path.join(_DELIVERABLES, "publiciste")
|
||||
_FAISABILITE = os.path.join(_DELIVERABLES, "faisabilite")
|
||||
|
||||
if _PUBLICISTE not in sys.path:
|
||||
sys.path.insert(0, _PUBLICISTE)
|
||||
|
||||
from lib import branding # type: ignore # noqa: E402
|
||||
from lib import validator as maison # type: ignore # noqa: E402
|
||||
|
||||
# Chemins des contrats/entrees par defaut.
|
||||
SPEC_PATH = os.path.join(_SEO, "seo_spec.json")
|
||||
SCHEMA_PATH = os.path.join(_SEO, "seo.schema.json")
|
||||
MASTER_DEFAULT = os.path.join(_SEO, "fixtures", "projets_master.json")
|
||||
MASTER_SCHEMA_PATH = os.path.join(_FAISABILITE, "projets_master.schema.json")
|
||||
|
||||
|
||||
def load_json(path: str) -> dict:
|
||||
with open(path, encoding="utf-8") as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
def load_spec() -> dict:
|
||||
return load_json(SPEC_PATH)
|
||||
|
||||
|
||||
def load_master(path: str | None = None) -> dict:
|
||||
return load_json(path or MASTER_DEFAULT)
|
||||
|
||||
|
||||
def validate_master(master: dict) -> list[str]:
|
||||
"""Verifie que l'entree respecte le contrat Publiciste avant tout usage."""
|
||||
return list(maison.validate(master, load_json(MASTER_SCHEMA_PATH)))
|
||||
|
||||
|
||||
def validate(instance, schema: dict) -> list[str]:
|
||||
return list(maison.validate(instance, schema))
|
||||
|
||||
|
||||
_SLUG_STRIP = re.compile(r"[^a-z0-9]+")
|
||||
|
||||
|
||||
def slugify(text: str) -> str:
|
||||
"""Slug URL deterministe : minuscule, sans accent, mots joints par '-'.
|
||||
|
||||
Conserve les chiffres presents dans la donnee source (ex. « 1069 Crisfer »
|
||||
-> « 1069-crisfer ») : on ne retire ni n'invente aucun chiffre (#6)."""
|
||||
norm = unicodedata.normalize("NFKD", text)
|
||||
norm = "".join(c for c in norm if not unicodedata.combining(c))
|
||||
return _SLUG_STRIP.sub("-", norm.lower()).strip("-")
|
||||
|
||||
|
||||
def digits(text: str) -> set[str]:
|
||||
"""Ensemble des chiffres presents dans une chaine (audit anti-invention)."""
|
||||
return set(re.findall(r"\d", text))
|
||||
|
||||
|
||||
# Re-exports pour les modules soeurs (une seule source d'import).
|
||||
STATUTS_SANS_PRIX = branding.STATUTS_SANS_PRIX
|
||||
DEVISE_PRIMAIRE = branding.DEVISE_PRIMAIRE
|
||||
Reference in New Issue
Block a user