mejorando apis para utilizar fechas

This commit is contained in:
Tatiana Villa Ema 2026-02-16 19:53:23 +01:00
parent ff4835cc66
commit 3f3e098177
3 changed files with 222 additions and 65 deletions

View File

@ -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 // Helper
// ==================== // ====================
@ -11,44 +7,50 @@ const monthNames = ["Enero","Febrero","Marzo","Abril","Mayo","Junio",
"Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"]; "Julio","Agosto","Septiembre","Octubre","Noviembre","Diciembre"];
let selectedMonth = new Date().getMonth(); // mes actual 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() { function updateMonthHeader() {
$("mes-nombre").textContent = monthNames[selectedMonth]; $("mes-nombre").textContent = monthNames[selectedMonth];
} }
// ==================== // ====================
// Cargar datos del JSON // Cargar datos desde la API
// ==================== // ====================
async function loadStats() { async function loadStats(options = {}) {
try { 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); if (!response.ok) throw new Error("Error cargando datos: " + response.status);
const data = await response.json(); const data = await response.json();
if (!data || !data.length) throw new Error("Datos vacíos"); if (!data || !data.length) throw new Error("Datos vacíos");
renderLastData(data); renderLastData(data);
renderMonthStats(data); renderMonthStats(data);
renderTrend(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) { } catch (err) {
console.error(err); console.error(err);
$("stats-location").textContent = "Error cargando datos"; $("stats-location").textContent = "Error cargando datos";
@ -67,11 +69,13 @@ function renderLastData(data) {
$("last-wind").textContent = last.viento_velocidad + " km/h"; $("last-wind").textContent = last.viento_velocidad + " km/h";
$("last-sunrise").textContent = last.amanecer; $("last-sunrise").textContent = last.amanecer;
$("last-sunset").textContent = last.anochecer; $("last-sunset").textContent = last.anochecer;
$("stats-location").textContent = `${ciudadActual} - Estadísticas`;
} }
/* ============================================== // ====================
FASES LUNARES // FASES LUNARES
============================================== */ // ====================
function getMoonPhase() { function getMoonPhase() {
const now = new Date(); const now = new Date();
const year = now.getFullYear(); const year = now.getFullYear();
@ -96,32 +100,13 @@ function getMoonPhase() {
else if (age < 27.68493) { phaseName = "Creciente Menguante"; icon = "🌘"; } else if (age < 27.68493) { phaseName = "Creciente Menguante"; icon = "🌘"; }
else { phaseName = "Luna Nueva"; icon = "🌑"; } else { phaseName = "Luna Nueva"; icon = "🌑"; }
document.getElementById("moon-phase").textContent = phaseName; $("moon-phase").textContent = phaseName;
document.getElementById("moon-icon").textContent = icon; $("moon-icon").textContent = icon;
document.getElementById("stats-location").textContent = `Hoy es ${day} de ${monthNames[month-1]} de ${year}`;
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 // Estadísticas del mes seleccionado
// ==================== // ====================
@ -164,16 +149,13 @@ function renderTrend(data) {
const container = $("trend-container"); const container = $("trend-container");
// Si no hay datos para el mes, mostrar mensaje
if (Object.keys(byYear).length === 0) { if (Object.keys(byYear).length === 0) {
container.innerHTML = "<p>No hay datos históricos para este mes.</p>"; container.innerHTML = "<p>No hay datos históricos para este mes.</p>";
return; return;
} }
// Construir tabla completa en una variable
let html = "<table class=\"trend-table\"><thead><tr><th>Año</th><th>Máx</th><th>Mín</th><th>Lluvia</th></tr></thead><tbody>"; let html = "<table class=\"trend-table\"><thead><tr><th>Año</th><th>Máx</th><th>Mín</th><th>Lluvia</th></tr></thead><tbody>";
// Ordenar años de más reciente a más antiguo
Object.keys(byYear).sort((a, b) => b - a).forEach(year => { 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 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); 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 += "</tbody></table>"; html += "</tbody></table>";
// Insertar tabla en el contenedor
container.innerHTML = html; 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", () => { document.addEventListener("DOMContentLoaded", () => {
updateMonthHeader(); updateMonthHeader();
loadStats(); getMoonPhase();
loadStatsToday(); loadCurrentMonth();
$("prev-month").addEventListener("click", () => { $("prev-month").addEventListener("click", () => {
selectedMonth = (selectedMonth + 11) % 12; selectedMonth = (selectedMonth + 11) % 12;
updateMonthHeader(); updateMonthHeader();
loadStats(); loadCurrentMonth();
loadStatsToday();
}); });
$("next-month").addEventListener("click", () => { $("next-month").addEventListener("click", () => {
selectedMonth = (selectedMonth + 1) % 12; selectedMonth = (selectedMonth + 1) % 12;
updateMonthHeader(); updateMonthHeader();
loadStats(); loadCurrentMonth();
loadStatsToday();
}); });
$("year").textContent = new Date().getFullYear(); $("year").textContent = new Date().getFullYear();
// ====================
// Selector de ciudad
// ====================
$("city-select").addEventListener("change", (e) => {
ciudadActual = e.target.value;
loadCurrentMonth();
});
}); });

View File

@ -0,0 +1,150 @@
<?php
// ============================
// 1. HEADERS
// ============================
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=utf-8");
// ============================
// 2. CONFIGURACIÓN BD
// (En producción esto debería ir
// en un archivo fuera del webroot)
// ============================
define('DB_HOST', 'localhost');
define('DB_USER', 'admin');
define('DB_PASS', 'Eavne,e1m');
define('DB_NAME', 'clima');
// ============================
// 3. VALIDACIÓN DE ENTRADA
// ============================
$ciudad = $_GET['ciudad'] ?? '';
$fecha = $_GET['fecha'] ?? '';
$desde = $_GET['desde'] ?? '';
$hasta = $_GET['hasta'] ?? '';
if (empty($ciudad)) {
http_response_code(400);
echo json_encode(["error" => "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();
?>

View File

@ -14,6 +14,7 @@ define('DB_NAME', 'clima');
// --- 2. Input Validation --- // --- 2. Input Validation ---
// Assuming 'ciudad' comes from a GET request. // Assuming 'ciudad' comes from a GET request.
$ciudad = $_GET['ciudad'] ?? ''; // Use null coalescing operator for cleaner default $ciudad = $_GET['ciudad'] ?? ''; // Use null coalescing operator for cleaner default
$fecha = $_GET['fecha'] ?? ''; // Optional: filter by date
if (empty($ciudad)) { if (empty($ciudad)) {
http_response_code(400); // Bad Request http_response_code(400); // Bad Request
@ -21,6 +22,12 @@ if (empty($ciudad)) {
exit(); // Stop script execution 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) // Optional: Further sanitize/validate the city name if needed (e.g., alphanumeric only)
// if (!preg_match('/^[a-zA-Z\s]+$/', $ciudad)) { // if (!preg_match('/^[a-zA-Z\s]+$/', $ciudad)) {
// http_response_code(400); // http_response_code(400);
@ -53,6 +60,7 @@ $stmt = $conn->prepare("
FROM weather FROM weather
WHERE DATE(fecha) >= '2024-10-01' WHERE DATE(fecha) >= '2024-10-01'
AND ciudad LIKE CONCAT('%', ?, '%') AND ciudad LIKE CONCAT('%', ?, '%')
AND DATE(fecha) LIKE CONCAT('%', ?, '%')
GROUP BY DATE(fecha) GROUP BY DATE(fecha)
ORDER BY DATE(fecha); ORDER BY DATE(fecha);
"); ");
@ -64,7 +72,8 @@ if (!$stmt) {
exit(); exit();
} }
$stmt->bind_param("s", $ciudad); $stmt->bind_param("ss", $ciudad, $fecha);
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();