Skip to content

Commit

Permalink
chore: Update user interface field info name. and fixed not respectin…
Browse files Browse the repository at this point in the history
…g "Lazy" translations on choiceitems
  • Loading branch information
dkmstr committed Aug 29, 2024
1 parent a4a07be commit 1fd5c5b
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 43 deletions.
6 changes: 3 additions & 3 deletions server/src/tests/core/ui/test_userinterface_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ def test_labels(self) -> None:
def test_order(self) -> None:
ui = TestingUserInterface()
self.assertEqual(
{k: v._fields_info.order for k, v in ui._gui.items()},
{k: v._field_info.order for k, v in ui._gui.items()},
{
'str_field': 0,
'str_auto_field': 1,
Expand All @@ -206,7 +206,7 @@ def test_order(self) -> None:
def test_required(self) -> None:
ui = TestingUserInterface()
self.assertEqual(
{k: v._fields_info.required for k, v in ui._gui.items()},
{k: v._field_info.required for k, v in ui._gui.items()},
{
'str_field': True,
'str_auto_field': True,
Expand All @@ -226,7 +226,7 @@ def test_required(self) -> None:
def test_tooltip(self) -> None:
ui = TestingUserInterface()
self.assertEqual(
{k: v._fields_info.tooltip for k, v in ui._gui.items()},
{k: v._field_info.tooltip for k, v in ui._gui.items()},
{
'str_field': 'This is a text field',
'str_auto_field': 'This is a text autocomplete field',
Expand Down
85 changes: 45 additions & 40 deletions server/src/uds/core/ui/user_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@

from django.conf import settings
from django.utils.translation import gettext
from django.utils.functional import Promise # To recognize lazy translations


from uds.core import consts, exceptions, types
from uds.core.managers.crypto import UDSK, CryptoManager
Expand Down Expand Up @@ -124,11 +126,13 @@ class AuthModule(Authenticator):
] = {}

@staticmethod
def choice_item(id_: typing.Union[str, int], text: str) -> 'types.ui.ChoiceItem':
def choice_item(id_: typing.Union[str, int], text: 'str|Promise|typing.Any') -> 'types.ui.ChoiceItem':
"""
Helper method to create a single choice item.
"""
return {'id': id_, 'text': str(text)}
if not isinstance(text, (str, Promise)):
text = str(text)
return {'id': id_, 'text': typing.cast(str, text)} # Cast to avoid mypy error, Promise is at all effects a str

@staticmethod
def choice_image(id_: typing.Union[str, int], text: str, img: str) -> types.ui.ChoiceItem:
Expand Down Expand Up @@ -286,7 +290,7 @@ class InputField(abc.ABC):
in a future version. (str|list[str]|None instead of str|None)
"""

_fields_info: types.ui.FieldInfo
_field_info: types.ui.FieldInfo

def __init__(
self,
Expand All @@ -305,7 +309,7 @@ def __init__(
) -> None:
# Length is not used on some kinds of fields, but present in all anyway
# This property only affects in "modify" operations
self._fields_info = types.ui.FieldInfo(
self._field_info = types.ui.FieldInfo(
old_field_name=old_field_name,
order=order,
label=label,
Expand All @@ -321,7 +325,7 @@ def __init__(

@property
def field_type(self) -> 'types.ui.FieldType':
return types.ui.FieldType(self._fields_info.type)
return types.ui.FieldType(self._field_info.type)

@field_type.setter
def field_type(self, type_: 'types.ui.FieldType') -> None:
Expand All @@ -331,13 +335,13 @@ def field_type(self, type_: 'types.ui.FieldType') -> None:
Args:
type: Type to set (from constants of this class)
"""
self._fields_info.type = type_
self._field_info.type = type_

def is_type(self, *type_: types.ui.FieldType) -> bool:
"""
Returns true if this field is of specified type
"""
return self._fields_info.type in type_
return self._field_info.type in type_

def is_serializable(self) -> bool:
return True
Expand All @@ -346,9 +350,9 @@ def old_field_name(self) -> list[str]:
"""
Returns the name of the field
"""
if isinstance(self._fields_info.old_field_name, list):
return self._fields_info.old_field_name
return [self._fields_info.old_field_name] if self._fields_info.old_field_name else []
if isinstance(self._field_info.old_field_name, list):
return self._field_info.old_field_name
return [self._field_info.old_field_name] if self._field_info.old_field_name else []

@property
def value(self) -> typing.Any:
Expand All @@ -358,9 +362,9 @@ def value(self) -> typing.Any:
returns default value instead.
This is mainly used for hidden fields, so we have correctly initialized
"""
if callable(self._fields_info.value):
return self._fields_info.value()
return self._fields_info.value if self._fields_info.value is not None else self.default
if callable(self._field_info.value):
return self._field_info.value()
return self._field_info.value if self._field_info.value is not None else self.default

@value.setter
def value(self, value: typing.Any) -> None:
Expand All @@ -373,7 +377,7 @@ def _set_value(self, value: typing.Any) -> None:
"""
So we can override value setter at descendants
"""
self._fields_info.value = value
self._field_info.value = value

def gui_description(self) -> dict[str, typing.Any]:
"""
Expand All @@ -382,7 +386,7 @@ def gui_description(self) -> dict[str, typing.Any]:
and don't want to
alter original values.
"""
data = self._fields_info.as_dict()
data = self._field_info.as_dict()
for i in ('value', 'old_field_name'):
if i in data:
del data[i] # We don't want to send some values on gui_description
Expand All @@ -398,7 +402,7 @@ def default(self) -> typing.Any:
"""
Returns the default value for this field
"""
defValue = self._fields_info.default
defValue = self._field_info.default
return defValue() if callable(defValue) else defValue

@default.setter
Expand All @@ -412,23 +416,23 @@ def set_default(self, value: typing.Any) -> None:
Args:
value: Default value (string)
"""
self._fields_info.default = value
self._field_info.default = value

@property
def label(self) -> str:
return self._fields_info.label
return self._field_info.label

@label.setter
def label(self, value: str) -> None:
self._fields_info.label = value
self._field_info.label = value

@property
def required(self) -> bool:
return self._fields_info.required or False
return self._field_info.required or False

@required.setter
def required(self, value: bool) -> None:
self._fields_info.required = value
self._field_info.required = value

def validate(self) -> bool:
"""
Expand Down Expand Up @@ -460,7 +464,7 @@ def as_bool(self) -> bool:
return gui.as_bool(self.value)

def __repr__(self) -> str:
return f'{self.__class__.__name__}: {repr(self._fields_info)}'
return f'{self.__class__.__name__}: {repr(self._field_info)}'

class TextField(InputField):
"""
Expand Down Expand Up @@ -522,7 +526,7 @@ def __init__(
value=value,
type=types.ui.FieldType.TEXT,
)
self._fields_info.lines = min(max(int(lines), 0), 8)
self._field_info.lines = min(max(int(lines), 0), 8)
# Pattern to validate the value
# Can contain an regex or PatternType
# - 'ipv4' # IPv4 address
Expand All @@ -538,7 +542,7 @@ def __init__(
# Note:
# Checks are performed on admin side, so they are not 100% reliable.
if pattern:
self._fields_info.pattern = (
self._field_info.pattern = (
pattern
if isinstance(pattern, types.ui.FieldPatternType)
else types.ui.FieldPatternType(pattern)
Expand All @@ -548,7 +552,7 @@ def validate(self) -> bool:
return super().validate() and self._validate_pattern()

def _validate_pattern(self) -> bool:
pattern = self._fields_info.pattern
pattern = self._field_info.pattern
if isinstance(pattern, types.ui.FieldPatternType):
try:
if pattern == types.ui.FieldPatternType.IPV4:
Expand Down Expand Up @@ -630,13 +634,13 @@ def __init__(
)
# Update parent type
self.field_type = types.ui.FieldType.TEXT_AUTOCOMPLETE
self._fields_info.choices = gui.as_choices(choices or [])
self._field_info.choices = gui.as_choices(choices or [])

def set_choices(self, values: collections.abc.Iterable[typing.Union[str, types.ui.ChoiceItem]]) -> None:
"""
Set the values for this choice field
"""
self._fields_info.choices = gui.as_choices(values)
self._field_info.choices = gui.as_choices(values)

class NumericField(InputField):
"""
Expand Down Expand Up @@ -688,8 +692,8 @@ def __init__(
value=value,
type=types.ui.FieldType.NUMERIC,
)
self._fields_info.min_value = min_value
self._fields_info.max_value = max_value
self._field_info.min_value = min_value
self._field_info.max_value = max_value

def _set_value(self, value: typing.Any) -> None:
"""
Expand Down Expand Up @@ -1108,14 +1112,14 @@ def __init__(
type=types.ui.FieldType.CHOICE,
)

self._fields_info.choices = gui.as_choices(choices)
self._field_info.choices = gui.as_choices(choices)
# if has fillers, set them
if fills:
if 'function' not in fills or 'callback_name' not in fills:
raise ValueError('Invalid fills parameters')
fnc = fills['function']
fills.pop('function')
self._fields_info.fills = fills
self._field_info.fills = fills
# Store it only if not already present
if fills['callback_name'] not in gui.callbacks:
gui.callbacks[fills['callback_name']] = fnc
Expand All @@ -1124,7 +1128,7 @@ def set_choices(self, values: collections.abc.Iterable[typing.Union[str, types.u
"""
Set the values for this choice field
"""
self._fields_info.choices = gui.as_choices(values)
self._field_info.choices = gui.as_choices(values)

def _set_value(self, value: typing.Any) -> None:
"""
Expand All @@ -1143,6 +1147,7 @@ def value(self) -> str:
def value(self, value: str) -> None:
self._set_value(value)


class ImageChoiceField(InputField):
def __init__(
self,
Expand Down Expand Up @@ -1170,13 +1175,13 @@ def __init__(
type=types.ui.FieldType.IMAGECHOICE,
)

self._fields_info.choices = gui.as_choices(choices or [])
self._field_info.choices = gui.as_choices(choices or [])

def set_choices(self, values: collections.abc.Iterable[typing.Union[str, types.ui.ChoiceItem]]) -> None:
"""
Set the values for this choice field
"""
self._fields_info.choices = gui.as_choices(values)
self._field_info.choices = gui.as_choices(values)

def _set_value(self, value: typing.Any) -> None:
"""
Expand Down Expand Up @@ -1258,16 +1263,16 @@ def __init__(
value=value,
)

self._fields_info.rows = rows
self._fields_info.choices = gui.as_choices(choices or [])
self._field_info.rows = rows
self._field_info.choices = gui.as_choices(choices or [])

def set_choices(
self, choices: collections.abc.Iterable[typing.Union[str, types.ui.ChoiceItem]]
) -> None:
"""
Set the values for this choice field
"""
self._fields_info.choices = gui.as_choices(choices)
self._field_info.choices = gui.as_choices(choices)

def _set_value(self, value: typing.Any) -> None:
"""
Expand Down Expand Up @@ -1423,7 +1428,7 @@ def __new__(
for attrName, attr in namespace.items():
if isinstance(attr, gui.InputField):
# Ensure we have a copy of the data, so we can modify it without affecting others
attr._fields_info = copy.deepcopy(attr._fields_info)
attr._field_info = copy.deepcopy(attr._field_info)
_gui[attrName] = attr

new_class_dict[attrName] = attr
Expand Down Expand Up @@ -1475,10 +1480,10 @@ def __init__(self, values: gui.ValuesType = None) -> None:

# Check for "callable" fields and update them if needed
for field in ['choices', 'default']: # Update references to self for callable fields
attr = getattr(fld._fields_info, field, None)
attr = getattr(fld._field_info, field, None)
if attr and callable(attr):
# val is an InputField derived instance, so it is a reference to self._gui[key]
setattr(fld._fields_info, field, attr())
setattr(fld._field_info, field, attr())

if values is not None:
if fld_name in values:
Expand Down

0 comments on commit 1fd5c5b

Please sign in to comment.