Report TTS WordPress read failures instead of false zero backlog

This commit is contained in:
2026-08-28 04:20:15 -04:00
parent de91485854
commit bda4a753c9
+47 -24
View File
@@ -39,18 +39,21 @@ AUTORES = {
386: ("Arregi", "ArregiFeadulta2026"), 386: ("Arregi", "ArregiFeadulta2026"),
} }
# Autor y rango que está locutando el cron ahora mismo (los valores por defecto # Autor y rango que está locutando el cron ahora mismo. Debe seguir el crontab:
# de tts_backlog_cron.sh). Solo afecta a qué línea se marca como "en curso". # solo afecta a qué línea se marca como «en curso» en el informe diario.
ACTIVO = (382, 2025, 2026) ACTIVO = (774, 2000, 2026)
def php(*args: str) -> str: def php(*args: str) -> str:
"""fea_post_io.php dentro del contenedor. stderr fuera: WP escupe warnings.""" """Llama al helper WP sin convertir un fallo de lectura en una cola vacía."""
r = subprocess.run( r = subprocess.run(
["docker", "exec", CONTAINER, "php", "/tmp/fea_post_io.php", *args], ["docker", "exec", CONTAINER, "php", "/tmp/fea_post_io.php", *args],
capture_output=True, text=True, capture_output=True, text=True,
) )
return r.stdout if r.returncode == 0 else "" if r.returncode != 0:
detail = (r.stderr or r.stdout).strip().replace("\n", " ")[:300]
raise RuntimeError(f"consulta WP {' '.join(args)} falló (rc={r.returncode}): {detail}")
return r.stdout
def pendientes(autor: int, desde: int, hasta: int, voz: str) -> int: def pendientes(autor: int, desde: int, hasta: int, voz: str) -> int:
@@ -143,41 +146,62 @@ def _campo_cron(campo: str, valores: range) -> set[int]:
def previstas_24h(ahora: datetime | None = None) -> int | None: def previstas_24h(ahora: datetime | None = None) -> int | None:
"""Cuántas ventanas TENÍA que haber corrido el cron en las últimas 24 h. """Cuántas ventanas tenían que haber corrido en las últimas 24 horas.
Sin esto, los martes/miércoles/jueves (días de carta, sin cron) el informe El backlog usa varias líneas de crontab (ritmo normal + sábado + domingo).
daba la alarma de 'ninguna ventana dejó rastro' estando todo correcto. La Se calcula la unión de sus instantes previstos, sin contar dos veces una
verdad está en el crontab, no aquí: si Rafa cambia los días, esto le sigue. coincidencia entre líneas.
""" """
try: try:
linea = next( lineas = [
l for l in subprocess.run(["crontab", "-l"], text=True, capture_output=True, l for l in subprocess.run(["crontab", "-l"], text=True, capture_output=True,
check=True).stdout.splitlines() check=True).stdout.splitlines()
if "tts_backlog_cron.sh" in l and not l.lstrip().startswith("#")) if "tts_backlog_cron.sh" in l and not l.lstrip().startswith("#")
]
except Exception: # noqa: BLE001 except Exception: # noqa: BLE001
return None return None
campos = linea.split(None, 5) if not lineas:
if len(campos) < 5:
return None return None
minutos = _campo_cron(campos[0], range(60))
horas = _campo_cron(campos[1], range(24))
dows = {d % 7 for d in _campo_cron(campos[4], range(7))} # cron: 0 y 7 = domingo
ahora = ahora or datetime.now() ahora = ahora or datetime.now()
n = 0 inicio = ahora - timedelta(hours=24)
previstos: set[datetime] = set()
for linea in lineas:
campos = linea.split(None, 5)
if len(campos) < 5:
continue
try:
minutos = _campo_cron(campos[0], range(60))
horas = _campo_cron(campos[1], range(24))
dows = {d % 7 for d in _campo_cron(campos[4], range(7))}
except (TypeError, ValueError):
continue
for h in range(25): for h in range(25):
t = (ahora - timedelta(hours=h)).replace(second=0, microsecond=0) t = (ahora - timedelta(hours=h)).replace(second=0, microsecond=0)
for m in minutos: for m in minutos:
cand = t.replace(minute=m) cand = t.replace(minute=m)
if not (ahora - timedelta(hours=24) < cand <= ahora): if inicio < cand <= ahora and cand.hour in horas and (cand.weekday() + 1) % 7 in dows:
continue previstos.add(cand)
if cand.hour in horas and (cand.weekday() + 1) % 7 in dows: return len(previstos)
n += 1
return n
def main() -> int: def main() -> int:
# Nunca informar «0 pendientes» cuando la lectura de WordPress ha fallado.
# El cron no-agent entrega stdout/errores tal cual: un aviso explícito permite
# arreglar Docker/helper sin que parezca que el backlog está terminado.
try:
hechos = hechos_24h() hechos = hechos_24h()
faltantes = {
aid: (pendientes(aid, 0, 9999, voz),
pendientes(aid, ACTIVO[1], ACTIVO[2], voz) if aid == ACTIVO[0] else None)
for aid, (_nombre, voz) in AUTORES.items()
}
except RuntimeError as exc:
print("⚠️ Fe Adulta — informe TTS inválido: no se pudo leer WordPress.")
print(f" {exc}")
print(" Los ceros no son datos reales; revisar wordpress-web y /tmp/fea_post_io.php.")
return 1
total = sum(len(v) for v in hechos.values()) total = sum(len(v) for v in hechos.values())
corridas, saltadas, fallos = logs_24h() corridas, saltadas, fallos = logs_24h()
p5, pw = cuota() p5, pw = cuota()
@@ -194,10 +218,9 @@ def main() -> int:
lineas.append("") lineas.append("")
lineas.append("Pendientes:") lineas.append("Pendientes:")
for aid, (nombre, voz) in AUTORES.items(): for aid, (nombre, voz) in AUTORES.items():
falta_todo = pendientes(aid, 0, 9999, voz) falta_todo, falta_lote = faltantes[aid]
marca = "" marca = ""
if aid == ACTIVO[0]: if aid == ACTIVO[0]:
falta_lote = pendientes(aid, ACTIVO[1], ACTIVO[2], voz)
marca = f" ← en curso, {falta_lote} del lote {ACTIVO[1]}-{ACTIVO[2]}" marca = f" ← en curso, {falta_lote} del lote {ACTIVO[1]}-{ACTIVO[2]}"
lineas.append(f" {nombre}: {falta_todo}{marca}") lineas.append(f" {nombre}: {falta_todo}{marca}")