Primer commit
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Chequeo agendado de réplicas huérfanas con link muerto en Marca.
|
||||
|
||||
Corre `reconcile_brand_deadlink_opps.py` en DRY-RUN (con --resync-first, no
|
||||
escribe nada en el CRM) y, si detecta dead-links accionables (DELETE/RELINK),
|
||||
deja una alerta en generated/runtime/deadlink_check_alert.json para que el owner
|
||||
los aplique desde el dashboard tras confirmar (protocolo dry-run → confirmación).
|
||||
|
||||
Ataca la causa raíz que el workflow n8n NO puede cubrir en tiempo real: cuando
|
||||
una opp de sucursal se BORRA, GHL no dispara webhook → la réplica de Marca queda
|
||||
huérfana con link muerto. Este chequeo la detecta de forma determinista.
|
||||
|
||||
Uso:
|
||||
python scripts/scheduled_deadlink_check.py
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
if SCRIPTS_DIR not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_DIR)
|
||||
|
||||
from paths import LOGS_DIR, RUNTIME_DIR # noqa: E402
|
||||
import reconcile_brand_deadlink_opps as recon # noqa: E402
|
||||
|
||||
ALERT_PATH = os.path.join(RUNTIME_DIR, "deadlink_check_alert.json")
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(LOGS_DIR, exist_ok=True)
|
||||
os.makedirs(RUNTIME_DIR, exist_ok=True)
|
||||
ts = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
log_path = os.path.join(LOGS_DIR, f"deadlink_check_{ts}.log")
|
||||
|
||||
lines = []
|
||||
result = recon.run(apply=False, resync_first=True, log=lambda *a: lines.append(" ".join(str(x) for x in a)))
|
||||
with open(log_path, "w", encoding="utf-8") as fh:
|
||||
fh.write("\n".join(lines))
|
||||
|
||||
s = result["summary"]
|
||||
actionable = s["delete"] + s["relink"]
|
||||
print(f"[deadlink-check] {ts} dry-run: candidatos={s['candidates']} "
|
||||
f"delete={s['delete']} relink={s['relink']} skip={s['skip']}")
|
||||
print(f"[deadlink-check] log: {log_path}")
|
||||
|
||||
if actionable > 0:
|
||||
alert = {
|
||||
"timestamp": ts,
|
||||
"status": "pendientes",
|
||||
"delete": s["delete"],
|
||||
"relink": s["relink"],
|
||||
"skip": s["skip"],
|
||||
"log": log_path,
|
||||
"snapshot": result.get("snapshot"),
|
||||
"aplicar_con": "python scripts/reconcile_brand_deadlink_opps.py --resync-first --apply --run-id <uuid>",
|
||||
"nota": "Réplicas huérfanas con link muerto detectadas. Revisa el plan y aplica desde el dashboard tras confirmar (protocolo dry-run).",
|
||||
}
|
||||
with open(ALERT_PATH, "w", encoding="utf-8") as fh:
|
||||
json.dump(alert, fh, ensure_ascii=False, indent=2)
|
||||
print(f"[deadlink-check] ALERTA: {actionable} accionables -> {ALERT_PATH}")
|
||||
else:
|
||||
if os.path.exists(ALERT_PATH):
|
||||
os.remove(ALERT_PATH)
|
||||
print("[deadlink-check] sin dead-links accionables. Todo al día.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user