714 lines
25 KiB
Python
714 lines
25 KiB
Python
"""
|
|
Tableau de bord résumé pour RPA Vision V2
|
|
Affiche les statistiques de tâches, niveaux de confiance et historique d'exécution
|
|
"""
|
|
|
|
from PyQt5.QtWidgets import (
|
|
QDialog, QVBoxLayout, QHBoxLayout, QTableWidget, QTableWidgetItem,
|
|
QPushButton, QLabel, QLineEdit, QComboBox, QHeaderView, QFileDialog,
|
|
QMessageBox, QWidget, QGroupBox
|
|
)
|
|
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
|
from PyQt5.QtGui import QFont, QColor, QBrush
|
|
from typing import Dict, Any, List, Optional
|
|
import logging
|
|
import json
|
|
import csv
|
|
from datetime import datetime
|
|
|
|
|
|
class SummaryDashboard(QDialog):
|
|
"""
|
|
Tableau de bord affichant les métriques de tâches en temps réel
|
|
avec filtrage, recherche et export
|
|
"""
|
|
|
|
# Signal émis quand une tâche est sélectionnée
|
|
task_selected = pyqtSignal(str) # task_id
|
|
|
|
def __init__(self, learning_manager=None, parent=None):
|
|
"""
|
|
Initialiser le tableau de bord
|
|
|
|
Args:
|
|
learning_manager: Instance du gestionnaire d'apprentissage
|
|
parent: Widget parent (optionnel)
|
|
"""
|
|
super().__init__(parent)
|
|
self.learning_manager = learning_manager
|
|
self.logger = logging.getLogger(__name__)
|
|
|
|
# Données des tâches
|
|
self.tasks_data: Dict[str, Dict[str, Any]] = {}
|
|
|
|
# Timer pour mise à jour automatique
|
|
self.update_timer = QTimer()
|
|
self.update_timer.timeout.connect(self.refresh_data)
|
|
|
|
self.init_ui()
|
|
|
|
# Charger les données initiales
|
|
if self.learning_manager:
|
|
self.refresh_data()
|
|
|
|
# Démarrer les mises à jour automatiques (toutes les 2 secondes)
|
|
self.update_timer.start(2000)
|
|
|
|
self.logger.info("SummaryDashboard initialisé")
|
|
|
|
def init_ui(self):
|
|
"""Initialiser l'interface utilisateur"""
|
|
self.setWindowTitle("Tableau de Bord - RPA Vision V2")
|
|
self.setMinimumWidth(1000)
|
|
self.setMinimumHeight(600)
|
|
|
|
# Layout principal
|
|
main_layout = QVBoxLayout()
|
|
self.setLayout(main_layout)
|
|
|
|
# Titre
|
|
title_label = QLabel("📊 Tableau de Bord des Tâches")
|
|
title_label.setFont(QFont("Arial", 16, QFont.Bold))
|
|
title_label.setStyleSheet("color: #2196F3; padding: 10px;")
|
|
main_layout.addWidget(title_label)
|
|
|
|
# Barre d'outils (recherche et filtres)
|
|
toolbar_layout = QHBoxLayout()
|
|
|
|
# Recherche
|
|
search_label = QLabel("🔍 Recherche:")
|
|
search_label.setFont(QFont("Arial", 10))
|
|
toolbar_layout.addWidget(search_label)
|
|
|
|
self.search_input = QLineEdit()
|
|
self.search_input.setPlaceholderText("Rechercher par nom de tâche...")
|
|
self.search_input.setFont(QFont("Arial", 10))
|
|
self.search_input.textChanged.connect(self.apply_filters)
|
|
toolbar_layout.addWidget(self.search_input)
|
|
|
|
# Filtre par mode
|
|
mode_label = QLabel("Mode:")
|
|
mode_label.setFont(QFont("Arial", 10))
|
|
toolbar_layout.addWidget(mode_label)
|
|
|
|
self.mode_filter = QComboBox()
|
|
self.mode_filter.setFont(QFont("Arial", 10))
|
|
self.mode_filter.addItems(["Tous", "Shadow", "Assisté", "Autopilot"])
|
|
self.mode_filter.currentTextChanged.connect(self.apply_filters)
|
|
toolbar_layout.addWidget(self.mode_filter)
|
|
|
|
# Bouton rafraîchir
|
|
refresh_button = QPushButton("🔄 Rafraîchir")
|
|
refresh_button.setFont(QFont("Arial", 10))
|
|
refresh_button.clicked.connect(self.refresh_data)
|
|
toolbar_layout.addWidget(refresh_button)
|
|
|
|
main_layout.addLayout(toolbar_layout)
|
|
|
|
# Statistiques globales
|
|
stats_group = QGroupBox("Statistiques Globales")
|
|
stats_group.setFont(QFont("Arial", 11, QFont.Bold))
|
|
stats_layout = QHBoxLayout()
|
|
stats_group.setLayout(stats_layout)
|
|
|
|
self.total_tasks_label = QLabel("Total: 0")
|
|
self.total_tasks_label.setFont(QFont("Arial", 10))
|
|
stats_layout.addWidget(self.total_tasks_label)
|
|
|
|
self.shadow_tasks_label = QLabel("👀 Shadow: 0")
|
|
self.shadow_tasks_label.setFont(QFont("Arial", 10))
|
|
stats_layout.addWidget(self.shadow_tasks_label)
|
|
|
|
self.assist_tasks_label = QLabel("🤝 Assisté: 0")
|
|
self.assist_tasks_label.setFont(QFont("Arial", 10))
|
|
stats_layout.addWidget(self.assist_tasks_label)
|
|
|
|
self.auto_tasks_label = QLabel("🤖 Autopilot: 0")
|
|
self.auto_tasks_label.setFont(QFont("Arial", 10))
|
|
stats_layout.addWidget(self.auto_tasks_label)
|
|
|
|
stats_layout.addStretch()
|
|
|
|
main_layout.addWidget(stats_group)
|
|
|
|
# Tableau des tâches
|
|
self.tasks_table = QTableWidget()
|
|
self.tasks_table.setFont(QFont("Arial", 9))
|
|
self.tasks_table.setColumnCount(8)
|
|
self.tasks_table.setHorizontalHeaderLabels([
|
|
"Tâche",
|
|
"Mode",
|
|
"Confiance",
|
|
"Observations",
|
|
"Concordance",
|
|
"Corrections",
|
|
"Taux Correction",
|
|
"Dernière Exécution"
|
|
])
|
|
|
|
# Configuration du tableau
|
|
header = self.tasks_table.horizontalHeader()
|
|
header.setSectionResizeMode(0, QHeaderView.Stretch) # Tâche
|
|
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # Mode
|
|
header.setSectionResizeMode(2, QHeaderView.ResizeToContents) # Confiance
|
|
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # Observations
|
|
header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # Concordance
|
|
header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # Corrections
|
|
header.setSectionResizeMode(6, QHeaderView.ResizeToContents) # Taux Correction
|
|
header.setSectionResizeMode(7, QHeaderView.Stretch) # Dernière Exécution
|
|
|
|
self.tasks_table.setAlternatingRowColors(True)
|
|
self.tasks_table.setSelectionBehavior(QTableWidget.SelectRows)
|
|
self.tasks_table.setSelectionMode(QTableWidget.SingleSelection)
|
|
self.tasks_table.itemDoubleClicked.connect(self.on_task_double_clicked)
|
|
|
|
self.tasks_table.setStyleSheet("""
|
|
QTableWidget {
|
|
border: 2px solid #E0E0E0;
|
|
border-radius: 5px;
|
|
gridline-color: #E0E0E0;
|
|
}
|
|
QTableWidget::item {
|
|
padding: 5px;
|
|
}
|
|
QTableWidget::item:selected {
|
|
background-color: #E3F2FD;
|
|
color: #1976D2;
|
|
}
|
|
QHeaderView::section {
|
|
background-color: #2196F3;
|
|
color: white;
|
|
padding: 8px;
|
|
border: none;
|
|
font-weight: bold;
|
|
}
|
|
""")
|
|
|
|
main_layout.addWidget(self.tasks_table)
|
|
|
|
# Boutons d'action
|
|
button_layout = QHBoxLayout()
|
|
|
|
export_csv_button = QPushButton("📄 Exporter CSV")
|
|
export_csv_button.setFont(QFont("Arial", 10))
|
|
export_csv_button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #4CAF50;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 5px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #45a049;
|
|
}
|
|
""")
|
|
export_csv_button.clicked.connect(self.export_to_csv)
|
|
button_layout.addWidget(export_csv_button)
|
|
|
|
export_json_button = QPushButton("📋 Exporter JSON")
|
|
export_json_button.setFont(QFont("Arial", 10))
|
|
export_json_button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #2196F3;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 5px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #1976D2;
|
|
}
|
|
""")
|
|
export_json_button.clicked.connect(self.export_to_json)
|
|
button_layout.addWidget(export_json_button)
|
|
|
|
button_layout.addStretch()
|
|
|
|
close_button = QPushButton("✗ Fermer")
|
|
close_button.setFont(QFont("Arial", 10))
|
|
close_button.setStyleSheet("""
|
|
QPushButton {
|
|
background-color: #f44336;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 5px;
|
|
}
|
|
QPushButton:hover {
|
|
background-color: #da190b;
|
|
}
|
|
""")
|
|
close_button.clicked.connect(self.close)
|
|
button_layout.addWidget(close_button)
|
|
|
|
main_layout.addLayout(button_layout)
|
|
|
|
# Note d'aide
|
|
help_label = QLabel(
|
|
"💡 Astuce: Double-cliquez sur une tâche pour voir les détails"
|
|
)
|
|
help_label.setFont(QFont("Arial", 9))
|
|
help_label.setStyleSheet("color: #666; padding: 5px;")
|
|
main_layout.addWidget(help_label)
|
|
|
|
def refresh_data(self):
|
|
"""Rafraîchir les données depuis le gestionnaire d'apprentissage"""
|
|
if not self.learning_manager:
|
|
return
|
|
|
|
try:
|
|
# Obtenir toutes les tâches
|
|
tasks = self.learning_manager.get_all_tasks()
|
|
|
|
# Mettre à jour les données
|
|
self.tasks_data = {task["task_id"]: task for task in tasks}
|
|
|
|
# Mettre à jour les statistiques globales
|
|
self.update_global_stats()
|
|
|
|
# Mettre à jour le tableau
|
|
self.update_table()
|
|
|
|
self.logger.debug(f"Données rafraîchies: {len(self.tasks_data)} tâches")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Erreur lors du rafraîchissement: {e}")
|
|
|
|
def update_global_stats(self):
|
|
"""Mettre à jour les statistiques globales"""
|
|
if not self.learning_manager:
|
|
return
|
|
|
|
try:
|
|
stats = self.learning_manager.get_task_stats()
|
|
|
|
self.total_tasks_label.setText(f"Total: {stats.get('total_tasks', 0)}")
|
|
self.shadow_tasks_label.setText(f"👀 Shadow: {stats.get('shadow_tasks', 0)}")
|
|
self.assist_tasks_label.setText(f"🤝 Assisté: {stats.get('assist_tasks', 0)}")
|
|
self.auto_tasks_label.setText(f"🤖 Autopilot: {stats.get('auto_tasks', 0)}")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Erreur lors de la mise à jour des stats: {e}")
|
|
|
|
def update_metrics(self, task_id: str, metrics: Dict[str, Any]):
|
|
"""
|
|
Mettre à jour les métriques d'une tâche spécifique
|
|
|
|
Args:
|
|
task_id: ID de la tâche
|
|
metrics: Dictionnaire de métriques
|
|
"""
|
|
if task_id in self.tasks_data:
|
|
self.tasks_data[task_id].update(metrics)
|
|
else:
|
|
self.tasks_data[task_id] = metrics
|
|
|
|
# Mettre à jour le tableau
|
|
self.update_table()
|
|
|
|
self.logger.debug(f"Métriques mises à jour pour tâche: {task_id}")
|
|
|
|
def update_table(self):
|
|
"""Mettre à jour le tableau avec les données filtrées"""
|
|
# Appliquer les filtres
|
|
filtered_tasks = self.get_filtered_tasks()
|
|
|
|
# Mettre à jour le nombre de lignes
|
|
self.tasks_table.setRowCount(len(filtered_tasks))
|
|
|
|
# Remplir le tableau
|
|
for row, task in enumerate(filtered_tasks):
|
|
self._populate_row(row, task)
|
|
|
|
self.logger.debug(f"Tableau mis à jour: {len(filtered_tasks)} tâches affichées")
|
|
|
|
def _populate_row(self, row: int, task: Dict[str, Any]):
|
|
"""
|
|
Remplir une ligne du tableau avec les données d'une tâche
|
|
|
|
Args:
|
|
row: Numéro de ligne
|
|
task: Dictionnaire de données de tâche
|
|
"""
|
|
# Tâche
|
|
task_item = QTableWidgetItem(task.get("task_name", ""))
|
|
task_item.setData(Qt.UserRole, task.get("task_id"))
|
|
self.tasks_table.setItem(row, 0, task_item)
|
|
|
|
# Mode
|
|
mode = task.get("mode", "shadow")
|
|
mode_icons = {"shadow": "👀", "assist": "🤝", "auto": "🤖"}
|
|
mode_names = {"shadow": "Shadow", "assist": "Assisté", "auto": "Autopilot"}
|
|
mode_text = f"{mode_icons.get(mode, '')} {mode_names.get(mode, mode)}"
|
|
mode_item = QTableWidgetItem(mode_text)
|
|
mode_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
# Couleur selon le mode
|
|
mode_colors = {
|
|
"shadow": QColor(33, 150, 243), # Bleu
|
|
"assist": QColor(255, 152, 0), # Orange
|
|
"auto": QColor(76, 175, 80) # Vert
|
|
}
|
|
if mode in mode_colors:
|
|
mode_item.setForeground(QBrush(mode_colors[mode]))
|
|
|
|
self.tasks_table.setItem(row, 1, mode_item)
|
|
|
|
# Confiance
|
|
confidence = task.get("confidence_score", 0.0)
|
|
confidence_item = QTableWidgetItem(f"{confidence * 100:.1f}%")
|
|
confidence_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
# Couleur selon la confiance
|
|
if confidence >= 0.95:
|
|
confidence_item.setForeground(QBrush(QColor(76, 175, 80))) # Vert
|
|
elif confidence >= 0.85:
|
|
confidence_item.setForeground(QBrush(QColor(255, 152, 0))) # Orange
|
|
else:
|
|
confidence_item.setForeground(QBrush(QColor(244, 67, 54))) # Rouge
|
|
|
|
self.tasks_table.setItem(row, 2, confidence_item)
|
|
|
|
# Observations
|
|
obs_count = task.get("observation_count", 0)
|
|
obs_item = QTableWidgetItem(str(obs_count))
|
|
obs_item.setTextAlignment(Qt.AlignCenter)
|
|
self.tasks_table.setItem(row, 3, obs_item)
|
|
|
|
# Concordance
|
|
concordance = task.get("concordance_rate", 0.0)
|
|
concordance_item = QTableWidgetItem(f"{concordance * 100:.1f}%")
|
|
concordance_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
# Couleur selon la concordance
|
|
if concordance >= 0.95:
|
|
concordance_item.setForeground(QBrush(QColor(76, 175, 80))) # Vert
|
|
elif concordance >= 0.85:
|
|
concordance_item.setForeground(QBrush(QColor(255, 152, 0))) # Orange
|
|
else:
|
|
concordance_item.setForeground(QBrush(QColor(244, 67, 54))) # Rouge
|
|
|
|
self.tasks_table.setItem(row, 4, concordance_item)
|
|
|
|
# Corrections
|
|
corrections = task.get("correction_count", 0)
|
|
corrections_item = QTableWidgetItem(str(corrections))
|
|
corrections_item.setTextAlignment(Qt.AlignCenter)
|
|
self.tasks_table.setItem(row, 5, corrections_item)
|
|
|
|
# Taux de correction
|
|
correction_rate = task.get("correction_rate", 0.0)
|
|
correction_rate_item = QTableWidgetItem(f"{correction_rate * 100:.1f}%")
|
|
correction_rate_item.setTextAlignment(Qt.AlignCenter)
|
|
|
|
# Couleur selon le taux de correction (inverse: moins c'est mieux)
|
|
if correction_rate <= 0.03:
|
|
correction_rate_item.setForeground(QBrush(QColor(76, 175, 80))) # Vert
|
|
elif correction_rate <= 0.05:
|
|
correction_rate_item.setForeground(QBrush(QColor(255, 152, 0))) # Orange
|
|
else:
|
|
correction_rate_item.setForeground(QBrush(QColor(244, 67, 54))) # Rouge
|
|
|
|
self.tasks_table.setItem(row, 6, correction_rate_item)
|
|
|
|
# Dernière exécution
|
|
last_exec = task.get("last_execution", "")
|
|
if last_exec:
|
|
try:
|
|
dt = datetime.fromisoformat(last_exec)
|
|
last_exec_text = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
except:
|
|
last_exec_text = last_exec
|
|
else:
|
|
last_exec_text = "Jamais"
|
|
|
|
last_exec_item = QTableWidgetItem(last_exec_text)
|
|
self.tasks_table.setItem(row, 7, last_exec_item)
|
|
|
|
def get_filtered_tasks(self) -> List[Dict[str, Any]]:
|
|
"""
|
|
Obtenir la liste des tâches filtrées selon les critères
|
|
|
|
Returns:
|
|
Liste de tâches filtrées
|
|
"""
|
|
filtered = list(self.tasks_data.values())
|
|
|
|
# Filtre de recherche
|
|
search_text = self.search_input.text().lower()
|
|
if search_text:
|
|
filtered = [
|
|
task for task in filtered
|
|
if search_text in task.get("task_name", "").lower() or
|
|
search_text in task.get("task_id", "").lower()
|
|
]
|
|
|
|
# Filtre de mode
|
|
mode_filter = self.mode_filter.currentText()
|
|
if mode_filter != "Tous":
|
|
mode_map = {
|
|
"Shadow": "shadow",
|
|
"Assisté": "assist",
|
|
"Autopilot": "auto"
|
|
}
|
|
target_mode = mode_map.get(mode_filter)
|
|
if target_mode:
|
|
filtered = [
|
|
task for task in filtered
|
|
if task.get("mode") == target_mode
|
|
]
|
|
|
|
# Trier par dernière exécution (plus récent en premier)
|
|
filtered.sort(
|
|
key=lambda t: t.get("last_execution", ""),
|
|
reverse=True
|
|
)
|
|
|
|
return filtered
|
|
|
|
def apply_filters(self):
|
|
"""Appliquer les filtres et mettre à jour le tableau"""
|
|
self.update_table()
|
|
|
|
def on_task_double_clicked(self, item):
|
|
"""
|
|
Gestionnaire de double-clic sur une tâche
|
|
|
|
Args:
|
|
item: Élément de tableau cliqué
|
|
"""
|
|
row = item.row()
|
|
task_id_item = self.tasks_table.item(row, 0)
|
|
|
|
if task_id_item:
|
|
task_id = task_id_item.data(Qt.UserRole)
|
|
|
|
if task_id and task_id in self.tasks_data:
|
|
self.show_task_details(task_id)
|
|
self.task_selected.emit(task_id)
|
|
|
|
def show_task_details(self, task_id: str):
|
|
"""
|
|
Afficher les détails d'une tâche dans une boîte de dialogue
|
|
|
|
Args:
|
|
task_id: ID de la tâche
|
|
"""
|
|
if task_id not in self.tasks_data:
|
|
return
|
|
|
|
task = self.tasks_data[task_id]
|
|
|
|
details = f"""
|
|
<h3>Détails de la Tâche</h3>
|
|
<p><b>ID:</b> {task.get('task_id', '')}</p>
|
|
<p><b>Nom:</b> {task.get('task_name', '')}</p>
|
|
<p><b>Mode:</b> {task.get('mode', '')}</p>
|
|
<p><b>Observations:</b> {task.get('observation_count', 0)}</p>
|
|
<p><b>Confiance:</b> {task.get('confidence_score', 0) * 100:.1f}%</p>
|
|
<p><b>Concordance:</b> {task.get('concordance_rate', 0) * 100:.1f}%</p>
|
|
<p><b>Corrections:</b> {task.get('correction_count', 0)}</p>
|
|
<p><b>Taux de correction:</b> {task.get('correction_rate', 0) * 100:.1f}%</p>
|
|
<p><b>Dernière exécution:</b> {task.get('last_execution', 'Jamais')}</p>
|
|
"""
|
|
|
|
msg_box = QMessageBox(self)
|
|
msg_box.setWindowTitle("Détails de la Tâche")
|
|
msg_box.setTextFormat(Qt.RichText)
|
|
msg_box.setText(details)
|
|
msg_box.setIcon(QMessageBox.Information)
|
|
msg_box.exec_()
|
|
|
|
def export_to_csv(self):
|
|
"""Exporter les données vers un fichier CSV"""
|
|
if not self.tasks_data:
|
|
QMessageBox.warning(
|
|
self,
|
|
"Aucune Donnée",
|
|
"Aucune donnée à exporter."
|
|
)
|
|
return
|
|
|
|
# Dialogue de sauvegarde
|
|
file_path, _ = QFileDialog.getSaveFileName(
|
|
self,
|
|
"Exporter vers CSV",
|
|
f"rpa_tasks_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
|
|
"CSV Files (*.csv)"
|
|
)
|
|
|
|
if not file_path:
|
|
return
|
|
|
|
try:
|
|
with open(file_path, 'w', newline='', encoding='utf-8') as f:
|
|
writer = csv.writer(f)
|
|
|
|
# En-têtes
|
|
writer.writerow([
|
|
"ID Tâche",
|
|
"Nom Tâche",
|
|
"Mode",
|
|
"Confiance (%)",
|
|
"Observations",
|
|
"Concordance (%)",
|
|
"Corrections",
|
|
"Taux Correction (%)",
|
|
"Dernière Exécution"
|
|
])
|
|
|
|
# Données
|
|
for task in self.get_filtered_tasks():
|
|
writer.writerow([
|
|
task.get("task_id", ""),
|
|
task.get("task_name", ""),
|
|
task.get("mode", ""),
|
|
f"{task.get('confidence_score', 0) * 100:.1f}",
|
|
task.get("observation_count", 0),
|
|
f"{task.get('concordance_rate', 0) * 100:.1f}",
|
|
task.get("correction_count", 0),
|
|
f"{task.get('correction_rate', 0) * 100:.1f}",
|
|
task.get("last_execution", "")
|
|
])
|
|
|
|
QMessageBox.information(
|
|
self,
|
|
"Export Réussi",
|
|
f"Données exportées vers:\n{file_path}"
|
|
)
|
|
|
|
self.logger.info(f"Données exportées vers CSV: {file_path}")
|
|
|
|
except Exception as e:
|
|
QMessageBox.critical(
|
|
self,
|
|
"Erreur d'Export",
|
|
f"Erreur lors de l'export CSV:\n{str(e)}"
|
|
)
|
|
self.logger.error(f"Erreur d'export CSV: {e}")
|
|
|
|
def export_to_json(self):
|
|
"""Exporter les données vers un fichier JSON"""
|
|
if not self.tasks_data:
|
|
QMessageBox.warning(
|
|
self,
|
|
"Aucune Donnée",
|
|
"Aucune donnée à exporter."
|
|
)
|
|
return
|
|
|
|
# Dialogue de sauvegarde
|
|
file_path, _ = QFileDialog.getSaveFileName(
|
|
self,
|
|
"Exporter vers JSON",
|
|
f"rpa_tasks_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
|
|
"JSON Files (*.json)"
|
|
)
|
|
|
|
if not file_path:
|
|
return
|
|
|
|
try:
|
|
export_data = {
|
|
"export_date": datetime.now().isoformat(),
|
|
"total_tasks": len(self.tasks_data),
|
|
"tasks": self.get_filtered_tasks()
|
|
}
|
|
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
json.dump(export_data, f, indent=2, ensure_ascii=False)
|
|
|
|
QMessageBox.information(
|
|
self,
|
|
"Export Réussi",
|
|
f"Données exportées vers:\n{file_path}"
|
|
)
|
|
|
|
self.logger.info(f"Données exportées vers JSON: {file_path}")
|
|
|
|
except Exception as e:
|
|
QMessageBox.critical(
|
|
self,
|
|
"Erreur d'Export",
|
|
f"Erreur lors de l'export JSON:\n{str(e)}"
|
|
)
|
|
self.logger.error(f"Erreur d'export JSON: {e}")
|
|
|
|
def closeEvent(self, event):
|
|
"""Gestionnaire de fermeture"""
|
|
# Arrêter le timer de mise à jour
|
|
self.update_timer.stop()
|
|
event.accept()
|
|
|
|
@staticmethod
|
|
def show_dashboard(learning_manager=None, parent=None) -> 'SummaryDashboard':
|
|
"""
|
|
Méthode statique pour afficher le tableau de bord
|
|
|
|
Args:
|
|
learning_manager: Instance du gestionnaire d'apprentissage
|
|
parent: Widget parent (optionnel)
|
|
|
|
Returns:
|
|
Instance du tableau de bord
|
|
"""
|
|
dashboard = SummaryDashboard(learning_manager, parent)
|
|
dashboard.show()
|
|
return dashboard
|
|
|
|
|
|
if __name__ == "__main__":
|
|
"""Test du tableau de bord"""
|
|
from PyQt5.QtWidgets import QApplication
|
|
import sys
|
|
|
|
app = QApplication(sys.argv)
|
|
|
|
# Créer un tableau de bord avec des données de test
|
|
dashboard = SummaryDashboard()
|
|
|
|
# Ajouter des données de test
|
|
test_tasks = [
|
|
{
|
|
"task_id": "task_001",
|
|
"task_name": "Ouvrir Facture",
|
|
"mode": "auto",
|
|
"confidence_score": 0.97,
|
|
"observation_count": 45,
|
|
"concordance_rate": 0.98,
|
|
"correction_count": 1,
|
|
"correction_rate": 0.022,
|
|
"last_execution": datetime.now().isoformat()
|
|
},
|
|
{
|
|
"task_id": "task_002",
|
|
"task_name": "Valider Commande",
|
|
"mode": "assist",
|
|
"confidence_score": 0.89,
|
|
"observation_count": 12,
|
|
"concordance_rate": 0.92,
|
|
"correction_count": 2,
|
|
"correction_rate": 0.167,
|
|
"last_execution": datetime.now().isoformat()
|
|
},
|
|
{
|
|
"task_id": "task_003",
|
|
"task_name": "Saisie Données Client",
|
|
"mode": "shadow",
|
|
"confidence_score": 0.65,
|
|
"observation_count": 3,
|
|
"concordance_rate": 0.67,
|
|
"correction_count": 0,
|
|
"correction_rate": 0.0,
|
|
"last_execution": datetime.now().isoformat()
|
|
}
|
|
]
|
|
|
|
for task in test_tasks:
|
|
dashboard.update_metrics(task["task_id"], task)
|
|
|
|
dashboard.show()
|
|
|
|
sys.exit(app.exec_())
|