122 lines
4.2 KiB
Python
122 lines
4.2 KiB
Python
"""generate_models_from_schema.py — schema model generation
|
|
|
|
Mirrors schemas/generated/*.schema.json into src/quant_engine/models/generated
|
|
as lightweight Python model descriptors.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
SCHEMA_DIR = ROOT / "schemas" / "generated"
|
|
OUT_DIR = ROOT / "src" / "quant_engine" / "models" / "generated"
|
|
|
|
|
|
def load_json(path: Path) -> dict[str, Any]:
|
|
try:
|
|
obj = json.loads(path.read_text(encoding="utf-8"))
|
|
except Exception:
|
|
return {}
|
|
return obj if isinstance(obj, dict) else {}
|
|
|
|
|
|
def to_module_name(path: Path) -> str:
|
|
return path.stem.replace(".", "_").lower()
|
|
|
|
|
|
def render_module(schema_path: Path, schema: dict[str, Any]) -> str:
|
|
title = str(schema.get("title") or schema_path.stem)
|
|
schema_id = str(schema.get("$id") or f"schema://{title}")
|
|
schema_rel_path = str(schema_path.relative_to(ROOT)).replace("\\", "/")
|
|
props = schema.get("properties") if isinstance(schema.get("properties"), dict) else {}
|
|
required = schema.get("required") if isinstance(schema.get("required"), list) else []
|
|
prop_names = list(props.keys())
|
|
return (
|
|
'"""Auto-generated schema model descriptor."""\n'
|
|
"from __future__ import annotations\n\n"
|
|
"from dataclasses import dataclass\n"
|
|
"import json\n"
|
|
"from pathlib import Path\n"
|
|
"from typing import Any\n\n"
|
|
f"SCHEMA_TITLE = {title!r}\n"
|
|
f"SCHEMA_ID = {schema_id!r}\n"
|
|
f"SCHEMA_PATH = {schema_rel_path!r}\n"
|
|
f"SCHEMA_PROPERTIES = {prop_names!r}\n"
|
|
f"SCHEMA_REQUIRED = {required!r}\n\n"
|
|
"@dataclass(frozen=True)\n"
|
|
"class SchemaModel:\n"
|
|
" title: str\n"
|
|
" schema_id: str\n"
|
|
" path: str\n"
|
|
" properties: list[str]\n"
|
|
" required: list[str]\n\n"
|
|
"def load_schema() -> dict[str, Any]:\n"
|
|
" return json.loads(Path(__file__).with_suffix('.schema.json').read_text(encoding='utf-8'))\n\n"
|
|
"def describe() -> SchemaModel:\n"
|
|
" return SchemaModel(\n"
|
|
" title=SCHEMA_TITLE,\n"
|
|
" schema_id=SCHEMA_ID,\n"
|
|
" path=SCHEMA_PATH,\n"
|
|
" properties=list(SCHEMA_PROPERTIES),\n"
|
|
" required=list(SCHEMA_REQUIRED),\n"
|
|
" )\n"
|
|
)
|
|
|
|
|
|
def write_text(path: Path, text: str) -> None:
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
path.write_text(text, encoding="utf-8")
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("--schemas", default=str(SCHEMA_DIR))
|
|
parser.add_argument("--out", default=str(OUT_DIR))
|
|
parser.add_argument("--report", default=str(ROOT / "Temp" / "schema_model_generation_v1.json"))
|
|
args = parser.parse_args()
|
|
|
|
schema_dir = Path(args.schemas)
|
|
out_dir = Path(args.out)
|
|
|
|
schema_files = sorted(schema_dir.glob("*.schema.json"))
|
|
modules = []
|
|
for schema_file in schema_files:
|
|
schema = load_json(schema_file)
|
|
module_name = to_module_name(schema_file)
|
|
modules.append(module_name)
|
|
write_text(out_dir / f"{module_name}.py", render_module(schema_file, schema))
|
|
write_text(out_dir / f"{module_name}.schema.json", json.dumps(schema, ensure_ascii=False, indent=2) + "\n")
|
|
|
|
write_text(out_dir / "__init__.py", '"""Auto-generated schema model package."""\n')
|
|
write_text(
|
|
out_dir.parent / "__init__.py",
|
|
'"""Auto-generated quant_engine.models package."""\n',
|
|
)
|
|
write_text(
|
|
out_dir.parent.parent / "__init__.py",
|
|
'"""Canonical quant_engine package."""\n',
|
|
)
|
|
write_text(
|
|
out_dir.parent.parent.parent / "__init__.py",
|
|
'"""Canonical src package."""\n',
|
|
)
|
|
|
|
report = {
|
|
"status": "OK",
|
|
"schema_count": len(schema_files),
|
|
"generated_module_count": len(modules),
|
|
"package_root": "src/quant_engine/models/generated",
|
|
"report_path": str(Path(args.report).relative_to(ROOT)),
|
|
}
|
|
write_text(Path(args.report), json.dumps(report, ensure_ascii=False, indent=2) + "\n")
|
|
print(json.dumps(report, ensure_ascii=False, indent=2))
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|