diff --git a/README.md b/README.md index 6c3069e..69f3d1a 100644 --- a/README.md +++ b/README.md @@ -1,67 +1,68 @@ # Proyecto Global Dashboard -Panel de control escrito en Python 3.14 + Tkinter que reúne las prácticas solicitadas (scraping, monitorización, alarmas, notas, música y más) con una estética cuidada y paneles diferenciados. +Laboratorio interactivo construido con Python 3.13 + Tkinter. Reúne scraping, monitorización, chat TCP, alarmas, reproductor musical y utilidades varias en una única aplicación de escritorio. -## 🚀 Características principales +## 🎬 Demo en video -- **Dashboard modular**: panel izquierdo con accesos rápidos (scraping, clima de Jávea, Camellos, copias de seguridad, etc.), cuaderno central por pestañas y panel derecho con chat y listado de alumnos. -- **Scraping integrado**: workflows para Wallapop y scraping genérico con popups dedicados y avisos de estado. -- **Monitor de sistema**: gráficas PSUtil actualizadas mediante `after` de Tk, evitando bloqueos y mostrando CPU/RAM/Net de forma fluida. -- **Bloc de notas y backups reales**: edición rápida de texto con copias automáticas a una carpeta de respaldo mostrando progreso. -- **Widgets temáticos**: reproductor musical con tarjetas, gestor de alarmas rediseñado y popup meteorológico (OpenWeather, coordenadas de Jávea) cacheado para reducir llamadas. -- **Servidor de mensajería**: `servidor.py` permite broadcast TCP para pruebas de chat local. +- YouTube: https://youtu.be/HgJwU_HagD8 + +## 🚀 Características clave + +- **Layout triple panel**: accesos rápidos a la izquierda, notebook central con pestañas temáticas y panel derecho para chat y utilidades. +- **Scraping Wallapop + genérico**: asistentes emergentes, validaciones y guardado de resultados. +- **Monitor de sistema**: gráficas en vivo de CPU, RAM e hilos gracias a psutil y matplotlib embebido. +- **Productividad integrada**: bloc de notas, gestor de alarmas, reproductor musical con pygame y lanzadores de procesos. +- **Popup meteorológico**: consulta OpenWeather para Jávea con caché y resumen formateado. +- **Servidor TCP incluido**: `servidor.py` permite pruebas de chat broadcast desde la propia app. ## ⚙️ Requisitos -- Python 3.8 o superior (desarrollado con 3.14) +- Python 3.8 o superior (desarrollado en 3.13) - Dependencias listadas en `requirements.txt` ```sh pip install -r requirements.txt ``` -## ▶️ Puesta en marcha rápida +## ▶️ Puesta en marcha -1. (Opcional) Arranca el servidor de mensajería: - ```sh - python3 servidor.py - ``` - Verás `Servidor escuchando en 0.0.0.0:3333` en consola. -2. Lanza la interfaz gráfica: - ```sh - python3 app.py - ``` -3. Desde el panel derecho ajusta host/puerto y pulsa `Conectar` para chatear. Explora el resto de pestañas (scraping, notas, alarmas, música, clima) desde los botones laterales. +1. (Opcional) Inicia el servidor de chat: + ```sh + python3 servidor.py + ``` +2. Lanza el panel principal: + ```sh + python3 app.py + ``` +3. Usa el panel izquierdo para abrir scraping, notas, alarmas o el popup del clima; el panel derecho gestiona el chat y el reproductor. -## 🧱 Arquitectura de carpetas +## 🧱 Estructura del proyecto ``` -app.py # GUI principal y lógica de scraping, clima, monitorización, alarmas... -servidor.py # Servidor TCP broadcast para el chat de pruebas -requirements.txt # Dependencias del proyecto -README.md # Este archivo +app.py # GUI principal y lógica de negocio +servidor.py # Servidor TCP broadcast para el chat +requirements.txt # Lista de dependencias +README.md # Documentación ``` -## 🛠️ Funcionalidades destacadas +## 🛠️ Flujos destacados -- **Scraping Wallapop y genérico**: ventanas emergentes, peticiones HTTP con Requests + BeautifulSoup, mensajes de éxito/error. -- **Weather popup “API Tiempo”**: botón dedicado que consulta OpenWeather (con clave fallback), muestra iconos, temperaturas y caché temporal. -- **Copias de seguridad guiadas**: barra de progreso y notificaciones durante la duplicación de directorios. -- **Editor y bloc de notas**: pestañas separadas para notas rápidas y bloc organizado. -- **Gestor de alarmas**: UI modernizada con tarjetas, botones primarios y feedback visual. -- **Música y utilidades**: reproductor basado en pygame y accesos a herramientas externas (“Camellos”, lanzadores, etc.). +- **Scraping**: workers en segundo plano con colas y retroalimentación visual en la pestaña de resultados. +- **Copias de seguridad**: selección interactiva de carpetas y reporte final con totales copiados/omitidos. +- **Gestor de alarmas**: creación, cancelación y popups dedicados con recordatorio sonoro. +- **Panel de recursos**: mezcla de gráficas lineales, de área y de barras para CPU/RAM/hilos. +- **Popup “API Tiempo”**: resumen meteorológico en ventana modal con refresco bajo demanda. -## 🌤️ Servicios externos +## ⚙️ Configuración opcional -- **OpenWeatherMap**: usado para el popup del clima (coordenadas de Jávea). Define `OPENWEATHER_API_KEY` en el entorno para usar tu propia clave. -- **Wallapop / sitios objetivo**: las rutinas de scraping respetan temporizadores y headers básicos; ajusta las URLs o parámetros dentro de `app.py` para nuevos escenarios. +- `OPENWEATHER_API_KEY` / `OPENWEATHER_FALLBACK_API_KEY`: claves para OpenWeather. +- Variables `WALLAPOP_*`: encabezados y parámetros usados por el scraper. +- Ajusta el host/puerto del chat en el panel derecho o modificando `SERVER_HOST_DEFAULT` y `SERVER_PORT_DEFAULT` en `app.py`. ## 📌 Próximos pasos sugeridos -1. Añadir pruebas unitarias para la lógica no gráfica (scraping, backups, parsers). -2. Persistir chats y notas en SQLite para mantener el historial. -3. Integrar reproductor completo dentro de la app (playlist, carátulas). +1. Añadir almacenamiento persistente (SQLite) para chats y notas. +2. Incorporar pruebas unitarias para scraping y rutinas de backup. +3. Extender el reproductor musical con colas y visualizaciones. ---- - -¿Necesitas extender alguna funcionalidad? Abre un issue o comenta qué módulo quieres potenciar (más scraping, dashboards adicionales, automatización de backups, etc.). +¿Quieres ampliar alguna sección (scraping extra, nuevos paneles, automatización de tareas)? Adelante, la base está lista para seguir creciendo. diff --git a/app.py b/app.py index f66faee..712d084 100644 --- a/app.py +++ b/app.py @@ -68,7 +68,6 @@ OPENWEATHER_FALLBACK_API_KEY = os.environ.get( ).strip() _OPENWEATHER_ENV_KEY = os.environ.get('OPENWEATHER_API_KEY', '').strip() OPENWEATHER_API_KEY = _OPENWEATHER_ENV_KEY or OPENWEATHER_FALLBACK_API_KEY -OPENWEATHER_CITY = os.environ.get('OPENWEATHER_CITY', 'Madrid,ES') JAVEA_LATITUDE = 38.789166 JAVEA_LONGITUDE = 0.163055 @@ -232,16 +231,12 @@ class DashboardApp(tk.Tk): self.game_camel_count = 3 self.game_speed_factor = 1.0 self.music_temp_file: str | None = None - self.weather_city = OPENWEATHER_CITY self.weather_api_key = OPENWEATHER_API_KEY self.results_area: tk.Frame | None = None self.results_title: tk.Label | None = None self.scraping_popup: tk.Toplevel | None = None self.simple_scraping_popup: tk.Toplevel | None = None self.weather_popup: tk.Toplevel | None = None - self._last_weather_data: dict[str, Any] | None = None - self._last_weather_error: str | None = None - self._last_weather_timestamp: datetime.datetime | None = None self.chart_canvas = None self.ax_cpu = None self.ax_mem = None @@ -263,8 +258,8 @@ class DashboardApp(tk.Tk): self._build_center_panel() self._build_right_panel() self._build_status_bar() - self._update_clock() + if psutil: self.after(1000, self._update_traffic) try: @@ -275,8 +270,6 @@ class DashboardApp(tk.Tk): threading.Thread(target=self._chat_loop, daemon=True).start() self.after(100, self._process_scraping_queue) self.after(1000, self._refresh_alarms_loop) - if REQUESTS_AVAILABLE and self.weather_api_key: - self.after(2000, self._update_weather) # ------------------------ UI ------------------------ def _maximize_with_borders(self) -> None: @@ -765,7 +758,7 @@ class DashboardApp(tk.Tk): def _build_status_bar(self) -> None: status = tk.Frame(self, bg='#f1f1f1', bd=2, relief='ridge') status.grid(row=2, column=0, columnspan=3, sticky='ew') - for idx in range(4): + for idx in range(3): status.columnconfigure(idx, weight=1) tk.Label(status, text='Correos sin leer', font=('Arial', 11, 'bold'), bg='#f1f1f1').grid(row=0, column=0, padx=16, pady=6, sticky='w') @@ -778,11 +771,8 @@ class DashboardApp(tk.Tk): ) self.traffic_label.grid(row=0, column=1, padx=16, pady=6) - self.weather_label = tk.Label(status, text='Clima: configure API', font=('Arial', 11, 'bold'), bg='#f1f1f1') - self.weather_label.grid(row=0, column=2, padx=16, pady=6, sticky='n') - self.clock_label = tk.Label(status, text='--:--:--', font=('Arial', 12, 'bold'), bg='#f1f1f1') - self.clock_label.grid(row=0, column=3, padx=16, pady=6, sticky='e') + self.clock_label.grid(row=0, column=2, padx=16, pady=6, sticky='e') # ------------------------ acciones ------------------------ def _open_web(self, url: str) -> None: @@ -2020,36 +2010,6 @@ class DashboardApp(tk.Tk): def _fetch_javea_weather(self) -> dict[str, Any]: return self._fetch_weather_by_coordinates(JAVEA_LATITUDE, JAVEA_LONGITUDE) - def _update_weather(self) -> None: - if not self._running: - return - try: - data = self._fetch_weather_data(self.weather_city) - temp = data.get('main', {}).get('temp') - desc = data.get('weather', [{}])[0].get('description', '').capitalize() - city = data.get('name') or self.weather_city - if temp is None: - raise ValueError('Respuesta sin temperatura') - self.weather_label.config(text=f'{city}: {temp:.1f}°C, {desc}') - self._last_weather_data = data - self._last_weather_error = None - self._last_weather_timestamp = datetime.datetime.now() - except RuntimeError as exc: - self.weather_label.config(text='Clima: configure API') - self._log(f'Clima: {exc}') - self._last_weather_data = None - self._last_weather_error = str(exc) - self._last_weather_timestamp = datetime.datetime.now() - except Exception as exc: - self.weather_label.config(text='Clima: N/D') - self._log(f'Clima: {exc}') - self._last_weather_data = None - self._last_weather_error = str(exc) - self._last_weather_timestamp = datetime.datetime.now() - finally: - if self._running: - self.after(300000, self._update_weather) - # ------------------------ chat ------------------------ def _connect_chat(self) -> None: host = self.host_entry.get().strip() or SERVER_HOST_DEFAULT