GuiCoreLib.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. # Copyright 2023 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. import typing as t
  12. from dateutil import parser
  13. import taipy as tp
  14. from taipy.core import Cycle, Scenario
  15. from taipy.core.notification import CoreEventConsumerBase, EventEntityType
  16. from taipy.core.notification.event import Event
  17. from taipy.core.notification.notifier import Notifier
  18. from taipy.gui import Gui, State
  19. from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType
  20. from taipy.gui.utils import _TaipyBase
  21. class GuiCoreScenarioAdapter(_TaipyBase):
  22. __INNER_PROPS = ["name"]
  23. def get(self):
  24. data = super().get()
  25. if isinstance(data, Scenario):
  26. return [
  27. data.id,
  28. data.is_primary,
  29. data.config_id,
  30. data.creation_date,
  31. data.get_simple_label(),
  32. list(data.tags),
  33. [(k, v) for k, v in data.properties.items() if k not in GuiCoreScenarioAdapter.__INNER_PROPS],
  34. [(p.id, p.get_simple_label()) for p in data.pipelines.values()],
  35. list(data.properties.get("authorized_tags", set())),
  36. ]
  37. return data
  38. @staticmethod
  39. def get_hash():
  40. return _TaipyBase._HOLDER_PREFIX + "Sc"
  41. class GuiCoreContext(CoreEventConsumerBase):
  42. __PROP_SCENARIO_ID = "id"
  43. __PROP_SCENARIO_CONFIG_ID = "config"
  44. __PROP_SCENARIO_DATE = "date"
  45. __PROP_SCENARIO_NAME = "name"
  46. __SCENARIO_PROPS = (__PROP_SCENARIO_CONFIG_ID, __PROP_SCENARIO_DATE, __PROP_SCENARIO_NAME)
  47. _CORE_CHANGED_NAME = "core_changed"
  48. _ERROR_VAR = "gui_core_error"
  49. _SCENARIO_SELECTOR_ID_VAR = "gui_core_sc_id"
  50. def __init__(self, gui: Gui) -> None:
  51. self.gui = gui
  52. self.cycles_scenarios: t.Optional[t.List[t.Union[Cycle, Scenario]]] = None
  53. self.scenario_configs: t.Optional[t.List[t.Tuple[str, str]]] = None
  54. # register to taipy core notification
  55. reg_id, reg_queue = Notifier.register()
  56. super().__init__(reg_id, reg_queue)
  57. self.start()
  58. def process_event(self, event: Event):
  59. if event.entity_type == EventEntityType.SCENARIO or event.entity_type == EventEntityType.CYCLE:
  60. self.cycles_scenarios = None
  61. self.gui.broadcast(GuiCoreContext._CORE_CHANGED_NAME, {"scenario": True})
  62. @staticmethod
  63. def scenario_adapter(data):
  64. if isinstance(data, Cycle):
  65. return (data.id, data.name, tp.get_scenarios(data), 0, False)
  66. elif isinstance(data, Scenario):
  67. return (data.id, data.name, None, 1, data.is_primary)
  68. return data
  69. def get_scenarios(self):
  70. if self.cycles_scenarios is None:
  71. self.cycles_scenarios = []
  72. for cycle, scenarios in tp.get_cycles_scenarios().items():
  73. if cycle is None:
  74. self.cycles_scenarios.extend(scenarios)
  75. else:
  76. self.cycles_scenarios.append(cycle)
  77. return self.cycles_scenarios
  78. def select_scenario(self, state: State, id: str, action: str, payload: t.Dict[str, str]):
  79. args = payload.get("args")
  80. if args is None or not isinstance(args, list) or len(args) == 0:
  81. return
  82. scenario_id = args[0]
  83. state.assign(GuiCoreContext._SCENARIO_SELECTOR_ID_VAR, scenario_id)
  84. def get_scenario_by_id(self, id: str) -> t.Optional[Scenario]:
  85. if not id:
  86. return None
  87. try:
  88. return tp.get(id)
  89. except Exception:
  90. return None
  91. def get_scenario_configs(self):
  92. if self.scenario_configs is None:
  93. configs = tp.Config.scenarios
  94. if isinstance(configs, dict):
  95. self.scenario_configs = [(id, f"{c.id}") for id, c in configs.items()]
  96. return self.scenario_configs
  97. def crud_scenario(self, state: State, id: str, action: str, payload: t.Dict[str, str]):
  98. args = payload.get("args")
  99. if (
  100. args is None
  101. or not isinstance(args, list)
  102. or len(args) < 3
  103. or not isinstance(args[0], bool)
  104. or not isinstance(args[1], bool)
  105. or not isinstance(args[2], dict)
  106. ):
  107. return
  108. update = args[0]
  109. delete = args[1]
  110. data = args[2]
  111. name = data.get(GuiCoreContext.__PROP_SCENARIO_NAME)
  112. if update:
  113. scenario_id = data.get(GuiCoreContext.__PROP_SCENARIO_ID)
  114. if delete:
  115. try:
  116. tp.delete(scenario_id)
  117. except Exception as e:
  118. state.assign(GuiCoreContext._ERROR_VAR, f"Error deleting Scenario. {e}")
  119. else:
  120. scenario = tp.get(scenario_id)
  121. else:
  122. config_id = data.get(GuiCoreContext.__PROP_SCENARIO_CONFIG_ID)
  123. scenario_config = tp.Config.scenarios.get(config_id)
  124. if scenario_config is None:
  125. state.assign(GuiCoreContext._ERROR_VAR, f"Invalid configuration id ({config_id})")
  126. return
  127. date_str = data.get(GuiCoreContext.__PROP_SCENARIO_DATE)
  128. try:
  129. date = parser.parse(date_str) if isinstance(date_str, str) else None
  130. except Exception as e:
  131. state.assign(GuiCoreContext._ERROR_VAR, f"Invalid date ({date_str}).{e}")
  132. return
  133. try:
  134. scenario = tp.create_scenario(scenario_config, date, name)
  135. except Exception as e:
  136. state.assign(GuiCoreContext._ERROR_VAR, f"Error creating Scenario. {e}")
  137. if scenario:
  138. with scenario as sc:
  139. sc._properties[GuiCoreContext.__PROP_SCENARIO_NAME] = name
  140. if props := data.get("properties"):
  141. try:
  142. for prop in props:
  143. key = prop.get("key")
  144. if key and key not in GuiCoreContext.__SCENARIO_PROPS:
  145. sc._properties[key] = prop.get("value")
  146. state.assign(GuiCoreContext._ERROR_VAR, "")
  147. except Exception as e:
  148. state.assign(GuiCoreContext._ERROR_VAR, f"Error creating Scenario. {e}")
  149. def broadcast_core_changed(self):
  150. self.gui.broadcast(GuiCoreContext._CORE_CHANGED_NAME, "")
  151. class GuiCore(ElementLibrary):
  152. __LIB_NAME = "taipy_gui_core"
  153. __CTX_VAR_NAME = f"__{__LIB_NAME}_Ctx"
  154. __elts = {
  155. "scenario_selector": Element(
  156. "value",
  157. {
  158. "show_add_button": ElementProperty(PropertyType.dynamic_boolean, True),
  159. "display_cycles": ElementProperty(PropertyType.dynamic_boolean, True),
  160. "show_primary_flag": ElementProperty(PropertyType.dynamic_boolean, True),
  161. "value": ElementProperty(PropertyType.lov_value),
  162. "on_change": ElementProperty(PropertyType.function),
  163. },
  164. inner_properties={
  165. "scenarios": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_scenarios()}}"),
  166. "on_scenario_crud": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.crud_scenario}}"),
  167. "configs": ElementProperty(PropertyType.react, f"{{{__CTX_VAR_NAME}.get_scenario_configs()}}"),
  168. "core_changed": ElementProperty(PropertyType.broadcast, GuiCoreContext._CORE_CHANGED_NAME),
  169. "error": ElementProperty(PropertyType.react, f"{{{GuiCoreContext._ERROR_VAR}}}"),
  170. "type": ElementProperty(PropertyType.inner, Scenario),
  171. "adapter": ElementProperty(PropertyType.inner, GuiCoreContext.scenario_adapter),
  172. "scenario_edit": ElementProperty(
  173. GuiCoreScenarioAdapter,
  174. f"{{{__CTX_VAR_NAME}.get_scenario_by_id({GuiCoreContext._SCENARIO_SELECTOR_ID_VAR})}}",
  175. ),
  176. "on_scenario_select": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.select_scenario}}"),
  177. },
  178. )
  179. }
  180. def get_name(self) -> str:
  181. return GuiCore.__LIB_NAME
  182. def get_elements(self) -> t.Dict[str, Element]:
  183. return GuiCore.__elts
  184. def get_scripts(self) -> t.List[str]:
  185. return ["lib/taipy-gui-core.js"]
  186. def on_init(self, gui: Gui) -> t.Optional[t.Tuple[str, t.Any]]:
  187. return GuiCore.__CTX_VAR_NAME, GuiCoreContext(gui)
  188. def on_user_init(self, state: State):
  189. state._add_attribute(GuiCoreContext._ERROR_VAR)
  190. state._gui._bind_var_val(GuiCoreContext._ERROR_VAR, "")
  191. state._add_attribute(GuiCoreContext._SCENARIO_SELECTOR_ID_VAR)
  192. state._gui._bind_var_val(GuiCoreContext._SCENARIO_SELECTOR_ID_VAR, "")