added attribute types: TextMultilingual, multiselect

This commit is contained in:
ValueOn AG 2025-12-08 00:13:26 +01:00
parent d009f93dba
commit 72f5fbde46
4 changed files with 84 additions and 15 deletions

View file

@ -5,6 +5,7 @@ from typing import Optional, Dict
from enum import Enum from enum import Enum
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from modules.shared.attributeUtils import registerModelLabels from modules.shared.attributeUtils import registerModelLabels
from modules.datamodels.datamodelUtils import TextMultilingual
from modules.datamodels.datamodelUam import AccessLevel from modules.datamodels.datamodelUam import AccessLevel
@ -26,9 +27,9 @@ class Role(BaseModel):
description="Unique role label identifier (e.g., 'admin', 'user', 'viewer')", description="Unique role label identifier (e.g., 'admin', 'user', 'viewer')",
json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True} json_schema_extra={"frontend_type": "text", "frontend_readonly": False, "frontend_required": True}
) )
description: Dict[str, str] = Field( description: TextMultilingual = Field(
description="Role description in multiple languages", description="Role description in multiple languages",
json_schema_extra={"frontend_type": "object", "frontend_readonly": False, "frontend_required": True} json_schema_extra={"frontend_type": "multilingual", "frontend_readonly": False, "frontend_required": True}
) )
isSystemRole: bool = Field( isSystemRole: bool = Field(
False, False,

View file

@ -1,6 +1,7 @@
"""Utility datamodels: Prompt.""" """Utility datamodels: Prompt, TextMultilingual."""
from pydantic import BaseModel, Field from typing import Dict, Optional
from pydantic import BaseModel, Field, field_validator
from modules.shared.attributeUtils import registerModelLabels from modules.shared.attributeUtils import registerModelLabels
import uuid import uuid
@ -22,3 +23,49 @@ registerModelLabels(
) )
class TextMultilingual(BaseModel):
"""
Multilingual text field supporting multiple languages.
Default languages: en (English), ge (German), fr (French), it (Italian)
English (en) is the default/required language.
"""
en: str = Field(description="English text (default language, required)")
ge: Optional[str] = Field(None, description="German text")
fr: Optional[str] = Field(None, description="French text")
it: Optional[str] = Field(None, description="Italian text")
@field_validator('en')
@classmethod
def validate_en_required(cls, v):
"""Ensure English text is not empty"""
if not v or not v.strip():
raise ValueError("English text (en) is required and cannot be empty")
return v
def model_dump(self, **kwargs) -> Dict[str, str]:
"""Return as dictionary, filtering out None values"""
result = {}
for lang in ['en', 'ge', 'fr', 'it']:
value = getattr(self, lang, None)
if value is not None:
result[lang] = value
return result
@classmethod
def from_dict(cls, data: Dict[str, str]) -> 'TextMultilingual':
"""Create TextMultilingual from dictionary"""
return cls(
en=data.get('en', ''),
ge=data.get('ge'),
fr=data.get('fr'),
it=data.get('it')
)
def get_text(self, lang: str = 'en') -> str:
"""Get text for a specific language, fallback to English if not available"""
value = getattr(self, lang, None)
if value:
return value
return self.en # Fallback to English

View file

@ -64,7 +64,17 @@ def getOptions(optionsName: str, currentUser: Optional[User] = None) -> List[Dic
options = [] options = []
for role in roles: for role in roles:
# Use English description as label, fallback to roleLabel # Use English description as label, fallback to roleLabel
label = role.description.get("en", role.roleLabel) if isinstance(role.description, dict) else role.roleLabel # Handle TextMultilingual object
if hasattr(role.description, 'get_text'):
# TextMultilingual object
label = role.description.get_text('en')
elif isinstance(role.description, dict):
# Dict format (backward compatibility)
label = role.description.get("en", role.roleLabel)
else:
# Fallback to roleLabel
label = role.roleLabel
options.append({ options.append({
"value": role.roleLabel, "value": role.roleLabel,
"label": label "label": label

View file

@ -166,16 +166,27 @@ def getModelAttributeDefinitions(modelClass: Type[BaseModel] = None, userLanguag
if frontend_options is None and "frontend_options" in json_extra: if frontend_options is None and "frontend_options" in json_extra:
frontend_options = json_extra.get("frontend_options") frontend_options = json_extra.get("frontend_options")
# Use frontend type if available, otherwise fall back to Python type # Use frontend type if available, otherwise detect from Python type
field_type = ( if frontend_type:
frontend_type field_type = frontend_type
if frontend_type else:
else ( # Check if it's TextMultilingual type
field.annotation.__name__ annotation_str = str(field.annotation)
if hasattr(field.annotation, "__name__") # Check both the module path and class name for TextMultilingual
else str(field.annotation) if ('TextMultilingual' in annotation_str or
) (hasattr(field.annotation, '__name__') and field.annotation.__name__ == 'TextMultilingual') or
) 'datamodelUtils.TextMultilingual' in annotation_str or
'datamodels.datamodelUtils.TextMultilingual' in annotation_str):
field_type = 'multilingual'
elif hasattr(field.annotation, "__name__"):
annotation_name = field.annotation.__name__
# Check if it's a Dict type (for JSON/object fields)
if annotation_name == 'Dict' or annotation_str.startswith('typing.Dict') or annotation_str.startswith('Dict['):
field_type = 'object' # Will be rendered as textarea for JSON editing
else:
field_type = annotation_name
else:
field_type = str(field.annotation)
# Extract default value from field # Extract default value from field
# In Pydantic v2, FieldInfo has a 'default' attribute # In Pydantic v2, FieldInfo has a 'default' attribute