#!/usr/bin/env python3 """ CLI: merge a mis-linked submission onto the real CASE-* initiative row and delete the orphan initiative. Usage (dry-run — default, no writes): cd be0 export INITIATIVE_DATABASE_URL="postgresql+asyncpg://user:pass@host:5432/initiatives" python scripts/repair_split_submission.py --submission-id sub-d560fbb6f2944ec6 Apply (commits one transaction): python scripts/repair_split_submission.py --submission-id sub-... --good-case CASE-YOURCODE --execute Requires the same Postgres URL as the API (`INITIATIVE_DATABASE_URL` / `DATABASE_URL`). """ from __future__ import annotations import argparse import asyncio import os import sys SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, "..")) if ROOT not in sys.path: sys.path.insert(0, ROOT) async def _main_async() -> int: p = argparse.ArgumentParser(description="Repair split submission / wrong initiative linkage.") p.add_argument( "--submission-id", required=True, help="submissionRecord.id (e.g. sub-d560fbb6f2944ec6)", ) p.add_argument( "--good-case", dest="good_case", default=None, help="Explicit CASE-* code for the autosave row (recommended if owner has multiple drafts)", ) p.add_argument( "--execute", action="store_true", help="Apply changes (otherwise dry-run only)", ) args = p.parse_args() os.environ.setdefault("INITIATIVE_DATABASE_URL", os.getenv("DATABASE_URL") or "") from src.initiative_db.engine import get_session, init_engine, is_postgres_enabled from src.initiative_db.repair_split_submission import repair_submission_cross_initiative_merge if not is_postgres_enabled(): print("Error: set INITIATIVE_DATABASE_URL=postgresql+asyncpg://.../initiatives", file=sys.stderr) return 2 await init_engine() async with get_session() as session: report = await repair_submission_cross_initiative_merge( session, submission_record_id=args.submission_id.strip(), good_case_code_explicit=(args.good_case or "").strip() or None, dry_run=not args.execute, ) lines = [ f"dry_run={report.dry_run}", f"submission_record_id={report.submission_record_id}", f"owner_id={report.owner_id or '(n/a)'}", f"bad_case={report.bad_case_code or '(n/a)'}", f"good_case={report.good_case_code or '(n/a)'}", ] if report.skipped: lines.append(f"SKIPPED: {report.skipped}") lines.extend(report.actions) print("\n".join(lines)) if args.execute and report.skipped: return 3 return 0 def main() -> None: raise SystemExit(asyncio.run(_main_async())) if __name__ == "__main__": main()