This commit is contained in:
alex wiesner
2026-03-13 20:15:22 +00:00
parent e68c95b2dd
commit c4d39f2812
33 changed files with 2383 additions and 162 deletions

View File

@@ -1,22 +1,17 @@
import hashlib
import logging
from datetime import date as date_cls
import requests
from django.core.cache import cache
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
logger = logging.getLogger(__name__)
from adventures.utils.weather import fetch_daily_temperature
class WeatherViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
OPEN_METEO_ARCHIVE_URL = "https://archive-api.open-meteo.com/v1/archive"
OPEN_METEO_FORECAST_URL = "https://api.open-meteo.com/v1/forecast"
CACHE_TIMEOUT_SECONDS = 60 * 60 * 6
MAX_DAYS_PER_REQUEST = 60
@@ -39,7 +34,15 @@ class WeatherViewSet(viewsets.ViewSet):
for entry in days:
if not isinstance(entry, dict):
results.append(
{"date": None, "available": False, "temperature_c": None}
{
"date": None,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
@@ -49,14 +52,30 @@ class WeatherViewSet(viewsets.ViewSet):
if not date or latitude is None or longitude is None:
results.append(
{"date": date, "available": False, "temperature_c": None}
{
"date": date,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
parsed_date = self._parse_date(date)
if parsed_date is None:
results.append(
{"date": date, "available": False, "temperature_c": None}
{
"date": date,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
@@ -65,7 +84,15 @@ class WeatherViewSet(viewsets.ViewSet):
lon = float(longitude)
except (TypeError, ValueError):
results.append(
{"date": date, "available": False, "temperature_c": None}
{
"date": date,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
@@ -82,57 +109,9 @@ class WeatherViewSet(viewsets.ViewSet):
return Response({"results": results}, status=status.HTTP_200_OK)
def _fetch_daily_temperature(self, date: str, latitude: float, longitude: float):
base_payload = {
"date": date,
"available": False,
"temperature_c": None,
}
for url in (self.OPEN_METEO_ARCHIVE_URL, self.OPEN_METEO_FORECAST_URL):
try:
response = requests.get(
url,
params={
"latitude": latitude,
"longitude": longitude,
"start_date": date,
"end_date": date,
"daily": "temperature_2m_max,temperature_2m_min",
"timezone": "UTC",
},
timeout=8,
)
response.raise_for_status()
data = response.json()
except requests.RequestException:
continue
except ValueError:
continue
daily = data.get("daily") or {}
max_values = daily.get("temperature_2m_max") or []
min_values = daily.get("temperature_2m_min") or []
if not max_values or not min_values:
continue
try:
avg = (float(max_values[0]) + float(min_values[0])) / 2
except (TypeError, ValueError, IndexError):
continue
return {
"date": date,
"available": True,
"temperature_c": round(avg, 1),
}
logger.info(
"No weather data available for date=%s lat=%s lon=%s",
date,
latitude,
longitude,
return fetch_daily_temperature(
date=date, latitude=latitude, longitude=longitude
)
return base_payload
def _cache_key(self, date: str, latitude: float, longitude: float) -> str:
rounded_lat = round(latitude, 3)