"""Duplique le workflow Demo_urgence_2 en Demo_urgence_2_interop. - Source : wf_d04d2dc7c118_1778493082 - Exclus : ord 13, 15, 16, 18, 19 (steps UI Codage Easily) - Conservés : ord 0-12, 14, 17 → renumérotés 0..14 - Anchors partagés (pas de duplication de visual_anchors) - Transaction SQLite : commit unique en fin. Usage : python tools/duplicate_demo_urgence_2_interop.py [--dry-run] """ from __future__ import annotations import argparse import secrets import sqlite3 import sys import time from pathlib import Path DB_PATH = Path(__file__).resolve().parent.parent / "visual_workflow_builder" / "backend" / "instance" / "workflows.db" SOURCE_WF_ID = "wf_d04d2dc7c118_1778493082" NEW_WF_NAME = "Demo_urgence_2_interop" ORDS_TO_EXCLUDE = {13, 15, 16, 18, 19} def new_id(prefix: str, ts: int) -> str: return f"{prefix}_{secrets.token_hex(6)}_{ts}" def main() -> int: parser = argparse.ArgumentParser() parser.add_argument("--dry-run", action="store_true", help="Pas de COMMIT, juste afficher.") args = parser.parse_args() if not DB_PATH.exists(): print(f"ERREUR : DB introuvable {DB_PATH}", file=sys.stderr) return 1 ts = int(time.time()) new_wf_id = new_id("wf", ts) conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row cur = conn.cursor() # 1. Vérifier que le nom de destination n'existe pas déjà row = cur.execute("SELECT id FROM workflows WHERE name = ?", (NEW_WF_NAME,)).fetchone() if row: print(f"ERREUR : un workflow nommé '{NEW_WF_NAME}' existe déjà (id={row['id']})", file=sys.stderr) return 2 # 2. Lire la ligne workflow source src_wf = cur.execute("SELECT * FROM workflows WHERE id = ?", (SOURCE_WF_ID,)).fetchone() if not src_wf: print(f"ERREUR : workflow source {SOURCE_WF_ID} introuvable", file=sys.stderr) return 3 # 3. Lire les steps à conserver, dans l'ordre src_steps = cur.execute( 'SELECT * FROM steps WHERE workflow_id = ? ORDER BY "order"', (SOURCE_WF_ID,), ).fetchall() kept_steps = [s for s in src_steps if s["order"] not in ORDS_TO_EXCLUDE] if len(kept_steps) != 15: print(f"ERREUR : attendu 15 steps conservés, obtenu {len(kept_steps)}", file=sys.stderr) return 4 # 4. Préparer mapping (renumérotation 0..14) mapping = [] for new_order, s in enumerate(kept_steps): new_step_id = new_id("step", ts + new_order) # ts unique par step mapping.append({ "old_id": s["id"], "new_id": new_step_id, "old_order": s["order"], "new_order": new_order, "action_type": s["action_type"], "label": s["label"], "position_x": s["position_x"], "position_y": s["position_y"], "parameters_json": s["parameters_json"], "anchor_id": s["anchor_id"], }) # 5. Affichage tableau avant/après print(f"\nWorkflow source : {SOURCE_WF_ID} (name={src_wf['name']})") print(f"Workflow cible : {new_wf_id} (name={NEW_WF_NAME})") print(f"Steps conservés : {len(mapping)} / {len(src_steps)}") print(f"\n{'old_ord':>7} → {'new_ord':>7} {'action_type':<20} label") print("-" * 80) for m in mapping: print(f"{m['old_order']:>7} → {m['new_order']:>7} {m['action_type']:<20} {m['label']}") print() if args.dry_run: print("--dry-run : aucune modification de la DB.") return 0 # 6. Exécution transactionnelle now_iso = time.strftime("%Y-%m-%d %H:%M:%S") try: cur.execute("BEGIN") cur.execute( """ INSERT INTO workflows (id, name, description, tags_json, trigger_examples_json, created_at, updated_at, is_active, source, review_status, review_feedback, reviewed_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( new_wf_id, NEW_WF_NAME, src_wf["description"], src_wf["tags_json"], src_wf["trigger_examples_json"], now_iso, now_iso, src_wf["is_active"], src_wf["source"], src_wf["review_status"], src_wf["review_feedback"], src_wf["reviewed_at"], ), ) for m in mapping: cur.execute( """ INSERT INTO steps (id, workflow_id, action_type, "order", position_x, position_y, parameters_json, anchor_id, label, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( m["new_id"], new_wf_id, m["action_type"], m["new_order"], m["position_x"], m["position_y"], m["parameters_json"], m["anchor_id"], m["label"], now_iso, now_iso, ), ) conn.commit() print(f"OK — workflow {NEW_WF_NAME} créé ({len(mapping)} steps), id={new_wf_id}") return 0 except Exception as e: conn.rollback() print(f"ROLLBACK — exception : {e}", file=sys.stderr) return 5 finally: conn.close() if __name__ == "__main__": sys.exit(main())