Skip to main content
ClimApp’s service layer is split across four modules: 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

On construction the class:
  1. Reads AEMET_API_KEY from the environment via os.getenv().
  2. Creates a retry-enabled requests.Session by calling get_retry_session().
  3. Sets self.base_url to the AEMET conventional-observations endpoint.
If AEMET_API_KEY is not set in your environment (or .env file), the constructor will raise ValueError: AEMET_API_KEY no encontrada en .env and the application will not start.

_obtener_datos_crudos()

Internal method. Performs the two-step AEMET request pattern:
  1. Step 1 — metadata request: GET to base_url with the API key in the headers. The response body contains a datos URL pointing to the actual dataset.
  2. Step 2 — data request: GET to the datos URL to download all current conventional observations as a JSON list.
Returns the parsed list of observation dicts, or an empty list on any error.
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.
number
required
User latitude in decimal degrees (e.g. 40.4168).
number
required
User longitude in decimal degrees (e.g. -3.7038).
Returns the raw AEMET observation dict for the nearest station, or 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 in services/normalizer_service.py. Transforms a raw AEMET observation dict (or list of dicts) into ClimApp’s standard internal format and appends alert labels.
object | array
required
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

All numeric fields are cast to 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:
Alerts are evaluated in order; only the first matching threshold fires.
These are evaluated independently and can fire alongside any temperature alert.
The method returns an empty list [] 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)

number
default:"3"
Total number of retry attempts before giving up.
number
default:"1.5"
Exponential backoff multiplier. With the default of 1.5, wait times between retries are approximately 1.5 s, 3 s, 4.5 s.
The strategy retries on HTTP status codes 429, 500, 502, 503, and 504, and only for GET requests. The configured adapter is mounted on both https:// and http:// prefixes.
Pass a custom retries value when testing against a slow or rate-limited staging endpoint. For production the defaults (3 retries, 1.5 s backoff) are appropriate for the AEMET open-data API.