120 lines
5.6 KiB
Python
120 lines
5.6 KiB
Python
#!/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())
|