Files
rips_manager/app/services/whatsapp_sync.py
T

162 lines
5.1 KiB
Python

"""
Servicio de sincronización de pacientes RIPS → WhatsApp Lab.
Mapea campos Firebird al formato esperado por /api/lab/ingest_paciente.php.
"""
import re
import json
import httpx
from datetime import datetime
from typing import Optional
from app.database import get_connection
def guardar_sync_log(
resultado: dict,
user_id: int,
origen: str = "manual",
modo: str = "insertar",
) -> None:
errores_det = None
errores = [d for d in resultado.get("detalle", []) if not d.get("ok")]
if errores:
errores_det = json.dumps(
[{"doc": e["doc"], "nombre": e["nombre"], "msg": e["message"]} for e in errores],
ensure_ascii=False,
)
conn = get_connection()
conn.execute(
"""INSERT INTO sync_wa_log
(user_id, origen, modo, total, created, skipped, updated, errores, errores_det)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
(
user_id,
origen,
modo,
resultado.get("total", 0),
resultado.get("created", 0),
resultado.get("skipped", 0),
resultado.get("updated", 0),
resultado.get("errores", 0),
errores_det,
),
)
conn.commit()
conn.close()
_TIPO_DOC_MAP = {
"CC": "CC", "TI": "TI", "RC": "RC", "CE": "CE",
"PA": "PA", "NIT": "NIT", "MS": "MS", "AS": "CC",
"SI": "CC", "CN": "CC", "DE": "CE", "CD": "PA",
"PE": "CE", "PT": "PA",
}
_SEXO_MAP = {"M": "M", "F": "F", "H": "M"}
def _fmt_fecha_iso(val) -> Optional[str]:
if not val:
return None
if hasattr(val, "strftime"):
return val.strftime("%Y-%m-%d")
s = str(val)[:10]
if re.match(r"\d{4}-\d{2}-\d{2}", s):
return s
# dd/mm/yyyy
parts = s.split("/")
if len(parts) == 3:
return f"{parts[2]}-{parts[1]}-{parts[0]}"
return None
def mapear_paciente(row: dict) -> dict:
"""Convierte una fila de Firebird PACIENTE al body de ingest_paciente.php."""
nombres = (row.get("NOMBRES") or "").strip()
apellidos = (row.get("APELLIDOS") or "").strip()
nombre_completo = f"{nombres} {apellidos}".strip().upper()
tipo_raw = str(row.get("TIPOIDENT") or "CC").strip()
tipo_doc = _TIPO_DOC_MAP.get(tipo_raw, "CC")
sexo_raw = str(row.get("SEXO") or "M").strip().upper()
genero = _SEXO_MAP.get(sexo_raw, "M")
email = (row.get("EMAIL") or "").strip().lower()
if not email or "@sinregistro" in email:
email = ""
return {
"nombre_completo": nombre_completo,
"numero_documento": str(row.get("DOCIDENT") or "").strip(),
"tipo_documento": tipo_doc,
"telefono": str(row.get("TELEFONOS") or "").strip(),
"email": email,
"fecha_nacimiento": _fmt_fecha_iso(row.get("F_NACIMIENTO")),
"genero": genero,
"direccion": (row.get("DIRECCION") or "").strip(),
"ciudad": (row.get("NOM_CIUDAD") or "").strip(),
"origen": "lab",
}
async def sync_paciente(
row: dict,
url: str,
api_key: str,
client: Optional[httpx.AsyncClient] = None,
modo: str = "upsert",
) -> dict:
"""Envía un paciente al endpoint de WhatsApp. Retorna {ok, action, message}."""
payload = mapear_paciente(row)
payload["modo"] = modo
headers = {
"Content-Type": "application/json",
"X-Lab-Key": api_key,
}
try:
if client:
resp = await client.post(url, json=payload, headers=headers)
else:
async with httpx.AsyncClient(timeout=15) as c:
resp = await c.post(url, json=payload, headers=headers)
data = resp.json()
return {
"ok": data.get("ok", False),
"action": data.get("action", ""),
"message": data.get("message") or data.get("error", ""),
"doc": payload["numero_documento"],
"nombre": payload["nombre_completo"],
}
except Exception as e:
return {
"ok": False,
"action": "error",
"message": str(e),
"doc": payload.get("numero_documento", ""),
"nombre": payload.get("nombre_completo", ""),
}
async def sync_todos(rows: list, url: str, api_key: str, timeout: int = 30, modo: str = "upsert") -> dict:
"""Envía una lista de filas de pacientes. Retorna resumen {total, created, skipped, updated, errores}."""
resultado = {"total": len(rows), "created": 0, "skipped": 0, "updated": 0, "errores": 0, "detalle": []}
async with httpx.AsyncClient(timeout=timeout) as client:
for row in rows:
r = await sync_paciente(row, url, api_key, client, modo)
if r["ok"]:
action = r["action"]
if action == "created":
resultado["created"] += 1
elif action == "skipped":
resultado["skipped"] += 1
else:
resultado["updated"] += 1
else:
resultado["errores"] += 1
resultado["detalle"].append(r)
return resultado