Add tile driven weather provider adapter
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate weather lookup coordinates for source-backed Agrarian tiles.
|
||||
|
||||
The weather provider can request live conditions for any active tile with
|
||||
latitude/longitude. This manifest is intentionally generated from the registry
|
||||
instead of per-tile hard-coded overrides, so future source-backed tiles are
|
||||
eligible automatically when they are added to the registry.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
REGISTRY_PATH = ROOT / "Data" / "Tiles" / "ground_zero_tiles.json"
|
||||
OUTPUT_PATH = ROOT / "Data" / "Tiles" / "tile_weather_manifest.json"
|
||||
WEATHER_READY_STATUSES = {"source_data_found", "generated", "validated", "packaged", "published"}
|
||||
|
||||
|
||||
def main() -> None:
|
||||
registry = json.loads(REGISTRY_PATH.read_text(encoding="utf-8"))
|
||||
records = []
|
||||
|
||||
for tile in registry.get("tiles", []):
|
||||
status = tile.get("status")
|
||||
tile_id = tile.get("tile_id")
|
||||
grid = tile.get("grid", {})
|
||||
latitude = grid.get("center_latitude")
|
||||
longitude = grid.get("center_longitude")
|
||||
if status not in WEATHER_READY_STATUSES or not tile_id or latitude is None or longitude is None:
|
||||
continue
|
||||
|
||||
records.append(
|
||||
{
|
||||
"tile_id": tile_id,
|
||||
"center_latitude": latitude,
|
||||
"center_longitude": longitude,
|
||||
"provider": "open-meteo",
|
||||
"lookup_rule": "Use tile center latitude/longitude for live MVP weather and temperature snapshots.",
|
||||
}
|
||||
)
|
||||
|
||||
OUTPUT_PATH.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"schema_version": 1,
|
||||
"generated_at_utc": datetime.now(timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z"),
|
||||
"source_registry": str(REGISTRY_PATH.relative_to(ROOT)),
|
||||
"generation_rule": "Every source-backed/generated tile with center coordinates is emitted; unknown placeholders are skipped.",
|
||||
"tiles": records,
|
||||
},
|
||||
indent=2,
|
||||
)
|
||||
+ "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
print(f"Wrote {len(records)} tile weather manifest record(s) to {OUTPUT_PATH.relative_to(ROOT)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,81 @@
|
||||
from pathlib import Path
|
||||
import json
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
BUILD_CS = ROOT / "Source" / "AgrarianGame" / "AgrarianGame.Build.cs"
|
||||
HEADER = ROOT / "Source" / "AgrarianGame" / "AgrarianWeatherProviderSubsystem.h"
|
||||
CPP = ROOT / "Source" / "AgrarianGame" / "AgrarianWeatherProviderSubsystem.cpp"
|
||||
GENERATOR = ROOT / "Scripts" / "generate_tile_weather_manifest.py"
|
||||
MANIFEST = ROOT / "Data" / "Tiles" / "tile_weather_manifest.json"
|
||||
TDD = ROOT / "Docs" / "TechnicalDesignDocument.md"
|
||||
ROADMAP = ROOT / "AGRARIAN_DEVELOPMENT_ROADMAP.md"
|
||||
|
||||
|
||||
EXPECTED = {
|
||||
BUILD_CS: ['"HTTP"', '"Json"'],
|
||||
HEADER: [
|
||||
"UAgrarianWeatherProviderSubsystem",
|
||||
"FAgrarianWeatherProviderSnapshot",
|
||||
"RequestWeatherForActiveGameState",
|
||||
"RequestWeatherForTile",
|
||||
"BuildOpenMeteoForecastUrl",
|
||||
"ApplySnapshotToGameState",
|
||||
"MapOpenMeteoWeatherCode",
|
||||
],
|
||||
CPP: [
|
||||
"current=temperature_2m,relative_humidity_2m,precipitation",
|
||||
"daily=temperature_2m_max,temperature_2m_min",
|
||||
"GameState->SetRegionalTemperatureProfile",
|
||||
"GameState->SetRegionalObservedTemperature",
|
||||
"GameState->SetWeather",
|
||||
"MapOpenMeteoWeatherCode",
|
||||
],
|
||||
GENERATOR: [
|
||||
"WEATHER_READY_STATUSES",
|
||||
"Every source-backed/generated tile",
|
||||
"tile_weather_manifest.json",
|
||||
],
|
||||
TDD: [
|
||||
"Open-Meteo",
|
||||
"tile center latitude/longitude",
|
||||
"Future source-backed",
|
||||
],
|
||||
ROADMAP: [
|
||||
"[x] Add real-world weather provider adapter for Ground Zero by latitude/longitude.",
|
||||
"tile-driven Open-Meteo adapter",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def assert_contains() -> None:
|
||||
missing = []
|
||||
for path, snippets in EXPECTED.items():
|
||||
text = path.read_text(encoding="utf-8")
|
||||
for snippet in snippets:
|
||||
if snippet not in text:
|
||||
missing.append(f"{path.relative_to(ROOT)}: {snippet}")
|
||||
if missing:
|
||||
raise RuntimeError("Weather provider adapter verification failed: " + "; ".join(missing))
|
||||
|
||||
|
||||
def assert_manifest() -> None:
|
||||
data = json.loads(MANIFEST.read_text(encoding="utf-8"))
|
||||
tiles = data.get("tiles", [])
|
||||
if len(tiles) != 1:
|
||||
raise RuntimeError(f"Expected one source-backed weather tile, got {len(tiles)}")
|
||||
tile = tiles[0]
|
||||
if tile.get("tile_id") != "gz_us_ca_pacifica_utm10n_e544_n4160":
|
||||
raise RuntimeError(f"Unexpected weather tile id: {tile.get('tile_id')}")
|
||||
if tile.get("center_latitude") is None or tile.get("center_longitude") is None:
|
||||
raise RuntimeError("Weather manifest tile is missing coordinates")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
assert_contains()
|
||||
assert_manifest()
|
||||
print("Agrarian weather provider adapter verification complete.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user