added api

This commit is contained in:
2026-06-10 10:29:21 +03:00
parent d0bdd1e95c
commit 73baf4dbe1
26 changed files with 1201 additions and 6 deletions
+142
View File
@@ -0,0 +1,142 @@
import time
from typing import Any
import httpx
from app.config import get_settings
WEATHER_CODES: dict[int, str] = {
0: "ясно",
1: "преимущественно ясно",
2: "переменная облачность",
3: "пасмурно",
45: "туман",
48: "изморозь",
51: "морось",
53: "морось",
55: "морось",
61: "дождь",
63: "дождь",
65: "сильный дождь",
71: "снег",
73: "снег",
75: "сильный снег",
80: "ливень",
81: "ливень",
82: "сильный ливень",
95: "гроза",
96: "гроза с градом",
99: "гроза с градом",
}
_cache: dict[str, Any] = {"data": None, "expires_at": 0.0}
class OpenMeteoClient:
def __init__(self) -> None:
settings = get_settings()
self.base_url = settings.openmeteo_base_url.rstrip("/")
self.lat = settings.weather_lat
self.lon = settings.weather_lon
self.location_name = settings.weather_location_name
self.cache_ttl = settings.weather_cache_sec
def _fetch_raw(self) -> dict[str, Any]:
now = time.time()
if _cache["data"] and now < _cache["expires_at"]:
return _cache["data"]
params = {
"latitude": self.lat,
"longitude": self.lon,
"current": (
"temperature_2m,apparent_temperature,relative_humidity_2m,"
"precipitation,weather_code,wind_speed_10m"
),
"hourly": "temperature_2m,precipitation_probability,precipitation,weather_code",
"timezone": "auto",
"forecast_days": 2,
}
with httpx.Client(timeout=15.0) as client:
response = client.get(f"{self.base_url}/v1/forecast", params=params)
response.raise_for_status()
data = response.json()
_cache["data"] = data
_cache["expires_at"] = now + self.cache_ttl
return data
def fetch_current_and_hourly(self, hours_ahead: int = 12) -> dict[str, Any]:
try:
raw = self._fetch_raw()
except Exception as exc:
return {"ok": False, "error": str(exc), "location": self.location_name}
current = raw.get("current") or {}
hourly = raw.get("hourly") or {}
times = hourly.get("time") or []
limit = min(hours_ahead, len(times))
hourly_slice = []
for i in range(limit):
hourly_slice.append({
"time": times[i],
"temperature_c": hourly.get("temperature_2m", [None])[i],
"precipitation_mm": hourly.get("precipitation", [None])[i],
"precipitation_probability": hourly.get("precipitation_probability", [None])[i],
"weather_code": hourly.get("weather_code", [None])[i],
})
code = current.get("weather_code")
return {
"ok": True,
"location": self.location_name,
"current": {
"time": current.get("time"),
"temperature_c": current.get("temperature_2m"),
"apparent_temperature_c": current.get("apparent_temperature"),
"humidity_pct": current.get("relative_humidity_2m"),
"precipitation_mm": current.get("precipitation"),
"wind_speed_kmh": current.get("wind_speed_10m"),
"weather_code": code,
"conditions": WEATHER_CODES.get(code, "неизвестно") if code is not None else "неизвестно",
},
"hourly": hourly_slice,
}
def rain_summary(self, hours_ahead: int = 12) -> str:
data = self.fetch_current_and_hourly(hours_ahead=hours_ahead)
if not data.get("ok"):
return f"Погода недоступна: {data.get('error', 'ошибка')}"
rainy_hours = []
for hour in data.get("hourly") or []:
prob = hour.get("precipitation_probability")
precip = hour.get("precipitation_mm") or 0
if (prob is not None and prob >= 40) or precip > 0:
time_str = (hour.get("time") or "")[11:16]
rainy_hours.append(f"{time_str} ({prob}% вероятность, {precip} мм)")
if rainy_hours:
return "Ожидаются осадки: " + ", ".join(rainy_hours[:6])
return "Существенных осадков в ближайшие часы не ожидается."
def format_weather_snapshot(data: dict[str, Any] | None = None) -> str:
client = OpenMeteoClient()
snapshot = data if data is not None else client.fetch_current_and_hourly(hours_ahead=6)
lines = ["[Погода]"]
if not snapshot.get("ok"):
lines.append(f"Данные недоступны ({snapshot.get('error', 'ошибка')}).")
lines.append("Для точного ответа вызови get_weather.")
return "\n".join(lines)
cur = snapshot.get("current") or {}
lines.append(
f"{snapshot.get('location')}: {cur.get('temperature_c')}°C "
f"(ощущается {cur.get('apparent_temperature_c')}°C), "
f"{cur.get('conditions')}, ветер {cur.get('wind_speed_kmh')} км/ч."
)
lines.append(client.rain_summary(hours_ahead=6))
lines.append("Вопросы «что на улице» / «будет ли дождь» — get_weather.")
return "\n".join(lines)