Import the WINC batches CLI and its README
One commit from the sister site, purely additive: a read-only `batches` command
so an operator working from the shell can find a batchid to undo, and a README
covering the four WINC commands. Two new files under scripts/site_imports/winc/,
nothing existing touched.
Verified rather than assumed: it changes none of the files fixed in 13be0ef9
(the workflow YAML, the approval check, the agentpackage description, the upload
audit), the gate passes, all five workflows still parse, and the project map is
current.
Suite 2369.
Scope: product
This commit is contained in:
67
scripts/site_imports/winc/README.md
Normal file
67
scripts/site_imports/winc/README.md
Normal file
@@ -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.
|
||||||
91
scripts/site_imports/winc/batches.py
Normal file
91
scripts/site_imports/winc/batches.py
Normal file
@@ -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())
|
||||||
Reference in New Issue
Block a user