Skip to content

Commit 9684c14

Browse files
committed
Add endpoint to fetch locations with latest data
Introduces a new API route `/locations/by-country-ids-with-data` that returns locations along with their latest monitoring data for specified country IDs. Updates `main.py` to include the new router.
1 parent f288156 commit 9684c14

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

src/main.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from routes.get_locations_by_name import router as get_locations_by_name_router
4242
from routes.get_locations_by_id import router as get_locations_by_id_router
4343
from routes.get_locations_by_adm2_ids import router as get_locations_by_adm2_ids_router
44+
from routes.get_locations_with_data import router as get_locations_with_latest_data_router
4445
from routes.get_climate_historical_daily_date_ranges import router as get_climate_historical_daily_date_ranges_router
4546
from routes.get_climate_historical_daily_by_date_range_and_measures import router as get_climate_historical_daily_by_date_range_and_measures_router
4647
from routes.get_climate_historical_climatology_date_ranges import router as get_climate_historical_climatology_date_ranges_router
@@ -97,6 +98,7 @@
9798
app.include_router(get_locations_by_adm2_ids_router)
9899
app.include_router(get_locations_by_name_router)
99100
app.include_router(get_locations_by_id_router)
101+
app.include_router(get_locations_with_latest_data_router)
100102
app.include_router(get_climate_historical_daily_date_ranges_router)
101103

102104
app.include_router(get_climate_historical_daily_by_date_router)
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from fastapi import APIRouter, Query
2+
from aclimate_v3_orm.services.mng_location_service import MngLocationService
3+
from aclimate_v3_orm.services.climate_historical_daily_service import ClimateHistoricalDailyService
4+
from typing import List, Optional, Dict, Any
5+
from pydantic import BaseModel
6+
from datetime import datetime
7+
8+
router = APIRouter(
9+
prefix="/locations",
10+
tags=["Locations"]
11+
)
12+
13+
14+
class MeasureData(BaseModel):
15+
"""Climate measure data"""
16+
measure_id: int
17+
measure_name: str
18+
measure_short_name: str
19+
measure_unit: Optional[str]
20+
value: Optional[float]
21+
22+
class Config:
23+
from_attributes = True
24+
25+
26+
class LatestData(BaseModel):
27+
"""Latest monitoring data for a location"""
28+
date: Optional[str]
29+
measures: List[MeasureData] = []
30+
31+
class Config:
32+
from_attributes = True
33+
34+
35+
class LocationWithData(BaseModel):
36+
"""Location with its latest monitoring data"""
37+
id: int
38+
name: str
39+
ext_id: Optional[str]
40+
enable: Optional[bool]
41+
altitude: Optional[float]
42+
latitude: Optional[float]
43+
longitude: Optional[float]
44+
visible: Optional[bool] = True
45+
admin2_id: Optional[int]
46+
admin2_name: Optional[str]
47+
admin2_ext_id: Optional[str]
48+
admin1_id: Optional[int]
49+
admin1_name: Optional[str]
50+
admin1_ext_id: Optional[str]
51+
country_id: int
52+
country_name: Optional[str]
53+
country_iso2: Optional[str]
54+
latest_data: Optional[LatestData]
55+
56+
class Config:
57+
from_attributes = True
58+
59+
60+
@router.get(
61+
"/by-country-ids-with-data",
62+
response_model=List[LocationWithData],
63+
summary="Get locations with latest monitoring data"
64+
)
65+
def get_locations_with_latest_data(
66+
country_ids: str = Query(..., description="Comma-separated country IDs, e.g. '1,2,3'"),
67+
days: int = Query(0, description="Number of days to look back for latest data (0 = no limit)", ge=0, le=365)
68+
):
69+
"""
70+
Return locations with their latest monitoring data in an optimized single response.
71+
72+
- **country_ids**: Comma-separated list of country IDs
73+
- **days**: How many days back to search for data (0 = no limit, gets most recent available)
74+
"""
75+
ids = [int(cid.strip()) for cid in country_ids.split(",")]
76+
77+
location_service = MngLocationService()
78+
climate_service = ClimateHistoricalDailyService()
79+
result = []
80+
81+
for country_id in ids:
82+
locations = location_service.get_by_country_id(country_id)
83+
84+
for loc in locations:
85+
# Build base location object
86+
flat_loc = {
87+
"id": loc.id,
88+
"name": loc.name,
89+
"ext_id": loc.ext_id,
90+
"enable": loc.enable,
91+
"altitude": loc.altitude,
92+
"latitude": loc.latitude,
93+
"longitude": loc.longitude,
94+
"visible": loc.visible,
95+
"admin2_id": loc.admin_2.id if loc.admin_2 else None,
96+
"admin2_name": loc.admin_2.name if loc.admin_2 else None,
97+
"admin2_ext_id": loc.admin_2.ext_id if loc.admin_2 else None,
98+
"admin1_id": loc.admin_2.admin_1.id if loc.admin_2 and loc.admin_2.admin_1 else None,
99+
"admin1_name": loc.admin_2.admin_1.name if loc.admin_2 and loc.admin_2.admin_1 else None,
100+
"admin1_ext_id": loc.admin_2.admin_1.ext_id if loc.admin_2 and loc.admin_2.admin_1 and loc.admin_2.admin_1.ext_id else None,
101+
"country_id": loc.admin_2.admin_1.country.id if loc.admin_2 and loc.admin_2.admin_1 and loc.admin_2.admin_1.country else country_id,
102+
"country_name": loc.admin_2.admin_1.country.name if loc.admin_2 and loc.admin_2.admin_1 and loc.admin_2.admin_1.country else None,
103+
"country_iso2": loc.admin_2.admin_1.country.iso2 if loc.admin_2 and loc.admin_2.admin_1 and loc.admin_2.admin_1.country else None,
104+
"latest_data": None
105+
}
106+
107+
# Try to get latest monitoring data
108+
try:
109+
latest_data = climate_service.get_latest_by_location(loc.id, days=days)
110+
if latest_data:
111+
flat_loc["latest_data"] = {
112+
"date": latest_data["date"].isoformat() if isinstance(latest_data["date"], datetime) else str(latest_data["date"]),
113+
"measures": latest_data["measures"]
114+
}
115+
except Exception as e:
116+
# Log error but continue - location is still valid without data
117+
print(f"Warning: Could not fetch data for location {loc.id}: {e}")
118+
119+
result.append(flat_loc)
120+
121+
return result

0 commit comments

Comments
 (0)