ClimApp’s service layer is split across four modules:Documentation Index
Fetch the complete documentation index at: https://mintlify.com/adrianaarang/climapp/llms.txt
Use this file to discover all available pages before exploring further.
WeatherAPIService retrieves raw observations from the AEMET open-data API, normalizar_datos_aemet() transforms those observations into a standard internal format, AlertService evaluates the normalised data against risk thresholds, and RetryService provides all HTTP calls with automatic retry logic. Together these modules isolate external API concerns from the Flask route handlers.
WeatherAPIService
WeatherAPIService is the single entry point for live AEMET data. Instantiating it requires the AEMET_API_KEY environment variable to be set; the constructor raises a ValueError immediately if the key is absent.
Constructor
- Reads
AEMET_API_KEYfrom the environment viaos.getenv(). - Creates a retry-enabled
requests.Sessionby callingget_retry_session(). - Sets
self.base_urlto the AEMET conventional-observations endpoint.
_obtener_datos_crudos()
Internal method. Performs the two-step AEMET request pattern:
- Step 1 — metadata request:
GETtobase_urlwith the API key in the headers. The response body contains adatosURL pointing to the actual dataset. - Step 2 — data request:
GETto thedatosURL to download all current conventional observations as a JSON list.
Both requests use
timeout=20 and benefit from the session’s automatic retry strategy. The method never raises; it logs the error and returns [].obtener_clima_por_coordenadas(user_lat, user_lon)
Finds the AEMET station closest to the supplied GPS coordinates by computing the Haversine distance to every observation in the raw dataset, then returns that station’s raw observation dict.
User latitude in decimal degrees (e.g.
40.4168).User longitude in decimal degrees (e.g.
-3.7038).None if no observations were received. Stations with missing or malformed lat/lon fields are silently skipped.
obtener_clima_por_id(station_id)
Placeholder method reserved for future implementation of station-by-ID lookups. Currently returns None.
Module-level bridge function
app.py imports and calls this module-level function directly. It instantiates a fresh WeatherAPIService on every call, keeping route handlers decoupled from the class.
normalizar_datos_aemet(data)
Defined inservices/normalizer_service.py. Transforms a raw AEMET observation dict (or list of dicts) into ClimApp’s standard internal format and appends alert labels.
Raw observation data from AEMET. If a list is passed, the last element is used. If
None or empty, returns {"error": "No hay datos disponibles"}.Field mapping
| AEMET key | Normalised key | Fallback |
|---|---|---|
ubi | estacion | "Desconocida" |
fint | fecha | "N/A" |
ta | temperatura | 0 |
hr | humedad | 0 |
vv | viento | 0 |
pres | presion | 0 |
prec | lluvia | 0 |
float. Missing AEMET keys fall back to 0.
After building the base dict, alert_service.evaluar_alertas() is called and its return value is stored under the "alertas" key. On any unhandled exception, the function returns {"error": "Error al procesar los datos"}.
AlertService thresholds
AlertService.evaluar_alertas() runs after normalisation. It applies the following rules and returns a list of active alert strings:
Temperature alerts (mutually exclusive)
Temperature alerts (mutually exclusive)
Alerts are evaluated in order; only the first matching threshold fires.
| Threshold | Alert label |
|---|---|
temperatura >= 40.0 °C | "ROJA" |
temperatura >= 35.0 °C | "NARANJA" |
temperatura <= 0.0 °C | "HELADA" |
Independent accumulative alerts
Independent accumulative alerts
These are evaluated independently and can fire alongside any temperature alert.
| Threshold | Alert label |
|---|---|
viento > 70.0 km/h | "VIENTO_FUERTE" |
lluvia > 30.0 mm | "LLUVIA_INTENSA" |
humedad >= 90 % | "HUMEDAD_ALTA" |
[] if validate_weather_data() fails or if any field cannot be converted to float.
RetryService
get_retry_session() is the sole export of services/retry_service.py. It wraps requests.Session with a urllib3.Retry strategy so that transient network failures against the AEMET API are handled automatically.
get_retry_session(retries, backoff_factor)
Total number of retry attempts before giving up.
Exponential backoff multiplier. With the default of
1.5, wait times between retries are approximately 1.5 s, 3 s, 4.5 s.429, 500, 502, 503, and 504, and only for GET requests. The configured adapter is mounted on both https:// and http:// prefixes.