196 lines
6.8 KiB
Python
196 lines
6.8 KiB
Python
"""Tab del Dashboard."""
|
|
|
|
from PySide6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox,
|
|
QTableWidget, QTableWidgetItem, QPushButton, QGridLayout
|
|
)
|
|
from PySide6.QtCore import Qt
|
|
from PySide6.QtGui import QFont
|
|
|
|
from ..db.job_repository import JobRepository
|
|
|
|
|
|
class DashboardTab(QWidget):
|
|
"""Tab del Dashboard con estadísticas y estado."""
|
|
|
|
def __init__(self, engine):
|
|
"""Inicializa el tab."""
|
|
super().__init__()
|
|
self.engine = engine
|
|
self._setup_ui()
|
|
self._load_initial_stats()
|
|
|
|
def _setup_ui(self):
|
|
"""Configura la interfaz."""
|
|
layout = QVBoxLayout(self)
|
|
layout.setSpacing(15)
|
|
|
|
# Título
|
|
title = QLabel("Dashboard - Estado del Sistema")
|
|
title_font = QFont()
|
|
title_font.setPointSize(16)
|
|
title_font.setBold(True)
|
|
title.setFont(title_font)
|
|
layout.addWidget(title)
|
|
|
|
# Grid de estadísticas
|
|
stats_group = QGroupBox("Estadísticas de Jobs")
|
|
stats_layout = QGridLayout()
|
|
|
|
self.stats_labels = {}
|
|
stats_items = [
|
|
("total", "Total de Jobs", 0, 0),
|
|
("queued", "En Cola", 0, 1),
|
|
("running", "Ejecutando", 0, 2),
|
|
("completed", "Completados", 1, 0),
|
|
("failed", "Fallados", 1, 1),
|
|
("active_threads", "Threads Activos", 1, 2)
|
|
]
|
|
|
|
for key, label_text, row, col in stats_items:
|
|
label = QLabel(label_text + ":")
|
|
value = QLabel("0")
|
|
value_font = QFont()
|
|
value_font.setPointSize(18)
|
|
value_font.setBold(True)
|
|
value.setFont(value_font)
|
|
value.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
|
|
stats_layout.addWidget(label, row * 2, col)
|
|
stats_layout.addWidget(value, row * 2 + 1, col)
|
|
|
|
self.stats_labels[key] = value
|
|
|
|
stats_group.setLayout(stats_layout)
|
|
layout.addWidget(stats_group)
|
|
|
|
# Tiempos promedio
|
|
times_group = QGroupBox("Tiempos Promedio (completados)")
|
|
times_layout = QHBoxLayout()
|
|
|
|
self.time_labels = {}
|
|
time_items = [
|
|
("total_ms", "Total"),
|
|
("extract_ms", "Extracción"),
|
|
("restore_ms", "Restauración")
|
|
]
|
|
|
|
for key, label_text in time_items:
|
|
vbox = QVBoxLayout()
|
|
label = QLabel(label_text + ":")
|
|
value = QLabel("0 s")
|
|
value_font = QFont()
|
|
value_font.setPointSize(14)
|
|
value_font.setBold(True)
|
|
value.setFont(value_font)
|
|
value.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
|
|
vbox.addWidget(label)
|
|
vbox.addWidget(value)
|
|
times_layout.addLayout(vbox)
|
|
|
|
self.time_labels[key] = value
|
|
|
|
times_group.setLayout(times_layout)
|
|
layout.addWidget(times_group)
|
|
|
|
# Estado del motor
|
|
motor_group = QGroupBox("Estado del Motor")
|
|
motor_layout = QHBoxLayout()
|
|
|
|
self.motor_status_label = QLabel("Detenido")
|
|
motor_status_font = QFont()
|
|
motor_status_font.setPointSize(14)
|
|
motor_status_font.setBold(True)
|
|
self.motor_status_label.setFont(motor_status_font)
|
|
self.motor_status_label.setStyleSheet("color: red;")
|
|
|
|
motor_layout.addWidget(QLabel("Estado:"))
|
|
motor_layout.addWidget(self.motor_status_label)
|
|
motor_layout.addStretch()
|
|
|
|
motor_group.setLayout(motor_layout)
|
|
layout.addWidget(motor_group)
|
|
|
|
# Últimos jobs
|
|
recent_group = QGroupBox("Últimos 10 Jobs")
|
|
recent_layout = QVBoxLayout()
|
|
|
|
self.recent_table = QTableWidget()
|
|
self.recent_table.setColumnCount(5)
|
|
self.recent_table.setHorizontalHeaderLabels([
|
|
"Job ID", "Archivo", "Nodo", "DB", "Estado"
|
|
])
|
|
self.recent_table.horizontalHeader().setStretchLastSection(True)
|
|
|
|
recent_layout.addWidget(self.recent_table)
|
|
|
|
refresh_btn = QPushButton("🔄 Actualizar")
|
|
refresh_btn.clicked.connect(self._refresh_recent_jobs)
|
|
recent_layout.addWidget(refresh_btn)
|
|
|
|
recent_group.setLayout(recent_layout)
|
|
layout.addWidget(recent_group)
|
|
|
|
layout.addStretch()
|
|
|
|
def _load_initial_stats(self):
|
|
"""Carga las estadísticas iniciales."""
|
|
stats = self.engine.get_stats()
|
|
self.update_stats(stats)
|
|
self._refresh_recent_jobs()
|
|
|
|
def update_stats(self, stats: dict):
|
|
"""Actualiza las estadísticas en la UI."""
|
|
# Contadores
|
|
for key in ["total", "queued", "running", "completed", "failed", "active_threads"]:
|
|
if key in stats:
|
|
self.stats_labels[key].setText(str(stats[key]))
|
|
|
|
# Tiempos
|
|
for key in ["total_ms", "extract_ms", "restore_ms"]:
|
|
if key in stats:
|
|
ms = stats[key]
|
|
seconds = ms / 1000
|
|
if seconds >= 60:
|
|
minutes = seconds / 60
|
|
text = f"{minutes:.1f} min"
|
|
else:
|
|
text = f"{seconds:.1f} s"
|
|
self.time_labels[key].setText(text)
|
|
|
|
# Estado del motor
|
|
if stats.get("running"):
|
|
if stats.get("paused"):
|
|
self.motor_status_label.setText("Pausado")
|
|
self.motor_status_label.setStyleSheet("color: orange;")
|
|
else:
|
|
self.motor_status_label.setText("Corriendo")
|
|
self.motor_status_label.setStyleSheet("color: green;")
|
|
else:
|
|
self.motor_status_label.setText("Detenido")
|
|
self.motor_status_label.setStyleSheet("color: red;")
|
|
|
|
def _refresh_recent_jobs(self):
|
|
"""Refresca la tabla de jobs recientes."""
|
|
jobs = JobRepository.get_all(limit=10)
|
|
|
|
self.recent_table.setRowCount(len(jobs))
|
|
|
|
for i, job in enumerate(jobs):
|
|
self.recent_table.setItem(i, 0, QTableWidgetItem(job.job_id[:8]))
|
|
self.recent_table.setItem(i, 1, QTableWidgetItem(job.source_name))
|
|
self.recent_table.setItem(i, 2, QTableWidgetItem(job.node_name or "N/A"))
|
|
self.recent_table.setItem(i, 3, QTableWidgetItem(job.db_name or "N/A"))
|
|
|
|
status_item = QTableWidgetItem(job.status)
|
|
# Colorear según estado
|
|
if job.status == "completed":
|
|
status_item.setForeground(Qt.GlobalColor.darkGreen)
|
|
elif "failed" in job.status:
|
|
status_item.setForeground(Qt.GlobalColor.red)
|
|
|
|
self.recent_table.setItem(i, 4, status_item)
|
|
|
|
self.recent_table.resizeColumnsToContents()
|