god / src /god_sim /analytics /history.py
Vikram Vasudevan
insights issue fix
b7228fc
Raw
History Blame Contribute Delete
2.26 kB
from __future__ import annotations
import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Any
from uuid import uuid4
import numpy as np
class SimulationEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (np.integer, np.int64, np.int32, np.int16, np.int8)):
return int(obj)
if isinstance(obj, (np.floating, np.float64, np.float32, np.float16)):
return float(obj)
if isinstance(obj, (np.bool_, bool)):
return bool(obj)
if isinstance(obj, (np.ndarray, np.generic)):
if hasattr(obj, "tolist"):
return obj.tolist()
return str(obj)
if isinstance(obj, (datetime, Path)):
return str(obj)
try:
return super().default(obj)
except TypeError:
return str(obj)
DEFAULT_HISTORY_PATH = Path("data/run_history.json")
def _ensure_parent(path: Path) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
def _safe_load_json_array(path: Path) -> list[dict[str, Any]]:
if not path.exists():
return []
try:
data = json.loads(path.read_text(encoding="utf-8"))
except json.JSONDecodeError:
return []
if not isinstance(data, list):
return []
return [x for x in data if isinstance(x, dict)]
def build_run_record(sim_output: dict[str, Any]) -> dict[str, Any]:
series = sim_output.get("series", [])
config = sim_output.get("config", {})
final = series[-1] if series else {}
return {
"run_id": str(uuid4()),
"created_at_utc": datetime.now(timezone.utc).isoformat(),
"config": config,
"final": final,
"ticks_recorded": len(series),
"series": series,
}
def append_run_history(sim_output: dict[str, Any], path: Path = DEFAULT_HISTORY_PATH) -> dict[str, Any]:
_ensure_parent(path)
runs = _safe_load_json_array(path)
record = build_run_record(sim_output)
runs.append(record)
path.write_text(json.dumps(runs, indent=2, cls=SimulationEncoder), encoding="utf-8")
return record
def load_run_history(path: Path = DEFAULT_HISTORY_PATH) -> list[dict[str, Any]]:
return _safe_load_json_array(path)