#!/usr/bin/env python3 """Materialize selected burst demo months from one normal day and event runs.""" from __future__ import annotations import argparse import asyncio import json import os import sys from datetime import datetime from pathlib import Path def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--manifest", type=Path, default=Path("resources/benchmarks/tjwater2-v2-burst-demo.json"), ) parser.add_argument( "--generation-state", type=Path, default=Path("data/benchmark-generation/tjwater2-20261105/state.json"), ) parser.add_argument( "--state", type=Path, default=Path("data/benchmark-generation/tjwater2-20261105/demo-scada-state.json"), ) args = parser.parse_args() sys.path.insert(0, str(Path(__file__).resolve().parents[1])) os.umask(0o077) from psycopg.conninfo import make_conninfo from scripts.benchmark.calibration import digest, save_json from scripts.benchmark.generation import write_demo_scada_window from scripts.benchmark.simulation_design import load_simulation_design from scripts.check_burst_simulation_design import resolve_project manifest = json.loads(args.manifest.read_text(encoding="utf-8")) generation = json.loads(args.generation_state.read_text(encoding="utf-8")) if manifest["project_code"] != generation["target"] or generation.get("status") != "prepared": raise ValueError("manifest and isolated generation state do not match") design = load_simulation_design(Path("resources/benchmarks/tjwater2-v2-burst-balanced.json")) source_biz, source_iot = asyncio.run(resolve_project(design)) business_dsn = make_conninfo(source_biz, dbname=generation["target"]) timescale_dsn = make_conninfo(source_iot, dbname=generation["target"]) state_path = args.state.resolve() state_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700) manifest_sha256 = digest(args.manifest) state = json.loads(state_path.read_text(encoding="utf-8")) if state_path.exists() else { "version": "burst-demo-scada-state@1", "target": generation["target"], "manifest_sha256": manifest_sha256, "cases": {}, "status": "materializing", } if state["target"] != generation["target"] or state["manifest_sha256"] != manifest_sha256: raise ValueError("demo state belongs to different inputs") save_json(state_path, state) normal_source_start = datetime.fromisoformat(manifest["normal_source_start"]) for case in manifest["cases"]: source_case = generation["cases"].get(case["case_key"]) if not source_case or source_case.get("status") != "scada_materialized": raise ValueError(f"event source is unavailable: {case['case_key']}") summary = write_demo_scada_window( business_dsn=business_dsn, timescale_dsn=timescale_dsn, normal_source_start=normal_source_start, target_start=datetime.fromisoformat(case["target_start"]), day_count=int(manifest["day_count"]), event_start=datetime.fromisoformat(case["event_start"]), event_run_id=source_case["analysis_run_id"], noise_seed=manifest["noise_seed"], ) state["cases"][case["demo_id"]] = { "case_key": case["case_key"], "entry_mode": case["entry_mode"], **summary, } save_json(state_path, state) print({"demo_id": case["demo_id"], "rows": summary["rows"]}, flush=True) state["status"] = "completed" save_json(state_path, state) print(json.dumps({"status": state["status"], "cases": state["cases"]}, ensure_ascii=False), flush=True) return 0 if __name__ == "__main__": raise SystemExit(main())