From 17825ec8f10e2fbec8dd98c9f4e717361eabc08c Mon Sep 17 00:00:00 2001 From: "benjamin.baker" Date: Thu, 3 Sep 2026 08:07:01 -0400 Subject: [PATCH] Add WINC batches CLI and a CLI how-to README The web import panel can list applied/undone batches, but the CLI had no equivalent, so an operator working from the shell had no way to find a batchid to feed into the undo command. Add a read-only batches command that reuses the same importbatches query, and a short README documenting the four WINC commands and their switches. --- scripts/site_imports/winc/README.md | 67 ++++++++++++++++++++ scripts/site_imports/winc/batches.py | 91 ++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 scripts/site_imports/winc/README.md create mode 100644 scripts/site_imports/winc/batches.py diff --git a/scripts/site_imports/winc/README.md b/scripts/site_imports/winc/README.md new file mode 100644 index 00000000..d487213b --- /dev/null +++ b/scripts/site_imports/winc/README.md @@ -0,0 +1,67 @@ +# WINC machine-adoption import (CLI) + +Adopts machines from a Maximo asset export (validated against the WINC / West +Manufacturing site). It only updates machines that already exist; it never +creates canonical per-table rows. Every commit is journaled so it can be undone +as one batch. + +Run all commands from the repo root. Each reads `.env` and connects to the +configured database. + +## 1. Preview (read-only) + +``` +python -m scripts.site_imports.winc.dry_run --export EXPORT.csv +``` + +| switch | what it does | +| --- | --- | +| `--export PATH` | the Maximo ASSET export CSV to analyze (required) | +| `--rows` | print the per-row plan, not just the summary | +| `--csv PATH` | write the full plan to a CSV so runs can be diffed | + +Nothing is written. Use this to see what would change and which values are +unresolved before committing. + +## 2. Commit (writes, reversibly) + +``` +python -m scripts.site_imports.winc.writer --export EXPORT.csv --commit --acknowledge businessunit +``` + +| switch | what it does | +| --- | --- | +| `--export PATH` | the same export CSV (required) | +| `--commit` | actually write; without it this is a dry run | +| `--acknowledge GROUP` | approve a needs-create group (repeatable), e.g. `businessunit` | +| `--createdby NAME` | who approved the import; stamped on the batch | + +On success it prints the new `batchid` and the exact undo command. + +## 3. List batches + +``` +python -m scripts.site_imports.winc.batches +``` + +| switch | what it does | +| --- | --- | +| `--limit N` | show at most N batches, newest first (default 50) | +| `--json` | emit JSON instead of a table | + +Read-only. Use it to find the `batchid` to undo. + +## 4. Undo a batch + +``` +python -m scripts.site_imports.winc.undo --batch BATCHID --commit +``` + +| switch | what it does | +| --- | --- | +| `--batch BATCHID` | the batch to reverse (required) | +| `--commit` | actually reverse; without it this is a no-op | + +Reversal is conservative: a cell a human changed since the import is left as-is +and reported, and a created business unit is deleted only when nothing outside +the batch still references it. diff --git a/scripts/site_imports/winc/batches.py b/scripts/site_imports/winc/batches.py new file mode 100644 index 00000000..00a4545d --- /dev/null +++ b/scripts/site_imports/winc/batches.py @@ -0,0 +1,91 @@ +"""WINC machine adoption - BATCHES (list applied/undone import batches). + +A read-only companion to writer.py and undo.py: it prints the WINC batches +newest-first so an operator can find a batchid to feed `--batch` into the undo +CLI. Like the other WINC commands it is WINC-named only for discoverability; the +underlying importbatches table is import-agnostic, so this simply filters to the +winc-machine-adoption kind. +""" + +import argparse +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname( + os.path.dirname(os.path.abspath(__file__)))))) + +from dotenv import load_dotenv +from pathlib import Path + +load_dotenv(Path(__file__).resolve().parents[3] / '.env') + +from sqlalchemy import text + +from scripts.site_imports.winc.dry_run import build_app_connection + +_KIND = 'winc-machine-adoption' + + +def _iso(value): + # PyMySQL hands back datetime objects; SQLite hands back the stored ISO + # string. Normalise both to an ISO string so the output is stable. + if value is None: + return '-' + if hasattr(value, 'isoformat'): + return value.isoformat() + return str(value) + + +def list_batches(connection, limit): + rows = connection.execute(text( + 'SELECT batchid, kind, rowcount, createdat, createdby, status, ' + 'undoneat FROM importbatches WHERE kind = :kind ' + 'ORDER BY createdat DESC LIMIT :limit'), + {'kind': _KIND, 'limit': limit}).fetchall() + return [ + {'batchid': r[0], 'kind': r[1], 'rowcount': r[2], + 'createdat': _iso(r[3]), 'createdby': r[4], 'status': r[5], + 'undoneat': _iso(r[6])} + for r in rows + ] + + +def _print_table(batches): + if not batches: + print('No WINC import batches found.') + return + header = '%-36s %-8s %5s %-20s %-16s %s' % ( + 'batchid', 'status', 'rows', 'createdat', 'createdby', 'undoneat') + print(header) + print('-' * len(header)) + for batch in batches: + print('%-36s %-8s %5s %-20s %-16s %s' % ( + batch['batchid'], batch['status'], batch['rowcount'], + batch['createdat'], batch['createdby'] or '-', batch['undoneat'])) + + +def main(argv=None): + parser = argparse.ArgumentParser( + description='List applied/undone WINC import batches, newest first.') + parser.add_argument('--limit', type=int, default=50, + help='Maximum number of batches to show (default 50).') + parser.add_argument('--json', action='store_true', + help='Emit the batches as JSON instead of a table.') + args = parser.parse_args(argv) + + if args.limit < 1: + parser.error('--limit must be a positive integer') + + _app, connection = build_app_connection() + batches = list_batches(connection, args.limit) + + if args.json: + import json + print(json.dumps(batches, indent=2)) + else: + _print_table(batches) + return 0 + + +if __name__ == '__main__': + sys.exit(main())