feat: Initial commit - Train tracking system
Some checks failed
Auto Tag on Merge to Main / auto-tag (push) Successful in 27s
CI - Lint and Build / lint-backend (push) Failing after 30s
CI - Lint and Build / lint-frontend (push) Failing after 2s
CI - Lint and Build / build-frontend (push) Has been skipped
CI - Lint and Build / docker-build-test (push) Has been skipped
Some checks failed
Auto Tag on Merge to Main / auto-tag (push) Successful in 27s
CI - Lint and Build / lint-backend (push) Failing after 30s
CI - Lint and Build / lint-frontend (push) Failing after 2s
CI - Lint and Build / build-frontend (push) Has been skipped
CI - Lint and Build / docker-build-test (push) Has been skipped
Complete real-time train tracking system for Spanish railways (Renfe/Cercanías): - Backend API (Node.js/Express) with GTFS-RT polling workers - Frontend dashboard (React/Vite) with Leaflet maps - Real-time updates via Socket.io WebSocket - PostgreSQL/PostGIS database with Flyway migrations - Redis caching layer - Docker Compose configuration for development and production - Gitea CI/CD workflows (lint, auto-tag, release) - Production deployment with nginx + Let's Encrypt SSL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
958
FASE3-ANALYTICS.md
Normal file
958
FASE3-ANALYTICS.md
Normal file
@@ -0,0 +1,958 @@
|
||||
# Fase 3: Analytics y Exploración Avanzada
|
||||
|
||||
## Estado: ✅ IMPLEMENTADO (Backend)
|
||||
|
||||
La Fase 3 añade capacidades avanzadas de análisis, exploración de rutas, planificación de viajes y exportación de datos.
|
||||
|
||||
---
|
||||
|
||||
## ✨ Características Implementadas
|
||||
|
||||
### Backend
|
||||
|
||||
#### Base de Datos
|
||||
- ✅ Migración V6: Vistas materializadas para analytics
|
||||
- ✅ Funciones para heatmaps, estadísticas y análisis
|
||||
- ✅ Tablas para cache de exportaciones
|
||||
- ✅ Vistas de sistema: traffic_by_hour, traffic_by_route, daily_statistics, route_performance
|
||||
|
||||
#### Workers
|
||||
- ✅ Analytics Refresher: Refresco automático de vistas materializadas cada 15 minutos
|
||||
|
||||
#### API REST
|
||||
- ✅ Analytics API (`/analytics`) - Heatmaps, estadísticas, performance
|
||||
- ✅ Explorer API (`/explorer`) - Explorador de rutas, planificador de viajes, búsqueda
|
||||
|
||||
### Frontend
|
||||
- ⏳ Componentes de Analytics (pendiente)
|
||||
- ⏳ Heatmap de tráfico (pendiente)
|
||||
- ⏳ Dashboard de estadísticas (pendiente)
|
||||
- ⏳ Planificador de viajes UI (pendiente)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Nuevos Archivos Phase 3
|
||||
|
||||
### Base de Datos
|
||||
```
|
||||
database/migrations/
|
||||
└── V6__analytics_and_statistics.sql # Vistas y funciones de analytics
|
||||
```
|
||||
|
||||
### Backend Workers
|
||||
```
|
||||
backend/src/worker/
|
||||
└── analytics-refresher.js # Refresco de vistas materializadas
|
||||
```
|
||||
|
||||
### Backend API
|
||||
```
|
||||
backend/src/api/routes/
|
||||
├── analytics.js # Endpoints de analytics y exportación
|
||||
└── explorer.js # Explorador de rutas y planificador
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Ejecutar con Fase 3
|
||||
|
||||
### Usando Docker Compose
|
||||
|
||||
```bash
|
||||
# 1. Ejecutar migraciones (incluye V6)
|
||||
make migrate
|
||||
|
||||
# 2. Iniciar todos los servicios (incluye analytics-refresher)
|
||||
make start
|
||||
|
||||
# El worker analytics-refresher se inicia automáticamente
|
||||
# y refresca las vistas cada 15 minutos
|
||||
```
|
||||
|
||||
### Desarrollo Local
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
|
||||
# Terminal 1: Analytics Refresher
|
||||
npm run dev:analytics
|
||||
|
||||
# Terminal 2: API Server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 Nuevos Endpoints API
|
||||
|
||||
### Analytics - Traffic
|
||||
|
||||
#### GET /analytics/traffic/heatmap
|
||||
Obtener datos de heatmap de tráfico.
|
||||
|
||||
**Query Parameters:**
|
||||
- `start_date` (opcional): Fecha inicio (ISO 8601)
|
||||
- `end_date` (opcional): Fecha fin (ISO 8601)
|
||||
- `grid_size` (opcional): Tamaño de celda en grados (default: 0.1 ≈ 11km)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
# Heatmap últimos 7 días
|
||||
curl http://localhost:3000/analytics/traffic/heatmap
|
||||
|
||||
# Heatmap con grid más fino
|
||||
curl http://localhost:3000/analytics/traffic/heatmap?grid_size=0.05
|
||||
|
||||
# Rango personalizado
|
||||
curl "http://localhost:3000/analytics/traffic/heatmap?start_date=2025-11-20T00:00:00Z&end_date=2025-11-27T23:59:59Z"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"lat": 40.4,
|
||||
"lon": -3.7,
|
||||
"intensity": 125,
|
||||
"avgSpeed": 78.5
|
||||
},
|
||||
{
|
||||
"lat": 41.3,
|
||||
"lon": 2.1,
|
||||
"intensity": 98,
|
||||
"avgSpeed": 65.3
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /analytics/traffic/hourly
|
||||
Obtener patrón de tráfico por hora del día.
|
||||
|
||||
**Query Parameters:**
|
||||
- `days` (opcional): Número de días para analizar (default: 7)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/traffic/hourly?days=30
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"hour_of_day": 0,
|
||||
"avg_trains": 15.3,
|
||||
"avg_speed": 45.2,
|
||||
"total_observations": 45230
|
||||
},
|
||||
{
|
||||
"hour_of_day": 7,
|
||||
"avg_trains": 87.5,
|
||||
"avg_speed": 68.7,
|
||||
"total_observations": 125890
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /analytics/traffic/by-hour
|
||||
Obtener estadísticas de tráfico agregadas por hora.
|
||||
|
||||
**Query Parameters:**
|
||||
- `limit` (opcional): Número de horas (default: 24)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/traffic/by-hour?limit=48
|
||||
```
|
||||
|
||||
#### GET /analytics/traffic/by-route
|
||||
Obtener estadísticas de tráfico por ruta.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/traffic/by-route
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"route_type": "HIGH_SPEED",
|
||||
"total_trains": 234,
|
||||
"active_days": 30,
|
||||
"avg_speed": 185.4,
|
||||
"total_positions": 125890
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Statistics
|
||||
|
||||
#### GET /analytics/statistics/daily
|
||||
Obtener estadísticas diarias del sistema.
|
||||
|
||||
**Query Parameters:**
|
||||
- `days` (opcional): Número de días (default: 30)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/statistics/daily?days=90
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"date": "2025-11-27",
|
||||
"unique_trains": 456,
|
||||
"total_positions": 65432,
|
||||
"avg_speed": 72.3,
|
||||
"stopped_count": 12890,
|
||||
"moving_count": 52542
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /analytics/statistics/system
|
||||
Obtener estado actual del sistema.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/statistics/system
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"active_trains": 234,
|
||||
"active_alerts": 5,
|
||||
"delayed_trips": 12,
|
||||
"avg_delay_seconds": 450,
|
||||
"active_routes": 45,
|
||||
"last_update": "2025-11-27T15:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Performance
|
||||
|
||||
#### GET /analytics/performance/routes
|
||||
Obtener métricas de rendimiento de rutas (puntualidad, retrasos).
|
||||
|
||||
**Query Parameters:**
|
||||
- `limit` (opcional): Número de rutas (default: 20)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/performance/routes?limit=10
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"total_trips": 345,
|
||||
"delayed_trips": 23,
|
||||
"on_time_trips": 322,
|
||||
"avg_delay_seconds": 180,
|
||||
"median_delay_seconds": 120,
|
||||
"max_delay_seconds": 1800,
|
||||
"punctuality_percentage": 93.33
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /analytics/performance/route/:routeId
|
||||
Obtener estadísticas detalladas de una ruta específica.
|
||||
|
||||
**Query Parameters:**
|
||||
- `days` (opcional): Número de días para analizar (default: 7)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/performance/route/AVE-MAD-BCN?days=30
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"total_trips": 234,
|
||||
"unique_trains": 45,
|
||||
"avg_speed": 185.4,
|
||||
"max_speed": 298.7,
|
||||
"total_distance_km": 98765.4,
|
||||
"avg_delay_seconds": 120,
|
||||
"on_time_percentage": 92.5
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Delays
|
||||
|
||||
#### GET /analytics/delays/top-routes
|
||||
Obtener rutas con más retrasos.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/delays/top-routes
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"route_id": "MD-MAD-VAL",
|
||||
"route_name": "Madrid - Valencia",
|
||||
"delayed_count": 45,
|
||||
"avg_delay": 600,
|
||||
"max_delay": 2400
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Stations
|
||||
|
||||
#### GET /analytics/stations/busiest
|
||||
Obtener estaciones más transitadas.
|
||||
|
||||
**Query Parameters:**
|
||||
- `limit` (opcional): Número de estaciones (default: 20)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/stations/busiest?limit=10
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"stop_id": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"stop_name": "Madrid Puerta de Atocha",
|
||||
"daily_trips": 456,
|
||||
"routes_count": 34
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
#### GET /analytics/stations/:stationId/statistics
|
||||
Obtener estadísticas de una estación específica.
|
||||
|
||||
**Query Parameters:**
|
||||
- `days` (opcional): Número de días (default: 7)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/analytics/stations/MADRID-PUERTA-DE-ATOCHA/statistics?days=30
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"total_departures": 3450,
|
||||
"total_arrivals": 3420,
|
||||
"unique_routes": 45,
|
||||
"avg_delay_minutes": 2.5,
|
||||
"busiest_hour": 8
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Trains
|
||||
|
||||
#### GET /analytics/trains/:trainId/distance
|
||||
Calcular distancia recorrida por un tren.
|
||||
|
||||
**Query Parameters:**
|
||||
- `start_time` (opcional): Timestamp inicio
|
||||
- `end_time` (opcional): Timestamp fin
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl "http://localhost:3000/analytics/trains/12345/distance?start_time=2025-11-27T00:00:00Z&end_time=2025-11-27T23:59:59Z"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"train_id": "12345",
|
||||
"start_time": "2025-11-27T00:00:00Z",
|
||||
"end_time": "2025-11-27T23:59:59Z",
|
||||
"distance_km": 1234.56
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Export
|
||||
|
||||
#### GET /analytics/export
|
||||
Exportar datos en diferentes formatos.
|
||||
|
||||
**Query Parameters:**
|
||||
- `table` (requerido): Tabla a exportar
|
||||
- `format` (opcional): json, csv, geojson (default: json)
|
||||
- `start_date` (opcional): Fecha inicio
|
||||
- `end_date` (opcional): Fecha fin
|
||||
- `limit` (opcional): Límite de registros (default: 1000)
|
||||
|
||||
**Tablas permitidas:**
|
||||
- train_positions
|
||||
- trains
|
||||
- routes
|
||||
- stations
|
||||
- alerts
|
||||
- trip_updates
|
||||
- traffic_by_hour
|
||||
- daily_statistics
|
||||
|
||||
**Ejemplo JSON:**
|
||||
```bash
|
||||
curl "http://localhost:3000/analytics/export?table=trains&format=json"
|
||||
```
|
||||
|
||||
**Ejemplo CSV:**
|
||||
```bash
|
||||
curl "http://localhost:3000/analytics/export?table=train_positions&format=csv&limit=5000" > positions.csv
|
||||
```
|
||||
|
||||
**Ejemplo GeoJSON:**
|
||||
```bash
|
||||
curl "http://localhost:3000/analytics/export?table=stations&format=geojson" > stations.geojson
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Analytics - Refresh
|
||||
|
||||
#### POST /analytics/refresh
|
||||
Refrescar manualmente las vistas materializadas.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/analytics/refresh
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Analytics views refreshed successfully",
|
||||
"timestamp": "2025-11-27T15:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📍 Explorer - Route Explorer
|
||||
|
||||
### GET /explorer/routes/:routeId
|
||||
Obtener información completa de una ruta (trips, stops, shape).
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/explorer/routes/AVE-MAD-BCN
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"route": {
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"route_type": "HIGH_SPEED"
|
||||
},
|
||||
"trips": [
|
||||
{
|
||||
"trip_id": "trip_001",
|
||||
"trip_headsign": "Barcelona Sants",
|
||||
"direction_id": 0
|
||||
}
|
||||
],
|
||||
"stops": [
|
||||
{
|
||||
"stop_id": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"stop_name": "Madrid Puerta de Atocha",
|
||||
"stop_lat": 40.4067,
|
||||
"stop_lon": -3.6906
|
||||
}
|
||||
],
|
||||
"shape": {
|
||||
"shape_id": "shape_001",
|
||||
"points": [
|
||||
{
|
||||
"lat": 40.4067,
|
||||
"lon": -3.6906,
|
||||
"sequence": 1,
|
||||
"distance": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
"total_trips": 45,
|
||||
"total_stops": 8
|
||||
}
|
||||
```
|
||||
|
||||
### GET /explorer/trips/:tripId/schedule
|
||||
Obtener horario completo de un viaje.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/explorer/trips/trip_12345/schedule
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"stop_id": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"stop_name": "Madrid Puerta de Atocha",
|
||||
"arrival_time": "08:00:00",
|
||||
"departure_time": "08:00:00",
|
||||
"stop_sequence": 1
|
||||
},
|
||||
{
|
||||
"stop_id": "BARCELONA-SANTS",
|
||||
"stop_name": "Barcelona Sants",
|
||||
"arrival_time": "10:45:00",
|
||||
"departure_time": "10:45:00",
|
||||
"stop_sequence": 3
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚉 Explorer - Stations
|
||||
|
||||
### GET /explorer/stations/:stationId
|
||||
Obtener información completa de una estación.
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"station": {
|
||||
"stop_id": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"stop_name": "Madrid Puerta de Atocha",
|
||||
"stop_lat": 40.4067,
|
||||
"stop_lon": -3.6906
|
||||
},
|
||||
"next_departures": [
|
||||
{
|
||||
"trip_id": "trip_001",
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"headsign": "Barcelona Sants",
|
||||
"scheduled_departure": "15:00:00",
|
||||
"estimated_delay": 180,
|
||||
"status": "DELAYED"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona"
|
||||
}
|
||||
],
|
||||
"statistics": {
|
||||
"total_departures": 456,
|
||||
"total_arrivals": 450,
|
||||
"unique_routes": 34,
|
||||
"avg_delay_minutes": 2.5,
|
||||
"busiest_hour": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### GET /explorer/stations/:stationId/nearby
|
||||
Obtener estaciones cercanas.
|
||||
|
||||
**Query Parameters:**
|
||||
- `radius` (opcional): Radio en km (default: 5)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl "http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA/nearby?radius=10"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"stop_id": "MADRID-CHAMARTIN",
|
||||
"stop_name": "Madrid Chamartín",
|
||||
"stop_lat": 40.4728,
|
||||
"stop_lon": -3.6797,
|
||||
"distance_km": 7.8
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Explorer - Trip Planner
|
||||
|
||||
### GET /explorer/planner
|
||||
Planificador de viajes entre dos estaciones.
|
||||
|
||||
**Query Parameters:**
|
||||
- `origin` (requerido): ID de estación origen
|
||||
- `destination` (requerido): ID de estación destino
|
||||
- `time` (opcional): Hora de salida (HH:MM:SS)
|
||||
- `date` (opcional): Fecha (YYYY-MM-DD)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS&time=08:00:00"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"origin": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"destination": "BARCELONA-SANTS",
|
||||
"requested_time": "08:00:00",
|
||||
"requested_date": "today",
|
||||
"direct_trips": [
|
||||
{
|
||||
"trip_id": "trip_001",
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"trip_headsign": "Barcelona Sants",
|
||||
"origin_departure": "08:00:00",
|
||||
"destination_arrival": "10:45:00",
|
||||
"duration_minutes": 165,
|
||||
"delay": {
|
||||
"delay_seconds": 180,
|
||||
"schedule_relationship": "SCHEDULED"
|
||||
}
|
||||
}
|
||||
],
|
||||
"trips_with_transfer": [
|
||||
{
|
||||
"trip1_id": "trip_002",
|
||||
"route1_name": "Madrid - Zaragoza",
|
||||
"trip2_id": "trip_003",
|
||||
"route2_name": "Zaragoza - Barcelona",
|
||||
"origin_departure": "08:30:00",
|
||||
"transfer_arrival": "09:50:00",
|
||||
"transfer_departure": "10:10:00",
|
||||
"destination_arrival": "11:30:00",
|
||||
"transfer_station": "ZARAGOZA-DELICIAS",
|
||||
"transfer_station_name": "Zaragoza Delicias",
|
||||
"total_duration_minutes": 180
|
||||
}
|
||||
],
|
||||
"total_options": 3
|
||||
}
|
||||
```
|
||||
|
||||
### GET /explorer/routes/between
|
||||
Encontrar rutas que conectan dos estaciones.
|
||||
|
||||
**Query Parameters:**
|
||||
- `origin` (requerido): ID de estación origen
|
||||
- `destination` (requerido): ID de estación destino
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl "http://localhost:3000/explorer/routes/between?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"route_id": "AVE-MAD-BCN",
|
||||
"route_name": "Madrid - Barcelona",
|
||||
"route_type": "HIGH_SPEED",
|
||||
"route_color": "FF6600",
|
||||
"daily_trips": 25
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Explorer - Search
|
||||
|
||||
### GET /explorer/search
|
||||
Buscar estaciones por nombre.
|
||||
|
||||
**Query Parameters:**
|
||||
- `query` (requerido): Término de búsqueda (mínimo 2 caracteres)
|
||||
- `limit` (opcional): Número de resultados (default: 10)
|
||||
|
||||
**Ejemplo:**
|
||||
```bash
|
||||
curl "http://localhost:3000/explorer/search?query=madrid&limit=5"
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"stop_id": "MADRID-PUERTA-DE-ATOCHA",
|
||||
"stop_name": "Madrid Puerta de Atocha",
|
||||
"stop_lat": 40.4067,
|
||||
"stop_lon": -3.6906,
|
||||
"location_type": 1,
|
||||
"parent_station": null
|
||||
},
|
||||
{
|
||||
"stop_id": "MADRID-CHAMARTIN",
|
||||
"stop_name": "Madrid Chamartín",
|
||||
"stop_lat": 40.4728,
|
||||
"stop_lon": -3.6797,
|
||||
"location_type": 1,
|
||||
"parent_station": null
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Vistas Materializadas
|
||||
|
||||
### traffic_by_hour
|
||||
Tráfico agregado por hora (últimos 30 días).
|
||||
|
||||
**Columnas:**
|
||||
- hour: Hora (timestamp)
|
||||
- active_trains: Número de trenes activos
|
||||
- total_positions: Total de posiciones registradas
|
||||
- avg_speed: Velocidad promedio
|
||||
- median_speed: Velocidad mediana
|
||||
- max_speed: Velocidad máxima
|
||||
|
||||
### traffic_by_route
|
||||
Tráfico por ruta (últimos 30 días).
|
||||
|
||||
**Columnas:**
|
||||
- route_id, route_name, route_type
|
||||
- total_trains: Total de trenes diferentes
|
||||
- active_days: Días con actividad
|
||||
- avg_speed: Velocidad promedio
|
||||
- total_positions: Posiciones registradas
|
||||
|
||||
### daily_statistics
|
||||
Estadísticas diarias del sistema (últimos 90 días).
|
||||
|
||||
**Columnas:**
|
||||
- date: Fecha
|
||||
- unique_trains: Trenes únicos
|
||||
- total_positions: Posiciones totales
|
||||
- avg_speed: Velocidad promedio
|
||||
- stopped_count: Posiciones paradas
|
||||
- moving_count: Posiciones en movimiento
|
||||
|
||||
### route_performance
|
||||
Rendimiento y puntualidad por ruta (últimos 30 días).
|
||||
|
||||
**Columnas:**
|
||||
- route_id, route_name
|
||||
- total_trips: Total de viajes
|
||||
- delayed_trips: Viajes retrasados
|
||||
- on_time_trips: Viajes puntuales
|
||||
- avg_delay_seconds: Retraso promedio
|
||||
- median_delay_seconds: Retraso mediano
|
||||
- max_delay_seconds: Retraso máximo
|
||||
- punctuality_percentage: % de puntualidad
|
||||
|
||||
**Refresco:** Cada 15 minutos automáticamente via analytics-refresher worker.
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Probar Phase 3
|
||||
|
||||
### 1. Verificar Analytics Refresher
|
||||
|
||||
```bash
|
||||
# Ver logs del worker
|
||||
docker-compose logs -f analytics-refresher
|
||||
|
||||
# Deberías ver cada 15 minutos:
|
||||
# "Starting analytics views refresh..."
|
||||
# "Analytics views refreshed successfully"
|
||||
```
|
||||
|
||||
### 2. Probar Analytics API
|
||||
|
||||
```bash
|
||||
# Sistema general
|
||||
curl http://localhost:3000/analytics/statistics/system | jq
|
||||
|
||||
# Heatmap
|
||||
curl http://localhost:3000/analytics/traffic/heatmap | jq
|
||||
|
||||
# Performance de rutas
|
||||
curl http://localhost:3000/analytics/performance/routes | jq
|
||||
|
||||
# Rutas más retrasadas
|
||||
curl http://localhost:3000/analytics/delays/top-routes | jq
|
||||
|
||||
# Estaciones más transitadas
|
||||
curl http://localhost:3000/analytics/stations/busiest | jq
|
||||
```
|
||||
|
||||
### 3. Probar Explorer API
|
||||
|
||||
```bash
|
||||
# Buscar estación
|
||||
curl "http://localhost:3000/explorer/search?query=madrid" | jq
|
||||
|
||||
# Info de estación
|
||||
curl http://localhost:3000/explorer/stations/MADRID-PUERTA-DE-ATOCHA | jq
|
||||
|
||||
# Info de ruta
|
||||
curl http://localhost:3000/explorer/routes/AVE-MAD-BCN | jq
|
||||
|
||||
# Planificar viaje
|
||||
curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS" | jq
|
||||
```
|
||||
|
||||
### 4. Probar Exportación
|
||||
|
||||
```bash
|
||||
# Exportar a CSV
|
||||
curl "http://localhost:3000/analytics/export?table=routes&format=csv" > routes.csv
|
||||
|
||||
# Exportar a GeoJSON
|
||||
curl "http://localhost:3000/analytics/export?table=stations&format=geojson" > stations.geojson
|
||||
|
||||
# Exportar posiciones filtradas
|
||||
curl "http://localhost:3000/analytics/export?table=train_positions&start_date=2025-11-27T00:00:00Z&limit=1000" > positions.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Use Cases
|
||||
|
||||
### Dashboard de Control
|
||||
```bash
|
||||
# Obtener datos para dashboard
|
||||
curl http://localhost:3000/analytics/statistics/system
|
||||
curl http://localhost:3000/analytics/traffic/by-route
|
||||
curl http://localhost:3000/analytics/delays/top-routes
|
||||
curl http://localhost:3000/analytics/stations/busiest?limit=5
|
||||
```
|
||||
|
||||
### Análisis de Rendimiento
|
||||
```bash
|
||||
# Análisis de una ruta específica
|
||||
curl http://localhost:3000/analytics/performance/route/AVE-MAD-BCN?days=30
|
||||
|
||||
# Estadísticas diarias últimos 30 días
|
||||
curl http://localhost:3000/analytics/statistics/daily?days=30
|
||||
|
||||
# Distancia recorrida por tren
|
||||
curl "http://localhost:3000/analytics/trains/12345/distance?start_time=2025-11-01T00:00:00Z"
|
||||
```
|
||||
|
||||
### Planificación de Viajes
|
||||
```bash
|
||||
# Buscar estación
|
||||
curl "http://localhost:3000/explorer/search?query=barcelona"
|
||||
|
||||
# Ver info completa de estación
|
||||
curl http://localhost:3000/explorer/stations/BARCELONA-SANTS
|
||||
|
||||
# Planificar viaje
|
||||
curl "http://localhost:3000/explorer/planner?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS&time=08:00:00"
|
||||
|
||||
# Ver rutas que conectan dos puntos
|
||||
curl "http://localhost:3000/explorer/routes/between?origin=MADRID-PUERTA-DE-ATOCHA&destination=BARCELONA-SANTS"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting Phase 3
|
||||
|
||||
### Las vistas materializadas están vacías
|
||||
|
||||
**Causa**: No hay suficientes datos históricos o no se han refrescado.
|
||||
|
||||
**Solución**:
|
||||
```bash
|
||||
# Refrescar manualmente
|
||||
curl -X POST http://localhost:3000/analytics/refresh
|
||||
|
||||
# Verificar en PostgreSQL
|
||||
make psql
|
||||
> SELECT COUNT(*) FROM traffic_by_hour;
|
||||
> SELECT COUNT(*) FROM traffic_by_route;
|
||||
```
|
||||
|
||||
### El planificador no encuentra viajes
|
||||
|
||||
**Causa**: Datos GTFS Static no cargados o formato de parámetros incorrecto.
|
||||
|
||||
**Solución**:
|
||||
```bash
|
||||
# Verificar que hay trips y stop_times
|
||||
make psql
|
||||
> SELECT COUNT(*) FROM trips;
|
||||
> SELECT COUNT(*) FROM stop_times;
|
||||
|
||||
# Verificar formato de parámetros
|
||||
curl "http://localhost:3000/explorer/planner?origin=STOP_ID_ORIGIN&destination=STOP_ID_DEST"
|
||||
```
|
||||
|
||||
### Export devuelve error
|
||||
|
||||
**Causa**: Tabla no permitida o parámetros inválidos.
|
||||
|
||||
**Solución**:
|
||||
```bash
|
||||
# Ver tablas permitidas
|
||||
curl "http://localhost:3000/analytics/export" | jq '.allowed_tables'
|
||||
|
||||
# Usar tabla válida
|
||||
curl "http://localhost:3000/analytics/export?table=routes&format=json"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Próximos Pasos
|
||||
|
||||
Funcionalidades pendientes de Phase 3:
|
||||
|
||||
- [ ] Frontend: Heatmap de tráfico en mapa
|
||||
- [ ] Frontend: Dashboard de estadísticas con gráficos
|
||||
- [ ] Frontend: UI del planificador de viajes
|
||||
- [ ] Frontend: Explorador de rutas interactivo
|
||||
- [ ] WebSocket para updates de analytics en tiempo real
|
||||
- [ ] Overlay de infraestructura ADIF (WMS)
|
||||
- [ ] Predicciones ML (Fase 4)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentación Relacionada
|
||||
|
||||
- [Arquitectura Completa](arquitectura-sistema-tracking-trenes.md)
|
||||
- [Fase 1 - MVP](FASE1-MVP.md)
|
||||
- [Fase 2 - Enriquecimiento](FASE2-ENRIQUECIMIENTO.md)
|
||||
- [Fuentes de Datos](FUENTES_DATOS.md)
|
||||
- [README Principal](README.md)
|
||||
|
||||
---
|
||||
|
||||
**Estado**: Fase 3 - Backend Completo, Frontend Pendiente
|
||||
**Fecha**: 27 noviembre 2025
|
||||
**Próxima Fase**: Frontend Phase 3 + Fase 4 (ML y Predicciones)
|
||||
Reference in New Issue
Block a user