Files
rar-autopass/app/web/labels.py
Claude Agent d7ba1195d4 feat(web): dashboard compact — import pe Acasa, status cu bife, Trimiteri lizibile, Mapari complete (3.5)
Acasa = ecran de import (tab Import scos, ?tab=import->Acasa). Bara status
compacta pe 2 randuri cu bife accesibile (glife + text) + data formatata.
'Coada'->'Trimiteri': coloane RO, stare umana, detaliu la click in panou
dedicat. Mapari pe 3 sectiuni (de rezolvat / op salvate / formate coloane),
Cont doar cheie+creds. Filtrare Trimiteri, corectie inline needs_data cu
re-enqueue + detectie coliziune idempotency, badge contoare pe tab-uri.
Helper pur partajat payload_view.py (web + GET /v1/prezentari).
Backend trimitere (worker/idempotenta/mapping/schema) neatins. 483 teste.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 08:56:45 +00:00

203 lines
6.8 KiB
Python

"""
labels.py — traducere stari tehnice in text uman + clasa CSS (US-001, PRD 3.4).
Functii pure: fara DB, fara request. Usor de testat unitar si de importat in template-uri.
Sursa de adevar pentru texte: tabelul din PRD 3.4 §3 US-001.
"""
import json
from datetime import datetime
from typing import Tuple
# ---------------------------------------------------------------------------
# Tipul returnat: (text_principal, subtext_tooltip, css_class)
# ---------------------------------------------------------------------------
Eticheta = Tuple[str, str, str]
# ---------------------------------------------------------------------------
# Etichete stari submissions
# Clasele CSS corespund celor definite in base.html:
# s-queued (accent/albastru), s-sending (warn/galben), s-sent (ok/verde),
# s-error, s-needs_data, s-needs_mapping (err/rosu).
# ---------------------------------------------------------------------------
STARI_SUBMISSION: dict[str, Eticheta] = {
"queued": (
"In asteptare sa fie trimise",
"",
"s-queued",
),
"sending": (
"Se trimite acum",
"",
"s-sending",
),
"sent": (
"Declarate la RAR (finalizate)",
"Confirmate cu numar de prezentare; nu se mai pot modifica.",
"s-sent",
),
"needs_mapping": (
"Lipseste codul prestatiei",
"Alege codul RAR in tab-ul Mapari.",
"s-needs_mapping",
),
"needs_data": (
"Date incomplete (respinse de RAR)",
"Corecteaza randul si reimporta.",
"s-needs_data",
),
"error": (
"Eroare la trimitere",
"Vezi detaliul randului; se reincearca automat sau necesita corectie.",
"s-error",
),
}
def eticheta_stare(status: str) -> Eticheta:
"""
Returneaza (text, subtext, css_class) pentru o stare de submission.
Arunca KeyError daca starea nu este mapata — intentionat, ca sa prinda
stari noi adaugate in schema fara mapare corespunzatoare.
"""
try:
return STARI_SUBMISSION[status]
except KeyError:
raise KeyError(
f"Starea de submission {status!r} nu are eticheta umana in labels.py. "
"Adauga-o in STARI_SUBMISSION."
)
# ---------------------------------------------------------------------------
# Etichete worker (viu / mort)
# ---------------------------------------------------------------------------
def eticheta_worker(viu: bool) -> Eticheta:
"""
Returneaza (text, subtext, css_class) pentru starea worker-ului.
viu=True => "Trimitere automata: activa" (clasa s-sent / verde)
viu=False => "Trimitere automata: oprita" (clasa s-error / rosu)
"""
if viu:
return (
"Trimitere automata: activa",
"Sistemul verifica coada si trimite la RAR la fiecare cateva secunde.",
"s-sent",
)
return (
"Trimitere automata: oprita",
"Nimic nu pleaca spre RAR pana reporneste. Anunta administratorul.",
"s-error",
)
# ---------------------------------------------------------------------------
# Etichete conexiune RAR (ok / indisponibil)
# ---------------------------------------------------------------------------
def eticheta_rar(stare: str) -> Eticheta:
"""
Returneaza (text, subtext, css_class) pentru starea conexiunii cu RAR.
stare="ok" => "Legatura cu RAR: functionala" (s-sent / verde)
stare="indisponibil" => "Legatura cu RAR: indisponibila" (s-error / rosu)
"""
if stare == "ok":
return (
"Legatura cu RAR: functionala",
"Portalul AUTOPASS raspunde.",
"s-sent",
)
return (
"Legatura cu RAR: indisponibila",
"Portalul RAR nu raspunde acum; coada se reia automat cand revine.",
"s-error",
)
# ---------------------------------------------------------------------------
# Format data RAR (US-001, PRD 3.5)
# ---------------------------------------------------------------------------
def format_data_rar(raw: object) -> str:
"""Formateaza un timestamp ISO ca `dd.mm.yyyy hh24:mi:ss` (ora romaneasca).
- Valoare lipsa (None / "") -> "".
- ISO valid (cu sau fara timezone / 'Z' / microsecunde) -> data formatata,
fara fractiuni de secunda.
- Format invalid -> fallback grijuliu: intoarce stringul brut (nu arunca),
ca operatorul sa vada totusi ceva, nu o pagina rupta.
"""
if raw is None:
return ""
s = str(raw).strip()
if not s:
return ""
iso = s.replace("Z", "+00:00") if s.endswith("Z") else s
try:
dt = datetime.fromisoformat(iso)
except (ValueError, TypeError):
return s
return dt.strftime("%d.%m.%Y %H:%M:%S")
# ---------------------------------------------------------------------------
# Motiv uman din rar_error (US-004, PRD 3.5)
# ---------------------------------------------------------------------------
def motiv_uman(status: str, rar_error: object) -> str:
"""Transforma `rar_error` (JSON tehnic) intr-un motiv lizibil pentru coloana Motiv.
Formele intalnite (vezi router.py / mapping.py):
- validare continut: list[{field, message}] -> mesajele concatenate.
- operatie nemapata: {"unmapped": [{cod_op_service, denumire}]}.
- auto-send oprit: {"auto_send": "..."}.
- eroare RAR: text simplu sau dict generic.
Fara rar_error -> "". Nu arunca niciodata (degradeaza la text brut trunchiat).
"""
if not rar_error:
return ""
raw = rar_error if isinstance(rar_error, str) else str(rar_error)
try:
data = json.loads(raw)
except (ValueError, TypeError):
return raw[:160]
if isinstance(data, dict):
if "unmapped" in data:
ops = data.get("unmapped") or []
nume = ", ".join(
(o.get("cod_op_service") or "") for o in ops if isinstance(o, dict)
).strip(", ")
return f"Cod RAR lipsa pentru: {nume}" if nume else "Cod RAR lipsa"
if "auto_send" in data:
return "Necesita confirmare manuala (auto-send oprit pentru cod)"
parti = [f"{k}: {v}" for k, v in data.items()]
return "; ".join(parti)[:200]
if isinstance(data, list):
msgs: list[str] = []
for e in data:
if isinstance(e, dict):
msgs.append(
str(e.get("message") or e.get("msg") or "; ".join(str(v) for v in e.values()))
)
else:
msgs.append(str(e))
return "; ".join(m for m in msgs if m)[:200]
return str(data)[:160]
# ---------------------------------------------------------------------------
# Constante auxiliare (microcopy fix, fara logica)
# ---------------------------------------------------------------------------
ETICHETA_ULTIMA_AUTENTIFICARE_RAR = "Ultima autentificare la RAR"