import os  import shutil    import taipy      def handle_services(use_rest, use_core):      if use_core or use_rest:          # Write "import taipy as tp" at the third line of the import.txt file          with open(os.path.join(os.getcwd(), "sections", "import.txt"), "r") as import_file:              import_lines = import_file.readlines()              import_lines[0] = "import taipy as tp\n" + import_lines[0] + "\n"          with open(os.path.join(os.getcwd(), "sections", "import.txt"), "w") as import_file:              import_file.writelines(import_lines)        # Import the necessary services      if use_core and use_rest:          with open(os.path.join(os.getcwd(), "sections", "import.txt"), "a") as import_file:              import_file.write("from taipy import Core, Rest\n")      elif use_core:          with open(os.path.join(os.getcwd(), "sections", "import.txt"), "a") as import_file:              import_file.write("from taipy import Core\n")      elif use_rest:          with open(os.path.join(os.getcwd(), "sections", "import.txt"), "a") as import_file:              import_file.write("from taipy import Rest\n")        # Start the Rest service      if use_rest:          with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a") as main_file:              main_file.write("    rest = Rest()\n")        if use_core:          # Create and submit the placeholder scenario          with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a") as main_file:              main_file.write("    core = Core()\n")              main_file.write("    core.run()\n")              main_file.write("    # #############################################################################\n")              main_file.write("    # PLACEHOLDER: Create and submit your scenario here                           #\n")              main_file.write("    #                                                                             #\n")              main_file.write("    # Example:                                                                    #\n")              main_file.write("    # from configuration import scenario_config                                   #\n")              main_file.write("    # scenario = tp.create_scenario(scenario_config)                              #\n")              main_file.write("    # scenario.submit()                                                           #\n")              main_file.write("    # Comment, remove or replace the previous lines with your own use case        #\n")              main_file.write("    # #############################################################################\n")      else:          shutil.rmtree(os.path.join(os.getcwd(), "algorithms"))          shutil.rmtree(os.path.join(os.getcwd(), "configuration"))      def handle_run_service():      with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a+") as main_file:          main_file.seek(0)          main_content = main_file.read()          # Run Rest service along with the GUI service          if "rest = Rest()" in main_content:              main_file.write('    tp.run(gui, rest, title="{{cookiecutter.__application_title}}")\n')          else:              main_file.write('    gui.run(title="{{cookiecutter.__application_title}}")\n')      def handle_single_page_app():      shutil.rmtree(os.path.join(os.getcwd(), "pages"))        with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a") as main_file:          main_file.write("\n")          main_file.write("    gui = Gui(page=page)\n")        handle_run_service()        with open(os.path.join(os.getcwd(), "sections", "page_content.txt"), "a") as page_content_file:          page_content_file.write(              '''  page = """  <center>  <|navbar|lov={[("home", "Homepage")]}|>  </center>    """  '''          )      def handle_multi_page_app(pages):      for page_name in pages:          os.mkdir(os.path.join(os.getcwd(), "pages", page_name))          with open(os.path.join(os.getcwd(), "pages", "page_example", "page_example.md"), "r") as page_md_file:              page_md_content = page_md_file.read()          page_md_content = page_md_content.replace("Page example", page_name.replace("_", " ").title())          with open(os.path.join(os.getcwd(), "pages", page_name, page_name + ".md"), "w") as page_md_file:              page_md_file.write(page_md_content)            with open(os.path.join(os.getcwd(), "pages", "page_example", "page_example.py"), "r") as page_content_file:              page_py_content = page_content_file.read()          page_py_content = page_py_content.replace("page_example", page_name)          with open(os.path.join(os.getcwd(), "pages", page_name, page_name + ".py"), "w") as page_content_file:              page_content_file.write(page_py_content)        with open(os.path.join(os.getcwd(), "pages", "__init__.py"), "a") as page_init_file:          for page_name in pages:              page_init_file.write(f"from .{page_name}.{page_name} import {page_name}\n")        shutil.rmtree(os.path.join(os.getcwd(), "pages", "page_example"))        newline = ",\n\t"      user_page_dict = newline.join(f'"{page_name}": {page_name}' for page_name in pages)      page_dict = """  pages = {      "/": root_page,      {pages}  }  """      with open(os.path.join(os.getcwd(), "sections", "page_content.txt"), "a") as page_content_file:          page_content_file.write(page_dict.replace("{pages}", user_page_dict))        with open(os.path.join(os.getcwd(), "sections", "import.txt"), "a") as import_file:          import_file.write("from pages import *\n")        with open(os.path.join(os.getcwd(), "sections", "main.txt"), "a") as main_file:          main_file.write("\n")          main_file.write("    gui = Gui(pages=pages)\n")        handle_run_service()      def generate_main_file():      with open(os.path.join(os.getcwd(), "sections", "import.txt"), "r") as import_file:          import_lines = import_file.read()      with open(os.path.join(os.getcwd(), "sections", "page_content.txt"), "r") as page_content_file:          page_content = page_content_file.read()      with open(os.path.join(os.getcwd(), "sections", "main.txt"), "r") as main_file:          main_lines = main_file.read()        with open(os.path.join(os.getcwd(), "{{cookiecutter.__main_file}}.py"), "a") as app_main_file:          app_main_file.write(import_lines)          app_main_file.write("\n")          app_main_file.write(page_content)          app_main_file.write("\n\n")          app_main_file.write(main_lines)      with open(os.path.join(os.getcwd(), "requirements.txt"), "a") as requirement_file:      requirement_file.write(f"taipy=={taipy.version._get_version()}\n")    use_core = "{{ cookiecutter.__core }}".upper()  use_rest = "{{ cookiecutter.__rest }}".upper()  handle_services(use_rest in ["YES", "Y"], use_core in ["YES", "Y"])    pages = "{{ cookiecutter.__pages }}".split(" ")  # Remove empty string from pages list  pages = [page for page in pages if page != ""]  if len(pages) == 0:      handle_single_page_app()  else:      handle_multi_page_app(pages)    generate_main_file()    # Remove the sections folder  shutil.rmtree(os.path.join(os.getcwd(), "sections"))    main_file_name = "{{cookiecutter.__main_file}}.py"  print(      f"New Taipy application has been created at {os.path.join(os.getcwd())}"      f"\n\nTo start the application, change directory to the newly created folder:"      f"\n\tcd {os.path.join(os.getcwd())}"      f"\nand run the application as follows:"      f"\n\ttaipy run {main_file_name}"  )


from ._GuiCoreLib import _GuiCore      def _init_gui_core():      from taipy.gui import Gui        Gui.add_library(_GuiCore())      _init_gui_core()


from ._init import *


import typing as t  from datetime import datetime    from taipy.gui import Gui, State  from taipy.gui.extension import Element, ElementLibrary, ElementProperty, PropertyType    from ..version import _get_version  from ._adapters import _GuiCoreDatanodeAdapter, _GuiCoreScenarioAdapter, _GuiCoreScenarioDagAdapter  from ._context import _GuiCoreContext      class _GuiCore(ElementLibrary):      __LIB_NAME = "taipy_gui_core"      __CTX_VAR_NAME = f"__{__LIB_NAME}_Ctx"      __SCENARIO_ADAPTER = "tgc_scenario"      __DATANODE_ADAPTER = "tgc_datanode"      __JOB_ADAPTER = "tgc_job"        __elts = {          "scenario_selector": Element(              "value",              {                  "id": ElementProperty(PropertyType.string),                  "show_add_button": ElementProperty(PropertyType.boolean, True),                  "display_cycles": ElementProperty(PropertyType.boolean, True),                  "show_primary_flag": ElementProperty(PropertyType.boolean, True),                  "value": ElementProperty(PropertyType.lov_value),                  "on_change": ElementProperty(PropertyType.function),                  "height": ElementProperty(PropertyType.string, "50vh"),                  "class_name": ElementProperty(PropertyType.dynamic_string),                  "show_pins": ElementProperty(PropertyType.boolean, False),                  "on_creation": ElementProperty(PropertyType.function),              },              inner_properties={                  "scenarios": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_scenarios()}}"),                  "on_scenario_crud": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.crud_scenario}}"),                  "configs": ElementProperty(PropertyType.react, f"{{{__CTX_VAR_NAME}.get_scenario_configs()}}"),                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),                  "error": ElementProperty(PropertyType.react, f"{{{_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR}}}"),                  "type": ElementProperty(PropertyType.inner, __SCENARIO_ADAPTER),                  "scenario_edit": ElementProperty(                      _GuiCoreScenarioAdapter,                      f"{{{__CTX_VAR_NAME}.get_scenario_by_id({_GuiCoreContext._SCENARIO_SELECTOR_ID_VAR})}}",                  ),                  "on_scenario_select": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.select_scenario}}"),              },          ),          "scenario": Element(              "scenario",              {                  "id": ElementProperty(PropertyType.string),                  "scenario": ElementProperty(_GuiCoreScenarioAdapter),                  "active": ElementProperty(PropertyType.dynamic_boolean, True),                  "expandable": ElementProperty(PropertyType.boolean, True),                  "expanded": ElementProperty(PropertyType.boolean, True),                  "show_submit": ElementProperty(PropertyType.boolean, True),                  "show_delete": ElementProperty(PropertyType.boolean, True),                  "show_config": ElementProperty(PropertyType.boolean, False),                  "show_cycle": ElementProperty(PropertyType.boolean, False),                  "show_tags": ElementProperty(PropertyType.boolean, True),                  "show_properties": ElementProperty(PropertyType.boolean, True),                  "show_sequences": ElementProperty(PropertyType.boolean, True),                  "show_submit_sequences": ElementProperty(PropertyType.boolean, True),                  "class_name": ElementProperty(PropertyType.dynamic_string),                  "on_submission_change": ElementProperty(PropertyType.function),              },              inner_properties={                  "on_edit": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.edit_entity}}"),                  "on_submit": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.submit_entity}}"),                  "on_delete": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.crud_scenario}}"),                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),                  "error": ElementProperty(PropertyType.react, f"{{{_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR}}}"),              },          ),          "scenario_dag": Element(              "scenario",              {                  "id": ElementProperty(PropertyType.string),                  "scenario": ElementProperty(_GuiCoreScenarioDagAdapter),                  "render": ElementProperty(PropertyType.dynamic_boolean, True),                  "show_toolbar": ElementProperty(PropertyType.boolean, True),                  "width": ElementProperty(PropertyType.string),                  "height": ElementProperty(PropertyType.string),                  "class_name": ElementProperty(PropertyType.dynamic_string),              },              inner_properties={                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),              },          ),          "data_node_selector": Element(              "value",              {                  "id": ElementProperty(PropertyType.string),                  "display_cycles": ElementProperty(PropertyType.boolean, True),                  "show_primary_flag": ElementProperty(PropertyType.boolean, True),                  "value": ElementProperty(PropertyType.lov_value),                  "on_change": ElementProperty(PropertyType.function),                  "height": ElementProperty(PropertyType.string, "50vh"),                  "class_name": ElementProperty(PropertyType.dynamic_string),                  "show_pins": ElementProperty(PropertyType.boolean, True),              },              inner_properties={                  "datanodes": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_datanodes_tree()}}"),                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),                  "type": ElementProperty(PropertyType.inner, __DATANODE_ADAPTER),              },          ),          "data_node": Element(              _GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP,              {                  "id": ElementProperty(PropertyType.string),                  _GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP: ElementProperty(_GuiCoreDatanodeAdapter),                  "active": ElementProperty(PropertyType.dynamic_boolean, True),                  "expandable": ElementProperty(PropertyType.boolean, True),                  "expanded": ElementProperty(PropertyType.boolean, True),                  "show_config": ElementProperty(PropertyType.boolean, False),                  "show_owner": ElementProperty(PropertyType.boolean, True),                  "show_edit_date": ElementProperty(PropertyType.boolean, False),                  "show_expiration_date": ElementProperty(PropertyType.boolean, False),                  "show_properties": ElementProperty(PropertyType.boolean, True),                  "show_history": ElementProperty(PropertyType.boolean, True),                  "show_data": ElementProperty(PropertyType.boolean, True),                  "chart_config": ElementProperty(PropertyType.dict),                  "class_name": ElementProperty(PropertyType.dynamic_string),                  "scenario": ElementProperty(PropertyType.lov_value, "optional"),                  "width": ElementProperty(PropertyType.string),              },              inner_properties={                  "on_edit": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.edit_data_node}}"),                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),                  "error": ElementProperty(PropertyType.react, f"{{{_GuiCoreContext._DATANODE_VIZ_ERROR_VAR}}}"),                  "scenarios": ElementProperty(                      PropertyType.lov,                      f"{{{__CTX_VAR_NAME}.get_scenarios_for_owner({_GuiCoreContext._DATANODE_VIZ_OWNER_ID_VAR})}}",                  ),                  "type": ElementProperty(PropertyType.inner, __SCENARIO_ADAPTER),                  "on_id_select": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.select_id}}"),                  "history": ElementProperty(                      PropertyType.react,                      f"{{{__CTX_VAR_NAME}.get_data_node_history("                      + f"<tp:prop:{_GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP}>, "                      + f"{_GuiCoreContext._DATANODE_VIZ_HISTORY_ID_VAR})}}",                  ),                  "data": ElementProperty(                      PropertyType.react,                      f"{{{__CTX_VAR_NAME}.get_data_node_data(<tp:prop:{_GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP}>,"                      + f" {_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR})}}",                  ),                  "tabular_data": ElementProperty(                      PropertyType.data,                      f"{{{__CTX_VAR_NAME}.get_data_node_tabular_data("                      + f"<tp:prop:{_GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP}>, "                      + f"{_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR})}}",                  ),                  "tabular_columns": ElementProperty(                      PropertyType.dynamic_string,                      f"{{{__CTX_VAR_NAME}.get_data_node_tabular_columns("                      + f"<tp:prop:{_GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP}>, "                      + f"{_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR})}}",                  ),                  "chart_config": ElementProperty(                      PropertyType.dynamic_string,                      f"{{{__CTX_VAR_NAME}.get_data_node_chart_config("                      + f"<tp:prop:{_GuiCoreContext._DATANODE_VIZ_DATA_NODE_PROP}>, "                      + f"{_GuiCoreContext._DATANODE_VIZ_DATA_CHART_ID_VAR})}}",                  ),                  "on_data_value": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.update_data}}"),                  "on_tabular_data_edit": ElementProperty(                      PropertyType.function, f"{{{__CTX_VAR_NAME}.tabular_data_edit}}"                  ),                  "on_lock": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.lock_datanode_for_edit}}"),              },          ),          "job_selector": Element(              "value",              {                  "id": ElementProperty(PropertyType.string),                  "class_name": ElementProperty(PropertyType.dynamic_string),                  "value": ElementProperty(PropertyType.lov_value),                  "show_id": ElementProperty(PropertyType.boolean, True),                  "show_submitted_label": ElementProperty(PropertyType.boolean, True),                  "show_submitted_id": ElementProperty(PropertyType.boolean, False),                  "show_submission_id": ElementProperty(PropertyType.boolean, False),                  "show_date": ElementProperty(PropertyType.boolean, True),                  "show_cancel": ElementProperty(PropertyType.boolean, True),                  "show_delete": ElementProperty(PropertyType.boolean, True),                  "on_change": ElementProperty(PropertyType.function),                  "height": ElementProperty(PropertyType.string, "50vh"),              },              inner_properties={                  "jobs": ElementProperty(PropertyType.lov, f"{{{__CTX_VAR_NAME}.get_jobs_list()}}"),                  "core_changed": ElementProperty(PropertyType.broadcast, _GuiCoreContext._CORE_CHANGED_NAME),                  "type": ElementProperty(PropertyType.inner, __JOB_ADAPTER),                  "on_job_action": ElementProperty(PropertyType.function, f"{{{__CTX_VAR_NAME}.act_on_jobs}}"),                  "error": ElementProperty(PropertyType.dynamic_string, f"{{{_GuiCoreContext._JOB_SELECTOR_ERROR_VAR}}}"),              },          ),      }        def get_name(self) -> str:          return _GuiCore.__LIB_NAME        def get_elements(self) -> t.Dict[str, Element]:          return _GuiCore.__elts        def get_scripts(self) -> t.List[str]:          return ["lib/taipy-gui-core.js"]        def on_init(self, gui: Gui) -> t.Optional[t.Tuple[str, t.Any]]:          gui._get_default_locals_bind().update(              {                  _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR: "",                  _GuiCoreContext._SCENARIO_SELECTOR_ID_VAR: "",                  _GuiCoreContext._SCENARIO_VIZ_ERROR_VAR: "",                  _GuiCoreContext._JOB_SELECTOR_ERROR_VAR: "",                  _GuiCoreContext._DATANODE_VIZ_ERROR_VAR: "",                  _GuiCoreContext._DATANODE_VIZ_OWNER_ID_VAR: "",                  _GuiCoreContext._DATANODE_VIZ_HISTORY_ID_VAR: "",                  _GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR: "",                  _GuiCoreContext._DATANODE_VIZ_DATA_CHART_ID_VAR: "",              }          )          ctx = _GuiCoreContext(gui)          gui._add_adapter_for_type(_GuiCore.__SCENARIO_ADAPTER, ctx.scenario_adapter)          gui._add_adapter_for_type(_GuiCore.__DATANODE_ADAPTER, ctx.data_node_adapter)          gui._add_adapter_for_type(_GuiCore.__JOB_ADAPTER, ctx.job_adapter)          return _GuiCore.__CTX_VAR_NAME, ctx        def on_user_init(self, state: State):          for var in [              _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR,              _GuiCoreContext._SCENARIO_SELECTOR_ID_VAR,              _GuiCoreContext._SCENARIO_VIZ_ERROR_VAR,              _GuiCoreContext._JOB_SELECTOR_ERROR_VAR,              _GuiCoreContext._DATANODE_VIZ_ERROR_VAR,              _GuiCoreContext._DATANODE_VIZ_OWNER_ID_VAR,              _GuiCoreContext._DATANODE_VIZ_HISTORY_ID_VAR,              _GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR,              _GuiCoreContext._DATANODE_VIZ_DATA_CHART_ID_VAR,          ]:              state._add_attribute(var, "")        def get_version(self) -> str:          if not hasattr(self, "version"):              self.version = _get_version() + str(datetime.now().timestamp())          return self.version


import json  import typing as t  from collections import defaultdict  from enum import Enum  from numbers import Number  from threading import Lock    try:      import zoneinfo  except ImportError:      from backports import zoneinfo  # type: ignore[no-redef]    import pandas as pd  from dateutil import parser    from taipy.config import Config  from taipy.core import Cycle, DataNode, Job, Scenario, Sequence, cancel_job, create_scenario  from taipy.core import delete as core_delete  from taipy.core import delete_job  from taipy.core import get as core_get  from taipy.core import (      get_cycles_scenarios,      get_data_nodes,      get_jobs,      is_deletable,      is_editable,      is_promotable,      is_readable,      is_submittable,      set_primary,  )  from taipy.core import submit as core_submit  from taipy.core.data._abstract_tabular import _AbstractTabularDataNode  from taipy.core.notification import CoreEventConsumerBase, EventEntityType  from taipy.core.notification.event import Event, EventOperation  from taipy.core.notification.notifier import Notifier  from taipy.gui import Gui, State  from taipy.gui._warnings import _warn  from taipy.gui.gui import _DoNotUpdate    from ._adapters import _EntityType      class _SubmissionStatus(Enum):      SUBMITTED = 0      COMPLETED = 1      CANCELED = 2      FAILED = 3      BLOCKED = 4      WAITING = 5      RUNNING = 6      UNDEFINED = 7      class _SubmissionDetails:      def __init__(          self,          client_id: str,          module_context: str,          callback: t.Callable,          entity_id: str,          status: _SubmissionStatus,          jobs: t.List[Job],      ) -> None:          self.client_id = client_id          self.module_context = module_context          self.callback = callback          self.entity_id = entity_id          self.status = status          self.jobs = jobs        def set_status(self, status: _SubmissionStatus):          self.status = status          return self      class _GuiCoreContext(CoreEventConsumerBase):      __PROP_ENTITY_ID = "id"      __PROP_ENTITY_COMMENT = "comment"      __PROP_CONFIG_ID = "config"      __PROP_DATE = "date"      __PROP_ENTITY_NAME = "name"      __PROP_SCENARIO_PRIMARY = "primary"      __PROP_SCENARIO_TAGS = "tags"      __ENTITY_PROPS = (__PROP_CONFIG_ID, __PROP_DATE, __PROP_ENTITY_NAME)      __ACTION = "action"      _CORE_CHANGED_NAME = "core_changed"      _SCENARIO_SELECTOR_ERROR_VAR = "gui_core_sc_error"      _SCENARIO_SELECTOR_ID_VAR = "gui_core_sc_id"      _SCENARIO_VIZ_ERROR_VAR = "gui_core_sv_error"      _JOB_SELECTOR_ERROR_VAR = "gui_core_js_error"      _DATANODE_VIZ_ERROR_VAR = "gui_core_dv_error"      _DATANODE_VIZ_OWNER_ID_VAR = "gui_core_dv_owner_id"      _DATANODE_VIZ_HISTORY_ID_VAR = "gui_core_dv_history_id"      _DATANODE_VIZ_DATA_ID_VAR = "gui_core_dv_data_id"      _DATANODE_VIZ_DATA_CHART_ID_VAR = "gui_core_dv_data_chart_id"      _DATANODE_VIZ_DATA_NODE_PROP = "data_node"        def __init__(self, gui: Gui) -> None:          self.gui = gui          self.scenario_by_cycle: t.Optional[t.Dict[t.Optional[Cycle], t.List[Scenario]]] = None          self.data_nodes_by_owner: t.Optional[t.Dict[t.Optional[str], DataNode]] = None          self.scenario_configs: t.Optional[t.List[t.Tuple[str, str]]] = None          self.jobs_list: t.Optional[t.List[Job]] = None          self.client_jobs_by_submission: t.Dict[str, _SubmissionDetails] = dict()          # register to taipy core notification          reg_id, reg_queue = Notifier.register()          # locks          self.lock = Lock()          self.submissions_lock = Lock()          # super          super().__init__(reg_id, reg_queue)          self.start()        def process_event(self, event: Event):          if event.entity_type == EventEntityType.SCENARIO:              if event.operation == EventOperation.SUBMISSION:                  self.scenario_status_callback(event.attribute_name, True)                  return              self.scenario_refresh(                  event.entity_id if event.operation != EventOperation.DELETION and is_readable(event.entity_id) else None              )          elif event.entity_type == EventEntityType.SEQUENCE and event.entity_id:              sequence = None              try:                  sequence = (                      core_get(event.entity_id)                      if event.operation != EventOperation.DELETION and is_readable(event.entity_id)                      else None                  )                  if sequence and hasattr(sequence, "parent_ids") and sequence.parent_ids:                      self.gui._broadcast(                          _GuiCoreContext._CORE_CHANGED_NAME, {"scenario": [x for x in sequence.parent_ids]}                      )              except Exception as e:                  _warn(f"Access to sequence {event.entity_id} failed", e)          elif event.entity_type == EventEntityType.JOB:              with self.lock:                  self.jobs_list = None              self.scenario_status_callback(event.entity_id)          elif event.entity_type == EventEntityType.DATA_NODE:              with self.lock:                  self.data_nodes_by_owner = None              self.gui._broadcast(                  _GuiCoreContext._CORE_CHANGED_NAME,                  {"datanode": event.entity_id if event.operation != EventOperation.DELETION else True},              )        def scenario_refresh(self, scenario_id: t.Optional[str]):          with self.lock:              self.scenario_by_cycle = None              self.data_nodes_by_owner = None          self.gui._broadcast(              _GuiCoreContext._CORE_CHANGED_NAME,              {"scenario": scenario_id or True},          )        def scenario_status_callback(self, job_id: str, is_submission: t.Optional[bool] = False):          if not job_id or not (is_submission or is_readable(job_id)):              return          try:              if is_submission:                  sub_id = job_id                  job = None              else:                  job = core_get(job_id)                  if not job:                      return                  sub_id = job.submit_id              sub_details = self.client_jobs_by_submission.get(sub_id)              if not sub_details:                  return                if not sub_details.client_id or not sub_details.entity_id or not sub_details.jobs:                  return                entity = core_get(sub_details.entity_id)              if not entity:                  return                new_status = self._get_submittable_status(sub_details.jobs)              if sub_details.status != new_status:                  # callback                  self.gui._call_user_callback(                      sub_details.client_id,                      sub_details.callback,                      [entity, {"submission_status": new_status.name, "job": job}],                      sub_details.module_context,                  )              with self.submissions_lock:                  if new_status in (                      _SubmissionStatus.COMPLETED,                      _SubmissionStatus.FAILED,                      _SubmissionStatus.CANCELED,                  ):                      self.client_jobs_by_submission.pop(sub_id, None)                  else:                      self.client_jobs_by_submission[sub_id] = sub_details.set_status(new_status)            except Exception as e:              _warn(f"Job ({job_id}) is not available", e)            finally:              self.gui._broadcast(_GuiCoreContext._CORE_CHANGED_NAME, {"jobs": True})        def scenario_adapter(self, scenario_or_cycle):          try:              if (                  hasattr(scenario_or_cycle, "id")                  and is_readable(scenario_or_cycle.id)                  and core_get(scenario_or_cycle.id) is not None              ):                  if self.scenario_by_cycle and isinstance(scenario_or_cycle, Cycle):                      return (                          scenario_or_cycle.id,                          scenario_or_cycle.get_simple_label(),                          self.scenario_by_cycle.get(scenario_or_cycle),                          _EntityType.CYCLE.value,                          False,                      )                  elif isinstance(scenario_or_cycle, Scenario):                      return (                          scenario_or_cycle.id,                          scenario_or_cycle.get_simple_label(),                          None,                          _EntityType.SCENARIO.value,                          scenario_or_cycle.is_primary,                      )          except Exception as e:              _warn(                  f"Access to {type(scenario_or_cycle)} "                  + f"({scenario_or_cycle.id if hasattr(scenario_or_cycle, 'id') else 'No_id'})"                  + " failed",                  e,              )          return None        def get_scenarios(self):          cycles_scenarios = []          with self.lock:              if self.scenario_by_cycle is None:                  self.scenario_by_cycle = get_cycles_scenarios()              for cycle, scenarios in self.scenario_by_cycle.items():                  if cycle is None:                      cycles_scenarios.extend(scenarios)                  else:                      cycles_scenarios.append(cycle)          return cycles_scenarios        def select_scenario(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) == 0:              return          state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ID_VAR, args[0])        def get_scenario_by_id(self, id: str) -> t.Optional[Scenario]:          if not id or not is_readable(id):              return None          try:              return core_get(id)          except Exception:              return None        def get_scenario_configs(self):          with self.lock:              if self.scenario_configs is None:                  configs = Config.scenarios                  if isinstance(configs, dict):                      self.scenario_configs = [(id, f"{c.id}") for id, c in configs.items() if id != "default"]              return self.scenario_configs        def crud_scenario(self, state: State, id: str, payload: t.Dict[str, str]):  # noqa: C901          args = payload.get("args")          if (              args is None              or not isinstance(args, list)              or len(args) < 3              or not isinstance(args[0], bool)              or not isinstance(args[1], bool)              or not isinstance(args[2], dict)          ):              return          update = args[0]          delete = args[1]          data = args[2]          scenario = None            name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)          if update:              scenario_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)              if delete:                  if not is_deletable(scenario_id):                      state.assign(                          _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Scenario. {scenario_id} is not deletable."                      )                      return                  try:                      core_delete(scenario_id)                  except Exception as e:                      state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error deleting Scenario. {e}")              else:                  if not self.__check_readable_editable(                      state, scenario_id, "Scenario", _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR                  ):                      return                  scenario = core_get(scenario_id)          else:              config_id = data.get(_GuiCoreContext.__PROP_CONFIG_ID)              scenario_config = Config.scenarios.get(config_id)              if scenario_config is None:                  state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Invalid configuration id ({config_id})")                  return              date_str = data.get(_GuiCoreContext.__PROP_DATE)              try:                  date = parser.parse(date_str) if isinstance(date_str, str) else None              except Exception as e:                  state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Invalid date ({date_str}).{e}")                  return              scenario_id = None              try:                  gui: Gui = state._gui                  on_creation = args[3] if len(args) > 3 and isinstance(args[3], str) else None                  on_creation_function = gui._get_user_function(on_creation) if on_creation else None                  if callable(on_creation_function):                      try:                          res = gui._call_function_with_state(                              on_creation_function,                              [                                  id,                                  {                                      "action": on_creation,                                      "config": scenario_config,                                      "date": date,                                      "label": name,                                      "properties": {                                          v.get("key"): v.get("value") for v in data.get("properties", dict())                                      },                                  },                              ],                          )                          if isinstance(res, Scenario):                              # everything's fine                              scenario_id = res.id                              state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, "")                              return                          if res:                              # do not create                              state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"{res}")                              return                      except Exception as e:  # pragma: no cover                          if not gui._call_on_exception(on_creation, e):                              _warn(f"on_creation(): Exception raised in '{on_creation}()'", e)                          state.assign(                              _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR,                              f"Error creating Scenario with '{on_creation}()'. {e}",                          )                          return                  elif on_creation is not None:                      _warn(f"on_creation(): '{on_creation}' is not a function.")                  scenario = create_scenario(scenario_config, date, name)                  scenario_id = scenario.id              except Exception as e:                  state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error creating Scenario. {e}")              finally:                  self.scenario_refresh(scenario_id)          if scenario:              if not is_editable(scenario):                  state.assign(                      _GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Scenario {scenario_id or name} is not editable."                  )                  return              with scenario as sc:                  sc.properties[_GuiCoreContext.__PROP_ENTITY_NAME] = name                  if props := data.get("properties"):                      try:                          new_keys = [prop.get("key") for prop in props]                          for key in t.cast(dict, sc.properties).keys():                              if key and key not in _GuiCoreContext.__ENTITY_PROPS and key not in new_keys:                                  t.cast(dict, sc.properties).pop(key, None)                          for prop in props:                              key = prop.get("key")                              if key and key not in _GuiCoreContext.__ENTITY_PROPS:                                  sc._properties[key] = prop.get("value")                          state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, "")                      except Exception as e:                          state.assign(_GuiCoreContext._SCENARIO_SELECTOR_ERROR_VAR, f"Error creating Scenario. {e}")        def edit_entity(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          if not self.__check_readable_editable(              state, entity_id, data.get("type", "Scenario"), _GuiCoreContext._SCENARIO_VIZ_ERROR_VAR          ):              return          entity: t.Union[Scenario, Sequence] = core_get(entity_id)          if entity:              try:                  if isinstance(entity, Scenario):                      primary = data.get(_GuiCoreContext.__PROP_SCENARIO_PRIMARY)                      if primary is True:                          if not is_promotable(entity):                              state.assign(                                  _GuiCoreContext._SCENARIO_VIZ_ERROR_VAR, f"Scenario {entity_id} is not promotable."                              )                              return                          set_primary(entity)                  self.__edit_properties(entity, data)                  state.assign(_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR, "")              except Exception as e:                  state.assign(_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR, f"Error updating Scenario. {e}")        def submit_entity(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          if not is_submittable(entity_id):              state.assign(                  _GuiCoreContext._SCENARIO_VIZ_ERROR_VAR,                  f"{data.get('type', 'Scenario')} {entity_id} is not submittable.",              )              return          entity = core_get(entity_id)          if entity:              try:                  jobs = core_submit(entity)                  if submission_cb := data.get("on_submission_change"):                      submission_fn = self.gui._get_user_function(submission_cb)                      if callable(submission_fn):                          job_ids = [j.id for j in (jobs if isinstance(jobs, list) else [jobs])]                          client_id = self.gui._get_client_id()                          module_context = self.gui._get_locals_context()                          sub_id = jobs[0].submit_id if isinstance(jobs, list) else jobs.submit_id                          with self.submissions_lock:                              self.client_jobs_by_submission[sub_id] = _SubmissionDetails(                                  client_id,                                  module_context,                                  submission_fn,                                  entity_id,                                  _SubmissionStatus.SUBMITTED,                                  job_ids,                              )                      else:                          _warn(f"on_submission_change(): '{submission_cb}' is not a valid function.")                  self.scenario_status_callback(jobs[0].id if len(jobs) else "" if isinstance(jobs, list) else jobs.id)                  state.assign(_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR, "")              except Exception as e:                  state.assign(_GuiCoreContext._SCENARIO_VIZ_ERROR_VAR, f"Error submitting entity. {e}")        def _get_submittable_status(self, jobs_ids: t.List[str]) -> _SubmissionStatus:          abandoned = False          canceled = False          blocked = False          waiting = False          running = False          completed = False          for id in jobs_ids:              job = core_get(id)              if not job:                  continue              if job.is_failed():                  return _SubmissionStatus.FAILED              if job.is_canceled():                  canceled = True              if job.is_blocked():                  blocked = True                  continue              if job.is_pending() or job.is_submitted():                  waiting = True                  continue              if job.is_running():                  running = True                  continue              if job.is_completed() or job.is_skipped():                  completed = True                  continue              if job.is_abandoned():                  abandoned = True          if canceled:              return _SubmissionStatus.CANCELED          if abandoned:              return _SubmissionStatus.UNDEFINED          if running:              return _SubmissionStatus.RUNNING          if waiting:              return _SubmissionStatus.WAITING          if blocked:              return _SubmissionStatus.BLOCKED          if completed:              return _SubmissionStatus.COMPLETED          return _SubmissionStatus.UNDEFINED        def __do_datanodes_tree(self):          if self.data_nodes_by_owner is None:              self.data_nodes_by_owner = defaultdict(list)              for dn in get_data_nodes():                  self.data_nodes_by_owner[dn.owner_id].append(dn)        def get_datanodes_tree(self):          with self.lock:              self.__do_datanodes_tree()          return self.data_nodes_by_owner.get(None, []) + self.get_scenarios()        def data_node_adapter(self, data):          try:              if hasattr(data, "id") and is_readable(data.id) and core_get(data.id) is not None:                  if isinstance(data, DataNode):                      return (data.id, data.get_simple_label(), None, _EntityType.DATANODE.value, False)                  else:                      with self.lock:                          self.__do_datanodes_tree()                          if self.data_nodes_by_owner:                              if isinstance(data, Cycle):                                  return (                                      data.id,                                      data.get_simple_label(),                                      self.data_nodes_by_owner[data.id] + self.scenario_by_cycle.get(data, []),                                      _EntityType.CYCLE.value,                                      False,                                  )                              elif isinstance(data, Scenario):                                  return (                                      data.id,                                      data.get_simple_label(),                                      self.data_nodes_by_owner[data.id] + list(data.sequences.values()),                                      _EntityType.SCENARIO.value,                                      data.is_primary,                                  )                              elif isinstance(data, Sequence):                                  if datanodes := self.data_nodes_by_owner.get(data.id):                                      return (                                          data.id,                                          data.get_simple_label(),                                          datanodes,                                          _EntityType.SEQUENCE.value,                                          False,                                      )          except Exception as e:              _warn(                  f"Access to {type(data)} ({data.id if hasattr(data, 'id') else 'No_id'}) failed",                  e,              )            return None        def get_jobs_list(self):          with self.lock:              if self.jobs_list is None:                  self.jobs_list = get_jobs()              return self.jobs_list        def job_adapter(self, job):          try:              if hasattr(job, "id") and is_readable(job.id) and core_get(job.id) is not None:                  if isinstance(job, Job):                      entity = core_get(job.owner_id)                      return (                          job.id,                          job.get_simple_label(),                          [],                          entity.get_simple_label() if entity else "",                          entity.id if entity else "",                          job.submit_id,                          job.creation_date,                          job.status.value,                          is_deletable(job),                          is_readable(job),                          is_editable(job),                      )          except Exception as e:              _warn(f"Access to job ({job.id if hasattr(job, 'id') else 'No_id'}) failed", e)          return None        def act_on_jobs(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          job_ids = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          job_action = data.get(_GuiCoreContext.__ACTION)          if job_action and isinstance(job_ids, list):              errs = []              if job_action == "delete":                  for job_id in job_ids:                      if not is_readable(job_id):                          errs.append(f"Job {job_id} is not readable.")                          continue                      if not is_deletable(job_id):                          errs.append(f"Job {job_id} is not deletable.")                          continue                      try:                          delete_job(core_get(job_id))                      except Exception as e:                          errs.append(f"Error deleting job. {e}")              elif job_action == "cancel":                  for job_id in job_ids:                      if not is_readable(job_id):                          errs.append(f"Job {job_id} is not readable.")                          continue                      if not is_editable(job_id):                          errs.append(f"Job {job_id} is not cancelable.")                          continue                      try:                          cancel_job(job_id)                      except Exception as e:                          errs.append(f"Error canceling job. {e}")              state.assign(_GuiCoreContext._JOB_SELECTOR_ERROR_VAR, "<br/>".join(errs) if errs else "")        def edit_data_node(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          if not self.__check_readable_editable(state, entity_id, "DataNode", _GuiCoreContext._DATANODE_VIZ_ERROR_VAR):              return          entity: DataNode = core_get(entity_id)          if isinstance(entity, DataNode):              try:                  self.__edit_properties(entity, data)                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, "")              except Exception as e:                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, f"Error updating Datanode. {e}")        def lock_datanode_for_edit(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          if not self.__check_readable_editable(state, entity_id, "Datanode", _GuiCoreContext._DATANODE_VIZ_ERROR_VAR):              return          lock = data.get("lock", True)          entity: DataNode = core_get(entity_id)          if isinstance(entity, DataNode):              try:                  if lock:                      entity.lock_edit(self.gui._get_client_id())                  else:                      entity.unlock_edit(self.gui._get_client_id())                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, "")              except Exception as e:                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, f"Error locking Datanode. {e}")        def __edit_properties(self, entity: t.Union[Scenario, Sequence, DataNode], data: t.Dict[str, str]):          with entity as ent:              if isinstance(ent, Scenario):                  tags = data.get(_GuiCoreContext.__PROP_SCENARIO_TAGS)                  if isinstance(tags, (list, tuple)):                      ent.tags = {t for t in tags}              name = data.get(_GuiCoreContext.__PROP_ENTITY_NAME)              if isinstance(name, str):                  ent.properties[_GuiCoreContext.__PROP_ENTITY_NAME] = name              props = data.get("properties")              if isinstance(props, (list, tuple)):                  for prop in props:                      key = prop.get("key")                      if key and key not in _GuiCoreContext.__ENTITY_PROPS:                          ent.properties[key] = prop.get("value")              deleted_props = data.get("deleted_properties")              if isinstance(deleted_props, (list, tuple)):                  for prop in deleted_props:                      key = prop.get("key")                      if key and key not in _GuiCoreContext.__ENTITY_PROPS:                          ent.properties.pop(key, None)        def get_scenarios_for_owner(self, owner_id: str):          cycles_scenarios: t.List[t.Union[Scenario, Cycle]] = []          with self.lock:              if self.scenario_by_cycle is None:                  self.scenario_by_cycle = get_cycles_scenarios()              if owner_id:                  if owner_id == "GLOBAL":                      for cycle, scenarios in self.scenario_by_cycle.items():                          if cycle is None:                              cycles_scenarios.extend(scenarios)                          else:                              cycles_scenarios.append(cycle)                  elif is_readable(owner_id):                      entity = core_get(owner_id)                      if entity and (scenarios := self.scenario_by_cycle.get(entity)):                          cycles_scenarios.extend(scenarios)                      elif isinstance(entity, Scenario):                          cycles_scenarios.append(entity)          return cycles_scenarios        def get_data_node_history(self, datanode: DataNode, id: str):          if (              id              and isinstance(datanode, DataNode)              and id == datanode.id              and (dn := core_get(id))              and isinstance(dn, DataNode)          ):              res = []              for e in dn.edits:                  job_id = e.get("job_id")                  job: Job = None                  if job_id:                      if not is_readable(job_id):                          job_id += " not readable"                      else:                          job = core_get(job_id)                  res.append(                      (                          e.get("timestamp"),                          job_id if job_id else e.get("writer_identifier", ""),                          f"Execution of task {job.task.get_simple_label()}."                          if job and job.task                          else e.get("comment", ""),                      )                  )              return list(reversed(sorted(res, key=lambda r: r[0])))          return _DoNotUpdate()        def get_data_node_data(self, datanode: DataNode, id: str):          if (              id              and isinstance(datanode, DataNode)              and id == datanode.id              and (dn := core_get(id))              and isinstance(dn, DataNode)          ):              if dn._last_edit_date:                  if isinstance(dn, _AbstractTabularDataNode):                      return (None, None, True, None)                  try:                      value = dn.read()                      if isinstance(value, (pd.DataFrame, pd.Series)):                          return (None, None, True, None)                      return (                          value,                          "date"                          if "date" in type(value).__name__                          else type(value).__name__                          if isinstance(value, Number)                          else None,                          None,                          None,                      )                  except Exception as e:                      return (None, None, None, f"read data_node: {e}")              return (None, None, None, f"Data unavailable for {dn.get_simple_label()}")          return _DoNotUpdate()        def __check_readable_editable(self, state: State, id: str, type: str, var: str):          if not is_readable(id):              state.assign(var, f"{type} {id} is not readable.")              return False          if not is_editable(id):              state.assign(var, f"{type} {id} is not editable.")              return False          return True        def update_data(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) < 1 or not isinstance(args[0], dict):              return          data = args[0]          entity_id = data.get(_GuiCoreContext.__PROP_ENTITY_ID)          if not self.__check_readable_editable(state, entity_id, "DataNode", _GuiCoreContext._DATANODE_VIZ_ERROR_VAR):              return          entity: DataNode = core_get(entity_id)          if isinstance(entity, DataNode):              try:                  entity.write(                      parser.parse(data.get("value"))                      if data.get("type") == "date"                      else int(data.get("value"))                      if data.get("type") == "int"                      else float(data.get("value"))                      if data.get("type") == "float"                      else data.get("value"),                      comment=data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT),                  )                  entity.unlock_edit(self.gui._get_client_id())                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, "")              except Exception as e:                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, f"Error updating Datanode value. {e}")              state.assign(_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR, entity_id)  # this will update the data value        def tabular_data_edit(self, state: State, var_name: str, payload: dict):          user_data = payload.get("user_data", {})          dn_id = user_data.get("dn_id")          if not self.__check_readable_editable(state, dn_id, "DataNode", _GuiCoreContext._DATANODE_VIZ_ERROR_VAR):              return          datanode = core_get(dn_id) if dn_id else None          if isinstance(datanode, DataNode):              try:                  idx = payload.get("index")                  col = payload.get("col")                  tz = payload.get("tz")                  val = (                      parser.parse(str(payload.get("value"))).astimezone(zoneinfo.ZoneInfo(tz)).replace(tzinfo=None)                      if tz is not None                      else payload.get("value")                  )                  # user_value = payload.get("user_value")                  data = self.__read_tabular_data(datanode)                  if hasattr(data, "at"):                      data.at[idx, col] = val                      datanode.write(data, comment=user_data.get(_GuiCoreContext.__PROP_ENTITY_COMMENT))                      state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, "")                  else:                      state.assign(                          _GuiCoreContext._DATANODE_VIZ_ERROR_VAR,                          "Error updating Datanode tabular value: type does not support at[] indexer.",                      )              except Exception as e:                  state.assign(_GuiCoreContext._DATANODE_VIZ_ERROR_VAR, f"Error updating Datanode tabular value. {e}")          setattr(state, _GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR, dn_id)        def __read_tabular_data(self, datanode: DataNode):          return datanode.read()        def get_data_node_tabular_data(self, datanode: DataNode, id: str):          if (              id              and isinstance(datanode, DataNode)              and id == datanode.id              and is_readable(id)              and (dn := core_get(id))              and isinstance(dn, DataNode)              and dn.is_ready_for_reading          ):              try:                  return self.__read_tabular_data(dn)              except Exception:                  return None          return None        def get_data_node_tabular_columns(self, datanode: DataNode, id: str):          if (              id              and isinstance(datanode, DataNode)              and id == datanode.id              and is_readable(id)              and (dn := core_get(id))              and isinstance(dn, DataNode)              and dn.is_ready_for_reading          ):              try:                  return self.gui._tbl_cols(                      True, True, "{}", json.dumps({"data": "tabular_data"}), tabular_data=self.__read_tabular_data(dn)                  )              except Exception:                  return None          return None        def get_data_node_chart_config(self, datanode: DataNode, id: str):          if (              id              and isinstance(datanode, DataNode)              and id == datanode.id              and is_readable(id)              and (dn := core_get(id))              and isinstance(dn, DataNode)              and dn.is_ready_for_reading          ):              try:                  return self.gui._chart_conf(                      True, True, "{}", json.dumps({"data": "tabular_data"}), tabular_data=self.__read_tabular_data(dn)                  )              except Exception:                  return None          return None        def select_id(self, state: State, id: str, payload: t.Dict[str, str]):          args = payload.get("args")          if args is None or not isinstance(args, list) or len(args) == 0 and isinstance(args[0], dict):              return          data = args[0]          if owner_id := data.get("owner_id"):              state.assign(_GuiCoreContext._DATANODE_VIZ_OWNER_ID_VAR, owner_id)          elif history_id := data.get("history_id"):              state.assign(_GuiCoreContext._DATANODE_VIZ_HISTORY_ID_VAR, history_id)          elif data_id := data.get("data_id"):              state.assign(_GuiCoreContext._DATANODE_VIZ_DATA_ID_VAR, data_id)          elif chart_id := data.get("chart_id"):              state.assign(_GuiCoreContext._DATANODE_VIZ_DATA_CHART_ID_VAR, chart_id)


import typing as t  from enum import Enum    from taipy.core import Cycle, DataNode, Job, Scenario, Sequence, Task  from taipy.core import get as core_get  from taipy.core import is_deletable, is_editable, is_promotable, is_readable, is_submittable  from taipy.gui._warnings import _warn  from taipy.gui.gui import _DoNotUpdate  from taipy.gui.utils import _TaipyBase      # prevent gui from trying to push scenario instances to the front-end  class _GCDoNotUpdate(_DoNotUpdate):      def __repr__(self):          return self.get_label() if hasattr(self, "get_label") else super().__repr__()      Scenario.__bases__ += (_GCDoNotUpdate,)  Sequence.__bases__ += (_GCDoNotUpdate,)  DataNode.__bases__ += (_GCDoNotUpdate,)  Cycle.__bases__ += (_GCDoNotUpdate,)  Job.__bases__ += (_GCDoNotUpdate,)  Task.__bases__ += (_GCDoNotUpdate,)      class _EntityType(Enum):      CYCLE = 0      SCENARIO = 1      SEQUENCE = 2      DATANODE = 3      class _GuiCoreScenarioAdapter(_TaipyBase):      __INNER_PROPS = ["name"]        def get(self):          data = super().get()          if isinstance(data, Scenario):              try:                  scenario = core_get(data.id)                  if scenario:                      return [                          scenario.id,                          scenario.is_primary,                          scenario.config_id,                          scenario.creation_date.isoformat(),                          scenario.cycle.get_simple_label() if scenario.cycle else "",                          scenario.get_simple_label(),                          list(scenario.tags) if scenario.tags else [],                          [                              (k, v)                              for k, v in scenario.properties.items()                              if k not in _GuiCoreScenarioAdapter.__INNER_PROPS                          ]                          if scenario.properties                          else [],                          [                              (p.id, p.get_simple_label(), is_submittable(p), is_editable(p))                              for p in scenario.sequences.values()                          ]                          if hasattr(scenario, "sequences") and scenario.sequences                          else [],                          list(scenario.properties.get("authorized_tags", [])) if scenario.properties else [],                          is_deletable(scenario),                          is_promotable(scenario),                          is_submittable(scenario),                          is_readable(scenario),                          is_editable(scenario),                      ]              except Exception as e:                  _warn(f"Access to scenario ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)            return None        @staticmethod      def get_hash():          return _TaipyBase._HOLDER_PREFIX + "Sc"      class _GuiCoreScenarioDagAdapter(_TaipyBase):      @staticmethod      def get_entity_type(node: t.Any):          return DataNode.__name__ if isinstance(node.entity, DataNode) else node.type        def get(self):          data = super().get()          if isinstance(data, Scenario):              try:                  scenario = core_get(data.id)                  if scenario:                      dag = data._get_dag()                      nodes = dict()                      for id, node in dag.nodes.items():                          entityType = _GuiCoreScenarioDagAdapter.get_entity_type(node)                          cat = nodes.get(entityType)                          if cat is None:                              cat = dict()                              nodes[entityType] = cat                          cat[id] = {                              "name": node.entity.get_simple_label(),                              "type": node.entity.storage_type() if hasattr(node.entity, "storage_type") else None,                          }                      return [                          data.id,                          nodes,                          [                              (                                  _GuiCoreScenarioDagAdapter.get_entity_type(e.src),                                  e.src.entity.id,                                  _GuiCoreScenarioDagAdapter.get_entity_type(e.dest),                                  e.dest.entity.id,                              )                              for e in dag.edges                          ],                      ]              except Exception as e:                  _warn(f"Access to scenario ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)            return None        @staticmethod      def get_hash():          return _TaipyBase._HOLDER_PREFIX + "ScG"      class _GuiCoreDatanodeAdapter(_TaipyBase):      __INNER_PROPS = ["name"]        def get(self):          data = super().get()          if isinstance(data, DataNode):              try:                  datanode = core_get(data.id)                  if datanode:                      owner = core_get(datanode.owner_id) if datanode.owner_id else None                      return [                          datanode.id,                          datanode.storage_type() if hasattr(datanode, "storage_type") else "",                          datanode.config_id,                          f"{datanode.last_edit_date}" if datanode.last_edit_date else "",                          f"{datanode.expiration_date}" if datanode.last_edit_date else "",                          datanode.get_simple_label(),                          datanode.owner_id or "",                          owner.get_simple_label() if owner else "GLOBAL",                          _EntityType.CYCLE.value                          if isinstance(owner, Cycle)                          else _EntityType.SCENARIO.value                          if isinstance(owner, Scenario)                          else -1,                          [                              (k, f"{v}")                              for k, v in datanode._get_user_properties().items()                              if k not in _GuiCoreDatanodeAdapter.__INNER_PROPS                          ],                          datanode._edit_in_progress,                          datanode._editor_id,                          is_readable(datanode),                          is_editable(datanode),                      ]              except Exception as e:                  _warn(f"Access to datanode ({data.id if hasattr(data, 'id') else 'No_id'}) failed", e)            return None        @staticmethod      def get_hash():          return _TaipyBase._HOLDER_PREFIX + "Dn"


import json  import os      def _get_version():      with open(f"{os.path.dirname(os.path.abspath(__file__))}{os.sep}version.json") as version_file:          version = json.load(version_file)          version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'          if vext := version.get("ext"):              version_string = f"{version_string}.{vext}"      return version_string


from .rest import Rest


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  from taipy.config import Config    from .app import create_app as _create_app      class Rest:      """      Runnable Rest application serving REST APIs on top of Taipy Core functionalities.      """        def __init__(self):          """          Initialize a REST API server.            A Flask application is instantiated and configured using three parameters from the global          config.              - Config.global_config.testing (bool): Run the application on testing mode.              - Config.global_config.env (Optional[str]): The application environment.              - Config.global_config.secret_key (Optional[str]): Application server secret key.            However, editing these parameters is only recommended for advanced users. Indeed, the default behavior of the          REST server without any required configuration satisfies all the standard and basic needs.          """          self._app = _create_app(              Config.global_config.testing or False, Config.global_config.env, Config.global_config.secret_key          )        def run(self, **kwargs):          """          Start a REST API server. This method is blocking.            Parameters:              **kwargs : Options to provide to the application server.          """          self._app.run(**kwargs)


"""# Taipy Rest    The Taipy Rest package exposes the Runnable `Rest^` service to provide REST APIs on top of Taipy Core. (more details  on Taipy Core functionalities in the [user manual](../../../manuals/core/)).    Once the `Rest^` service runs, users can call REST APIs to create, read, update, submit and remove Taipy entities  (including cycles, scenarios, sequences, tasks, jobs, and data nodes). It is handy when it comes to integrating a  Taipy application in a more complex IT ecosystem.    Please refer to [REST API](../../reference_rest/) page to get the exhaustive list of available APIs."""    from ._init import *  from .version import _get_version    __version__ = _get_version()


"""Extensions registry    All extensions here are used as singletons and  initialized in application factory  """    from .commons.apispec import APISpecExt    apispec = APISpecExt()


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.  import json  import os    from setuptools import find_namespace_packages, find_packages, setup    with open("README.md") as readme_file:      readme = readme_file.read()    with open(f"src{os.sep}taipy{os.sep}rest{os.sep}version.json") as version_file:      version = json.load(version_file)      version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'      if vext := version.get("ext"):          version_string = f"{version_string}.{vext}"    setup(      author="Avaiga",      name="taipy-rest",      keywords="taipy-rest",      python_requires=">=3.8",      version=version_string,      author_email="dev@taipy.io",      packages=find_namespace_packages(where="src") + find_packages(include=["taipy", "taipy.rest"]),      package_dir={"": "src"},      include_package_data=True,      long_description=readme,      long_description_content_type="text/markdown",      description="Library to expose taipy-core REST APIs.",      license="Apache License 2.0",      classifiers=[          "Intended Audience :: Developers",          "License :: OSI Approved :: Apache Software License",          "Natural Language :: English",          "Programming Language :: Python :: 3",          "Programming Language :: Python :: 3.8",          "Programming Language :: Python :: 3.9",          "Programming Language :: Python :: 3.10",          "Programming Language :: Python :: 3.11",      ],      install_requires=[          "flask>=3.0.0,<3.1",          "flask-restful>=0.3.9,<0.4",          "passlib>=1.7.4,<1.8",          "marshmallow>=3.20.1,<3.30",          "apispec[yaml]>=6.3,<7.0",          "apispec-webframeworks>=0.5.2,<0.6",          "taipy-core@git+https://git@github.com/Avaiga/taipy-core.git@develop",      ],  )


import os    from flask import Flask    from . import api  from .commons.encoder import _CustomEncoder  from .extensions import apispec      def create_app(testing=False, flask_env=None, secret_key=None):      """Application factory, used to create application"""      app = Flask(__name__)      app.config.update(          ENV=os.getenv("FLASK_ENV", flask_env),          TESTING=os.getenv("TESTING", testing),          SECRET_KEY=os.getenv("SECRET_KEY", secret_key),      )      app.url_map.strict_slashes = False      app.config["RESTFUL_JSON"] = {"cls": _CustomEncoder}        configure_apispec(app)      register_blueprints(app)      with app.app_context():          api.views.register_views()        return app      def configure_apispec(app):      """Configure APISpec for swagger support"""      apispec.init_app(app)        apispec.spec.components.schema(          "PaginatedResult",          {              "properties": {                  "total": {"type": "integer"},                  "pages": {"type": "integer"},                  "next": {"type": "string"},                  "prev": {"type": "string"},              }          },      )      def register_blueprints(app):      """Register all blueprints for application"""      app.register_blueprint(api.views.blueprint)


from taipy.core.cycle._cycle_converter import _CycleConverter  from taipy.core.data._data_converter import _DataNodeConverter  from taipy.core.scenario._scenario_converter import _ScenarioConverter  from taipy.core.sequence._sequence_converter import _SequenceConverter  from taipy.core.task._task_converter import _TaskConverter    entity_to_models = {      "scenario": _ScenarioConverter._entity_to_model,      "sequence": _SequenceConverter._entity_to_model,      "task": _TaskConverter._entity_to_model,      "data": _DataNodeConverter._entity_to_model,      "cycle": _CycleConverter._entity_to_model,  }      def _to_model(repository, entity, **kwargs):      return entity_to_models[repository](entity)


#  # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with  # the License. You may obtain a copy of the License at  #  #        http://www.apache.org/licenses/LICENSE-2.0  #  # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on  # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the  # specific language governing permissions and limitations under the License.


from apispec import APISpec  from apispec.exceptions import APISpecError  from apispec.ext.marshmallow import MarshmallowPlugin  from apispec_webframeworks.flask import FlaskPlugin  from flask import Blueprint, jsonify, render_template      class FlaskRestfulPlugin(FlaskPlugin):      """Small plugin override to handle flask-restful resources"""        @staticmethod      def _rule_for_view(view, app=None):          view_funcs = app.view_functions          endpoint = None            for ept, view_func in view_funcs.items():              if hasattr(view_func, "view_class"):                  view_func = view_func.view_class                if view_func == view:                  endpoint = ept            if not endpoint:              raise APISpecError("Could not find endpoint for view {0}".format(view))            # WARNING: Assume 1 rule per view function for now          rule = app.url_map._rules_by_endpoint[endpoint][0]          return rule      class APISpecExt:      """Very simple and small extension to use apispec with this API as a flask extension"""        def __init__(self, app=None, **kwargs):          self.spec = None            if app is not None:              self.init_app(app, **kwargs)        def init_app(self, app, **kwargs):          app.config.setdefault("APISPEC_TITLE", "Taipy Rest")          app.config.setdefault("APISPEC_VERSION", "1.0.0")          app.config.setdefault("OPENAPI_VERSION", "3.0.2")          app.config.setdefault("SWAGGER_JSON_URL", "/swagger.json")          app.config.setdefault("SWAGGER_UI_URL", "/swagger-ui")          app.config.setdefault("OPENAPI_YAML_URL", "/openapi.yaml")          app.config.setdefault("REDOC_UI_URL", "/redoc-ui")          app.config.setdefault("SWAGGER_URL_PREFIX", None)            self.spec = APISpec(              title=app.config["APISPEC_TITLE"],              version=app.config["APISPEC_VERSION"],              openapi_version=app.config["OPENAPI_VERSION"],              plugins=[MarshmallowPlugin(), FlaskRestfulPlugin()],              **kwargs          )            blueprint = Blueprint(              "swagger",              __name__,              template_folder="./templates",              url_prefix=app.config["SWAGGER_URL_PREFIX"],          )            blueprint.add_url_rule(app.config["SWAGGER_JSON_URL"], "swagger_json", self.swagger_json)          blueprint.add_url_rule(app.config["SWAGGER_UI_URL"], "swagger_ui", self.swagger_ui)          blueprint.add_url_rule(app.config["OPENAPI_YAML_URL"], "openapi_yaml", self.openapi_yaml)          blueprint.add_url_rule(app.config["REDOC_UI_URL"], "redoc_ui", self.redoc_ui)            app.register_blueprint(blueprint)        def swagger_json(self):          return jsonify(self.spec.to_dict())        def swagger_ui(self):          return render_template("swagger.j2")        def openapi_yaml(self):          # Manually inject ReDoc's Authentication legend, then remove it          self.spec.tag(              {                  "name": "authentication",                  "x-displayName": "Authentication",                  "description": "<SecurityDefinitions />",              }          )          redoc_spec = self.spec.to_yaml()          self.spec._tags.pop(0)          return redoc_spec        def redoc_ui(self):          return render_template("redoc.j2")


import json  from datetime import datetime  from enum import Enum  from typing import Any, Union    Json = Union[dict, list, str, int, float, bool, None]      class _CustomEncoder(json.JSONEncoder):      def default(self, o: Any) -> Json:          if isinstance(o, Enum):              result = o.value          elif isinstance(o, datetime):              result = {"__type__": "Datetime", "__value__": o.isoformat()}          else:              result = json.JSONEncoder.default(self, o)          return result


"""Simple helper to paginate query  """  from flask import request, url_for    DEFAULT_PAGE_SIZE = 50  DEFAULT_PAGE_NUMBER = 1      def extract_pagination(page=None, per_page=None, **request_args):      page = int(page) if page is not None else DEFAULT_PAGE_NUMBER      per_page = int(per_page) if per_page is not None else DEFAULT_PAGE_SIZE      return page, per_page, request_args      def paginate(query, schema):      page, per_page, other_request_args = extract_pagination(**request.args)      page_obj = query.paginate(page=page, per_page=per_page)      next_ = url_for(          request.endpoint,          page=page_obj.next_num if page_obj.has_next else page_obj.page,          per_page=per_page,          **other_request_args,          **request.view_args      )      prev = url_for(          request.endpoint,          page=page_obj.prev_num if page_obj.has_prev else page_obj.page,          per_page=per_page,          **other_request_args,          **request.view_args      )        return {          "total": page_obj.total,          "pages": page_obj.pages,          "next": next_,          "prev": prev,          "results": schema.dump(page_obj.items),      }


from . import error_handler, views    __all__ = ["views", "error_handler"]


from flask import jsonify  from marshmallow import ValidationError    from taipy.core.exceptions.exceptions import (      NonExistingCycle,      NonExistingDataNode,      NonExistingDataNodeConfig,      NonExistingJob,      NonExistingScenario,      NonExistingScenarioConfig,      NonExistingSequence,      NonExistingSequenceConfig,      NonExistingTask,      NonExistingTaskConfig,  )    from .exceptions.exceptions import ConfigIdMissingException, ScenarioIdMissingException, SequenceNameMissingException  from .views import blueprint      def _create_404(e):      return {"message": e.message}, 404      @blueprint.errorhandler(ValidationError)  def handle_marshmallow_error(e):      """Return json error for marshmallow validation errors.        This will avoid having to try/catch ValidationErrors in all endpoints, returning      correct JSON response with associated HTTP 400 Status (https://tools.ietf.org/html/rfc7231#section-6.5.1)      """      return jsonify(e.messages), 400      @blueprint.errorhandler(ConfigIdMissingException)  def handle_config_id_missing_exception(e):      return jsonify({"message": e.message}), 400      @blueprint.errorhandler(ScenarioIdMissingException)  def handle_scenario_id_missing_exception(e):      return jsonify({"message": e.message}), 400      @blueprint.errorhandler(SequenceNameMissingException)  def handle_sequence_name_missing_exception(e):      return jsonify({"message": e.message}), 400      @blueprint.errorhandler(NonExistingDataNode)  def handle_data_node_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingDataNodeConfig)  def handle_data_node_config_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingCycle)  def handle_cycle_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingJob)  def handle_job_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingSequence)  def handle_sequence_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingSequenceConfig)  def handle_sequence_config_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingScenario)  def handle_scenario_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingScenarioConfig)  def handle_scenario_config_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingTask)  def handle_task_not_found(e):      return _create_404(e)      @blueprint.errorhandler(NonExistingTaskConfig)  def handle_task_config_not_found(e):      return _create_404(e)


from flask import Blueprint, current_app  from flask_restful import Api    from taipy.core.common._utils import _load_fct  from taipy.logger._taipy_logger import _TaipyLogger    from ..extensions import apispec  from .middlewares._middleware import _using_enterprise  from .resources import (      CycleList,      CycleResource,      DataNodeList,      DataNodeReader,      DataNodeResource,      DataNodeWriter,      JobExecutor,      JobList,      JobResource,      ScenarioExecutor,      ScenarioList,      ScenarioResource,      SequenceExecutor,      SequenceList,      SequenceResource,      TaskExecutor,      TaskList,      TaskResource,  )  from .schemas import CycleSchema, DataNodeSchema, JobSchema, ScenarioSchema, SequenceSchema, TaskSchema    _logger = _TaipyLogger._get_logger()      blueprint = Blueprint("api", __name__, url_prefix="/api/v1")    api = Api(blueprint)    api.add_resource(      DataNodeResource,      "/datanodes/<string:datanode_id>/",      endpoint="datanode_by_id",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      DataNodeReader,      "/datanodes/<string:datanode_id>/read/",      endpoint="datanode_reader",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      DataNodeWriter,      "/datanodes/<string:datanode_id>/write/",      endpoint="datanode_writer",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      DataNodeList,      "/datanodes/",      endpoint="datanodes",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      TaskResource,      "/tasks/<string:task_id>/",      endpoint="task_by_id",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(TaskList, "/tasks/", endpoint="tasks", resource_class_kwargs={"logger": _logger})  api.add_resource(      TaskExecutor,      "/tasks/submit/<string:task_id>/",      endpoint="task_submit",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      SequenceResource,      "/sequences/<string:sequence_id>/",      endpoint="sequence_by_id",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(      SequenceList,      "/sequences/",      endpoint="sequences",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(      SequenceExecutor,      "/sequences/submit/<string:sequence_id>/",      endpoint="sequence_submit",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      ScenarioResource,      "/scenarios/<string:scenario_id>/",      endpoint="scenario_by_id",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(      ScenarioList,      "/scenarios/",      endpoint="scenarios",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(      ScenarioExecutor,      "/scenarios/submit/<string:scenario_id>/",      endpoint="scenario_submit",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      CycleResource,      "/cycles/<string:cycle_id>/",      endpoint="cycle_by_id",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(      CycleList,      "/cycles/",      endpoint="cycles",      resource_class_kwargs={"logger": _logger},  )    api.add_resource(      JobResource,      "/jobs/<string:job_id>/",      endpoint="job_by_id",      resource_class_kwargs={"logger": _logger},  )  api.add_resource(JobList, "/jobs/", endpoint="jobs", resource_class_kwargs={"logger": _logger})  api.add_resource(      JobExecutor,      "/jobs/cancel/<string:job_id>/",      endpoint="job_cancel",      resource_class_kwargs={"logger": _logger},  )      def load_enterprise_resources(api: Api):      """      Load enterprise resources.      """        if not _using_enterprise():          return      load_resources = _load_fct("taipy.enterprise.rest.api.views", "_load_resources")      load_resources(api)      load_enterprise_resources(api)      def register_views():      apispec.spec.components.schema("DataNodeSchema", schema=DataNodeSchema)      apispec.spec.path(view=DataNodeResource, app=current_app)      apispec.spec.path(view=DataNodeList, app=current_app)      apispec.spec.path(view=DataNodeReader, app=current_app)      apispec.spec.path(view=DataNodeWriter, app=current_app)        apispec.spec.components.schema("TaskSchema", schema=TaskSchema)      apispec.spec.path(view=TaskResource, app=current_app)      apispec.spec.path(view=TaskList, app=current_app)      apispec.spec.path(view=TaskExecutor, app=current_app)        apispec.spec.components.schema("SequenceSchema", schema=SequenceSchema)      apispec.spec.path(view=SequenceResource, app=current_app)      apispec.spec.path(view=SequenceList, app=current_app)      apispec.spec.path(view=SequenceExecutor, app=current_app)        apispec.spec.components.schema("ScenarioSchema", schema=ScenarioSchema)      apispec.spec.path(view=ScenarioResource, app=current_app)      apispec.spec.path(view=ScenarioList, app=current_app)      apispec.spec.path(view=ScenarioExecutor, app=current_app)        apispec.spec.components.schema("CycleSchema", schema=CycleSchema)      apispec.spec.path(view=CycleResource, app=current_app)      apispec.spec.path(view=CycleList, app=current_app)        apispec.spec.components.schema("JobSchema", schema=JobSchema)      apispec.spec.path(view=JobResource, app=current_app)      apispec.spec.path(view=JobList, app=current_app)      apispec.spec.path(view=JobExecutor, app=current_app)        apispec.spec.components.schema(          "Any",          {              "description": "Any value",              "nullable": True,          },      )        if _using_enterprise():          _register_views = _load_fct("taipy.enterprise.rest.api.views", "_register_views")          _register_views(apispec)