_adapter.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. from __future__ import annotations
  12. import typing as t
  13. from operator import add
  14. from .._warnings import _warn
  15. from ..icon import Icon
  16. from . import _MapDict
  17. class _AdaptedLov:
  18. def __init__(self, lov: t.Any, var_type: str) -> None:
  19. self._lov = lov
  20. self._type = var_type
  21. @staticmethod
  22. def get_lov(lov: t.Any):
  23. return lov._lov if isinstance(lov, _AdaptedLov) else lov
  24. @staticmethod
  25. def get_type(lov: t.Any):
  26. return lov._type if isinstance(lov, _AdaptedLov) else None
  27. class _Adapter:
  28. def __init__(self) -> None:
  29. self.__adapter_for_type: t.Dict[str, t.Callable] = {}
  30. self.__type_for_variable: t.Dict[str, str] = {}
  31. self.__warning_by_type: t.Set[str] = set()
  32. def _get_adapted_lov(self, lov: t.Any, var_type: str) -> _AdaptedLov:
  33. return _AdaptedLov(lov, var_type)
  34. def _add_for_type(self, type_name: str, adapter: t.Callable) -> None:
  35. self.__adapter_for_type[type_name] = adapter
  36. def _add_type_for_var(self, var_name: str, type_name: str) -> None:
  37. self.__type_for_variable[var_name] = type_name
  38. def _get_for_type(self, type_name: str) -> t.Optional[t.Callable]:
  39. return self.__adapter_for_type.get(type_name)
  40. def _get_unique_type(self, type_name: str) -> str:
  41. index = 0
  42. while type_name in self.__adapter_for_type:
  43. type_name = f"{type_name}{index}"
  44. index += 1
  45. return type_name
  46. def run(self, var_name: str, value: t.Any, id_only=False) -> t.Any:
  47. lov = _AdaptedLov.get_lov(value)
  48. adapter = self.__get_for_var(var_name, value)
  49. if isinstance(lov, (list, tuple)):
  50. res = []
  51. for elt in lov:
  52. v = self._run(adapter, elt, var_name, id_only)
  53. res.append(v if v is not None else elt)
  54. return res
  55. return self._run(adapter, lov, var_name, id_only)
  56. def __get_for_var(self, var_name: str, value: t.Any) -> t.Optional[t.Callable]:
  57. adapter = None
  58. type_name = _AdaptedLov.get_type(value)
  59. if type_name:
  60. adapter = self.__adapter_for_type.get(type_name)
  61. if callable(adapter):
  62. return adapter
  63. type_name = self.__type_for_variable.get(var_name)
  64. if not isinstance(type_name, str):
  65. adapter = self.__adapter_for_type.get(var_name)
  66. lov = _AdaptedLov.get_lov(value)
  67. elt = lov[0] if isinstance(lov, (list, tuple)) and len(lov) else None
  68. type_name = var_name if callable(adapter) else type(elt).__name__
  69. if adapter is None:
  70. adapter = self.__adapter_for_type.get(type_name)
  71. return adapter if callable(adapter) else None
  72. def _get_elt_per_ids(
  73. self, var_name: str, lov: t.List[t.Any], adapter: t.Optional[t.Callable] = None
  74. ) -> t.Dict[str, t.Any]:
  75. dict_res = {}
  76. type_name = _AdaptedLov.get_type(lov)
  77. lov = _AdaptedLov.get_lov(lov)
  78. if not adapter and type_name:
  79. adapter = self.__adapter_for_type.get(type_name)
  80. if not adapter:
  81. adapter = self.__get_for_var(var_name, lov[0] if lov else None)
  82. for value in lov:
  83. try:
  84. result = adapter(value._dict if isinstance(value, _MapDict) else value) if adapter else value
  85. if result is not None:
  86. dict_res[self.__get_id(result)] = value
  87. children = self.__get_children(result)
  88. if children is not None:
  89. dict_res.update(self._get_elt_per_ids(var_name, children, adapter))
  90. except Exception as e:
  91. _warn(f"Cannot run adapter for {var_name}", e)
  92. return dict_res
  93. def _run(
  94. self, adapter: t.Optional[t.Callable], value: t.Any, var_name: str, id_only=False
  95. ) -> t.Union[t.Tuple[str, ...], str, None]:
  96. if value is None:
  97. return None
  98. try:
  99. result = value._dict if isinstance(value, _MapDict) else value
  100. if adapter:
  101. result = adapter(result)
  102. if result is None:
  103. return result
  104. elif isinstance(result, (str, float, int)):
  105. return str(result)
  106. tpl_res = self._get_valid_result(result, id_only)
  107. if tpl_res is None:
  108. _warn(
  109. f"Adapter for {var_name} did not return a valid result. Please check the documentation on List of Values Adapters." # noqa: E501
  110. )
  111. else:
  112. if not id_only and len(tpl_res) > 2 and isinstance(tpl_res[2], list) and len(tpl_res[2]) > 0:
  113. tpl_res = (tpl_res[0], tpl_res[1], self.__on_tree(adapter, tpl_res[2]))
  114. return (
  115. add(type(result)(tpl_res), result[len(tpl_res) :])
  116. if isinstance(result, (tuple, list)) and isinstance(tpl_res, (tuple, list))
  117. else tpl_res
  118. )
  119. except Exception as e:
  120. _warn(f"Cannot run adapter for {var_name}", e)
  121. return None
  122. def __on_tree(self, adapter: t.Optional[t.Callable], tree: t.List[t.Any]):
  123. ret_list = []
  124. for elt in tree:
  125. ret = self._run(adapter, elt, adapter.__name__ if adapter else "adapter")
  126. if ret is not None:
  127. ret_list.append(ret)
  128. return ret_list
  129. def _get_valid_result(self, value: t.Any, id_only=False) -> t.Union[t.Tuple[str, ...], str, None]:
  130. id = self.__get_id(value)
  131. if id_only:
  132. return id
  133. label = self.__get_label(value)
  134. if label is None:
  135. return None
  136. children = self.__get_children(value)
  137. return (id, label) if children is None else (id, label, children) # type: ignore
  138. def __get_id(self, value: t.Any, dig=True) -> str:
  139. if isinstance(value, str):
  140. return value
  141. elif dig:
  142. if isinstance(value, (list, tuple)) and len(value):
  143. return self.__get_id(value[0], False)
  144. elif hasattr(value, "id"):
  145. return self.__get_id(value.id, False)
  146. elif hasattr(value, "__getitem__") and "id" in value:
  147. return self.__get_id(value.get("id"), False)
  148. if value is not None and type(value).__name__ not in self.__warning_by_type:
  149. _warn(f"LoV id must be a string, using a string representation of {type(value)}.")
  150. self.__warning_by_type.add(type(value).__name__)
  151. return "" if value is None else str(value)
  152. def __get_label(self, value: t.Any, dig=True) -> t.Union[str, t.Dict, None]:
  153. if isinstance(value, (str, Icon)):
  154. return Icon.get_dict_or(value)
  155. elif dig:
  156. if isinstance(value, (list, tuple)) and len(value) > 1:
  157. return self.__get_label(value[1], False)
  158. elif hasattr(value, "label"):
  159. return self.__get_label(value.label, False)
  160. elif hasattr(value, "__getitem__") and "label" in value:
  161. return self.__get_label(value["label"], False)
  162. return None
  163. def __get_children(self, value: t.Any) -> t.Optional[t.List[t.Any]]:
  164. if isinstance(value, (tuple, list)) and len(value) > 2:
  165. return value[2] if isinstance(value[2], list) else [value[2]]
  166. elif hasattr(value, "children"):
  167. return value.children if isinstance(value.children, list) else [value.children]
  168. elif hasattr(value, "__getitem__") and "children" in value:
  169. return value["children"] if isinstance(value["children"], list) else [value["children"]]
  170. return None