VERIFY PASS pe corpus k-NN exemple etichetate (seed real 17181 Haiku, comis
in 756f777): suita 1392 passed, 1 deselected (live); smoke init_db seeder
(17181/NUL=2200/idempotent); toate codurile in nomenclator.
US-007 (cerere user la CLOSE) — badge sursa pe sugestia fuzzy din editor:
- _mapari.html: chip confirmat (GOLD) / similar (SILVER+k-NN) / non-operatie (NUL)
- base.html: .sugg-sursa--{confirmat,similar,nul} pe tokeni de tema (color-mix)
- routes.py: cheia `nul` adaugata in surse_sugestie default (finding cross-file)
- tests/test_web_badge_sursa.py: gold/silver/nul/fara-sursa (4 teste)
- E2E render live verificat in serverul real (/_fragments/mapari)
CLOSE /code-review high (main..HEAD, 3 finder x 8 unghiuri) — runtime curat,
invariant #13 intact; 3 findings low/cosmetic REPARATE + lock-uite:
- shared_store.seed_suggestions: cod whitespace -> NULL (era ''), + test lock
- genereaza_seed.py: with open(...) in loc de open().read() (FD leak tool offline)
- embeddings.py: docstring-uri aliniate la [{cod, is_nul, similaritate}]
ROADMAP: 5.18 LIVRAT. PRD: raport VERIFY/CLOSE scris.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
143 lines
5.1 KiB
Python
143 lines
5.1 KiB
Python
"""Store partajat pentru sugestii (SILVER) si mapari validate de oameni (GOLD cross-account).
|
|
|
|
Straturi (L14-S3, PRD 5.14):
|
|
- mapping_suggestions (SILVER): sugestii LLM/embedding, citite DOAR de suggest/pending_unmapped,
|
|
NICIODATA de load_mapping/resolve_prestatii (separare structurala #13).
|
|
- shared_mappings (GOLD partajat): mapari validate de om din orice cont; pot pre-completa
|
|
editorul (suggestion-only cross-account, F-A/#11); auto-send DOAR GOLD propriu
|
|
(operations_mapping per-cont, Eng-F2).
|
|
|
|
Invariante:
|
|
- INSERT OR IGNORE la seed: nu suprascrie randuri existente (#2).
|
|
- NUL = is_nul=1, cod_prestatie NULL; NU se promoveaza la cod RAR (#4).
|
|
- source/confidence pe fiecare rand (provenienta + rollback batch model prost, #5).
|
|
- Wiring in resolve_prestatii/load_mapping vine in L14-S6; modulul de fata e API pur.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sqlite3
|
|
from typing import Any
|
|
|
|
from .mapping import normalize_for_match
|
|
|
|
|
|
def seed_suggestions(
|
|
conn: sqlite3.Connection,
|
|
items: list[dict[str, Any]],
|
|
) -> int:
|
|
"""Insereaza sugestii in mapping_suggestions (SILVER). INSERT OR IGNORE.
|
|
|
|
Nu suprascrie randuri deja existente (#2): re-rularea seeder-ului e sigura.
|
|
Fiecare item trebuie sa contina:
|
|
- 'denumire': str — text brut (se normalizeaza intern cu normalize_for_match)
|
|
- 'source': str — 'llm', 'embedding', etc.
|
|
Optional:
|
|
- 'cod_prestatie': str | None — ignorat cand is_nul=True
|
|
- 'is_nul': bool — True pt non-operatii (supresie, #4); cod_prestatie stocat NULL
|
|
- 'confidence': float — 0..1 (default 0.0)
|
|
|
|
Returneaza numarul de randuri inserate efectiv (0 daca toate existau deja).
|
|
"""
|
|
inserted = 0
|
|
for item in items:
|
|
den_norm = normalize_for_match(item.get("denumire") or "")
|
|
if not den_norm:
|
|
continue
|
|
is_nul = 1 if item.get("is_nul") else 0
|
|
# NUL -> cod NULL obligatoriu (supresie stricta, #4)
|
|
# Normalizeaza INAINTE de truthiness: un cod whitespace-only (" ") sau
|
|
# ne-string trebuie sa devina NULL, nu '' (altfel rand non-NUL cu cod gol).
|
|
cod = None
|
|
if not is_nul:
|
|
raw_cod = str(item.get("cod_prestatie") or "").strip().upper()
|
|
cod = raw_cod or None
|
|
source = str(item.get("source") or "llm")
|
|
confidence = float(item.get("confidence") or 0.0)
|
|
cur = conn.execute(
|
|
"""
|
|
INSERT OR IGNORE INTO mapping_suggestions
|
|
(denumire_normalizata, cod_prestatie, is_nul, source, confidence)
|
|
VALUES (?, ?, ?, ?, ?)
|
|
""",
|
|
(den_norm, cod, is_nul, source, confidence),
|
|
)
|
|
inserted += cur.rowcount
|
|
return inserted
|
|
|
|
|
|
def lookup_suggestion(
|
|
conn: sqlite3.Connection,
|
|
denumire: str,
|
|
) -> sqlite3.Row | None:
|
|
"""Cauta sugestie SILVER dupa denumire normalizata.
|
|
|
|
Returneaza randul din mapping_suggestions sau None daca nu exista.
|
|
NOTA: apelantul trebuie sa verifice is_nul inainte de a folosi cod_prestatie.
|
|
"""
|
|
den_norm = normalize_for_match(denumire)
|
|
if not den_norm:
|
|
return None
|
|
return conn.execute(
|
|
"SELECT * FROM mapping_suggestions WHERE denumire_normalizata = ?",
|
|
(den_norm,),
|
|
).fetchone()
|
|
|
|
|
|
def lookup_shared_gold(
|
|
conn: sqlite3.Connection,
|
|
denumire: str,
|
|
) -> sqlite3.Row | None:
|
|
"""Cauta mapare GOLD partajata dupa denumire normalizata.
|
|
|
|
Returneaza randul din shared_mappings sau None daca nu exista.
|
|
NOTA (F-A/#11): acest GOLD partajat e suggestion-only cross-account;
|
|
auto-send vine DOAR din operations_mapping (GOLD propriu per-cont).
|
|
"""
|
|
den_norm = normalize_for_match(denumire)
|
|
if not den_norm:
|
|
return None
|
|
return conn.execute(
|
|
"SELECT * FROM shared_mappings WHERE denumire_normalizata = ?",
|
|
(den_norm,),
|
|
).fetchone()
|
|
|
|
|
|
def record_human_validation(
|
|
conn: sqlite3.Connection,
|
|
denumire: str,
|
|
cod_prestatie: str,
|
|
*,
|
|
source: str = "human",
|
|
provenance: str | None = None,
|
|
confidence: float = 1.0,
|
|
) -> None:
|
|
"""Inregistreaza o mapare validata de om in GOLD partajat (shared_mappings).
|
|
|
|
Daca denumirea exista deja: incrementeaza confirmations + actualizeaza updated_at.
|
|
Daca nu exista: insert nou cu confirmations=1.
|
|
|
|
Apelat la confirmarea umana a unei mapari (din editorul needs_mapping).
|
|
Wiring efectiv vine in L14-S6 (dupa 5.15); aceasta functie e API-ul store.
|
|
|
|
NOTA: NU intra in operations_mapping (GOLD per-cont) — acela e gestionat
|
|
separat de editorul existent. Ambele pot coexista.
|
|
"""
|
|
den_norm = normalize_for_match(denumire)
|
|
if not den_norm:
|
|
return
|
|
cod = (cod_prestatie or "").strip().upper()
|
|
if not cod:
|
|
return
|
|
conn.execute(
|
|
"""
|
|
INSERT INTO shared_mappings
|
|
(denumire_normalizata, cod_prestatie, source, provenance, confidence, confirmations)
|
|
VALUES (?, ?, ?, ?, ?, 1)
|
|
ON CONFLICT(denumire_normalizata) DO UPDATE SET
|
|
confirmations = confirmations + 1,
|
|
updated_at = datetime('now')
|
|
""",
|
|
(den_norm, cod, source, provenance, confidence),
|
|
)
|