from pathlib import Path import json from typing import Dict, Any, List, Optional from datetime import datetime class ProfileManager: """Manages application profiles""" DEFAULT_PROFILE = { "id": "default", "name": "Default Profile", "llm_settings": {"model": "gpt-4", "temperature": 0.7, "api_key": ""}, } def __init__(self, data_dir: Path): self.data_dir = data_dir self.profiles_file = data_dir / "profiles.json" self.schema_file = data_dir / "profile_schema.json" self.profiles: Dict[str, Dict] = self._load_profiles() self._schema = self._load_schema() def _load_schema(self) -> Dict[str, Any]: """Load profile schema""" if self.schema_file.exists(): try: with open(self.schema_file, "r", encoding="utf-8") as f: return json.load(f) except Exception as e: print(f"Error loading profile schema: {e}") return {"config_schema": {}} def _load_profiles(self) -> Dict[str, Dict]: """Load all profiles and ensure default profile exists""" profiles = {} # Crear perfil por defecto si no existe default_profile = self._create_default_profile() try: if self.profiles_file.exists(): with open(self.profiles_file, "r", encoding="utf-8") as f: loaded_profiles = json.load(f) profiles.update(loaded_profiles) except Exception as e: print(f"Error loading profiles: {e}") # Asegurar que existe el perfil por defecto if "default" not in profiles: profiles["default"] = default_profile self._save_profiles(profiles) return profiles def _create_default_profile(self) -> Dict[str, Any]: """Create default profile""" profile = self.DEFAULT_PROFILE.copy() now = datetime.now().isoformat() profile["created_at"] = now profile["updated_at"] = now return profile def _save_profiles(self, profiles: Optional[Dict] = None): """Save all profiles""" if profiles is None: profiles = self.profiles try: self.profiles_file.parent.mkdir(parents=True, exist_ok=True) with open(self.profiles_file, "w", encoding="utf-8") as f: json.dump(profiles, f, indent=4) except Exception as e: print(f"Error saving profiles: {e}") raise def _validate_profile(self, profile_data: Dict[str, Any]) -> Dict[str, Any]: """Validate profile data against schema""" schema = self._schema.get("config_schema", {}) validated = {} for key, field_schema in schema.items(): if key in profile_data: validated[key] = self._validate_field( key, profile_data[key], field_schema ) elif field_schema.get("required", False): raise ValueError(f"Required field '{key}' is missing") else: validated[key] = field_schema.get("default") # Pass through non-schema fields for key, value in profile_data.items(): if key not in schema: validated[key] = value return validated def _validate_field( self, key: str, value: Any, field_schema: Dict[str, Any] ) -> Any: """Validate a single field value""" field_type = field_schema.get("type") if value is None or value == "": if field_schema.get("required", False): raise ValueError(f"Field '{key}' is required") return field_schema.get("default") try: if field_type == "string": return str(value) elif field_type == "number": return float(value) if "." in str(value) else int(value) elif field_type == "boolean": if isinstance(value, str): return value.lower() == "true" return bool(value) elif field_type == "select": if value not in field_schema.get("options", []): raise ValueError(f"Invalid option '{value}' for field '{key}'") return value else: return value except Exception as e: raise ValueError(f"Invalid value for field '{key}': {str(e)}") def get_all_profiles(self) -> List[Dict[str, Any]]: """Get all profiles""" return list(self.profiles.values()) def get_profile(self, profile_id: str) -> Optional[Dict[str, Any]]: """Get specific profile with fallback to default""" profile = self.profiles.get(profile_id) if not profile and profile_id != "default": # Si no se encuentra el perfil y no es el perfil por defecto, # intentar retornar el perfil por defecto profile = self.profiles.get("default") if not profile: # Si tampoco existe el perfil por defecto, crearlo profile = self._create_default_profile() self.profiles["default"] = profile self._save_profiles(self.profiles) return profile def create_profile(self, profile_data: Dict[str, Any]) -> Dict[str, Any]: """Create new profile""" if "id" not in profile_data: raise ValueError("Profile must have an id") profile_id = profile_data["id"] if profile_id in self.profiles: raise ValueError(f"Profile {profile_id} already exists") # Add timestamps now = datetime.now().isoformat() profile_data["created_at"] = now profile_data["updated_at"] = now # Validate profile data validated_data = self._validate_profile(profile_data) # Save profile self.profiles[profile_id] = validated_data self._save_profiles() return validated_data def update_profile( self, profile_id: str, profile_data: Dict[str, Any] ) -> Dict[str, Any]: """Update existing profile""" if profile_id not in self.profiles: raise ValueError(f"Profile {profile_id} not found") if profile_id == "default" and "id" in profile_data: raise ValueError("Cannot change id of default profile") # Update timestamp profile_data["updated_at"] = datetime.now().isoformat() # Validate profile data validated_data = self._validate_profile(profile_data) # Update profile current_profile = self.profiles[profile_id].copy() current_profile.update(validated_data) self.profiles[profile_id] = current_profile self._save_profiles() return current_profile def delete_profile(self, profile_id: str): """Delete profile""" if profile_id == "default": raise ValueError("Cannot delete default profile") if profile_id not in self.profiles: raise ValueError(f"Profile {profile_id} not found") del self.profiles[profile_id] self._save_profiles() def get_schema(self) -> Dict[str, Any]: """Get profile schema""" return self._schema def update_schema(self, schema: Dict[str, Any]): """Update profile schema""" try: self.schema_file.parent.mkdir(parents=True, exist_ok=True) with open(self.schema_file, "w", encoding="utf-8") as f: json.dump(schema, f, indent=4) self._schema = schema except Exception as e: print(f"Error saving schema: {e}") raise