170 lines
5.9 KiB
Python
170 lines
5.9 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
import os
|
|
from dataclasses import dataclass
|
|
from functools import lru_cache
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
_TRIPLETS_ENV_VAR = "TIM_PROTOCOL_TRIPLETS_JSON"
|
|
_STAGES = ("open", "close")
|
|
_FIELDS = ("reason1", "reason2", "reason3")
|
|
|
|
|
|
@dataclass(frozen=True, slots=True)
|
|
class ProtocolTriplet:
|
|
reason1: str
|
|
reason2: str
|
|
reason3: str
|
|
|
|
|
|
_DEFAULT_CATALOG: dict[str, dict[str, ProtocolTriplet]] = {
|
|
"atendimento_geral": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Valor"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
},
|
|
"cancelamento_vas_avulso": {
|
|
"open": ProtocolTriplet(
|
|
"Solicitação", "Serviço VAS", "Ativação/Desativação"
|
|
),
|
|
"close": ProtocolTriplet(
|
|
"Solicitação", "Serviço VAS", "Ativação/Desativação"
|
|
),
|
|
},
|
|
"contestacao": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
"close": ProtocolTriplet("Reclamação", "Conta", "Valor"),
|
|
},
|
|
"vas_estrategico": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
},
|
|
"invoice_explanation_aceite_fechado": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
},
|
|
"finalizacao_informacional_fechada": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Esclarecimento"),
|
|
},
|
|
"valor_divergente": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Valor"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Valor"),
|
|
},
|
|
"pro_rata": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Valor"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Valor"),
|
|
},
|
|
"pro_rata_mensalidade": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Mensalidade"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Mensalidade"),
|
|
},
|
|
"pro_rata_reclamacao": {
|
|
"open": ProtocolTriplet("Reclamação", "Conta", "Valor"),
|
|
"close": ProtocolTriplet("Reclamação", "Conta", "Valor"),
|
|
},
|
|
"termino_desconto": {
|
|
"open": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
"close": ProtocolTriplet("Informação", "Conta", "Serviço"),
|
|
},
|
|
"conta_certa_manual": {
|
|
"open": ProtocolTriplet(
|
|
"Processo Interno", "Conta", "Conta Certa - Venc {dia_vencimento}"
|
|
),
|
|
"close": ProtocolTriplet(
|
|
"Processo Interno", "Conta", "Conta Certa - Venc {dia_vencimento}"
|
|
),
|
|
},
|
|
}
|
|
|
|
|
|
class _SafeFormatDict(dict[str, Any]):
|
|
def __missing__(self, key: str) -> str:
|
|
return ""
|
|
|
|
|
|
def _normalize_text(value: Any) -> str:
|
|
return str(value or "").strip()
|
|
|
|
|
|
def _override_triplet(base: ProtocolTriplet, override: dict[str, Any]) -> ProtocolTriplet:
|
|
return ProtocolTriplet(
|
|
reason1=_normalize_text(override.get("reason1")) or base.reason1,
|
|
reason2=_normalize_text(override.get("reason2")) or base.reason2,
|
|
reason3=_normalize_text(override.get("reason3")) or base.reason3,
|
|
)
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def _catalog_with_overrides() -> dict[str, dict[str, ProtocolTriplet]]:
|
|
catalog = {
|
|
scenario: dict(stages)
|
|
for scenario, stages in _DEFAULT_CATALOG.items()
|
|
}
|
|
raw = os.getenv(_TRIPLETS_ENV_VAR, "").strip()
|
|
if not raw:
|
|
return catalog
|
|
try:
|
|
payload = json.loads(raw)
|
|
except json.JSONDecodeError:
|
|
logger.warning("triplets.override.invalid_json env=%s", _TRIPLETS_ENV_VAR)
|
|
return catalog
|
|
if not isinstance(payload, dict):
|
|
logger.warning("triplets.override.invalid_root env=%s", _TRIPLETS_ENV_VAR)
|
|
return catalog
|
|
|
|
for scenario, stage_payload in payload.items():
|
|
scenario_key = _normalize_text(scenario).lower()
|
|
if not scenario_key or not isinstance(stage_payload, dict):
|
|
continue
|
|
current = catalog.get(scenario_key, {})
|
|
for stage in _STAGES:
|
|
override = stage_payload.get(stage)
|
|
if not isinstance(override, dict):
|
|
continue
|
|
if stage in current:
|
|
current[stage] = _override_triplet(current[stage], override)
|
|
continue
|
|
if any(_normalize_text(override.get(field)) for field in _FIELDS):
|
|
current[stage] = ProtocolTriplet(
|
|
reason1=_normalize_text(override.get("reason1")),
|
|
reason2=_normalize_text(override.get("reason2")),
|
|
reason3=_normalize_text(override.get("reason3")),
|
|
)
|
|
if current:
|
|
catalog[scenario_key] = current
|
|
return catalog
|
|
|
|
|
|
def resolve_protocol_triplet(
|
|
scenario: str,
|
|
*,
|
|
stage: str,
|
|
context: dict[str, Any] | None = None,
|
|
fallback_scenario: str = "atendimento_geral",
|
|
) -> ProtocolTriplet:
|
|
stage_key = _normalize_text(stage).lower()
|
|
if stage_key not in _STAGES:
|
|
stage_key = "open"
|
|
scenario_key = _normalize_text(scenario).lower() or fallback_scenario
|
|
fallback_key = _normalize_text(fallback_scenario).lower() or "atendimento_geral"
|
|
|
|
catalog = _catalog_with_overrides()
|
|
stages = catalog.get(scenario_key) or catalog.get(fallback_key) or {}
|
|
raw_triplet = stages.get(stage_key)
|
|
if raw_triplet is None:
|
|
fallback_stages = catalog.get("atendimento_geral", {})
|
|
raw_triplet = fallback_stages.get(stage_key) or ProtocolTriplet("", "", "")
|
|
|
|
fmt_values = _SafeFormatDict(
|
|
{key: _normalize_text(value) for key, value in (context or {}).items()}
|
|
)
|
|
return ProtocolTriplet(
|
|
reason1=raw_triplet.reason1.format_map(fmt_values).strip(),
|
|
reason2=raw_triplet.reason2.format_map(fmt_values).strip(),
|
|
reason3=raw_triplet.reason3.format_map(fmt_values).strip(),
|
|
)
|