""" Shared utilities for model attributes and labels. """ from pydantic import BaseModel, Field from typing import Dict, Any, List, Type, ClassVar import inspect import importlib import os class BaseModelWithUI(BaseModel): """Base model class with UI support and common functionality""" @classmethod def get_ui_schema(cls) -> Dict[str, Any]: """Get UI schema for frontend""" return { "fields": cls.fieldLabels if hasattr(cls, 'fieldLabels') else {}, "validations": cls.get_validations() if hasattr(cls, 'get_validations') else {} } def to_dict(self) -> Dict[str, Any]: """Convert to dictionary with proper validation""" # Handle both Pydantic v1 and v2 if hasattr(self, 'model_dump'): return self.model_dump() # Pydantic v2 return self.dict() # Pydantic v1 @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'BaseModelWithUI': """Create instance from dictionary with validation""" return cls(**data) @classmethod def getModelAttributeDefinitions(cls) -> List[Dict[str, Any]]: """ Get attribute definitions for this model class. Override this method in model classes to provide custom attribute definitions. Returns: List[Dict[str, Any]]: List of attribute definitions """ attributes = [] # Handle both Pydantic v1 and v2 if hasattr(cls, 'model_fields'): # Pydantic v2 fields = cls.model_fields for name, field in fields.items(): attributes.append({ "name": name, "type": field.annotation.__name__ if hasattr(field.annotation, "__name__") else str(field.annotation), "required": field.is_required() if hasattr(field, "is_required") else True, "description": field.description if hasattr(field, "description") else "", "label": cls.fieldLabels.get(name, Label(default=name)).getLabel() if hasattr(cls, "fieldLabels") else name, "placeholder": f"Please enter {name}", "editable": True, "visible": True, "order": len(attributes) }) else: # Pydantic v1 fields = cls.__fields__ for name, field in fields.items(): attributes.append({ "name": name, "type": field.type_.__name__ if hasattr(field.type_, "__name__") else str(field.type_), "required": field.required, "description": field.field_info.description if hasattr(field.field_info, "description") else "", "label": cls.fieldLabels.get(name, Label(default=name)).getLabel() if hasattr(cls, "fieldLabels") else name, "placeholder": f"Please enter {name}", "editable": True, "visible": True, "order": len(attributes) }) return attributes def getModelAttributes(modelClass): """ Get all attributes of a model class. Args: modelClass: The model class to get attributes from Returns: List[str]: List of attribute names """ return [attr for attr in dir(modelClass) if not callable(getattr(modelClass, attr)) and not attr.startswith('_') and attr not in ('metadata', 'query', 'query_class', 'label', 'field_labels')] class Label(BaseModel): """ Label for an attribute or a class with support for multiple languages. Attributes: default: Default label text translations: Dictionary of translations for different languages """ default: str = Field(..., description="Default label text") translations: Dict[str, str] = Field(default_factory=dict, description="Translations for different languages") class Config: title = "Label" description = "A label with support for multiple languages" schema_extra = { "example": { "default": "Document", "translations": { "en": "Document", "fr": "Document" } } } def getLabel(self, language: str = None) -> str: """ Returns the label in the specified language, or the default value if not available. Args: language: Language code to get the label for Returns: str: Label text in the specified language or default """ if language and language in self.translations: return self.translations[language] return self.default def getModelClasses() -> Dict[str, Type[BaseModel]]: """ Dynamically get all model classes from all model modules. Returns: Dict[str, Type[BaseModel]]: Dictionary of model class names to their classes """ modelClasses = {} # Get the interfaces directory path interfaces_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'interfaces') # Find all model files for filename in os.listdir(interfaces_dir): if filename.endswith('Model.py'): # Convert filename to module name (e.g., gatewayModel.py -> gatewayModel) module_name = filename[:-3] # Import the module dynamically module = importlib.import_module(f'modules.interfaces.{module_name}') # Get all classes from the module for name, obj in inspect.getmembers(module): if inspect.isclass(obj) and issubclass(obj, BaseModel) and obj != BaseModel: modelClasses[name] = obj return modelClasses def getModelAttributeDefinitions(modelClass: Type[BaseModel] = None, userLanguage: str = "en") -> Dict[str, Any]: """ Get attribute definitions for model classes. If modelClass is provided, returns attributes for that specific class. If no modelClass is provided, returns attributes for all model classes. Args: modelClass: Optional specific model class to get attributes for userLanguage: Language code for translations (default: "en") Returns: Dict[str, Any]: Dictionary of model class names to their attribute definitions """ if modelClass: return getModelAttributes(modelClass) # Get all model classes modelClasses = getModelClasses() # Create dictionary of model class names to their attribute definitions return { name: getModelAttributes(cls) for name, cls in modelClasses.items() }