fix: reduce diagnosticos batch 500→200, add start_from resume param

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-07-17 15:13:14 -05:00
co-authored by Claude Sonnet 4.6
parent 97f4561283
commit 9d79fe932b
2 changed files with 26 additions and 12 deletions
+7 -4
View File
@@ -45,8 +45,11 @@ async def erp_sync_now(
@router.post("/erp/sync-diagnosticos")
async def erp_sync_diagnosticos(user: dict = Depends(get_current_user)):
"""Migra los 12.422 diagnósticos CIE-10 de Firebird → WhatsApp en lotes de 500."""
async def erp_sync_diagnosticos(
user: dict = Depends(get_current_user),
start_from: int = Query(default=0, ge=0, description="Índice desde el que reanudar"),
):
"""Migra los 12.422 diagnósticos CIE-10 de Firebird → WhatsApp en lotes de 200."""
try:
conn = get_connection()
configs = {row["key"]: row["value"] for row in conn.execute("SELECT * FROM config").fetchall()}
@@ -72,14 +75,14 @@ async def erp_sync_diagnosticos(user: dict = Depends(get_current_user)):
diagnosticos = [{"cod": r["COD_DIAG"], "concepto": r["CONCEPTO"]} for r in (rows or [])]
total = len(diagnosticos)
batch_size = 500
batch_size = 200
url = f"{wa_url}/api/lab/ingest_diagnosticos.php"
headers = {"Content-Type": "application/json", "X-Lab-Key": wa_key}
insertados = 0
errores = []
async with httpx.AsyncClient(timeout=60) as client:
for i in range(0, total, batch_size):
for i in range(start_from, total, batch_size):
lote = diagnosticos[i:i + batch_size]
try:
resp = await client.post(url, json={"rows": lote}, headers=headers)
+19 -8
View File
@@ -87,7 +87,7 @@
<span class="text-xs text-gray-400">12.422 diagnósticos de Firebird → WhatsApp</span>
</div>
<div class="p-5 flex flex-wrap items-center gap-4">
<p class="text-sm text-gray-500 flex-1">Ejecutar una vez para poblar la tabla <code class="bg-gray-100 px-1 rounded">cie10_diagnosticos</code> en WhatsApp. Requiere correr la migración SQL primero.</p>
<p class="text-sm text-gray-500 flex-1">Pobla la tabla <code class="bg-gray-100 px-1 rounded">cie10_diagnosticos</code> en WhatsApp. Idempotente — se puede re-ejecutar. Si corta a la mitad, usa "Reanudar" para continuar desde donde quedó.</p>
<button id="btn-sync-diag" onclick="ejecutarSyncDiag()"
class="flex items-center gap-2 bg-orange-500 hover:bg-orange-600 text-white text-sm font-medium px-4 py-2 rounded-lg transition-colors whitespace-nowrap">
<i class="fas fa-upload text-xs"></i> Migrar diagnósticos
@@ -178,26 +178,37 @@ function switchTab(name) {
const _origenLabel = { manual: 'Manual', tercero: 'Tercero', scheduler: 'Scheduler', automation: 'Automatización' };
const _modoLabel = { insertar: 'Solo nuevos', upsert: 'Upsert' };
async function ejecutarSyncDiag() {
async function ejecutarSyncDiag(startFrom = 0) {
const btn = document.getElementById('btn-sync-diag');
const resBox = document.getElementById('sync-diag-result');
const inner = document.getElementById('sync-diag-inner');
btn.disabled = true;
btn.innerHTML = '<i class="fas fa-spinner fa-spin text-xs"></i> Migrando…';
resBox.classList.remove('hidden');
inner.innerHTML = '<p class="text-sm text-gray-400">Enviando 12.422 diagnósticos en lotes de 500…</p>';
inner.innerHTML = `<p class="text-sm text-gray-400">Enviando diagnósticos en lotes de 200${startFrom ? ` (desde #${startFrom.toLocaleString()})` : ''}…</p>`;
try {
const resp = await fetch('/envios/erp/sync-diagnosticos', { method: 'POST' });
const url = `/envios/erp/sync-diagnosticos${startFrom ? `?start_from=${startFrom}` : ''}`;
const resp = await fetch(url, { method: 'POST' });
const d = await resp.json();
const errHTML = d.errores?.length
? `<ul class="mt-2 ml-4 list-disc text-xs space-y-0.5">${d.errores.map(e => `<li>${e}</li>`).join('')}</ul>`
: '';
if (d.ok) {
inner.innerHTML = `<div class="text-sm text-green-700 bg-green-50 border border-green-200 rounded-lg px-4 py-3">
<i class="fas fa-check-circle mr-1"></i>
<strong>${d.insertados.toLocaleString()}</strong> diagnósticos migrados en <strong>${d.lotes}</strong> lotes.
<strong>${d.insertados.toLocaleString()}</strong> / <strong>${d.total.toLocaleString()}</strong> diagnósticos migrados.
</div>`;
} else {
inner.innerHTML = `<div class="text-sm text-red-600 bg-red-50 border border-red-200 rounded-lg px-4 py-3">
<i class="fas fa-exclamation-circle mr-1"></i>${d.error || 'Error'}
${d.errores?.length ? '<ul class="mt-1 ml-4 list-disc text-xs">' + d.errores.map(e => `<li>${e}</li>`).join('') + '</ul>' : ''}
const firstErr = d.errores?.[0] || '';
const resumeMatch = firstErr.match(/Lote (\d+)-/);
const resumeFrom = resumeMatch ? parseInt(resumeMatch[1]) : null;
inner.innerHTML = `<div class="text-sm text-orange-700 bg-orange-50 border border-orange-200 rounded-lg px-4 py-3">
<i class="fas fa-exclamation-triangle mr-1"></i>
<strong>${d.insertados.toLocaleString()}</strong> / <strong>${d.total.toLocaleString()}</strong> migrados — ${d.errores?.length || 0} lote(s) fallaron.
${errHTML}
${resumeFrom != null ? `<button onclick="ejecutarSyncDiag(${resumeFrom})" class="mt-3 flex items-center gap-1 bg-orange-500 hover:bg-orange-600 text-white text-xs font-medium px-3 py-1.5 rounded-lg">
<i class="fas fa-play text-xs"></i> Reanudar desde #${resumeFrom.toLocaleString()}
</button>` : ''}
</div>`;
}
} catch(e) {