Files
rar-autopass/app/shared_store.py
Claude Agent 3fc53534e2 feat(5.15+5.14): CLOSE — fix-uri code-review + embeddings functional
5.15 (propagare design + dashboard editare) si 5.14 (mapare LLM distilata)
inchise dupa /code-review high. 8 buguri reparate TDD:

- HIGH modal nu se deschidea pe randul slim (base.html: trimitere-slim)
- HIGH /repune trunchia prestatii (declaratie incompleta la RAR) -> iterare
  peste existing, codes pozitional
- HIGH embeddings incarca model ~230MB degeaba pe corpus gol -> poarta has_corpus()
- HIGH picker chips gol pe re-render eroare -> conn/account_id pe toate ramurile
- MED obs re-derivat dupa stergere explicita -> _merge_override pastreaza obs=''
- MED mapare salvata fara denumire poluă GOLD -> _record_gold_validation guard
- MED typo nome_prestatie -> nume_prestatie in select /repune
- MED bucketare timp +3h gresita iarna -> SQLite localtime + TZ=Europe/Bucharest

Embeddings WIRE-uit functional (PRD #15, decizie user): ensure_embeddings_corpus
construieste corpus din nomenclator, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default
off). Marime model corectata ~50MB->~230MB (estimare PRD gresita).

Cleanup: hoist load_* din bucla bulk-fix; import re la top.
Regresie: 1256 passed, 1 deselected (live), 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 20:48:34 +00:00

140 lines
4.9 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)
cod = None if is_nul else ((item.get("cod_prestatie") or "") or None)
if cod:
cod = cod.strip().upper()
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),
)