#!/usr/bin/env python3 """Read-only inventory analysis; writes only explicitly requested reports.""" import argparse from datetime import datetime, timezone import json from pathlib import Path from migration_workflows import delivery_route, workflow_family from prepare_business_acceptance_batch import inventory, plan def report(state): groups = {} for row in state["rows"]: if not delivery_route(row): continue key = (row["module"], workflow_family(row)) group = groups.setdefault(key, {"module": key[0], "workflow": key[1], "total": 0, "accepted": 0, "reusableReceipts": 0, "missingCases": [], "unrouted": []}) record_id = row["id"] group["total"] += 1 group["accepted"] += record_id in state["accepted"] receipt = state["receipts"].get(record_id, {}) group["reusableReceipts"] += receipt.get("status") in {"implementation_ready", "implemented_pending_use"} if record_id not in state["covered"].get(row["module"], set()) and record_id not in state["accepted"]: group["missingCases"].append(record_id) if row.get("implementationStatus", "routed") != "routed": group["unrouted"].append(record_id) return {"schema": "freecad-web-delivery-inventory/v1", "observedAt": datetime.now(timezone.utc).isoformat(), "fullScope": len(state["rows"]), "fullAccepted": len(state["accepted"]), "deliverySubset": sum(row["total"] for row in groups.values()), "deliveryAccepted": sum(row["accepted"] for row in groups.values()), "groups": list(groups.values()), "nextDeliveryAcceptance": plan(state, delivery_only=True), "note": "Delivery subset orders work; it never replaces full scope or counts receipts as business acceptance."} def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--report-prefix", type=Path) args = parser.parse_args() result = report(inventory()) if args.report_prefix: args.report_prefix.parent.mkdir(parents=True, exist_ok=True) args.report_prefix.with_suffix(".json").write_text(json.dumps(result, ensure_ascii=False, indent=2) + "\n") lines = ["# Migration Delivery Inventory", "", f"Observed: {result['observedAt']}", "", f"Full scope: {result['fullAccepted']}/{result['fullScope']} accepted; priority subset: {result['deliveryAccepted']}/{result['deliverySubset']} accepted.", "Receipts are reusable work, not business completion. This subset does not remove desktop/supporting scope.", "", "| Module | Workflow | Total | Accepted | Receipts | Missing Cases | Unrouted |", "|---|---|---:|---:|---:|---:|---:|"] for row in result["groups"]: lines.append(f"| {row['module']} | {row['workflow']} | {row['total']} | {row['accepted']} | {row['reusableReceipts']} | {len(row['missingCases'])} | {len(row['unrouted'])} |") args.report_prefix.with_suffix(".md").write_text("\n".join(lines) + "\n") print(json.dumps({key: value for key, value in result.items() if key != "groups"}, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())