from __future__ import annotations import json from dataclasses import dataclass from datetime import datetime from gibil.classes.models import WeatherForecastPoint, WeatherResolvedTruth @dataclass(frozen=True) class WeatherDisplayDataset: forecast_points: list[WeatherForecastPoint] resolved_truth: list[WeatherResolvedTruth] class WeatherDisplay: """Renders weather source data for the Astrape web UI.""" def render(self) -> str: return """

Weather

External forecast history

Horizons
Resolved truth
""" def data_payload(self, dataset: WeatherDisplayDataset | None = None) -> str: if dataset is None: dataset = WeatherDisplayDataset(forecast_points=[], resolved_truth=[]) forecast_points = [self._forecast_point(point) for point in dataset.forecast_points] resolved_truth = [self._truth_point(point) for point in dataset.resolved_truth] horizons = sorted({point["horizon_hours"] for point in forecast_points}) return json.dumps( { "now": datetime.now().astimezone().isoformat(), "forecast_points": forecast_points, "resolved_truth": resolved_truth, "horizons": horizons, "min_horizon": 1, "max_horizon": 47, } ) def _forecast_point(self, point: WeatherForecastPoint) -> dict[str, object]: return { "issued_at": self._iso(point.issued_at), "target_at": self._iso(point.target_at), "horizon_hours": point.horizon_hours, "source": point.source, "temperature_c": point.temperature_c, "shortwave_radiation_w_m2": point.shortwave_radiation_w_m2, "cloud_cover_pct": point.cloud_cover_pct, } def _truth_point(self, point: WeatherResolvedTruth) -> dict[str, object]: return { "resolved_at": self._iso(point.resolved_at), "source": point.source, "temperature_c": point.temperature_c, "shortwave_radiation_w_m2": point.shortwave_radiation_w_m2, "cloud_cover_pct": point.cloud_cover_pct, } def _iso(self, value: datetime) -> str: return value.isoformat()