Actualiza barra de estado y README
This commit is contained in:
parent
9f7d1e247c
commit
2d62bf2530
83
README.md
83
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.
|
||||
|
|
|
|||
46
app.py
46
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
|
||||
|
|
|
|||
Loading…
Reference in New Issue