"""Dashboard overview tab""" from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QGroupBox, QListWidget, QListWidgetItem, QPushButton ) from PyQt6.QtCore import Qt from PyQt6.QtGui import QFont from core import DatabaseManager from datetime import datetime, timedelta class DashboardTab(QWidget): """Dashboard overview tab""" def __init__(self): """Initialize dashboard""" super().__init__() self.db = DatabaseManager() self._init_ui() # NO automatic refresh - user must click "Refresh Dashboard" button def _init_ui(self): """Create UI layout""" layout = QVBoxLayout() self.setLayout(layout) # Title title = QLabel("DaemonControl Dashboard") title_font = QFont() title_font.setPointSize(16) title_font.setBold(True) title.setFont(title_font) layout.addWidget(title) # Stats row stats_layout = QHBoxLayout() # Jobs stats jobs_group = QGroupBox("Jobs") jobs_layout = QVBoxLayout() self.total_jobs_label = QLabel("Total: 0") self.enabled_jobs_label = QLabel("Enabled: 0") self.disabled_jobs_label = QLabel("Disabled: 0") jobs_layout.addWidget(self.total_jobs_label) jobs_layout.addWidget(self.enabled_jobs_label) jobs_layout.addWidget(self.disabled_jobs_label) jobs_group.setLayout(jobs_layout) stats_layout.addWidget(jobs_group) # Executions stats (last 24h) exec_group = QGroupBox("Last 24 Hours") exec_layout = QVBoxLayout() self.total_exec_label = QLabel("Executions: 0") self.success_exec_label = QLabel("✅ Success: 0") self.failed_exec_label = QLabel("❌ Failed: 0") exec_layout.addWidget(self.total_exec_label) exec_layout.addWidget(self.success_exec_label) exec_layout.addWidget(self.failed_exec_label) exec_group.setLayout(exec_layout) stats_layout.addWidget(exec_group) # Status status_group = QGroupBox("Status") status_layout = QVBoxLayout() self.daemon_status_label = QLabel("Daemon: Unknown") self.db_status_label = QLabel("Database: OK") status_layout.addWidget(self.daemon_status_label) status_layout.addWidget(self.db_status_label) status_group.setLayout(status_layout) stats_layout.addWidget(status_group) layout.addLayout(stats_layout) # Lists row lists_layout = QHBoxLayout() # Active jobs active_group = QGroupBox("Active Jobs") active_layout = QVBoxLayout() self.active_jobs_list = QListWidget() active_layout.addWidget(self.active_jobs_list) active_group.setLayout(active_layout) lists_layout.addWidget(active_group) # Recent executions recent_group = QGroupBox("Recent Executions") recent_layout = QVBoxLayout() self.recent_exec_list = QListWidget() recent_layout.addWidget(self.recent_exec_list) recent_group.setLayout(recent_layout) lists_layout.addWidget(recent_group) layout.addLayout(lists_layout) # Refresh button refresh_btn = QPushButton("🔄 Refresh Dashboard") refresh_btn.clicked.connect(self.refresh) layout.addWidget(refresh_btn) layout.addStretch() def refresh(self): """Refresh all dashboard data""" self._refresh_job_stats() self._refresh_execution_stats() self._refresh_active_jobs() self._refresh_recent_executions() def _refresh_job_stats(self): """Refresh job statistics""" with self.db.get_connection() as conn: cursor = conn.cursor() cursor.execute("SELECT COUNT(*) FROM jobs") total = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM jobs WHERE enabled = 1") enabled = cursor.fetchone()[0] disabled = total - enabled self.total_jobs_label.setText(f"Total: {total}") self.enabled_jobs_label.setText(f"Enabled: {enabled}") self.disabled_jobs_label.setText(f"Disabled: {disabled}") def _refresh_execution_stats(self): """Refresh execution statistics for last 24 hours""" cutoff = datetime.now() - timedelta(hours=24) with self.db.get_connection() as conn: cursor = conn.cursor() cursor.execute(""" SELECT COUNT(*) FROM executions WHERE start_time >= ? """, (cutoff.isoformat(),)) total = cursor.fetchone()[0] cursor.execute(""" SELECT COUNT(*) FROM executions WHERE start_time >= ? AND status = 'success' """, (cutoff.isoformat(),)) success = cursor.fetchone()[0] cursor.execute(""" SELECT COUNT(*) FROM executions WHERE start_time >= ? AND status = 'failed' """, (cutoff.isoformat(),)) failed = cursor.fetchone()[0] self.total_exec_label.setText(f"Executions: {total}") self.success_exec_label.setText(f"✅ Success: {success}") self.failed_exec_label.setText(f"❌ Failed: {failed}") def _refresh_active_jobs(self): """Refresh active jobs list""" self.active_jobs_list.clear() with self.db.get_connection() as conn: cursor = conn.cursor() cursor.execute(""" SELECT j.name, s.cron_expression FROM jobs j LEFT JOIN schedules s ON s.job_id = j.id AND s.enabled = 1 WHERE j.enabled = 1 ORDER BY j.name """) jobs = cursor.fetchall() if not jobs: self.active_jobs_list.addItem("No active jobs") else: for job in jobs: schedule = job['cron_expression'] if job['cron_expression'] else "(no schedule)" item_text = f"{job['name']} - {schedule}" self.active_jobs_list.addItem(item_text) def _refresh_recent_executions(self): """Refresh recent executions list (last 10)""" self.recent_exec_list.clear() with self.db.get_connection() as conn: cursor = conn.cursor() cursor.execute(""" SELECT e.status, j.name, e.start_time, e.exit_code FROM executions e JOIN jobs j ON j.id = e.job_id ORDER BY e.start_time DESC LIMIT 10 """) executions = cursor.fetchall() if not executions: self.recent_exec_list.addItem("No recent executions") else: for execution in executions: status = execution['status'] if status == 'success': icon = "✅" elif status == 'failed': icon = "❌" elif status == 'timeout': icon = "⏱️" else: icon = "▶️" start_time = datetime.fromisoformat(execution['start_time']) time_str = start_time.strftime('%H:%M:%S') item_text = f"{icon} {execution['name']} - {time_str}" self.recent_exec_list.addItem(item_text)