feat(pacientes): modo insertar en sync-all — solo crea nuevos, omite existentes
sync_todos() ahora acepta modo='insertar'|'upsert'. La ruta /pacientes/sync-all usa insertar: skippea cédulas que ya están en lab_pacientes. Los hooks automáticos (terceros/automation) siguen en upsert. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e7c6bc794a
commit
1aab14f705
@@ -117,12 +117,12 @@ async def sync_all(
|
|||||||
return JSONResponse({"success": False, "message": "No se encontraron pacientes con esos criterios."})
|
return JSONResponse({"success": False, "message": "No se encontraron pacientes con esos criterios."})
|
||||||
|
|
||||||
timeout = int(configs.get("api_timeout", 30))
|
timeout = int(configs.get("api_timeout", 30))
|
||||||
resultado = await sync_todos(rows, ingest_url, wa_key, timeout)
|
resultado = await sync_todos(rows, ingest_url, wa_key, timeout, modo="insertar")
|
||||||
resultado["success"] = True
|
resultado["success"] = True
|
||||||
resultado["message"] = (
|
resultado["message"] = (
|
||||||
f"{resultado['total']} pacientes procesados — "
|
f"{resultado['total']} procesados — "
|
||||||
f"{resultado['created']} creados, "
|
f"{resultado['created']} nuevos, "
|
||||||
f"{resultado['updated']} actualizados, "
|
f"{resultado['skipped']} ya existían, "
|
||||||
f"{resultado['errores']} errores."
|
f"{resultado['errores']} errores."
|
||||||
)
|
)
|
||||||
return JSONResponse(resultado)
|
return JSONResponse(resultado)
|
||||||
|
|||||||
@@ -63,9 +63,16 @@ def mapear_paciente(row: dict) -> dict:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def sync_paciente(row: dict, url: str, api_key: str, client: Optional[httpx.AsyncClient] = None) -> dict:
|
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}."""
|
"""Envía un paciente al endpoint de WhatsApp. Retorna {ok, action, message}."""
|
||||||
payload = mapear_paciente(row)
|
payload = mapear_paciente(row)
|
||||||
|
payload["modo"] = modo
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
"X-Lab-Key": api_key,
|
"X-Lab-Key": api_key,
|
||||||
@@ -95,17 +102,19 @@ async def sync_paciente(row: dict, url: str, api_key: str, client: Optional[http
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def sync_todos(rows: list, url: str, api_key: str, timeout: int = 30) -> dict:
|
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, updated, errores}."""
|
"""Envía una lista de filas de pacientes. Retorna resumen {total, created, skipped, updated, errores}."""
|
||||||
resultado = {"total": len(rows), "created": 0, "updated": 0, "errores": 0, "detalle": []}
|
resultado = {"total": len(rows), "created": 0, "skipped": 0, "updated": 0, "errores": 0, "detalle": []}
|
||||||
headers = {"Content-Type": "application/json", "X-Lab-Key": api_key}
|
|
||||||
|
|
||||||
async with httpx.AsyncClient(timeout=timeout) as client:
|
async with httpx.AsyncClient(timeout=timeout) as client:
|
||||||
for row in rows:
|
for row in rows:
|
||||||
r = await sync_paciente(row, url, api_key, client)
|
r = await sync_paciente(row, url, api_key, client, modo)
|
||||||
if r["ok"]:
|
if r["ok"]:
|
||||||
if r["action"] == "created":
|
action = r["action"]
|
||||||
|
if action == "created":
|
||||||
resultado["created"] += 1
|
resultado["created"] += 1
|
||||||
|
elif action == "skipped":
|
||||||
|
resultado["skipped"] += 1
|
||||||
else:
|
else:
|
||||||
resultado["updated"] += 1
|
resultado["updated"] += 1
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ async function runSync() {
|
|||||||
result.innerHTML = `
|
result.innerHTML = `
|
||||||
<div class="bg-green-50 border border-green-200 rounded-lg p-4 space-y-3">
|
<div class="bg-green-50 border border-green-200 rounded-lg p-4 space-y-3">
|
||||||
<p class="font-medium text-green-800"><i class="fas fa-check-circle mr-2"></i>${json.message}</p>
|
<p class="font-medium text-green-800"><i class="fas fa-check-circle mr-2"></i>${json.message}</p>
|
||||||
<div class="grid grid-cols-3 gap-3 text-center text-sm">
|
<div class="grid grid-cols-4 gap-3 text-center text-sm">
|
||||||
<div class="bg-white rounded-lg p-3 border border-green-100">
|
<div class="bg-white rounded-lg p-3 border border-green-100">
|
||||||
<p class="text-2xl font-bold text-gray-800">${json.total}</p>
|
<p class="text-2xl font-bold text-gray-800">${json.total}</p>
|
||||||
<p class="text-gray-500">Total</p>
|
<p class="text-gray-500">Total</p>
|
||||||
@@ -145,8 +145,12 @@ async function runSync() {
|
|||||||
<p class="text-gray-500">Nuevos</p>
|
<p class="text-gray-500">Nuevos</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="bg-white rounded-lg p-3 border border-green-100">
|
<div class="bg-white rounded-lg p-3 border border-green-100">
|
||||||
<p class="text-2xl font-bold text-green-600">${json.updated}</p>
|
<p class="text-2xl font-bold text-gray-400">${json.skipped ?? 0}</p>
|
||||||
<p class="text-gray-500">Actualizados</p>
|
<p class="text-gray-500">Ya existían</p>
|
||||||
|
</div>
|
||||||
|
<div class="bg-white rounded-lg p-3 border border-green-100">
|
||||||
|
<p class="text-2xl font-bold text-red-500">${json.errores}</p>
|
||||||
|
<p class="text-gray-500">Errores</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
${json.errores > 0 ? `<p class="text-sm text-red-600"><i class="fas fa-exclamation-triangle mr-1"></i>${json.errores} errores — revisa los detalles abajo.</p>` : ''}
|
${json.errores > 0 ? `<p class="text-sm text-red-600"><i class="fas fa-exclamation-triangle mr-1"></i>${json.errores} errores — revisa los detalles abajo.</p>` : ''}
|
||||||
|
|||||||
Reference in New Issue
Block a user