from __future__ import annotations import json from dataclasses import asdict from datetime import datetime from gibil.classes.oracle.builder import EnergyOracleBuilder from gibil.classes.models import ( NetPowerForecastPoint, PowerForecastPoint, PowerForecastRun, ) from gibil.classes.oracle.store import OracleStore class OracleDisplay: """Renders energy oracle curves for the Astrape web UI.""" def render(self) -> str: return """

Energy Oracle

Solar, usage, and net power projection curves

""" def data_payload(self) -> str: builder = EnergyOracleBuilder.from_env() solar_run, load_run, net_run = builder.build() actual_points = builder.sigen_store.load_recent_actual_points() try: oracle_store = OracleStore.from_env() historical_net_runs = oracle_store.load_lagged_net_runs() historical_solar_runs = oracle_store.load_lagged_power_runs("solar") historical_load_runs = oracle_store.load_lagged_power_runs("load") except Exception: historical_net_runs = [] historical_solar_runs = [] historical_load_runs = [] return json.dumps( { "issued_at": self._iso(net_run.issued_at), "now": self._iso(net_run.issued_at), "solar_model": solar_run.model_version, "load_model": load_run.model_version, "solar_points": [ self._power_point(point) for point in solar_run.points ], "load_points": [ self._power_point(point) for point in load_run.points ], "net_points": [self._net_point(point) for point in net_run.points], "actual_points": [ self._actual_point(point) for point in actual_points ], "historical_net_runs": [ self._historical_net_run(run) for run in historical_net_runs ], "historical_solar_runs": [ self._historical_power_run(run) for run in historical_solar_runs ], "historical_load_runs": [ self._historical_power_run(run) for run in historical_load_runs ], } ) def _power_point(self, point: PowerForecastPoint) -> dict[str, object]: return { "target_at": self._iso(point.target_at), "horizon_minutes": point.horizon_minutes, "expected_power_w": point.expected_power_w, "p10_power_w": point.p10_power_w, "p50_power_w": point.p50_power_w, "p90_power_w": point.p90_power_w, "confidence": point.confidence, "source": point.source, "model_version": point.model_version, "metadata": point.metadata, } def _net_point(self, point: NetPowerForecastPoint) -> dict[str, object]: return asdict(point) | {"target_at": self._iso(point.target_at)} def _actual_point(self, point: dict[str, object]) -> dict[str, object]: return { "target_at": self._iso(point["target_at"]), "solar_power_w": point["solar_power_w"], "load_power_w": point["load_power_w"], "net_power_w": point["net_power_w"], "grid_import_w": point["grid_import_w"], "grid_export_w": point["grid_export_w"], "sample_count": point["sample_count"], } def _historical_net_run(self, run: dict[str, object]) -> dict[str, object]: return { "lag_hours": run.get("lag_hours"), "issued_at": self._iso(run["issued_at"]), "points": [ { "target_at": self._iso(point["target_at"]), "horizon_minutes": point["horizon_minutes"], "expected_net_power_w": point["expected_net_power_w"], "safe_net_power_w": point["safe_net_power_w"], "p10_net_power_w": point.get("p10_net_power_w"), "p50_net_power_w": point.get("p50_net_power_w"), "p90_net_power_w": point.get("p90_net_power_w"), "solar_p50_power_w": point["solar_p50_power_w"], "load_p50_power_w": point["load_p50_power_w"], "solar_p10_power_w": point["solar_p10_power_w"], "solar_p90_power_w": point.get("solar_p90_power_w"), "load_p10_power_w": point.get("load_p10_power_w"), "load_p90_power_w": point["load_p90_power_w"], } for point in run["points"] ], } def _historical_power_run(self, run: dict[str, object]) -> dict[str, object]: return { "lag_hours": run.get("lag_hours"), "issued_at": self._iso(run["issued_at"]), "kind": run["kind"], "source": run["source"], "model_version": run["model_version"], "points": [ { "target_at": self._iso(point["target_at"]), "horizon_minutes": point["horizon_minutes"], "expected_power_w": point["expected_power_w"], "p10_power_w": point["p10_power_w"], "p50_power_w": point["p50_power_w"], "p90_power_w": point["p90_power_w"], "confidence": point["confidence"], } for point in run["points"] ], } def _iso(self, value: datetime) -> str: return value.isoformat()