[moods] Add weather lookup to moods

This commit is contained in:
2026-05-20 12:47:27 -04:00
parent db5b673cd5
commit c7850878fe
3 changed files with 102 additions and 0 deletions

View File

@ -37,6 +37,8 @@ class MoodLogData(BaseLogData):
mood_type: Optional[str] = None mood_type: Optional[str] = None
mood_reason_ids: Optional[list[int]] = None mood_reason_ids: Optional[list[int]] = None
mood_quality: Optional[int] = None mood_quality: Optional[int] = None
weather_temp: Optional[float] = None
weather_description: Optional[str] = None
@classmethod @classmethod
def override_fields(cls) -> dict: def override_fields(cls) -> dict:

View File

@ -0,0 +1,80 @@
import logging
import xml.etree.ElementTree as ET
from typing import Optional
from urllib.parse import urlencode
import requests
logger = logging.getLogger(__name__)
NWS_URL = "https://forecast.weather.gov/MapClick.php"
def fetch_current_weather(lat: float, lon: float) -> Optional[dict]:
"""Fetch current weather from NWS DWML feed for the given lat/lon.
Returns dict with 'temp' and 'description' keys, or None on failure.
"""
params = {
"lat": lat,
"lon": lon,
"unit": 0,
"lg": "english",
"FcstType": "dwml",
}
url = f"{NWS_URL}?{urlencode(params)}"
try:
resp = requests.get(url, timeout=10)
resp.raise_for_status()
except requests.RequestException as e:
logger.warning("Failed to fetch NWS weather data: %s", e)
return None
try:
root = ET.fromstring(resp.content)
except ET.ParseError as e:
logger.warning("Failed to parse NWS XML: %s", e)
return None
# Find current observations data block
data_blocks = root.findall("data")
obs_block = None
for block in data_blocks:
if block.get("type") == "current observations":
obs_block = block
break
if obs_block is None:
logger.warning("No current observations block in NWS response")
return None
params_el = obs_block.find("parameters")
if params_el is None:
return None
# Temperature (apparent / feels-like)
temp = None
for temp_el in params_el.findall("temperature"):
if temp_el.get("type") == "apparent":
val_el = temp_el.find("value")
if val_el is not None and val_el.text:
try:
temp = int(round(float(val_el.text)))
except (ValueError, TypeError):
pass
break
# Weather description
description = None
weather_el = params_el.find("weather")
if weather_el is not None:
cond_el = weather_el.find("weather-conditions")
if cond_el is not None:
description = cond_el.get("weather-summary")
if temp is None and description is None:
logger.warning("No weather data found in NWS response")
return None
return {"temp": temp, "description": description}

View File

@ -5,6 +5,7 @@ from django.shortcuts import redirect, render
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from moods.models import Mood, MoodLogData, MoodQuality, MoodReason from moods.models import Mood, MoodLogData, MoodQuality, MoodReason
from moods.utils import fetch_current_weather
from scrobbles.models import Scrobble from scrobbles.models import Scrobble
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -66,6 +67,25 @@ def checkin(request):
mood_reason_ids=[int(reason_id)] if reason_id else [], mood_reason_ids=[int(reason_id)] if reason_id else [],
) )
# Attempt to add current weather from most recent location
last_loc = (
Scrobble.objects.filter(
user=request.user,
media_type=Scrobble.MediaType.GEO_LOCATION,
geo_location__isnull=False,
)
.order_by("-timestamp")
.first()
)
if last_loc and last_loc.geo_location:
weather = fetch_current_weather(
last_loc.geo_location.lat,
last_loc.geo_location.lon,
)
if weather:
log_data.weather_temp = weather["temp"]
log_data.weather_description = weather["description"]
scrobble = Scrobble.objects.create( scrobble = Scrobble.objects.create(
user=request.user, user=request.user,
mood=mood, mood=mood,