Phase 3 : sources, citations et seed complet — 368 sources, 0 texte orphelin
Parseurs : - blocs_claim : les quatre dialectes des rapports de dimension. Le cahier des charges en postulait un seul ; sans les quatre, dim05 à dim12 étaient perdus, soit les dimensions associations, entreprises, migration et calendrier. - notes_bas_page : 258 notes, dont 104 sans URL. Les identifiants JORFTEXT donnent lieu à une URL canonique reconstruite (schéma fixe, rien d'inventé) ; les autres restent sans lien plutôt que d'être devinées. - bibliographie : 481 références, toutes liées. Indexées par numéro de loi, par affaire DC et par cote de document parlementaire — cette dernière étant la seule identité d'un texte déposé et non examiné. - chapitres : rattachement phrase par phrase, résumés repris tels quels. Résolution des citations : 100 % des 332 marqueurs du rapport, 93 % avec un lien vérifiable. Il a fallu six conventions de numérotation, une par famille de dimension (marqueur de fin, en-tête de claim, section, rang, note, section.rang). Trois pièges corrigés, tous couverts par des tests : - « décision n° 2026-903 DC » lu comme une loi, ce qui volait à la loi 2026-403 les phrases — et donc les sources — qui parlaient d'elle - retrait des marqueurs laissant « … dans les DROM . » ; corrigé à la source sans toucher aux espaces avant « ; » et « » » qu'exige le français - « programmation militaire 2024-2030 » lu comme la loi n° 2024-2030 make seed : 52 textes, 368 sources, 83 événements, 12 affaires CC, 7 analyses. Zéro texte sans source URL (critère §8.2). Les 2 textes sans résumé et les 17 pertinences déduites sont marqués « à vérifier » avec leur motif. 99 tests. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c36b8dc631
commit
c28243cf87
@@ -0,0 +1,92 @@
|
||||
"""Détection de la pertinence guadeloupéenne et ultramarine d'un texte.
|
||||
|
||||
Le tableau A de l'annexe cote lui-même les 27 lois promulguées ; le tableau B
|
||||
ne cote pas les textes en cours. Pour ces derniers, la cotation est déduite du
|
||||
vocabulaire employé par le rapport, avec des poids déclarés dans
|
||||
`pipeline/config.yaml`.
|
||||
|
||||
Toute cotation obtenue ainsi est signalée : le texte porte `a_verifier = 1` et
|
||||
un motif explicite. L'interface l'affiche comme telle. Une cotation déduite
|
||||
n'est pas une cotation établie.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import functools
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
|
||||
import yaml
|
||||
|
||||
from pipeline import chemins
|
||||
from pipeline.modeles import Pertinence
|
||||
from pipeline.parseurs.dates_fr import sans_accents
|
||||
|
||||
|
||||
@dataclass(slots=True)
|
||||
class Cotation:
|
||||
"""Résultat d'une détection, avec ce qui l'a motivée."""
|
||||
|
||||
pertinence: Pertinence | None
|
||||
score: int
|
||||
expressions: list[str]
|
||||
|
||||
@property
|
||||
def note(self) -> str | None:
|
||||
if not self.expressions:
|
||||
return None
|
||||
return "Détecté d'après : " + ", ".join(self.expressions[:6])
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def _configuration() -> dict:
|
||||
return yaml.safe_load(chemins.CONFIG.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
@functools.lru_cache(maxsize=1)
|
||||
def _motifs() -> list[tuple[re.Pattern[str], str, int]]:
|
||||
"""Compile un motif par expression, ancré sur des frontières de mot."""
|
||||
expressions = _configuration()["guadeloupe"]["expressions"]
|
||||
compiles: list[tuple[re.Pattern[str], str, int]] = []
|
||||
|
||||
for expression, poids in expressions.items():
|
||||
plat = sans_accents(str(expression)).lower()
|
||||
compiles.append((re.compile(rf"(?<!\w){re.escape(plat)}\w{{0,3}}(?!\w)"), plat, int(poids)))
|
||||
|
||||
# Les expressions les plus lourdes d'abord : le compte-rendu cite en
|
||||
# priorité ce qui a réellement décidé de la cotation.
|
||||
compiles.sort(key=lambda triplet: -triplet[2])
|
||||
return compiles
|
||||
|
||||
|
||||
def coter(*fragments: str | None) -> Cotation:
|
||||
"""Cote la pertinence guadeloupéenne d'un ensemble de fragments de texte."""
|
||||
plat = sans_accents(" ".join(f for f in fragments if f)).lower()
|
||||
if not plat.strip():
|
||||
return Cotation(pertinence=None, score=0, expressions=[])
|
||||
|
||||
score = 0
|
||||
trouvees: list[str] = []
|
||||
|
||||
for motif, expression, poids in _motifs():
|
||||
if motif.search(plat):
|
||||
score += poids
|
||||
trouvees.append(expression)
|
||||
|
||||
seuils = _configuration()["guadeloupe"]["seuils"]
|
||||
if score >= seuils["forte"]:
|
||||
pertinence = Pertinence.FORTE
|
||||
elif score >= seuils["moyenne"]:
|
||||
pertinence = Pertinence.MOYENNE
|
||||
elif score >= seuils["faible"]:
|
||||
pertinence = Pertinence.FAIBLE
|
||||
else:
|
||||
pertinence = None
|
||||
|
||||
return Cotation(pertinence=pertinence, score=score, expressions=trouvees)
|
||||
|
||||
|
||||
MOTIF_VERIFICATION = (
|
||||
"Pertinence Guadeloupe déduite automatiquement : l'annexe du rapport ne "
|
||||
"cote que les lois promulguées, pas les textes encore en discussion."
|
||||
)
|
||||
Reference in New Issue
Block a user