Skip to content

Commit d60ce42

Browse files
committed
feat: ✨ first simple approach introducing fetching data
1 parent 721833d commit d60ce42

6 files changed

Lines changed: 103 additions & 3 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ fireform: build up
6363
@echo "Run 'make logs' to view live logs, 'make down' to stop."
6464

6565
build:
66-
@$(COMPOSE) build --progress=quiet
66+
@$(COMPOSE) build
6767

6868
up:
6969
@$(COMPOSE) up -d

app/api/router.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55

66
from fastapi import APIRouter
77

8-
from app.api.routes import forms, templates
8+
from app.api.routes import forms, templates, weather
99

1010
api_router = APIRouter()
1111
api_router.include_router(templates.router)
1212
api_router.include_router(forms.router)
13+
api_router.include_router(weather.router)

app/api/routes/weather.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from fastapi import APIRouter
2+
3+
from app.core.errors.base import AppError
4+
from app.services.controller import Controller
5+
6+
router = APIRouter(prefix="/weather", tags=["weather"])
7+
8+
9+
@router.get("/forecast")
10+
def get_weather_forecast(latitude: float, longitude: float):
11+
"""
12+
Fetch weather forecast data for the given coordinates.
13+
"""
14+
controller = Controller()
15+
try:
16+
weather_data = controller.get_weather(latitude, longitude)
17+
return weather_data
18+
except Exception as e:
19+
raise AppError(str(e), status_code=500)

app/services/controller.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
from app.services.file_manipulator import FileManipulator
2+
from app.services.external_apis_coordinator import ExternalAPIsCoordinator
3+
24

35
class Controller:
46
def __init__(self):
57
self.file_manipulator = FileManipulator()
8+
self.external_apis_coordinator = ExternalAPIsCoordinator()
69

710
def fill_form(self, user_input: str, fields: list, pdf_form_path: str, model: str = None):
811
return self.file_manipulator.fill_form(user_input, fields, pdf_form_path, model=model)
912

1013
def prepare_fillable(self, pdf_path: str):
11-
return self.file_manipulator.prepare_fillable(pdf_path)
14+
return self.file_manipulator.prepare_fillable(pdf_path)
15+
16+
def get_weather(self, latitude: float, longitude: float):
17+
return self.external_apis_coordinator.get_weather(latitude, longitude)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import openmeteo_requests
2+
3+
import pandas as pd
4+
import requests_cache
5+
from retry_requests import retry
6+
7+
class ExternalAPIsCoordinator:
8+
def __init__(self):
9+
pass
10+
11+
def get_weather(self, latitude: float, longitude: float):
12+
"""
13+
Get weather data for a given latitude and longitude.
14+
15+
Args:
16+
latitude (float): The latitude of the location.
17+
longitude (float): The longitude of the location.
18+
19+
Returns:
20+
dict: A dictionary containing the weather data.
21+
"""
22+
23+
# 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)
27+
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
30+
url = "https://api.open-meteo.com/v1/forecast"
31+
params = {
32+
"latitude": latitude,
33+
"longitude": longitude,
34+
"hourly": "temperature_2m",
35+
}
36+
responses = openmeteo.weather_api(url, params = params)
37+
38+
# Process first location. Add a for-loop for multiple locations or weather models
39+
response = responses[0]
40+
print(f"Coordinates: {response.Latitude()}°N {response.Longitude()}°E")
41+
print(f"Elevation: {response.Elevation()} m asl")
42+
print(f"Timezone difference to GMT+0: {response.UtcOffsetSeconds()}s")
43+
44+
# Process hourly data. The order of variables needs to be the same as requested.
45+
hourly = response.Hourly()
46+
hourly_temperature_2m = hourly.Variables(0).ValuesAsNumpy()
47+
48+
hourly_data = {
49+
"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"
54+
)
55+
}
56+
57+
hourly_data["temperature_2m"] = hourly_temperature_2m.tolist()
58+
59+
# Convert pandas Timestamp objects to ISO strings for JSON serialization
60+
hourly_data["date"] = [d.isoformat() for d in hourly_data["date"]]
61+
62+
return {
63+
"latitude": response.Latitude(),
64+
"longitude": response.Longitude(),
65+
"elevation": response.Elevation(),
66+
"timezone": response.Timezone(),
67+
"timezone_abbreviation": response.TimezoneAbbreviation(),
68+
"utc_offset_seconds": response.UtcOffsetSeconds(),
69+
"hourly": hourly_data
70+
}

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,7 @@ numpy<2
1212
ollama
1313
pypdf
1414
python-multipart
15+
openmeteo-requests
16+
requests-cache
17+
retry-requests
18+
pandas

0 commit comments

Comments
 (0)