|
|
| from __future__ import annotations
|
|
|
| from typing import Any
|
|
|
| from ui.country_coords import resolve_countries
|
|
|
|
|
| def empty_globe_state() -> dict[str, Any]:
|
| return {
|
| "version": 0,
|
| "details_version": 0,
|
| "markers": [],
|
| "highlights": [],
|
| "fly_to": None,
|
| "country_details": {},
|
| }
|
|
|
|
|
| def _next_version(state: dict[str, Any]) -> int:
|
| return int(state.get("version", 0)) + 1
|
|
|
|
|
| def _marker_payload(
|
| country: dict[str, Any],
|
| *,
|
| country_details: dict[str, Any] | None = None,
|
| ) -> dict[str, Any]:
|
| payload = {
|
| "iso2": country["iso2"],
|
| "name": country.get("name"),
|
| "label": country["label"],
|
| "lng": country["lng"],
|
| "lat": country["lat"],
|
| }
|
| if country_details:
|
| detail = country_details.get(country["iso2"])
|
| if detail:
|
| payload["detail"] = detail
|
| return payload
|
|
|
|
|
| def _highlight_payload(country: dict[str, Any]) -> dict[str, Any]:
|
| return {
|
| "iso2": country["iso2"],
|
| "lng": country["lng"],
|
| "lat": country["lat"],
|
| }
|
|
|
|
|
| def _fly_to_payload(
|
| countries: list[dict[str, Any]],
|
| *,
|
| zoom: float | None,
|
| ) -> dict[str, Any] | None:
|
| if not countries:
|
| return None
|
| if len(countries) == 1:
|
| country = countries[0]
|
| return {
|
| "center": [country["lng"], country["lat"]],
|
| "zoom": zoom if zoom is not None else 4.0,
|
| }
|
|
|
| lngs = [country["lng"] for country in countries]
|
| lats = [country["lat"] for country in countries]
|
| return {
|
| "bounds": [[min(lngs), min(lats)], [max(lngs), max(lats)]],
|
| "padding": 80,
|
| }
|
|
|
|
|
| def apply_update_globe(
|
| state: dict[str, Any],
|
| args: dict[str, Any],
|
| ) -> tuple[dict[str, Any], dict[str, Any]]:
|
| action = (args.get("action") or "show").strip().lower()
|
| countries = args.get("countries") or []
|
| labels = args.get("labels")
|
| zoom = args.get("zoom")
|
|
|
| if action == "clear":
|
| new_state = empty_globe_state()
|
| new_state["version"] = _next_version(state)
|
| return {"status": "cleared", "action": action}, new_state
|
|
|
| if not countries:
|
| return (
|
| {"error": "countries is required unless action is clear"},
|
| state,
|
| )
|
|
|
| resolved, unknown = resolve_countries(countries, labels)
|
| if not resolved:
|
| return (
|
| {
|
| "error": "No valid ISO-2 country codes were provided",
|
| "unknown_codes": unknown,
|
| },
|
| state,
|
| )
|
|
|
| country_details = dict(state.get("country_details") or {})
|
| new_state = {
|
| "version": _next_version(state),
|
| "details_version": int(state.get("details_version", 0)),
|
| "markers": list(state.get("markers", [])),
|
| "highlights": list(state.get("highlights", [])),
|
| "fly_to": state.get("fly_to"),
|
| "country_details": country_details,
|
| }
|
|
|
| if action == "show":
|
| new_state["markers"] = [
|
| _marker_payload(country, country_details=country_details)
|
| for country in resolved
|
| ]
|
| new_state["highlights"] = [_highlight_payload(country) for country in resolved]
|
| new_state["fly_to"] = _fly_to_payload(resolved, zoom=zoom)
|
| elif action in {"markers", "add_markers"}:
|
| existing = {marker["iso2"] for marker in new_state["markers"]}
|
| for country in resolved:
|
| if country["iso2"] not in existing:
|
| new_state["markers"].append(
|
| _marker_payload(country, country_details=country_details)
|
| )
|
| elif action == "highlight":
|
| existing = {item["iso2"] for item in new_state["highlights"]}
|
| for country in resolved:
|
| if country["iso2"] not in existing:
|
| new_state["highlights"].append(_highlight_payload(country))
|
| elif action == "fly_to":
|
| new_state["fly_to"] = _fly_to_payload(resolved, zoom=zoom)
|
| else:
|
| return (
|
| {
|
| "error": (
|
| "Unknown action. Use show, markers, highlight, fly_to, or clear."
|
| )
|
| },
|
| state,
|
| )
|
|
|
| result: dict[str, Any] = {
|
| "status": "ok",
|
| "action": action,
|
| "countries": [
|
| {
|
| "iso2": country["iso2"],
|
| "name": country["name"],
|
| "label": country["label"],
|
| }
|
| for country in resolved
|
| ],
|
| }
|
| if unknown:
|
| result["unknown_codes"] = unknown
|
| return result, new_state
|
|
|