Skip to main content

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.

ClimApp is organized into four distinct layers that keep responsibilities separate: controllers handle HTTP routing and template rendering, services contain all business logic (API calls, normalization, alerts, and retries), repositories manage JSON persistence, and models define the data structures passed between layers. Each layer depends only on the one below it, making the codebase straightforward to extend and test.

Layer diagram

Controllers  →  Services  →  Repositories  →  Models
(HTTP/views)    (logic)       (persistence)    (data)
Requests enter through a controller or a route defined directly in app.py, travel down through the service layer for processing, optionally reach the repository to read or write stored records, and return a response back up the stack.

Component breakdown

Controllers

view_controller — renders the main dashboard, historical records list, API demo page, and the filter/query (/consulta) form via view_bp.manual_controller — handles the manual data-entry form at /registro and the POST /api/registrar endpoint via manual_bp.compare_controller — orchestrates the side-by-side comparison between a stored manual record and the live AEMET reading for the same station and date.

Services

WeatherAPIService — fetches all conventional station observations from AEMET using a two-step HTTP pattern and locates the nearest station via Haversine distance.NormalizerService — maps raw AEMET field names (ta, hr, vv, pres, prec) to standard keys and calls AlertService to append alert labels.AlertService — evaluates normalized weather data against fixed thresholds and returns a list of active alert strings.RetryService — provides a requests.Session pre-configured with exponential back-off and automatic retries on transient HTTP errors.

Repositories

JSONRepository — reads and writes data/registros_climaticos.json. It exposes guardar() to append a record, filtrar_registros() to query by municipality, date, or source, and obtener_ultimo_registro() to retrieve the most recent entry for a given station.

Models

RegistroClimatico — holds a single weather observation (station ID, date, temperature, humidity, wind, and rain). All numeric fields are coerced to float on construction, and to_dict() serializes it for storage or JSON responses.Zona — represents the link between a municipality and its AEMET reference station. Holds municipio, cod_ine, id_estacion, and estacion_referencia. Created from a JSON dict via Zona.from_dict() using entries from estacion_por_municipio.json.

Routes

MethodPathDescription
GET/Main dashboard — shows the live weather widget and navigation.
GET/registroManual data-entry form for logging a new observation.
GET/apiAPI demo page showing the live /api/clima response.
GET, POST/consultaFilter and browse stored records by municipality, date, or source.
GET, POST/compararCompare a stored manual record against the current AEMET reading.
POST/api/registrarAccepts a JSON payload, validates it, and persists a new RegistroClimatico.
GET/api/clima?lat=&lon=Fetches the nearest AEMET station data for the given coordinates and returns a normalized JSON response including any active alerts.

Tech stack

PackageRole
FlaskWeb framework — routing, templating (Jinja2), and JSON responses.
python-dotenvLoads AEMET_API_KEY and SECRET_KEY from a .env file at startup.
requestsHTTP client used by WeatherAPIService and RetryService.
pandasAvailable for data manipulation in filtering and comparison workflows.
pytestTest runner for unit and integration tests.

Blueprint registration

view_bp and manual_bp are Flask Blueprints registered in app.py with app.register_blueprint(). This keeps each controller’s routes, templates, and helpers self-contained. The /comparar and /api/clima routes are defined directly on the app object because they orchestrate logic from multiple services and do not belong to a single controller domain.
A context_processor registered in app.py automatically injects breadcrumb data (breadcrumbs) into every template, so individual controllers do not need to build navigation trails themselves.

Build docs developers (and LLMs) love