se agregaron nuevas caracteristicas

This commit is contained in:
Carlos Tello
2026-08-13 23:07:01 -03:00
parent 1bfb808c79
commit d736db982e
23 changed files with 1244 additions and 100 deletions
+69 -16
View File
@@ -4,11 +4,14 @@ import time
import socket
import platform
import threading
import logging
from pathlib import Path
from typing import Optional, List
import httpx
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize
logger = logging.getLogger("OnEverAgentGUI")
from PyQt6.QtCore import Qt, QThread, pyqtSignal, QSize, QTimer
from PyQt6.QtGui import QIcon, QFont, QColor, QAction
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
@@ -233,11 +236,10 @@ class AddFolderDialog(QDialog):
self.txt_patterns.setText("*.bak,*.mdf")
form.addRow("Filtros de archivo:", self.txt_patterns)
self.spin_interval = QSpinBox()
self.spin_interval.setRange(5, 1440)
self.spin_interval.setValue(60)
self.spin_interval.setSuffix(" min")
form.addRow("Frecuencia de sondeo:", self.spin_interval)
self.cmb_schedule = QComboBox()
self.cmb_schedule.addItems(["hourly", "daily", "weekly", "monthly"])
self.cmb_schedule.setCurrentText("daily")
form.addRow("Planificación (Schedule):", self.cmb_schedule)
self.spin_stable = QSpinBox()
self.spin_stable.setRange(10, 600)
@@ -279,7 +281,7 @@ class AddFolderDialog(QDialog):
name=self.txt_name.text().strip(),
source_path=self.txt_path.text().strip(),
file_patterns=self.txt_patterns.text().strip() or "*.*",
schedule_interval_minutes=self.spin_interval.value(),
schedule_cron=self.cmb_schedule.currentText(),
min_stable_seconds=self.spin_stable.value()
)
@@ -311,6 +313,12 @@ class OnEverDriveMainWindow(QMainWindow):
self._init_ui()
self._init_tray()
# Timer to refresh folders list in GUI with server-assigned jobs periodically
self.timer_refresh = QTimer(self)
self.timer_refresh.setInterval(10000) # every 10 seconds
self.timer_refresh.timeout.connect(self._refresh_folders_table)
self.timer_refresh.start()
if self.config.device_id:
self.daemon.start()
@@ -611,7 +619,13 @@ class OnEverDriveMainWindow(QMainWindow):
self._build_tray_menu()
def _on_tray_activated(self, reason):
if reason == QSystemTrayIcon.ActivationReason.DoubleClick or reason == QSystemTrayIcon.ActivationReason.Trigger:
# Safely convert to int to bypass PyQt6 enum comparison bugs
try:
val = int(reason)
except Exception:
val = reason.value if hasattr(reason, 'value') else reason
if val in (2, 3): # 2: DoubleClick, 3: Trigger
self.showNormal()
self.activateWindow()
@@ -650,13 +664,34 @@ class OnEverDriveMainWindow(QMainWindow):
def _refresh_folders_table(self):
self.config = load_config()
self.tbl_folders.setRowCount(len(self.config.local_folders))
for row, job in enumerate(self.config.local_folders):
server_jobs = getattr(self.daemon, "last_server_jobs", [])
# Combine local folders and server-assigned jobs
total_rows = len(server_jobs) + len(self.config.local_folders)
self.tbl_folders.setRowCount(total_rows)
# Show server jobs first
row = 0
for job in server_jobs:
self.tbl_folders.setItem(row, 0, QTableWidgetItem(job.get("name", "")))
self.tbl_folders.setItem(row, 1, QTableWidgetItem(job.get("source_path", "")))
self.tbl_folders.setItem(row, 2, QTableWidgetItem(job.get("file_patterns", "")))
self.tbl_folders.setItem(row, 3, QTableWidgetItem(job.get("schedule_cron", "")))
status_item = QTableWidgetItem("Sincronizado (Web)")
status_item.setForeground(QColor("#34D399")) # Light green color in dark mode
self.tbl_folders.setItem(row, 4, status_item)
row += 1
# Then show local folders
for job in self.config.local_folders:
self.tbl_folders.setItem(row, 0, QTableWidgetItem(job.name))
self.tbl_folders.setItem(row, 1, QTableWidgetItem(job.source_path))
self.tbl_folders.setItem(row, 2, QTableWidgetItem(job.file_patterns))
self.tbl_folders.setItem(row, 3, QTableWidgetItem(f"{job.schedule_interval_minutes} min"))
self.tbl_folders.setItem(row, 4, QTableWidgetItem(job.last_status or "En espera"))
self.tbl_folders.setItem(row, 3, QTableWidgetItem(getattr(job, "schedule_cron", "daily")))
self.tbl_folders.setItem(row, 4, QTableWidgetItem(job.last_status or "En espera (Local)"))
row += 1
def _delete_selected_folder(self):
row = self.tbl_folders.currentRow()
@@ -665,11 +700,29 @@ class OnEverDriveMainWindow(QMainWindow):
return
job_name = self.tbl_folders.item(row, 0).text()
reply = QMessageBox.question(self, "Confirmar", f"¿Eliminar el monitoreo de la carpeta '{job_name}'?")
reply = QMessageBox.question(self, "Confirmar", f"¿Eliminar el trabajo de backup '{job_name}' del Agente y del Servidor?")
if reply == QMessageBox.StandardButton.Yes:
self.config = load_config()
if row < len(self.config.local_folders):
self.config.local_folders.pop(row)
path_val = self.tbl_folders.item(row, 1).text()
matched_jobs = [f for f in self.config.local_folders if f.source_path == path_val]
if matched_jobs:
job = matched_jobs[0]
if job.job_id:
try:
base_url = self.config.server_url.rstrip("/")
headers = {
"X-Device-Id": self.config.device_id,
"X-Device-Token": self.config.device_token
}
resp = httpx.delete(f"{base_url}/api/jobs/agent/{job.job_id}", headers=headers, timeout=5.0)
if resp.status_code != 200:
QMessageBox.warning(self, "Advertencia", f"No se pudo eliminar en el servidor: {resp.text}.\nSe eliminará localmente.")
except Exception as e:
logger.warning(f"Failed to delete job on server: {e}")
QMessageBox.warning(self, "Advertencia", f"No se pudo contactar al servidor: {e}.\nSe eliminará localmente.")
self.config.local_folders = [f for f in self.config.local_folders if f.source_path != path_val]
save_config(self.config)
self._refresh_folders_table()
self._update_header_status()
@@ -735,7 +788,7 @@ class OnEverDriveMainWindow(QMainWindow):
QMessageBox.warning(self, "Sin Registro", "Primero vincula el dispositivo en la pestaña Servidor & Config.")
return
threading.Thread(target=self.daemon._run_backup_cycle, daemon=True).start()
threading.Thread(target=lambda: self.daemon._run_backup_cycle(force=True), daemon=True).start()
self.lbl_transfer_info.setText("Iniciando escaneo de carpetas y comprobación de locks...")
# --- DISCRETE NOTIFICATION HANDLERS (START & FINISH ONLY) ---