128 lines
4.4 KiB
Python
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
|