#!/usr/bin/env python3 """Prepare module-level import/export workflows without API-sized tasks.""" from __future__ import annotations import hashlib import json from collections import defaultdict from datetime import datetime, timezone from pathlib import Path from implementation_first import all_implementation_ready_ids from scheduler_progress import cooling_ids, retry_details ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT / "docs/p6/format-coverage-kernel.json" STATE = ROOT / "docs/current/task-state.json" OUTPUT = ROOT / "docs/current/module-batch-manifest.json" MODULE_ORDER = { "Part": 0, "Mesh": 1, "Import": 2, "TechDraw": 3, "Spreadsheet": 4, "Points": 5, "BIM": 6, "Draft": 7, "OpenSCAD": 8, "Fem": 9, "CAM": 10, } def business_id(module: str, direction: str, extension: str) -> str: return f"business-format:{module}:{direction}:{extension}" def main() -> int: source = json.loads(SOURCE.read_text(encoding="utf-8")) ready = all_implementation_ready_ids() cooling = cooling_ids(candidates={business_id(str(row.get("module")), str(row.get("direction")), str(row.get("extension"))) for row in source.get("records", []) if isinstance(row, dict)}) deferred_ids = set() grouped: dict[tuple[str, str, str], list[dict]] = defaultdict(list) for row in source.get("records", []): if not isinstance(row, dict) or row.get("direction") not in {"import", "export"}: continue key = (str(row.get("module")), str(row.get("direction")), str(row.get("extension"))) grouped[key].append(row) pending: dict[str, list[dict]] = defaultdict(list) for (module, direction, extension), rows in grouped.items(): record_id = business_id(module, direction, extension) if record_id in ready or any(row.get("status") == "passed" for row in rows): continue if record_id in cooling: deferred_ids.add(record_id) continue source_row = min(rows, key=lambda row: (str(row.get("path", "")), int(row.get("line", 0) or 0))) pending[module].append({ "id": record_id, "name": f"{direction} {extension}", "direction": direction, "extension": extension, "source": source_row.get("path"), "sourceLine": source_row.get("line"), "sourceHandlerCount": len(rows), }) if not pending: result = { "schema": "freecad-web-module-function-batch/v1", "status": "no_ready_batch", "generatedAt": datetime.now(timezone.utc).isoformat(), "strategy": "module-function-direct-deferred-validation", "reason": "all source-declared import/export workflows have module implementation receipts", "records": [], "queue": [], **retry_details(deferred_ids), } OUTPUT.write_text(json.dumps(result, ensure_ascii=False, separators=(",", ":")) + "\n", encoding="utf-8") print(json.dumps({"status": result["status"], "formatWorkflows": len(grouped), "records": 0}, ensure_ascii=False)) return 0 module = min(pending, key=lambda value: (MODULE_ORDER.get(value, 50), value)) records = sorted(pending[module], key=lambda row: (row["extension"], row["direction"])) identity = "|".join([module, "format-workflow", "import-export", *(str(row["id"]) for row in records)]) batch_id = hashlib.sha256(identity.encode("utf-8")).hexdigest()[:24] state = json.loads(STATE.read_text(encoding="utf-8")) queue = [ {"module": name, "tier": "format-workflow", "family": "import-export", "unresolved": len(rows), "nextSize": len(rows)} for name, rows in sorted(pending.items(), key=lambda item: (MODULE_ORDER.get(item[0], 50), item[0])) ] result = { "schema": "freecad-web-module-function-batch/v1", "status": "ready", "generatedAt": datetime.now(timezone.utc).isoformat(), "taskId": state.get("active_task"), "batchId": batch_id, "strategy": "module-function-direct-deferred-validation", "businessBundle": { "module": module, "implementationUnit": "module-import-export-dispatcher", "selectedRecordsAreUserWorkflows": True, "sourceHandlerCount": sum(int(row["sourceHandlerCount"]) for row in records), }, "minimumRecords": 1, "singletonFallback": len(records) == 1, "selected": { "module": module, "tier": "format-workflow", "family": "import-export", "recordCount": len(records), "dispositionCounts": {"not_implemented": len(records)}, }, "fields": ["id", "name", "disposition", "source", "sourceLine"], "records": [[row["id"], row["name"], "not_implemented", row["source"], row["sourceLine"]] for row in records], "queue": queue, "boundaries": [ "One record is one user-visible module import/export workflow, not a Python API.", "All same-module extensions share the existing native dispatcher, Worker and WASM artifact.", "Source mentions with unknown direction are not user format handlers and are excluded.", "Implementation performs build/sync/receipt only; validation is disabled.", ], } OUTPUT.write_text(json.dumps(result, ensure_ascii=False, separators=(",", ":")) + "\n", encoding="utf-8") print(json.dumps({"status": "ready", "batchId": batch_id, "module": module, "family": "import-export", "records": len(records)}, ensure_ascii=False)) return 0 if __name__ == "__main__": raise SystemExit(main())