#!/usr/bin/env python3 """ SessionManager - Segmente les actions en sessions pour détecter les workflows """ from datetime import datetime, timedelta from typing import List, Dict, Any, Optional, Callable from dataclasses import dataclass, field @dataclass class Session: """Représente une session d'actions utilisateur.""" session_id: str start_time: datetime end_time: Optional[datetime] = None actions: List[Dict[str, Any]] = field(default_factory=list) window: Optional[str] = None @property def duration(self) -> timedelta: """Durée de la session.""" if self.end_time: return self.end_time - self.start_time return datetime.now() - self.start_time @property def action_count(self) -> int: """Nombre d'actions dans la session.""" return len(self.actions) class SessionManager: """ Gestionnaire de sessions pour segmenter les actions en sessions. Une session = groupe d'actions dans une fenêtre de temps. """ def __init__(self, logger, config: Dict[str, Any]): """ Initialise le gestionnaire de sessions. Args: logger: Logger pour journalisation config: Configuration globale """ self.logger = logger self.config = config # Configuration self.session_timeout = config.get("workflow", {}).get( "session_timeout", 300 # 5 minutes par défaut ) # Session courante self.current_session: Optional[Session] = None # Historique des sessions self.sessions: List[Session] = [] # Callback pour session complétée self.on_session_completed: Optional[Callable] = None if self.logger: self.logger.log_action({ "action": "session_manager_initialized", "session_timeout": self.session_timeout }) def add_action(self, action: Dict[str, Any]): """ Ajoute une action à la session courante. Crée une nouvelle session si nécessaire. Args: action: Action à ajouter """ # Vérifier si on doit créer une nouvelle session if self.should_start_new_session(action): self.finalize_current_session() self.start_new_session(action) # Ajouter l'action à la session courante if self.current_session: self.current_session.actions.append(action) def should_start_new_session(self, action: Dict[str, Any]) -> bool: """ Détermine si une nouvelle session doit être créée. Args: action: Action à évaluer Returns: True si nouvelle session nécessaire """ # Pas de session courante if not self.current_session: return True # Vérifier le timeout if self.current_session.actions: last_action = self.current_session.actions[-1] last_time = last_action.get("timestamp") if last_time: if isinstance(last_time, str): last_time = datetime.fromisoformat(last_time) current_time = action.get("timestamp") if isinstance(current_time, str): current_time = datetime.fromisoformat(current_time) elif not current_time: current_time = datetime.now() time_gap = (current_time - last_time).total_seconds() if time_gap > self.session_timeout: return True # Changement de fenêtre majeur current_window = action.get("window") if current_window and self.current_session.window: if current_window != self.current_session.window: return True return False def start_new_session(self, first_action: Dict[str, Any]): """ Démarre une nouvelle session. Args: first_action: Première action de la session """ session_id = f"session_{datetime.now().strftime('%Y%m%d_%H%M%S')}" timestamp = first_action.get("timestamp") if isinstance(timestamp, str): timestamp = datetime.fromisoformat(timestamp) elif not timestamp: timestamp = datetime.now() self.current_session = Session( session_id=session_id, start_time=timestamp, window=first_action.get("window") ) if self.logger: self.logger.log_action({ "action": "session_started", "session_id": session_id, "window": first_action.get("window") }) def finalize_current_session(self): """Finalise la session courante.""" if not self.current_session: return # Marquer la fin self.current_session.end_time = datetime.now() # Ajouter à l'historique self.sessions.append(self.current_session) # Callback (l'Orchestrator loggera avec plus de détails) if self.on_session_completed: self.on_session_completed(self.current_session) # Réinitialiser self.current_session = None def force_finalize_session(self): """Force la finalisation de la session courante.""" self.finalize_current_session() def get_recent_sessions(self, n: int = 10) -> List[Session]: """ Retourne les N sessions les plus récentes. Args: n: Nombre de sessions à retourner Returns: Liste des sessions récentes """ return self.sessions[-n:] if len(self.sessions) >= n else self.sessions def get_stats(self) -> Dict[str, Any]: """Retourne les statistiques des sessions.""" return { "total_sessions": len(self.sessions), "current_session_actions": self.current_session.action_count if self.current_session else 0, "avg_session_duration": sum(s.duration.total_seconds() for s in self.sessions) / len(self.sessions) if self.sessions else 0, "avg_actions_per_session": sum(s.action_count for s in self.sessions) / len(self.sessions) if self.sessions else 0 }