#!/usr/bin/env python3 """Mark manually cleaned outbox failures as ignored. Use when old Jasmin/Packlink test or duplicate failures were already handled outside the worker and should no longer pollute /operations. """ from __future__ import annotations import argparse from sqlalchemy import text from app.db import engine MATCH_SQL = """ status = 'failed' AND ( COALESCE(last_error, '') ILIKE '%limpo manualmente%' OR COALESCE(last_error, '') ILIKE '%resolvido manualmente%' ) """ def main() -> int: parser = argparse.ArgumentParser(description="Ignore outbox failures already resolved manually.") parser.add_argument("--dry-run", action="store_true", help="Only show matching rows; do not update.") parser.add_argument("--limit", type=int, default=200, help="Maximum rows to inspect/update.") args = parser.parse_args() with engine.begin() as conn: rows = conn.execute(text(f""" SELECT id::text, target_system, action_type, status, last_error, created_at FROM integration_outbox WHERE {MATCH_SQL} ORDER BY created_at DESC LIMIT :limit """), {"limit": args.limit}).mappings().all() print(f"Matched {len(rows)} manually cleaned failed outbox item(s).") for row in rows: print(f"- {row['id']} {row['target_system']}.{row['action_type']} :: {row['last_error']}") if args.dry_run or not rows: print("Dry-run/no-op; no rows updated.") return 0 ids = [row["id"] for row in rows] conn.execute(text(""" UPDATE integration_outbox SET status = 'ignored', last_error = COALESCE(NULLIF(last_error, ''), 'Ignorado por limpeza operacional manual.'), updated_at = now() WHERE id::text = ANY(:ids) """), {"ids": ids}) print(f"Updated {len(ids)} item(s) to ignored.") return 0 if __name__ == "__main__": raise SystemExit(main())