Files
voyage/backend/server/adventures/views/weather_view.py
alex wiesner c4d39f2812 changes
2026-03-13 20:15:22 +00:00

128 lines
4.4 KiB
Python

import hashlib
from datetime import date as date_cls
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
from adventures.utils.weather import fetch_daily_temperature
class WeatherViewSet(viewsets.ViewSet):
permission_classes = [IsAuthenticated]
CACHE_TIMEOUT_SECONDS = 60 * 60 * 6
MAX_DAYS_PER_REQUEST = 60
@action(detail=False, methods=["post"], url_path="daily-temperatures")
def daily_temperatures(self, request):
days = request.data.get("days", [])
if not isinstance(days, list):
return Response(
{"error": "'days' must be a list"}, status=status.HTTP_400_BAD_REQUEST
)
if len(days) > self.MAX_DAYS_PER_REQUEST:
return Response(
{
"error": f"A maximum of {self.MAX_DAYS_PER_REQUEST} days is allowed per request"
},
status=status.HTTP_400_BAD_REQUEST,
)
results = []
for entry in days:
if not isinstance(entry, dict):
results.append(
{
"date": None,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
date = entry.get("date")
latitude = entry.get("latitude")
longitude = entry.get("longitude")
if not date or latitude is None or longitude is None:
results.append(
{
"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_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
try:
lat = float(latitude)
lon = float(longitude)
except (TypeError, ValueError):
results.append(
{
"date": date,
"available": False,
"temperature_low_c": None,
"temperature_high_c": None,
"temperature_c": None,
"is_estimate": False,
"source": None,
}
)
continue
cache_key = self._cache_key(date, lat, lon)
cached = cache.get(cache_key)
if cached is not None:
results.append(cached)
continue
payload = self._fetch_daily_temperature(date, lat, lon)
cache.set(cache_key, payload, timeout=self.CACHE_TIMEOUT_SECONDS)
results.append(payload)
return Response({"results": results}, status=status.HTTP_200_OK)
def _fetch_daily_temperature(self, date: str, latitude: float, longitude: float):
return fetch_daily_temperature(
date=date, latitude=latitude, longitude=longitude
)
def _cache_key(self, date: str, latitude: float, longitude: float) -> str:
rounded_lat = round(latitude, 3)
rounded_lon = round(longitude, 3)
raw = f"{date}:{rounded_lat}:{rounded_lon}"
digest = hashlib.sha256(raw.encode()).hexdigest()
return f"weather_daily:{digest}"
def _parse_date(self, value: str):
try:
return date_cls.fromisoformat(value)
except ValueError:
return None