Skip to content

Commit 4994cfb

Browse files
committed
feat: ✨ multiple weather fields can be fetched, fetched data graphically shown and incorporated to the LLM input
1 parent d60ce42 commit 4994cfb

3 files changed

Lines changed: 64 additions & 25 deletions

File tree

app/api/routes/weather.py

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,38 @@
55

66
router = APIRouter(prefix="/weather", tags=["weather"])
77

8+
ALL_HOURLY_FIELDS = [
9+
"temperature_2m",
10+
"relative_humidity_2m",
11+
"rain",
12+
"apparent_temperature",
13+
"precipitation_probability",
14+
"precipitation",
15+
"wind_direction_10m",
16+
"wind_speed_10m",
17+
"soil_temperature_0cm",
18+
"uv_index",
19+
]
20+
821

922
@router.get("/forecast")
10-
def get_weather_forecast(latitude: float, longitude: float):
23+
def get_weather_forecast(
24+
latitude: float,
25+
longitude: float,
26+
fields: str | None = None,
27+
):
1128
"""
1229
Fetch weather forecast data for the given coordinates.
30+
31+
Query params:
32+
- latitude, longitude: required floats
33+
- fields: optional comma-separated list of hourly variables to include.
34+
Defaults to all supported fields when omitted.
1335
"""
1436
controller = Controller()
1537
try:
16-
weather_data = controller.get_weather(latitude, longitude)
38+
requested = [f.strip() for f in fields.split(",") if f.strip()] if fields else []
39+
weather_data = controller.get_weather(latitude, longitude, hourly_fields=requested)
1740
return weather_data
1841
except Exception as e:
1942
raise AppError(str(e), status_code=500)

app/services/controller.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ def fill_form(self, user_input: str, fields: list, pdf_form_path: str, model: st
1313
def prepare_fillable(self, pdf_path: str):
1414
return self.file_manipulator.prepare_fillable(pdf_path)
1515

16-
def get_weather(self, latitude: float, longitude: float):
17-
return self.external_apis_coordinator.get_weather(latitude, longitude)
16+
def get_weather(self, latitude: float, longitude: float, hourly_fields: list = None):
17+
return self.external_apis_coordinator.get_weather(latitude, longitude, hourly_fields=hourly_fields)

app/services/external_apis_coordinator.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,58 +4,74 @@
44
import requests_cache
55
from retry_requests import retry
66

7+
# All supported hourly fields exposed by this service
8+
ALL_HOURLY_FIELDS = [
9+
"temperature_2m",
10+
"relative_humidity_2m",
11+
"rain",
12+
"apparent_temperature",
13+
"precipitation_probability",
14+
"precipitation",
15+
"wind_direction_10m",
16+
"wind_speed_10m",
17+
"soil_temperature_0cm",
18+
"uv_index",
19+
]
20+
21+
722
class ExternalAPIsCoordinator:
823
def __init__(self):
924
pass
10-
11-
def get_weather(self, latitude: float, longitude: float):
25+
26+
def get_weather(self, latitude: float, longitude: float, hourly_fields: list[str] | None = None):
1227
"""
1328
Get weather data for a given latitude and longitude.
1429
1530
Args:
1631
latitude (float): The latitude of the location.
1732
longitude (float): The longitude of the location.
33+
hourly_fields (list[str] | None): Subset of hourly variables to request.
34+
Defaults to ALL_HOURLY_FIELDS when None or empty.
1835
1936
Returns:
2037
dict: A dictionary containing the weather data.
2138
"""
22-
39+
requested = [f for f in (hourly_fields or []) if f in ALL_HOURLY_FIELDS]
40+
if not requested:
41+
requested = list(ALL_HOURLY_FIELDS)
42+
2343
# Setup the Open-Meteo API client with cache and retry on error
24-
cache_session = requests_cache.CachedSession('.cache', expire_after = 3600)
25-
retry_session = retry(cache_session, retries = 5, backoff_factor = 0.2)
26-
openmeteo = openmeteo_requests.Client(session = retry_session)
44+
cache_session = requests_cache.CachedSession('.cache', expire_after=3600)
45+
retry_session = retry(cache_session, retries=5, backoff_factor=0.2)
46+
openmeteo = openmeteo_requests.Client(session=retry_session)
2747

28-
# Make sure all required weather variables are listed here
29-
# The order of variables in hourly or daily is important to assign them correctly below
3048
url = "https://api.open-meteo.com/v1/forecast"
3149
params = {
3250
"latitude": latitude,
3351
"longitude": longitude,
34-
"hourly": "temperature_2m",
52+
"hourly": requested,
3553
}
36-
responses = openmeteo.weather_api(url, params = params)
54+
responses = openmeteo.weather_api(url, params=params)
3755

38-
# Process first location. Add a for-loop for multiple locations or weather models
3956
response = responses[0]
4057
print(f"Coordinates: {response.Latitude()}°N {response.Longitude()}°E")
4158
print(f"Elevation: {response.Elevation()} m asl")
4259
print(f"Timezone difference to GMT+0: {response.UtcOffsetSeconds()}s")
4360

44-
# Process hourly data. The order of variables needs to be the same as requested.
4561
hourly = response.Hourly()
46-
hourly_temperature_2m = hourly.Variables(0).ValuesAsNumpy()
4762

48-
hourly_data = {
63+
hourly_data: dict = {
4964
"date": pd.date_range(
50-
start = pd.to_datetime(hourly.Time(), unit = "s", utc = True),
51-
end = pd.to_datetime(hourly.TimeEnd(), unit = "s", utc = True),
52-
freq = pd.Timedelta(seconds = hourly.Interval()),
53-
inclusive = "left"
65+
start=pd.to_datetime(hourly.Time(), unit="s", utc=True),
66+
end=pd.to_datetime(hourly.TimeEnd(), unit="s", utc=True),
67+
freq=pd.Timedelta(seconds=hourly.Interval()),
68+
inclusive="left",
5469
)
5570
}
5671

57-
hourly_data["temperature_2m"] = hourly_temperature_2m.tolist()
58-
72+
for i, field in enumerate(requested):
73+
hourly_data[field] = hourly.Variables(i).ValuesAsNumpy().tolist()
74+
5975
# Convert pandas Timestamp objects to ISO strings for JSON serialization
6076
hourly_data["date"] = [d.isoformat() for d in hourly_data["date"]]
6177

@@ -66,5 +82,5 @@ def get_weather(self, latitude: float, longitude: float):
6682
"timezone": response.Timezone(),
6783
"timezone_abbreviation": response.TimezoneAbbreviation(),
6884
"utc_offset_seconds": response.UtcOffsetSeconds(),
69-
"hourly": hourly_data
85+
"hourly": hourly_data,
7086
}

0 commit comments

Comments
 (0)