UPDATE
This commit is contained in:
+29
-95
@@ -5,12 +5,12 @@
|
||||
import logging
|
||||
from datetime import date, timedelta
|
||||
|
||||
import requests
|
||||
from django.conf import settings
|
||||
from django.apps import apps
|
||||
from django.db import transaction
|
||||
|
||||
from location_data.models import SoilLocation
|
||||
|
||||
from .adapters import DEFAULT_FORECAST_DAYS
|
||||
from .models import WeatherForecast
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -18,60 +18,15 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
def fetch_weather_from_api(latitude: float, longitude: float) -> dict | None:
|
||||
"""
|
||||
اتصال به API هواشناسی و دریافت پیشبینی ۷ روزه.
|
||||
|
||||
TODO: پیادهسازی اتصال واقعی به API (مثلاً Open-Meteo).
|
||||
در حال حاضر این تابع خالی است و None برمیگرداند.
|
||||
|
||||
پارامترها:
|
||||
latitude: عرض جغرافیایی
|
||||
longitude: طول جغرافیایی
|
||||
|
||||
خروجی مورد انتظار (وقتی پیادهسازی شود):
|
||||
{
|
||||
"daily": {
|
||||
"time": ["2025-07-01", "2025-07-02", ...],
|
||||
"temperature_2m_max": [35.2, 36.1, ...],
|
||||
"temperature_2m_min": [22.1, 23.0, ...],
|
||||
"temperature_2m_mean": [28.6, 29.5, ...],
|
||||
"precipitation_sum": [0.0, 2.5, ...],
|
||||
"precipitation_probability_max": [0, 60, ...],
|
||||
"relative_humidity_2m_mean": [30.0, 45.0, ...],
|
||||
"wind_speed_10m_max": [15.0, 20.0, ...],
|
||||
"et0_fao_evapotranspiration": [6.5, 5.8, ...],
|
||||
"weather_code": [0, 61, ...],
|
||||
}
|
||||
}
|
||||
واکشی پیشبینی هواشناسی از provider فعال.
|
||||
خروجی در قالب سازگار با Open-Meteo daily format برگردانده میشود.
|
||||
"""
|
||||
params = {
|
||||
"latitude": latitude,
|
||||
"longitude": longitude,
|
||||
"forecast_days": 7,
|
||||
"timezone": "auto",
|
||||
"daily": [
|
||||
"temperature_2m_max",
|
||||
"temperature_2m_min",
|
||||
"temperature_2m_mean",
|
||||
"precipitation_sum",
|
||||
"precipitation_probability_max",
|
||||
"relative_humidity_2m_mean",
|
||||
"wind_speed_10m_max",
|
||||
"et0_fao_evapotranspiration",
|
||||
"weather_code",
|
||||
],
|
||||
}
|
||||
headers = {"accept": "application/json"}
|
||||
if settings.WEATHER_API_KEY:
|
||||
headers["Authorization"] = f"Bearer {settings.WEATHER_API_KEY}"
|
||||
|
||||
response = requests.get(
|
||||
settings.WEATHER_API_BASE_URL,
|
||||
params=params,
|
||||
headers=headers,
|
||||
timeout=60,
|
||||
adapter = apps.get_app_config("weather").get_weather_data_adapter()
|
||||
return adapter.fetch_forecast(
|
||||
latitude=latitude,
|
||||
longitude=longitude,
|
||||
days=DEFAULT_FORECAST_DAYS,
|
||||
)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
|
||||
def parse_weather_response(data: dict) -> list[dict]:
|
||||
@@ -83,37 +38,28 @@ def parse_weather_response(data: dict) -> list[dict]:
|
||||
times = daily.get("time", [])
|
||||
forecasts = []
|
||||
|
||||
for i, date_str in enumerate(times):
|
||||
for index, date_str in enumerate(times):
|
||||
forecasts.append(
|
||||
{
|
||||
"forecast_date": date_str,
|
||||
"temperature_max": _safe_index(
|
||||
daily.get("temperature_2m_max"), i
|
||||
),
|
||||
"temperature_min": _safe_index(
|
||||
daily.get("temperature_2m_min"), i
|
||||
),
|
||||
"temperature_mean": _safe_index(
|
||||
daily.get("temperature_2m_mean"), i
|
||||
),
|
||||
"precipitation": _safe_index(
|
||||
daily.get("precipitation_sum"), i
|
||||
),
|
||||
"temperature_max": _safe_index(daily.get("temperature_2m_max"), index),
|
||||
"temperature_min": _safe_index(daily.get("temperature_2m_min"), index),
|
||||
"temperature_mean": _safe_index(daily.get("temperature_2m_mean"), index),
|
||||
"precipitation": _safe_index(daily.get("precipitation_sum"), index),
|
||||
"precipitation_probability": _safe_index(
|
||||
daily.get("precipitation_probability_max"), i
|
||||
daily.get("precipitation_probability_max"),
|
||||
index,
|
||||
),
|
||||
"humidity_mean": _safe_index(
|
||||
daily.get("relative_humidity_2m_mean"), i
|
||||
),
|
||||
"wind_speed_max": _safe_index(
|
||||
daily.get("wind_speed_10m_max"), i
|
||||
daily.get("relative_humidity_2m_mean"),
|
||||
index,
|
||||
),
|
||||
"wind_speed_max": _safe_index(daily.get("wind_speed_10m_max"), index),
|
||||
"et0": _safe_index(
|
||||
daily.get("et0_fao_evapotranspiration"), i
|
||||
),
|
||||
"weather_code": _safe_index(
|
||||
daily.get("weather_code"), i
|
||||
daily.get("et0_fao_evapotranspiration"),
|
||||
index,
|
||||
),
|
||||
"weather_code": _safe_index(daily.get("weather_code"), index),
|
||||
}
|
||||
)
|
||||
return forecasts
|
||||
@@ -147,24 +93,21 @@ def update_weather_for_location(location: SoilLocation) -> dict:
|
||||
}
|
||||
|
||||
if data is None:
|
||||
logger.info(
|
||||
"Weather API returned no data for location %s (stub mode).",
|
||||
location.id,
|
||||
)
|
||||
logger.info("Weather provider returned no data for location %s.", location.id)
|
||||
return {
|
||||
"status": "no_data",
|
||||
"location_id": location.id,
|
||||
"message": "API connection not implemented yet.",
|
||||
"message": "Weather provider returned no data.",
|
||||
}
|
||||
|
||||
forecasts = parse_weather_response(data)
|
||||
|
||||
with transaction.atomic():
|
||||
for fc in forecasts:
|
||||
for forecast in forecasts:
|
||||
WeatherForecast.objects.update_or_create(
|
||||
location=location,
|
||||
forecast_date=fc.pop("forecast_date"),
|
||||
defaults=fc,
|
||||
forecast_date=forecast.pop("forecast_date"),
|
||||
defaults=forecast,
|
||||
)
|
||||
|
||||
return {
|
||||
@@ -180,14 +123,13 @@ def update_weather_for_all_locations() -> list[dict]:
|
||||
"""
|
||||
results = []
|
||||
for location in SoilLocation.objects.all():
|
||||
result = update_weather_for_location(location)
|
||||
results.append(result)
|
||||
results.append(update_weather_for_location(location))
|
||||
return results
|
||||
|
||||
|
||||
def get_forecast_for_location(
|
||||
location: SoilLocation,
|
||||
days: int = 7,
|
||||
days: int = DEFAULT_FORECAST_DAYS,
|
||||
) -> list[WeatherForecast]:
|
||||
"""
|
||||
دریافت پیشبینیهای ذخیرهشده برای یک location (تا N روز آینده).
|
||||
@@ -207,14 +149,6 @@ def should_irrigate_today(location: SoilLocation) -> dict:
|
||||
"""
|
||||
بررسی ساده: آیا فردا باران میبارد؟
|
||||
اگر بارش فردا بیشتر از آستانه باشد → آبیاری لازم نیست.
|
||||
|
||||
خروجی:
|
||||
{
|
||||
"needs_irrigation": bool | None,
|
||||
"tomorrow_precipitation": float | None,
|
||||
"tomorrow_date": str,
|
||||
"reason": str,
|
||||
}
|
||||
"""
|
||||
tomorrow = date.today() + timedelta(days=1)
|
||||
forecast = WeatherForecast.objects.filter(
|
||||
|
||||
Reference in New Issue
Block a user