diff --git a/eltiempo/js/estadisticas.js b/eltiempo/js/estadisticas.js
index 80d9616..13a0e71 100644
--- a/eltiempo/js/estadisticas.js
+++ b/eltiempo/js/estadisticas.js
@@ -1,7 +1,3 @@
-// API URL absoluta
-const API_URL = "https://aplicacionesdevanguardia.es/eltiempo/servidor/api-weather.php?ciudad=madrid";
-const API_URL_TODAY = "https://aplicacionesdevanguardia.es/eltiempo/servidor/weather-hoy.php?ciudad=madrid";
-
// ====================
// Helper
// ====================
@@ -11,44 +7,50 @@ const monthNames = ["Enero","Febrero","Marzo","Abril","Mayo","Junio",
"Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"];
let selectedMonth = new Date().getMonth(); // mes actual
+let ciudadActual = "Madrid"; // ciudad por defecto
+const BASE_API = "https://aplicacionesdevanguardia.es/eltiempo/servidor/api-weather-fechas.php";
+
+// ====================
+// Construir URL de API según filtros
+// ====================
+function buildApiUrl({ ciudad, fecha = null, desde = null, hasta = null }) {
+ const params = new URLSearchParams();
+ params.append("ciudad", ciudad);
+ if (fecha) params.append("fecha", fecha);
+ if (desde) params.append("desde", desde);
+ if (hasta) params.append("hasta", hasta);
+
+ return `${BASE_API}?${params.toString()}`;
+}
+
+// ====================
+// Actualizar nombre de mes
+// ====================
function updateMonthHeader() {
$("mes-nombre").textContent = monthNames[selectedMonth];
}
// ====================
-// Cargar datos del JSON
+// Cargar datos desde la API
// ====================
-async function loadStats() {
+async function loadStats(options = {}) {
try {
- const response = await fetch(API_URL);
+ const url = buildApiUrl({
+ ciudad: ciudadActual,
+ ...options
+ });
+
+ const response = await fetch(url);
if (!response.ok) throw new Error("Error cargando datos: " + response.status);
const data = await response.json();
-
if (!data || !data.length) throw new Error("Datos vacíos");
renderLastData(data);
renderMonthStats(data);
renderTrend(data);
- } catch (err) {
- console.error(err);
- $("stats-location").textContent = "Error cargando datos";
- }
-}
-async function loadStatsToday() {
- try {
- const response = await fetch(API_URL);
- if (!response.ok) throw new Error("Error cargando datos: " + response.status);
-
- const data = await response.json();
-
- if (!data || !data.length) throw new Error("Datos vacíos");
-
- renderLastData(data);
- renderMonthStats(data);
- renderTrend(data);
} catch (err) {
console.error(err);
$("stats-location").textContent = "Error cargando datos";
@@ -67,11 +69,13 @@ function renderLastData(data) {
$("last-wind").textContent = last.viento_velocidad + " km/h";
$("last-sunrise").textContent = last.amanecer;
$("last-sunset").textContent = last.anochecer;
-}
-/* ==============================================
- FASES LUNARES
-============================================== */
+ $("stats-location").textContent = `${ciudadActual} - Estadísticas`;
+}
+
+// ====================
+// FASES LUNARES
+// ====================
function getMoonPhase() {
const now = new Date();
const year = now.getFullYear();
@@ -96,32 +100,13 @@ function getMoonPhase() {
else if (age < 27.68493) { phaseName = "Creciente Menguante"; icon = "🌘"; }
else { phaseName = "Luna Nueva"; icon = "🌑"; }
- document.getElementById("moon-phase").textContent = phaseName;
- document.getElementById("moon-icon").textContent = icon;
-
- document.getElementById("stats-location").textContent = `Hoy es ${day} de ${monthNames[month-1]} de ${year}`;
+ $("moon-phase").textContent = phaseName;
+ $("moon-icon").textContent = icon;
+ const today = new Date();
+ $("stats-location").textContent += ` | Hoy ${today.getDate()} de ${monthNames[today.getMonth()]} de ${today.getFullYear()}`;
}
-function moonPhaseForDate(year, month, day) {
- const c = Math.floor(365.25 * year);
- const e = Math.floor(30.6 * (month + 1));
- const jd = c + e + day - 694039.09;
- const phase = (jd / 29.53) % 1;
- const age = phase * 29.53;
-
- if (age < 1.84566) return "🌑";
- if (age < 5.53699) return "🌒";
- if (age < 9.22831) return "🌓";
- if (age < 12.91963) return "🌔";
- if (age < 16.61096) return "🌕";
- if (age < 20.30228) return "🌖";
- if (age < 23.99361) return "🌗";
- if (age < 27.68493) return "🌘";
- return "🌑";
-}
-
-
// ====================
// Estadísticas del mes seleccionado
// ====================
@@ -164,16 +149,13 @@ function renderTrend(data) {
const container = $("trend-container");
- // Si no hay datos para el mes, mostrar mensaje
if (Object.keys(byYear).length === 0) {
container.innerHTML = "
No hay datos históricos para este mes.
";
return;
}
- // Construir tabla completa en una variable
let html = "| Año | Máx | Mín | Lluvia |
";
- // Ordenar años de más reciente a más antiguo
Object.keys(byYear).sort((a, b) => b - a).forEach(year => {
const maxAvg = (byYear[year].max.reduce((a,b)=>a+b,0)/byYear[year].max.length).toFixed(1);
const minAvg = (byYear[year].min.reduce((a,b)=>a+b,0)/byYear[year].min.length).toFixed(1);
@@ -190,33 +172,49 @@ function renderTrend(data) {
});
html += "
";
-
- // Insertar tabla en el contenedor
container.innerHTML = html;
}
+// ====================
+// Cargar mes actual según selectedMonth
+// ====================
+function loadCurrentMonth() {
+ const year = new Date().getFullYear();
+ const month = selectedMonth + 1;
+
+ const firstDay = `${year}-${String(month).padStart(2,"0")}-01`;
+ const lastDay = new Date(year, month, 0).toISOString().split("T")[0];
+
+ loadStats({ desde: firstDay, hasta: lastDay });
+}
// ====================
-// Botones de mes
+// Inicialización
// ====================
document.addEventListener("DOMContentLoaded", () => {
updateMonthHeader();
- loadStats();
- loadStatsToday();
+ getMoonPhase();
+ loadCurrentMonth();
$("prev-month").addEventListener("click", () => {
selectedMonth = (selectedMonth + 11) % 12;
updateMonthHeader();
- loadStats();
- loadStatsToday();
+ loadCurrentMonth();
});
$("next-month").addEventListener("click", () => {
selectedMonth = (selectedMonth + 1) % 12;
updateMonthHeader();
- loadStats();
- loadStatsToday();
+ loadCurrentMonth();
});
$("year").textContent = new Date().getFullYear();
+
+ // ====================
+ // Selector de ciudad
+ // ====================
+ $("city-select").addEventListener("change", (e) => {
+ ciudadActual = e.target.value;
+ loadCurrentMonth();
+ });
});
diff --git a/eltiempo/servidor/api-weather-fechas.php b/eltiempo/servidor/api-weather-fechas.php
new file mode 100644
index 0000000..1e60bd1
--- /dev/null
+++ b/eltiempo/servidor/api-weather-fechas.php
@@ -0,0 +1,150 @@
+ "El parámetro 'ciudad' es obligatorio."]);
+ exit();
+}
+
+// Validar formato fecha YYYY-MM-DD
+function fechaValida($fecha) {
+ return preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha);
+}
+
+if (!empty($fecha) && !fechaValida($fecha)) {
+ http_response_code(400);
+ echo json_encode(["error" => "El parámetro 'fecha' debe tener formato YYYY-MM-DD."]);
+ exit();
+}
+
+if (!empty($desde) && !fechaValida($desde)) {
+ http_response_code(400);
+ echo json_encode(["error" => "El parámetro 'desde' debe tener formato YYYY-MM-DD."]);
+ exit();
+}
+
+if (!empty($hasta) && !fechaValida($hasta)) {
+ http_response_code(400);
+ echo json_encode(["error" => "El parámetro 'hasta' debe tener formato YYYY-MM-DD."]);
+ exit();
+}
+
+// ============================
+// 4. CONEXIÓN BD
+// ============================
+$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
+
+if ($conn->connect_error) {
+ http_response_code(500);
+ echo json_encode(["error" => "Error de conexión a la base de datos."]);
+ exit();
+}
+
+// ============================
+// 5. CONSTRUIR QUERY DINÁMICA
+// ============================
+$sql = "
+ SELECT
+ DATE(fecha) AS dia,
+ MIN(fecha) AS primera_fecha_del_dia,
+ MIN(amanecer) AS amanecer,
+ MAX(anochecer) AS anochecer,
+ MAX(temp_max) AS temp_max,
+ MIN(temp_min) AS temp_min,
+ AVG(humedad) AS humedad,
+ SUM(lluvia) AS lluvia,
+ AVG(nubes) AS nubes,
+ AVG(viento_velocidad) AS viento_velocidad,
+ AVG(viento_direccion) AS viento_direccion
+ FROM weather
+ WHERE ciudad LIKE CONCAT('%', ?, '%')
+";
+
+$params = [$ciudad];
+$types = "s";
+
+// Filtro por fecha exacta
+if (!empty($fecha)) {
+ $sql .= " AND DATE(fecha) = ?";
+ $params[] = $fecha;
+ $types .= "s";
+}
+
+// Filtro por rango
+if (!empty($desde) && !empty($hasta)) {
+ $sql .= " AND DATE(fecha) BETWEEN ? AND ?";
+ $params[] = $desde;
+ $params[] = $hasta;
+ $types .= "ss";
+} elseif (!empty($desde)) {
+ $sql .= " AND DATE(fecha) >= ?";
+ $params[] = $desde;
+ $types .= "s";
+} elseif (!empty($hasta)) {
+ $sql .= " AND DATE(fecha) <= ?";
+ $params[] = $hasta;
+ $types .= "s";
+}
+
+$sql .= "
+ GROUP BY DATE(fecha)
+ ORDER BY DATE(fecha);
+";
+
+// ============================
+// 6. PREPARAR Y EJECUTAR
+// ============================
+$stmt = $conn->prepare($sql);
+
+if (!$stmt) {
+ http_response_code(500);
+ echo json_encode(["error" => "Error al preparar la consulta."]);
+ $conn->close();
+ exit();
+}
+
+$stmt->bind_param($types, ...$params);
+$stmt->execute();
+$result = $stmt->get_result();
+
+$datos = [];
+
+while ($row = $result->fetch_assoc()) {
+ $datos[] = $row;
+}
+
+// ============================
+// 7. RESPUESTA
+// ============================
+http_response_code(200);
+echo json_encode($datos);
+
+// ============================
+// 8. CIERRE
+// ============================
+$stmt->close();
+$conn->close();
+?>
diff --git a/eltiempo/servidor/api-weather.php b/eltiempo/servidor/api-weather.php
index 37a811f..dddb587 100755
--- a/eltiempo/servidor/api-weather.php
+++ b/eltiempo/servidor/api-weather.php
@@ -14,6 +14,7 @@ define('DB_NAME', 'clima');
// --- 2. Input Validation ---
// Assuming 'ciudad' comes from a GET request.
$ciudad = $_GET['ciudad'] ?? ''; // Use null coalescing operator for cleaner default
+$fecha = $_GET['fecha'] ?? ''; // Optional: filter by date
if (empty($ciudad)) {
http_response_code(400); // Bad Request
@@ -21,6 +22,12 @@ if (empty($ciudad)) {
exit(); // Stop script execution
}
+if (!empty($fecha) && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $fecha)) {
+ http_response_code(400); // Bad Request
+ echo json_encode(["error" => "Parámetro 'fecha' debe tener el formato YYYY-MM-DD."]);
+ exit();
+}
+
// Optional: Further sanitize/validate the city name if needed (e.g., alphanumeric only)
// if (!preg_match('/^[a-zA-Z\s]+$/', $ciudad)) {
// http_response_code(400);
@@ -53,6 +60,7 @@ $stmt = $conn->prepare("
FROM weather
WHERE DATE(fecha) >= '2024-10-01'
AND ciudad LIKE CONCAT('%', ?, '%')
+ AND DATE(fecha) LIKE CONCAT('%', ?, '%')
GROUP BY DATE(fecha)
ORDER BY DATE(fecha);
");
@@ -64,7 +72,8 @@ if (!$stmt) {
exit();
}
-$stmt->bind_param("s", $ciudad);
+$stmt->bind_param("ss", $ciudad, $fecha);
+
$stmt->execute();
$result = $stmt->get_result();