ClimApp is organized as a strict layered architecture where each tier has a single, well-defined responsibility. Requests enter through the HTTP layer, travel down through controllers and services, and finally reach the persistence layer — with data flowing back up in the opposite direction. This separation means each layer can be changed, tested, or replaced without breaking adjacent tiers.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/elenacarino-max/mas-climapp/llms.txt
Use this file to discover all available pages before exploring further.
Layers at a glance
Templates (Presentation)
Jinja2 HTML views rendered by Flask. The six templates are
index.html (real-time dashboard), comparar.html (AEMET vs. manual comparison), consulta.html (historical query), login.html, registro.html, and api.html. None contain business logic — they only display data passed by a controller.Controllers
Flask Blueprints that translate HTTP requests into service calls and return responses.
view_controller (view_bp) serves HTML pages, auth_controller (auth_bp) manages login and registration sessions, api_controller (api_bp) exposes the JSON weather endpoints, manual_controller (manual_bp) handles manual data entry, and scheduler_controller wires up background jobs.Services
The business-logic tier, holding all computation that is independent of HTTP or storage concerns.
WeatherAPIService connects to AEMET, NormalizerService maps raw API fields to the application’s standard format, AlertService evaluates meteorological thresholds, RetryService provides fault-tolerant HTTP sessions with exponential back-off, and LoggingService writes structured entries to logs/app.log.Repositories
Data-access objects that abstract storage details away from services.
JSONRepository reads and writes data/registros_climaticos.json, providing filtering by municipality, date, and source. SQLiteRepository manages the relational clima.db database used for manual records and user accounts.Models
Plain Python classes that define the data structures passed between layers.
RegistroClimatico holds a weather observation (station, date, temperature, humidity, wind, rain). Usuario represents an authenticated user (email and password). Zona maps a Madrid municipality to its official AEMET station via INE code and station reference.Utils
Cross-cutting helpers with no layer affiliation.
validators.py enforces business-rule ranges before records are persisted (temperature −50 to 60 °C, humidity 0–100%, non-negative wind and rain). datetime_utils.py handles date formatting and parsing. helpers.py provides the Haversine distance formula used by WeatherAPIService to locate the nearest weather station.How requests flow through the layers
A typical browser request follows a straight path downward through the stack:- HTTP → Controller — Flask routes the incoming request to the matching Blueprint handler in
controllers/. - Controller → Service — The controller calls one or more services, passing only validated, typed arguments.
- Service → Repository — If data needs to be read or written, the service delegates to a repository.
- Repository → Storage — The repository performs the actual file or database operation and returns Python objects.
- Back up the stack — Each layer returns its result to the caller above; the controller serialises the final value as JSON or an HTML response.
Flask Blueprints
Flask Blueprints give each controller its own isolated routing namespace.app.py registers all four at startup:
app.py
ClimApp uses Flask-APScheduler to run a background job every two hours. The
init_scheduler() function in scheduler_controller.py registers job_clima_auto, which silently calls WeatherAPIService for Madrid’s coordinates (40.4167, −3.7033) and persists the result with fuente: "automatico". This ensures the historical archive grows even when no users are active.