Files
cloudinit-bot a8ec7b80de feat(scripts+workflows+docs): explain_run, reclassify, replay, dashboard, daily_digest, operator-tutorial (REQ-INB-42 + -44 + -45 + -50 + -47 + -53)
---ci---
project: inboxos
phase: 1
milestone: v1.4
status: execute
wave: 2
tasks: [T3, T4, T5, T6, T7, T8]
---/ci---
2026-06-09 14:19:49 +00:00

85 lines
2.6 KiB
Python
Executable File

#!/usr/bin/env python3
"""Reclassify MIDs between modules.
v1.4 REQ-INB-44.
"""
from __future__ import annotations
import argparse
import csv
import json
import os
import sys
from typing import Iterable
import requests
NOCODB_BASE_URL = os.environ.get("NOCODB_BASE_URL", "http://localhost:8080")
NOCODB_API_TOKEN = os.environ.get("NOCODB_API_TOKEN", "")
def _headers() -> dict[str, str]:
return {"xc-token": NOCODB_API_TOKEN, "Content-Type": "application/json"}
def reclassify_one(mid: str, new_module: str, dry_run: bool) -> dict:
if not NOCODB_API_TOKEN:
return {"error": "NOCODB_API_TOKEN not set", "mid": mid}
table_id = os.environ.get(f"NOCODB_TABLE_{new_module.upper()}", "")
if not table_id:
return {"error": f"NOCODB_TABLE_{new_module.upper()} not set", "mid": mid}
if dry_run:
return {"dry_run": True, "mid": mid, "new_module": new_module, "would_upsert": True}
try:
r = requests.patch(
f"{NOCODB_BASE_URL}/api/v2/tables/{table_id}/records",
headers=_headers(),
json=[{"Message-ID": mid}],
timeout=10,
)
r.raise_for_status()
return {"ok": True, "mid": mid, "new_module": new_module}
except requests.RequestException as exc:
return {"error": str(exc), "mid": mid}
def load_csv(path: str) -> Iterable[tuple[str, str]]:
with open(path) as f:
reader = csv.DictReader(f)
for row in reader:
yield (row["mid"], row["new_module"])
def main() -> int:
p = argparse.ArgumentParser(description="Reclassify MIDs between modules")
g = p.add_mutually_exclusive_group(required=True)
g.add_argument("--csv", help="CSV with mid,new_module rows")
g.add_argument("--mid", help="Single Message-ID")
p.add_argument("--new-module", help="Target module (when --mid is used)")
p.add_argument("--dry-run", action="store_true", default=True)
p.add_argument("--confirm", action="store_true", help="Actually write")
args = p.parse_args()
if args.csv:
pairs = list(load_csv(args.csv))
else:
if not args.new_module:
sys.stderr.write("--new-module required with --mid\n")
return 1
pairs = [(args.mid, args.new_module)]
if not args.confirm and not args.dry_run:
sys.stderr.write("Refusing to run without --dry-run or --confirm.\n")
return 1
dry_run = not args.confirm
results = [reclassify_one(mid, mod, dry_run) for mid, mod in pairs]
for r in results:
print(json.dumps(r))
return 0
if __name__ == "__main__":
raise SystemExit(main())