ClimApp handles two distinct data paths: a live path that pulls real-time observations from AEMET using the device’s GPS coordinates, and a manual path that accepts user-submitted readings through a web form. Both paths converge on the same normalized data format before returning a response or writing to storage.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.
Live weather data flow
Browser requests GPS permission
Coordinates sent to GET /api/clima
GET /api/clima?lat=X&lon=Y. The api_clima() view in app.py reads the lat and lon query parameters and validates that both are present before proceeding.WeatherAPIService fetches the AEMET metadata URL
WeatherAPIService._obtener_datos_crudos() sends an authenticated GET request to:api_key is read from the AEMET_API_KEY environment variable. This first response does not contain observation data — it returns a JSON object with a datos key pointing to a second URL.Second request fetches the actual observation data
WeatherAPIService extracts the datos URL from the first response and makes a second GET request to retrieve the full list of station observations as a JSON array.requests.Session.calcular_distancia() finds the nearest station
WeatherAPIService.obtener_clima_por_coordenadas() iterates over every observation in the array. For each station it calls calcular_distancia() from utils/helpers.py, which applies the Haversine formula to compute the great-circle distance (in kilometres) between the user’s coordinates and the station’s lat/lon fields. The station with the smallest distance is selected.normalizar_datos_aemet() maps raw fields to standard names
normalizar_datos_aemet(). It maps AEMET’s abbreviated field names to readable keys:| Raw AEMET field | Normalized key | Description |
|---|---|---|
ubi | estacion | Station name |
fint | fecha | Observation timestamp |
ta | temperatura | Air temperature (°C) |
hr | humedad | Relative humidity (%) |
vv | viento | Wind speed (km/h) |
pres | presion | Atmospheric pressure (hPa) |
prec | lluvia | Precipitation (mm) |
0. All values are cast to float.AlertService.evaluar_alertas() appends alert labels
normalizar_datos_aemet() calls alert_service.evaluar_alertas() with the normalized dictionary. The method checks each reading against fixed thresholds and returns a list of active alert strings (for example ["NARANJA", "VIENTO_FUERTE"]). The list is added to the dictionary under the alertas key.Manual data flow
User fills the form on /registro
manual_controller.POST /api/registrar receives the JSON payload
manual_controller reads the submitted values from the request body. The route accepts POST requests to /api/registrar.validate_weather_data() checks all fields
validate_weather_data() from utils/validators.py verifies that all required fields are present and that numeric values are in acceptable ranges. An invalid payload returns an error response immediately.RegistroClimatico model is created
RegistroClimatico instance is constructed with the validated values. The model coerces all numeric fields to float and exposes a to_dict() method for serialization.JSONRepository.guardar() appends the record
JSONRepository.guardar() reads the current contents of data/registros_climaticos.json, appends the new record dictionary (via RegistroClimatico.to_dict()), and writes the updated list back to disk with UTF-8 encoding and 4-space indentation. If the file does not exist yet, _ensure_file_exists() creates it with an empty array on first use.Retry logic
All outbound HTTP calls made byWeatherAPIService go through a session created by get_retry_session() in services/retry_service.py. The session mounts a urllib3.util.retry.Retry adapter on both https:// and http:// prefixes with the following configuration:
| Setting | Value |
|---|---|
| Total retries | 3 |
| Backoff factor | 1.5 (waits 1.5 s, 3 s, 4.5 s between attempts) |
| Retried status codes | 429, 500, 502, 503, 504 |
| Allowed methods | GET only |