import os  from collections import defaultdict  from datetime import datetime, timedelta  from os.path import isfile  from typing import Any, Dict, List, Optional, Set, Tuple, Union    import modin.pandas as modin_pd  import numpy as np  import pandas as pd  from openpyxl import load_workbook    from taipy.config.common.scope import Scope    from .._backup._backup import _replace_in_backup_file  from .._entity._reload import _self_reload  from .._version._version_manager_factory import _VersionManagerFactory  from ..exceptions.exceptions import ExposedTypeLengthMismatch, NonExistingExcelSheet, SheetNameLengthMismatch  from ..job.job_id import JobId  from ._abstract_file import _AbstractFileDataNode  from ._abstract_tabular import _AbstractTabularDataNode  from .data_node import DataNode  from .data_node_id import DataNodeId, Edit      class ExcelDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):      """Data Node stored as an Excel file.        The Excel file format is _xlsx_.        Attributes:          config_id (str): Identifier of this data node configuration. It must be a valid Python              identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              `None`.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          path (str): The path to the Excel file.          properties (dict[str, Any]): A dictionary of additional properties. The _properties_              must have a _"default_path"_ or _"path"_ entry with the path of the Excel file:                - _"default_path"_ `(str)`: The path of the Excel file.\n              - _"has_header"_ `(bool)`: If True, indicates that the Excel file has a header.\n              - _"sheet_name"_ `(Union[List[str], str])`: The list of sheet names to be used. This                  can be a unique name.\n              - _"exposed_type"_: The exposed type of the data read from Excel file. The default value is `pandas`.\n      """        __STORAGE_TYPE = "excel"      __EXPOSED_TYPE_PROPERTY = "exposed_type"      __EXPOSED_TYPE_NUMPY = "numpy"      __EXPOSED_TYPE_PANDAS = "pandas"      __EXPOSED_TYPE_MODIN = "modin"      __VALID_STRING_EXPOSED_TYPES = [__EXPOSED_TYPE_PANDAS, __EXPOSED_TYPE_MODIN, __EXPOSED_TYPE_NUMPY]      __PATH_KEY = "path"      __DEFAULT_DATA_KEY = "default_data"      __DEFAULT_PATH_KEY = "default_path"      __HAS_HEADER_PROPERTY = "has_header"      __SHEET_NAME_PROPERTY = "sheet_name"      _REQUIRED_PROPERTIES: List[str] = []        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: List[Edit] = None,          version: str = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties: Dict = None,      ):          if properties is None:              properties = {}            default_value = properties.pop(self.__DEFAULT_DATA_KEY, None)          self._path = properties.get(self.__PATH_KEY, properties.get(self.__DEFAULT_PATH_KEY))          properties[self.__PATH_KEY] = self._path            if self.__SHEET_NAME_PROPERTY not in properties.keys():              properties[self.__SHEET_NAME_PROPERTY] = None          if self.__HAS_HEADER_PROPERTY not in properties.keys():              properties[self.__HAS_HEADER_PROPERTY] = True          if self.__EXPOSED_TYPE_PROPERTY not in properties.keys():              properties[self.__EXPOSED_TYPE_PROPERTY] = self.__EXPOSED_TYPE_PANDAS          self._check_exposed_type(properties[self.__EXPOSED_TYPE_PROPERTY], self.__VALID_STRING_EXPOSED_TYPES)            super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              **properties,          )          if not self._path:              self._path = self._build_path(self.storage_type())              properties[self.__PATH_KEY] = self._path            if default_value is not None and not os.path.exists(self._path):              self._write(default_value)              self._last_edit_date = datetime.now()              self._edits.append(                  Edit(                      {                          "timestamp": self._last_edit_date,                          "writer_identifier": "TAIPY",                          "comments": "Default data written.",                      }                  )              )            if not self._last_edit_date and isfile(self._path):              self._last_edit_date = datetime.now()            self._TAIPY_PROPERTIES.update(              {                  self.__EXPOSED_TYPE_PROPERTY,                  self.__PATH_KEY,                  self.__DEFAULT_PATH_KEY,                  self.__DEFAULT_DATA_KEY,                  self.__HAS_HEADER_PROPERTY,                  self.__SHEET_NAME_PROPERTY,              }          )        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def path(self):          return self._path        @path.setter      def path(self, value):          tmp_old_path = self._path          self._path = value          self.properties[self.__PATH_KEY] = value          _replace_in_backup_file(old_file_path=tmp_old_path, new_file_path=self._path)        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        @staticmethod      def _check_exposed_type(exposed_type, valid_string_exposed_types):          if isinstance(exposed_type, str):              _AbstractTabularDataNode._check_exposed_type(exposed_type, valid_string_exposed_types)          elif isinstance(exposed_type, list):              for t in exposed_type:                  _AbstractTabularDataNode._check_exposed_type(t, valid_string_exposed_types)          elif isinstance(exposed_type, dict):              for t in exposed_type.values():                  _AbstractTabularDataNode._check_exposed_type(t, valid_string_exposed_types)        def _read(self):          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_PANDAS:              return self._read_as_pandas_dataframe()          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_MODIN:              return self._read_as_modin_dataframe()          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_NUMPY:              return self._read_as_numpy()          return self._read_as()        def __sheet_name_to_list(self, properties):          if properties[self.__SHEET_NAME_PROPERTY]:              sheet_names = properties[self.__SHEET_NAME_PROPERTY]          else:              excel_file = load_workbook(properties[self.__PATH_KEY])              sheet_names = excel_file.sheetnames              excel_file.close()          return sheet_names if isinstance(sheet_names, (List, Set, Tuple)) else [sheet_names]        def _read_as(self):          excel_file = load_workbook(self._path)          exposed_type = self.properties[self.__EXPOSED_TYPE_PROPERTY]          work_books = defaultdict()          sheet_names = excel_file.sheetnames          provided_sheet_names = self.__sheet_name_to_list(self.properties)            for sheet_name in provided_sheet_names:              if sheet_name not in sheet_names:                  raise NonExistingExcelSheet(sheet_name, self._path)            if isinstance(exposed_type, List):              if len(provided_sheet_names) != len(self.properties[self.__EXPOSED_TYPE_PROPERTY]):                  raise ExposedTypeLengthMismatch(                      f"Expected {len(provided_sheet_names)} exposed types, got "                      f"{len(self.properties[self.__EXPOSED_TYPE_PROPERTY])}"                  )            for i, sheet_name in enumerate(provided_sheet_names):              work_sheet = excel_file[sheet_name]              sheet_exposed_type = exposed_type                if not isinstance(sheet_exposed_type, str):                  if isinstance(exposed_type, dict):                      sheet_exposed_type = exposed_type.get(sheet_name, self.__EXPOSED_TYPE_PANDAS)                  elif isinstance(exposed_type, List):                      sheet_exposed_type = exposed_type[i]                    if isinstance(sheet_exposed_type, str):                      if sheet_exposed_type == self.__EXPOSED_TYPE_NUMPY:                          work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name).to_numpy()                      elif sheet_exposed_type == self.__EXPOSED_TYPE_PANDAS:                          work_books[sheet_name] = self._read_as_pandas_dataframe(sheet_name)                      continue                res = list()              for row in work_sheet.rows:                  res.append([col.value for col in row])              if self.properties[self.__HAS_HEADER_PROPERTY] and res:                  header = res.pop(0)                  for i, row in enumerate(res):                      res[i] = sheet_exposed_type(**dict([[h, r] for h, r in zip(header, row)]))              else:                  for i, row in enumerate(res):                      res[i] = sheet_exposed_type(*row)              work_books[sheet_name] = res            excel_file.close()            if len(provided_sheet_names) == 1:              return work_books[provided_sheet_names[0]]          return work_books        def _read_as_numpy(self):          sheets = self._read_as_pandas_dataframe()          if isinstance(sheets, dict):              return {sheet_name: df.to_numpy() for sheet_name, df in sheets.items()}          return sheets.to_numpy()        def _do_read_excel(self, engine, sheet_names, kwargs) -> pd.DataFrame:          df = pd.read_excel(              self._path,              sheet_name=sheet_names,              **kwargs,          )          # We are using pandas to load modin dataframes because of a modin issue          # https://github.com/modin-project/modin/issues/4924          if engine == "modin":              if isinstance(df, dict):  # Check if it s a multiple sheet Excel file                  for key, value in df.items():                      df[key] = modin_pd.DataFrame(value)                  return df              return modin_pd.DataFrame(df)          return df        def __get_sheet_names_and_header(self, sheet_names):          kwargs: Dict[str, Any] = {}          if sheet_names is None:              sheet_names = self.properties[self.__SHEET_NAME_PROPERTY]          if not self.properties[self.__HAS_HEADER_PROPERTY]:              kwargs["header"] = None          return sheet_names, kwargs        def _read_as_pandas_dataframe(self, sheet_names=None) -> Union[Dict[Union[int, str], pd.DataFrame], pd.DataFrame]:          sheet_names, kwargs = self.__get_sheet_names_and_header(sheet_names)          try:              return self._do_read_excel("pandas", sheet_names, kwargs)          except pd.errors.EmptyDataError:              return pd.DataFrame()        def _read_as_modin_dataframe(          self, sheet_names=None      ) -> Union[Dict[Union[int, str], modin_pd.DataFrame], modin_pd.DataFrame]:          sheet_names, kwargs = self.__get_sheet_names_and_header(sheet_names)          try:              if kwargs.get("header", None):                  return modin_pd.read_excel(                      self._path,                      sheet_name=sheet_names,                      **kwargs,                  )              else:                  return self._do_read_excel("modin", sheet_names, kwargs)          except pd.errors.EmptyDataError:              return modin_pd.DataFrame()        def __append_excel_with_single_sheet(self, append_excel_fct, *args, **kwargs):          sheet_name = self.properties.get(self.__SHEET_NAME_PROPERTY)            with pd.ExcelWriter(self._path, mode="a", engine="openpyxl", if_sheet_exists="overlay") as writer:              if sheet_name:                  if not isinstance(sheet_name, str):                      sheet_name = sheet_name[0]                  append_excel_fct(                      writer, *args, **kwargs, sheet_name=sheet_name, startrow=writer.sheets[sheet_name].max_row                  )              else:                  sheet_name = list(writer.sheets.keys())[0]                  append_excel_fct(writer, *args, **kwargs, startrow=writer.sheets[sheet_name].max_row)        def __append_excel_with_multiple_sheets(self, data: Any, columns: List[str] = None):          with pd.ExcelWriter(self._path, mode="a", engine="openpyxl", if_sheet_exists="overlay") as writer:              # Each key stands for a sheet name              for sheet_name in data.keys():                  if isinstance(data[sheet_name], np.ndarray):                      df = pd.DataFrame(data[sheet_name])                  else:                      df = data[sheet_name]                    if columns:                      data[sheet_name].columns = columns                    df.to_excel(                      writer, sheet_name=sheet_name, index=False, header=False, startrow=writer.sheets[sheet_name].max_row                  )        def _append(self, data: Any):          if isinstance(data, Dict) and all(              [isinstance(x, (pd.DataFrame, modin_pd.DataFrame, np.ndarray)) for x in data.values()]          ):              self.__append_excel_with_multiple_sheets(data)          elif isinstance(data, (pd.DataFrame, modin_pd.DataFrame)):              self.__append_excel_with_single_sheet(data.to_excel, index=False, header=False)          else:              self.__append_excel_with_single_sheet(pd.DataFrame(data).to_excel, index=False, header=False)        def __write_excel_with_single_sheet(self, write_excel_fct, *args, **kwargs):          sheet_name = self.properties.get(self.__SHEET_NAME_PROPERTY)          if sheet_name:              if not isinstance(sheet_name, str):                  if len(sheet_name) > 1:                      raise SheetNameLengthMismatch                  else:                      sheet_name = sheet_name[0]              write_excel_fct(*args, **kwargs, sheet_name=sheet_name)          else:              write_excel_fct(*args, **kwargs)        def __write_excel_with_multiple_sheets(self, data: Any, columns: List[str] = None):          with pd.ExcelWriter(self._path) as writer:              # Each key stands for a sheet name              for key in data.keys():                  if isinstance(data[key], np.ndarray):                      df = pd.DataFrame(data[key])                  else:                      df = data[key]                    if columns:                      data[key].columns = columns                    df.to_excel(writer, key, index=False)        def _write(self, data: Any):          if isinstance(data, Dict) and all(              [isinstance(x, (pd.DataFrame, modin_pd.DataFrame, np.ndarray)) for x in data.values()]          ):              self.__write_excel_with_multiple_sheets(data)          elif isinstance(data, (pd.DataFrame, modin_pd.DataFrame)):              self.__write_excel_with_single_sheet(data.to_excel, self._path, index=False)          else:              self.__write_excel_with_single_sheet(pd.DataFrame(data).to_excel, self._path, index=False)        def write_with_column_names(self, data: Any, columns: List[str] = None, job_id: Optional[JobId] = None):          """Write a set of columns.            Parameters:              data (Any): The data to write.              columns (List[str]): The list of column names to write.              job_id (JobId^): An optional identifier of the writer.          """          if isinstance(data, Dict) and all(              [isinstance(x, (pd.DataFrame, modin_pd.DataFrame, np.ndarray)) for x in data.values()]          ):              self.__write_excel_with_multiple_sheets(data, columns=columns)          else:              df = pd.DataFrame(data)              if columns:                  df.columns = columns              self.__write_excel_with_single_sheet(df.to_excel, self.path, index=False)          self.track_edit(timestamp=datetime.now(), job_id=job_id)


import os  from typing import Dict, Iterable, List, Optional, Set, Union    from taipy.config._config import _Config  from taipy.config.common.scope import Scope  from taipy.config.config import Config    from .._backup._backup import _append_to_backup_file, _remove_from_backup_file  from .._manager._manager import _Manager  from .._version._version_mixin import _VersionMixin  from ..config.data_node_config import DataNodeConfig  from ..cycle.cycle_id import CycleId  from ..exceptions.exceptions import InvalidDataNodeType  from ..notification import Event, EventEntityType, EventOperation, Notifier, _make_event  from ..scenario.scenario_id import ScenarioId  from ..sequence.sequence_id import SequenceId  from ._abstract_file import _AbstractFileDataNode  from ._data_fs_repository import _DataFSRepository  from .data_node import DataNode  from .data_node_id import DataNodeId  from .pickle import PickleDataNode      class _DataManager(_Manager[DataNode], _VersionMixin):      __DATA_NODE_CLASS_MAP = DataNode._class_map()  # type: ignore      _ENTITY_NAME = DataNode.__name__      _EVENT_ENTITY_TYPE = EventEntityType.DATA_NODE      _repository: _DataFSRepository      __NAME_KEY = "name"        @classmethod      def _bulk_get_or_create(          cls,          data_node_configs: List[DataNodeConfig],          cycle_id: Optional[CycleId] = None,          scenario_id: Optional[ScenarioId] = None,      ) -> Dict[DataNodeConfig, DataNode]:          data_node_configs = [Config.data_nodes[dnc.id] for dnc in data_node_configs]          dn_configs_and_owner_id = []          for dn_config in data_node_configs:              scope = dn_config.scope              owner_id: Union[Optional[SequenceId], Optional[ScenarioId], Optional[CycleId]]              if scope == Scope.SCENARIO:                  owner_id = scenario_id              elif scope == Scope.CYCLE:                  owner_id = cycle_id              else:                  owner_id = None              dn_configs_and_owner_id.append((dn_config, owner_id))            data_nodes = cls._repository._get_by_configs_and_owner_ids(              dn_configs_and_owner_id, cls._build_filters_with_version(None)          )            return {              dn_config: data_nodes.get((dn_config, owner_id)) or cls._create_and_set(dn_config, owner_id, None)              for dn_config, owner_id in dn_configs_and_owner_id          }        @classmethod      def _create_and_set(          cls, data_node_config: DataNodeConfig, owner_id: Optional[str], parent_ids: Optional[Set[str]]      ) -> DataNode:          data_node = cls.__create(data_node_config, owner_id, parent_ids)          cls._set(data_node)          if isinstance(data_node, _AbstractFileDataNode):              _append_to_backup_file(new_file_path=data_node._path)          Notifier.publish(_make_event(data_node, EventOperation.CREATION))          return data_node        @classmethod      def __create(          cls, data_node_config: DataNodeConfig, owner_id: Optional[str], parent_ids: Optional[Set[str]]      ) -> DataNode:          try:              version = cls._get_latest_version()              props = data_node_config._properties.copy()                if data_node_config.storage_type:                  storage_type = data_node_config.storage_type              else:                  storage_type = Config.sections[DataNodeConfig.name][_Config.DEFAULT_KEY].storage_type                return cls.__DATA_NODE_CLASS_MAP[storage_type](                  config_id=data_node_config.id,                  scope=data_node_config.scope or DataNodeConfig._DEFAULT_SCOPE,                  validity_period=data_node_config.validity_period,                  owner_id=owner_id,                  parent_ids=parent_ids,                  version=version,                  properties=props,              )          except KeyError:              raise InvalidDataNodeType(data_node_config.storage_type)        @classmethod      def _get_all(cls, version_number: Optional[str] = None) -> List[DataNode]:          """          Returns all entities.          """          filters = cls._build_filters_with_version(version_number)          return cls._repository._load_all(filters)        @classmethod      def _clean_pickle_file(cls, data_node: DataNode):          if not isinstance(data_node, PickleDataNode):              return          if data_node.is_generated and os.path.exists(data_node.path):              os.remove(data_node.path)        @classmethod      def _clean_pickle_files(cls, data_nodes: Iterable[DataNode]):          for data_node in data_nodes:              cls._clean_pickle_file(data_node)        @classmethod      def _remove_dn_file_path_in_backup_file(cls, data_node: DataNode):          if isinstance(data_node, _AbstractFileDataNode):              _remove_from_backup_file(to_remove_file_path=data_node.path)        @classmethod      def _remove_dn_file_paths_in_backup_file(cls, data_nodes: Iterable[DataNode]):          for data_node in data_nodes:              cls._remove_dn_file_path_in_backup_file(data_node)        @classmethod      def _delete(cls, data_node_id: DataNodeId):          data_node = cls._get(data_node_id, None)          if data_node:              cls._clean_pickle_file(data_node)              cls._remove_dn_file_path_in_backup_file(data_node)          super()._delete(data_node_id)        @classmethod      def _delete_many(cls, data_node_ids: Iterable[DataNodeId]):          data_nodes = []          for data_node_id in data_node_ids:              if data_node := cls._get(data_node_id):                  data_nodes.append(data_node)          cls._clean_pickle_files(data_nodes)          cls._remove_dn_file_paths_in_backup_file(data_nodes)          super()._delete_many(data_node_ids)        @classmethod      def _delete_all(cls):          data_nodes = cls._get_all()          cls._clean_pickle_files(data_nodes)          cls._remove_dn_file_paths_in_backup_file(data_nodes)          super()._delete_all()        @classmethod      def _delete_by_version(cls, version_number: str):          data_nodes = cls._get_all(version_number)          cls._clean_pickle_files(data_nodes)          cls._remove_dn_file_paths_in_backup_file(data_nodes)          cls._repository._delete_by(attribute="version", value=version_number)          Notifier.publish(              Event(EventEntityType.DATA_NODE, EventOperation.DELETION, metadata={"delete_by_version": version_number})          )        @classmethod      def _get_by_config_id(cls, config_id: str, version_number: Optional[str] = None) -> List[DataNode]:          """          Get all datanodes by its config id.          """          filters = cls._build_filters_with_version(version_number)          if not filters:              filters = [{}]          for fil in filters:              fil.update({"config_id": config_id})          return cls._repository._load_all(filters)


from datetime import datetime, timedelta  from typing import Any, Dict, List, Optional, Set, Tuple, Union    import modin.pandas as modin_pd  import numpy as np  import pandas as pd  from sqlalchemy import MetaData, Table    from taipy.config.common.scope import Scope    from .._version._version_manager_factory import _VersionManagerFactory  from ..exceptions.exceptions import MissingRequiredProperty  from ._abstract_sql import _AbstractSQLDataNode  from .data_node_id import DataNodeId, Edit      class SQLTableDataNode(_AbstractSQLDataNode):      """Data Node stored in a SQL table.        Attributes:          config_id (str): Identifier of the data node configuration. It must be a valid Python              identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              None.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          properties (dict[str, Any]): A dictionary of additional properties. Note that the              _properties_ parameter must at least contain an entry for _"db_name"_, _"db_engine"_, _"table_name"_:                - _"db_name"_ `(str)`: The database name, or the name of the SQLite database file.              - _"db_engine"_ `(str)`: The database engine. For now, the accepted values are _"sqlite"_, _"mssql"_,                  _"mysql"_, or _"postgresql"_.              - _"table_name"_ `(str)`: The name of the SQL table.              - _"db_username"_ `(str)`: The database username.              - _"db_password"_ `(str)`: The database password.              - _"db_host"_ `(str)`: The database host. The default value is _"localhost"_.              - _"db_port"_ `(int)`: The database port. The default value is 1433.              - _"db_driver"_ `(str)`: The database driver.              - _"sqlite_folder_path"_ (str): The path to the folder that contains SQLite file. The default value                  is the current working folder.              - _"sqlite_file_extension"_ (str): The filename extension of the SQLite file. The default value is ".db".              - _"db_extra_args"_ `(Dict[str, Any])`: A dictionary of additional arguments to be passed into database                  connection string.              - _"exposed_type"_: The exposed type of the data read from SQL query. The default value is `pandas`.      """        __STORAGE_TYPE = "sql_table"      __TABLE_KEY = "table_name"        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: Optional[str] = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties: Optional[Dict] = None,      ):          if properties is None:              properties = {}          if properties.get(self.__TABLE_KEY) is None:              raise MissingRequiredProperty(f"Property {self.__TABLE_KEY} is not informed and is required.")          super().__init__(              config_id,              scope,              id=id,              owner_id=owner_id,              parent_ids=parent_ids,              last_edit_date=last_edit_date,              edits=edits,              version=version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period=validity_period,              edit_in_progress=edit_in_progress,              editor_id=editor_id,              editor_expiration_date=editor_expiration_date,              properties=properties,          )          self._TAIPY_PROPERTIES.update({self.__TABLE_KEY})        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        def _get_base_read_query(self) -> str:          return f"SELECT * FROM {self.properties[self.__TABLE_KEY]}"        def _do_append(self, data, engine, connection) -> None:          self.__insert_data(data, engine, connection)        def _do_write(self, data, engine, connection) -> None:          self.__insert_data(data, engine, connection, delete_table=True)        def __insert_data(self, data, engine, connection, delete_table: bool = False) -> None:          """          Insert data into a SQL table.            Parameters:              data (List[Dict]): a list of dictionaries, where each dictionary represents a row of the table.              table: a SQLAlchemy object that represents a table.              connection: a SQLAlchemy connection to write the data.              delete_table (bool): indicates if the table should be deleted before inserting the data.          """          table = self._create_table(engine)          if isinstance(data, (modin_pd.DataFrame, pd.DataFrame)):              self.__insert_dataframe(data, table, connection, delete_table)              return            if isinstance(data, np.ndarray):              data = data.tolist()          if not isinstance(data, list):              data = [data]            if len(data) == 0:              self.__delete_all_rows(table, connection, delete_table)              return            if isinstance(data[0], (tuple, list)):              self.__insert_tuples(data, table, connection, delete_table)          elif isinstance(data[0], dict):              self.__insert_dicts(data, table, connection, delete_table)          # If data is a primitive type, it will be inserted as a tuple of one element.          else:              self.__insert_tuples([(x,) for x in data], table, connection, delete_table)        def _create_table(self, engine) -> Table:          return Table(              self.properties[self.__TABLE_KEY],              MetaData(),              autoload_with=engine,          )        @classmethod      def __insert_dicts(cls, data: List[Dict], table: Any, connection: Any, delete_table: bool) -> None:          """          This method will insert the data contained in a list of dictionaries into a table. The query itself is handled          by SQLAlchemy, so it's only needed to pass the correct data type.          """          cls.__delete_all_rows(table, connection, delete_table)          connection.execute(table.insert(), data)        @classmethod      def __insert_dataframe(          cls, df: Union[modin_pd.DataFrame, pd.DataFrame], table: Any, connection: Any, delete_table: bool      ) -> None:          cls.__insert_dicts(df.to_dict(orient="records"), table, connection, delete_table)        @classmethod      def __insert_tuples(cls, data: List[Union[Tuple, List]], table: Any, connection: Any, delete_table: bool) -> None:          """          This method will look up the length of the first object of the list and build the insert through          creation of a string of '?' equivalent to the length of the element. The '?' character is used as          placeholder for a tuple of same size.          """          cls.__delete_all_rows(table, connection, delete_table)          markers = ",".join("?" * len(data[0]))          ins = "INSERT INTO {tablename} VALUES ({markers})"          ins = ins.format(tablename=table.name, markers=markers)          connection.execute(ins, data)        @classmethod      def __delete_all_rows(cls, table: Any, connection: Any, delete_table: bool) -> None:          if delete_table:              connection.execute(table.delete())


import csv  import os  from datetime import datetime, timedelta  from os.path import isfile  from typing import Any, Dict, List, Optional, Set    import modin.pandas as modin_pd  import numpy as np  import pandas as pd    from taipy.config.common.scope import Scope    from .._backup._backup import _replace_in_backup_file  from .._entity._reload import _self_reload  from .._version._version_manager_factory import _VersionManagerFactory  from ..job.job_id import JobId  from ._abstract_file import _AbstractFileDataNode  from ._abstract_tabular import _AbstractTabularDataNode  from .data_node import DataNode  from .data_node_id import DataNodeId, Edit      class CSVDataNode(DataNode, _AbstractFileDataNode, _AbstractTabularDataNode):      """Data Node stored as a CSV file.        Attributes:          config_id (str): Identifier of the data node configuration. This string must be a valid              Python identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or `None`.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          path (str): The path to the CSV file.          properties (dict[str, Any]): A dictionary of additional properties. The _properties_              must have a _"default_path"_ or _"path"_ entry with the path of the CSV file:                - _"default_path"_ `(str)`: The default path of the CSV file.\n              - _"encoding"_ `(str)`: The encoding of the CSV file. The default value is `utf-8`.\n              - _"default_data"_: The default data of the data nodes instantiated from this csv data node.\n              - _"has_header"_ `(bool)`: If True, indicates that the CSV file has a header.\n              - _"exposed_type"_: The exposed type of the data read from CSV file. The default value is `pandas`.\n      """        __STORAGE_TYPE = "csv"      __EXPOSED_TYPE_PROPERTY = "exposed_type"      __EXPOSED_TYPE_NUMPY = "numpy"      __EXPOSED_TYPE_PANDAS = "pandas"      __EXPOSED_TYPE_MODIN = "modin"      __VALID_STRING_EXPOSED_TYPES = [__EXPOSED_TYPE_PANDAS, __EXPOSED_TYPE_MODIN, __EXPOSED_TYPE_NUMPY]      __PATH_KEY = "path"      __DEFAULT_PATH_KEY = "default_path"      __ENCODING_KEY = "encoding"      __DEFAULT_DATA_KEY = "default_data"      __HAS_HEADER_PROPERTY = "has_header"      _REQUIRED_PROPERTIES: List[str] = []        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: Optional[str] = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties: Optional[Dict] = None,      ):          if properties is None:              properties = {}            default_value = properties.pop(self.__DEFAULT_DATA_KEY, None)            if self.__ENCODING_KEY not in properties.keys():              properties[self.__ENCODING_KEY] = "utf-8"            if self.__HAS_HEADER_PROPERTY not in properties.keys():              properties[self.__HAS_HEADER_PROPERTY] = True            if self.__EXPOSED_TYPE_PROPERTY not in properties.keys():              properties[self.__EXPOSED_TYPE_PROPERTY] = self.__EXPOSED_TYPE_PANDAS          self._check_exposed_type(properties[self.__EXPOSED_TYPE_PROPERTY], self.__VALID_STRING_EXPOSED_TYPES)            super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              **properties,          )          self._path = properties.get(self.__PATH_KEY, properties.get(self.__DEFAULT_PATH_KEY))          if not self._path:              self._path = self._build_path(self.storage_type())          properties[self.__PATH_KEY] = self._path            if not self._last_edit_date and isfile(self._path):              self._last_edit_date = datetime.now()          if default_value is not None and not os.path.exists(self._path):              self._write(default_value)              self._last_edit_date = datetime.now()              self._edits.append(                  Edit(                      {                          "timestamp": self._last_edit_date,                          "writer_identifier": "TAIPY",                          "comments": "Default data written.",                      }                  )              )            self._TAIPY_PROPERTIES.update(              {                  self.__EXPOSED_TYPE_PROPERTY,                  self.__PATH_KEY,                  self.__DEFAULT_PATH_KEY,                  self.__ENCODING_KEY,                  self.__DEFAULT_DATA_KEY,                  self.__HAS_HEADER_PROPERTY,              }          )        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def path(self):          return self._path        @path.setter      def path(self, value):          tmp_old_path = self._path          self._path = value          self.properties[self.__PATH_KEY] = value          _replace_in_backup_file(old_file_path=tmp_old_path, new_file_path=self._path)        def _read(self):          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_PANDAS:              return self._read_as_pandas_dataframe()          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_MODIN:              return self._read_as_modin_dataframe()          if self.properties[self.__EXPOSED_TYPE_PROPERTY] == self.__EXPOSED_TYPE_NUMPY:              return self._read_as_numpy()          return self._read_as()        def _read_as(self):          custom_class = self.properties[self.__EXPOSED_TYPE_PROPERTY]          with open(self._path, encoding=self.properties[self.__ENCODING_KEY]) as csvFile:              res = list()              if self.properties[self.__HAS_HEADER_PROPERTY]:                  reader = csv.DictReader(csvFile)                  for line in reader:                      res.append(custom_class(**line))              else:                  reader = csv.reader(                      csvFile,                  )                  for line in reader:                      res.append(custom_class(*line))              return res        def _read_as_numpy(self) -> np.ndarray:          return self._read_as_pandas_dataframe().to_numpy()        def _read_as_pandas_dataframe(          self, usecols: Optional[List[int]] = None, column_names: Optional[List[str]] = None      ) -> pd.DataFrame:          try:              if self.properties[self.__HAS_HEADER_PROPERTY]:                  if column_names:                      return pd.read_csv(self._path, encoding=self.properties[self.__ENCODING_KEY])[column_names]                  return pd.read_csv(self._path, encoding=self.properties[self.__ENCODING_KEY])              else:                  if usecols:                      return pd.read_csv(                          self._path, encoding=self.properties[self.__ENCODING_KEY], header=None, usecols=usecols                      )                  return pd.read_csv(self._path, encoding=self.properties[self.__ENCODING_KEY], header=None)          except pd.errors.EmptyDataError:              return pd.DataFrame()        def _read_as_modin_dataframe(          self, usecols: Optional[List[int]] = None, column_names: Optional[List[str]] = None      ) -> modin_pd.DataFrame:          try:              if self.properties[self.__HAS_HEADER_PROPERTY]:                  if column_names:                      return modin_pd.read_csv(self._path, encoding=self.properties[self.__ENCODING_KEY])[column_names]                  return modin_pd.read_csv(self._path, encoding=self.properties[self.__ENCODING_KEY])              else:                  if usecols:                      return modin_pd.read_csv(                          self._path, header=None, usecols=usecols, encoding=self.properties[self.__ENCODING_KEY]                      )                  return modin_pd.read_csv(self._path, header=None, encoding=self.properties[self.__ENCODING_KEY])          except pd.errors.EmptyDataError:              return modin_pd.DataFrame()        def _append(self, data: Any):          if isinstance(data, (pd.DataFrame, modin_pd.DataFrame)):              data.to_csv(self._path, mode="a", index=False, encoding=self.properties[self.__ENCODING_KEY], header=False)          else:              pd.DataFrame(data).to_csv(                  self._path, mode="a", index=False, encoding=self.properties[self.__ENCODING_KEY], header=False              )        def _write(self, data: Any):          if isinstance(data, (pd.DataFrame, modin_pd.DataFrame)):              data.to_csv(self._path, index=False, encoding=self.properties[self.__ENCODING_KEY])          else:              pd.DataFrame(data).to_csv(self._path, index=False, encoding=self.properties[self.__ENCODING_KEY])        def write_with_column_names(self, data: Any, columns: Optional[List[str]] = None, job_id: Optional[JobId] = None):          """Write a selection of columns.            Parameters:              data (Any): The data to write.              columns (Optional[List[str]]): The list of column names to write.              job_id (JobId^): An optional identifier of the writer.          """          if not columns:              df = pd.DataFrame(data)          else:              df = pd.DataFrame(data, columns=columns)          df.to_csv(self._path, index=False, encoding=self.properties[self.__ENCODING_KEY])          self.track_edit(timestamp=datetime.now(), job_id=job_id)


from typing import Type    from .._manager._manager_factory import _ManagerFactory  from ..common._utils import _load_fct  from ._data_fs_repository import _DataFSRepository  from ._data_manager import _DataManager  from ._data_sql_repository import _DataSQLRepository      class _DataManagerFactory(_ManagerFactory):        __REPOSITORY_MAP = {"default": _DataFSRepository, "sql": _DataSQLRepository}        @classmethod      def _build_manager(cls) -> Type[_DataManager]:  # type: ignore          if cls._using_enterprise():              data_manager = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".data._data_manager", "_DataManager"              )  # type: ignore              build_repository = _load_fct(                  cls._TAIPY_ENTERPRISE_CORE_MODULE + ".data._data_manager_factory", "_DataManagerFactory"              )._build_repository  # type: ignore          else:              data_manager = _DataManager              build_repository = cls._build_repository          data_manager._repository = build_repository()  # type: ignore          return data_manager  # type: ignore        @classmethod      def _build_repository(cls):          return cls._get_repository_with_repo_map(cls.__REPOSITORY_MAP)()


import os  import pickle  from datetime import datetime, timedelta  from typing import Any, List, Optional, Set    import modin.pandas as pd    from taipy.config.common.scope import Scope    from .._backup._backup import _replace_in_backup_file  from .._entity._reload import _self_reload  from .._version._version_manager_factory import _VersionManagerFactory  from ._abstract_file import _AbstractFileDataNode  from .data_node import DataNode  from .data_node_id import DataNodeId, Edit      class PickleDataNode(DataNode, _AbstractFileDataNode):      """Data Node stored as a pickle file.        Attributes:          config_id (str): Identifier of the data node configuration. It must be a valid Python              identifer.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              `None`.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          properties (dict[str, Any]): A dictionary of additional properties.              When creating a pickle data node, if the _properties_ dictionary contains a              _"default_data"_ entry, the data node is automatically written with the corresponding              _"default_data"_ value.              If the _properties_ dictionary contains a _"default_path"_ or _"path"_ entry, the data will be stored              using the corresponding value as the name of the pickle file.      """        __STORAGE_TYPE = "pickle"      __PATH_KEY = "path"      __DEFAULT_PATH_KEY = "default_path"      __DEFAULT_DATA_KEY = "default_data"      __IS_GENERATED_KEY = "is_generated"      _REQUIRED_PROPERTIES: List[str] = []        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: str = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties=None,      ):          if properties is None:              properties = {}          default_value = properties.pop(self.__DEFAULT_DATA_KEY, None)          self._path = properties.get(self.__PATH_KEY, properties.get(self.__DEFAULT_PATH_KEY))          if self._path is not None:              properties[self.__PATH_KEY] = self._path          self._is_generated = properties.get(self.__IS_GENERATED_KEY, self._path is None)          properties[self.__IS_GENERATED_KEY] = self._is_generated          super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              **properties,          )          if self._path is None:              self._path = self._build_path(self.storage_type())          if not self._last_edit_date and os.path.exists(self._path):              self._last_edit_date = datetime.now()          if default_value is not None and not os.path.exists(self._path):              self._write(default_value)              self._last_edit_date = datetime.now()              self._edits.append(                  Edit(                      {                          "timestamp": self._last_edit_date,                          "writer_identifier": "TAIPY",                          "comments": "Default data written.",                      }                  )              )            self._TAIPY_PROPERTIES.update(              {                  self.__PATH_KEY,                  self.__DEFAULT_PATH_KEY,                  self.__DEFAULT_DATA_KEY,                  self.__IS_GENERATED_KEY,              }          )        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def path(self) -> Any:          return self._path        @path.setter      def path(self, value):          tmp_old_path = self._path          self._path = value          self.properties[self.__PATH_KEY] = value          self.properties[self.__IS_GENERATED_KEY] = False          _replace_in_backup_file(old_file_path=tmp_old_path, new_file_path=self._path)        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def is_generated(self) -> bool:          return self._is_generated        def _read(self):          os.environ["MODIN_PERSISTENT_PICKLE"] = "True"          with open(self._path, "rb") as pf:              return pickle.load(pf)        def _write(self, data):          if isinstance(data, (pd.DataFrame, pd.Series)):              os.environ["MODIN_PERSISTENT_PICKLE"] = "True"          with open(self._path, "wb") as pf:              pickle.dump(data, pf)


from datetime import datetime, timedelta  from typing import Dict, List, Optional, Set    from sqlalchemy import text    from taipy.config.common.scope import Scope    from .._version._version_manager_factory import _VersionManagerFactory  from ..exceptions.exceptions import MissingAppendQueryBuilder, MissingRequiredProperty  from ._abstract_sql import _AbstractSQLDataNode  from .data_node_id import DataNodeId, Edit      class SQLDataNode(_AbstractSQLDataNode):      """Data Node stored in a SQL database.        Attributes:          config_id (str): Identifier of the data node configuration. It must be a valid Python              identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              None.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          properties (dict[str, Any]): A dictionary of additional properties. Note that the              _properties_ parameter must at least contain an entry for _"db_name"_, _"db_engine"_, _"read_query"_,              and _"write_query_builder"_:                - _"db_name"_ `(str)`: The database name, or the name of the SQLite database file.              - _"db_engine"_ `(str)`: The database engine. Possible values are _"sqlite"_, _"mssql"_, _"mysql"_, or                  _"postgresql"_.              - _"read_query"_ `(str)`: The SQL query string used to read the data from the database.              - _"write_query_builder"_ `(Callable)`: A callback function that takes the data as an input parameter and                  returns a list of SQL queries to be executed when writing data to the data node.              - _"append_query_builder"_ `(Callable)`: A callback function that takes the data as an input parameter and                  returns a list of SQL queries to be executed when appending data to the data node.              - _"db_username"_ `(str)`: The database username.              - _"db_password"_ `(str)`: The database password.              - _"db_host"_ `(str)`: The database host. The default value is _"localhost"_.              - _"db_port"_ `(int)`: The database port. The default value is 1433.              - _"db_driver"_ `(str)`: The database driver.              - _"sqlite_folder_path"_ (str): The path to the folder that contains SQLite file. The default value                  is the current working folder.              - _"sqlite_file_extension"_ (str): The filename extension of the SQLite file. The default value is ".db".              - _"db_extra_args"_ `(Dict[str, Any])`: A dictionary of additional arguments to be passed into database                  connection string.              - _"exposed_type"_: The exposed type of the data read from SQL query. The default value is `pandas`.      """        __STORAGE_TYPE = "sql"      __READ_QUERY_KEY = "read_query"      _WRITE_QUERY_BUILDER_KEY = "write_query_builder"      _APPEND_QUERY_BUILDER_KEY = "append_query_builder"        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: Optional[str] = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties: Optional[Dict] = None,      ):          if properties is None:              properties = {}          if properties.get(self.__READ_QUERY_KEY) is None:              raise MissingRequiredProperty(f"Property {self.__READ_QUERY_KEY} is not informed and is required.")          if properties.get(self._WRITE_QUERY_BUILDER_KEY) is None:              raise MissingRequiredProperty(f"Property {self._WRITE_QUERY_BUILDER_KEY} is not informed and is required.")            super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              properties=properties,          )            self._TAIPY_PROPERTIES.update(              {                  self.__READ_QUERY_KEY,                  self._WRITE_QUERY_BUILDER_KEY,                  self._APPEND_QUERY_BUILDER_KEY,              }          )        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        def _get_base_read_query(self) -> str:          return self.properties.get(self.__READ_QUERY_KEY)        def _do_append(self, data, engine, connection) -> None:          if not self.properties.get(self._APPEND_QUERY_BUILDER_KEY):              raise MissingAppendQueryBuilder            queries = self.properties.get(self._APPEND_QUERY_BUILDER_KEY)(data)          self.__execute_queries(queries, connection)        def _do_write(self, data, engine, connection) -> None:          queries = self.properties.get(self._WRITE_QUERY_BUILDER_KEY)(data)          self.__execute_queries(queries, connection)        def __execute_queries(self, queries, connection) -> None:          if not isinstance(queries, List):              queries = [queries]          for query in queries:              if isinstance(query, str):                  connection.execute(text(query))              else:                  statement = query[0]                  parameters = query[1]                  connection.execute(text(statement), parameters)


#  # 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 .._repository._sql_repository import _SQLRepository  from ._data_converter import _DataNodeConverter  from ._data_model import _DataNodeModel      class _DataSQLRepository(_SQLRepository):      def __init__(self):          super().__init__(model_type=_DataNodeModel, converter=_DataNodeConverter)


import os  import uuid  from abc import abstractmethod  from datetime import datetime, timedelta  from typing import Any, Dict, List, Optional, Set, Tuple, Union    import networkx as nx    from taipy.config.common._validate_id import _validate_id  from taipy.config.common.scope import Scope  from taipy.logger._taipy_logger import _TaipyLogger    from .._entity._entity import _Entity  from .._entity._labeled import _Labeled  from .._entity._properties import _Properties  from .._entity._reload import _Reloader, _self_reload, _self_setter  from .._version._version_manager_factory import _VersionManagerFactory  from ..common._warnings import _warn_deprecated  from ..exceptions.exceptions import DataNodeIsBeingEdited, NoData  from ..job.job_id import JobId  from ..notification.event import Event, EventEntityType, EventOperation, _make_event  from ._filter import _FilterDataNode  from .data_node_id import DataNodeId, Edit  from .operator import JoinOperator      class DataNode(_Entity, _Labeled):      """Reference to a dataset.        A Data Node is an abstract class that holds metadata related to the dataset it refers to.      In particular, a data node holds the name, the scope, the owner identifier, the last      edit date, and some additional properties of the data.
      A Data Node also contains information and methods needed to access the dataset. This      information depends on the type of storage, and it is held by subclasses (such as      SQL Data Node, CSV Data Node, ...).        !!! note          It is recommended not to instantiate subclasses of `DataNode` directly.        Attributes:          config_id (str): Identifier of the data node configuration. It must be a valid Python              identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          name (str): A user-readable name of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              None.          parent_ids (Optional[Set[str]]): The set of identifiers of the parent tasks.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The list of Edits (an alias for dict) containing metadata about each              data edition including but not limited to timestamp, comments, job_id:              timestamp: The time instant of the writing              comments: Representation of a free text to explain or comment on a data change              job_id: Only populated when the data node is written by a task execution and corresponds to the job's id.              Additional metadata related to the edition made to the data node can also be provided in Edits.          version (str): The string indicates the application version of the data node to              instantiate. If not provided, the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if the data node is locked for modification. False              otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          kwargs: A dictionary of additional properties.      """        _ID_PREFIX = "DATANODE"      __ID_SEPARATOR = "_"      __logger = _TaipyLogger._get_logger()      _REQUIRED_PROPERTIES: List[str] = []      _MANAGER_NAME = "data"      __PATH_KEY = "path"      __EDIT_TIMEOUT = 30        _TAIPY_PROPERTIES: Set[str] = set()        def __init__(          self,          config_id,          scope: Scope = Scope(Scope.SCENARIO),          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: Optional[str] = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          **kwargs,      ):          self.config_id = _validate_id(config_id)          self.id = id or DataNodeId(self.__ID_SEPARATOR.join([self._ID_PREFIX, self.config_id, str(uuid.uuid4())]))          self.owner_id = owner_id          self._parent_ids = parent_ids or set()          self._scope = scope          self._last_edit_date = last_edit_date          self._edit_in_progress = edit_in_progress          self._version = version or _VersionManagerFactory._build_manager()._get_latest_version()          self._validity_period = validity_period          self._editor_id: Optional[str] = editor_id          self._editor_expiration_date: Optional[datetime] = editor_expiration_date            # Track edits          self._edits = edits or list()            self._properties = _Properties(self, **kwargs)        def get_parents(self):          """Get all parents of this data node."""          from ... import core as tp            return tp.get_parents(self)        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def parent_ids(self):          """List of parent ids of this data node."""          return self._parent_ids        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def edits(self):          """Get all `Edit^`s of this data node."""          return self._edits        def get_last_edit(self) -> Optional[Edit]:          """Get last `Edit^` of this data node.            Returns:              None if there has been no `Edit^` on this data node.          """          if self._edits:              return self._edits[-1]          return None        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def last_edit_date(self):          last_modified_datetime = self.__get_last_modified_datetime()          if last_modified_datetime and last_modified_datetime > self._last_edit_date:              return last_modified_datetime          else:              return self._last_edit_date        @last_edit_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def last_edit_date(self, val):          self._last_edit_date = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def scope(self):          return self._scope        @scope.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def scope(self, val):          self._scope = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def validity_period(self) -> Optional[timedelta]:          return self._validity_period if self._validity_period else None        @validity_period.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def validity_period(self, val):          self._validity_period = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def expiration_date(self) -> datetime:          """Datetime instant of the expiration date of this data node."""          last_edit_date = self.last_edit_date          validity_period = self._validity_period            if not last_edit_date:              raise NoData(f"Data node {self.id} from config {self.config_id} has not been written yet.")            return last_edit_date + validity_period if validity_period else last_edit_date        @property  # type: ignore      def name(self) -> Optional[str]:          return self.properties.get("name")        @name.setter  # type: ignore      def name(self, val):          self.properties["name"] = val        @property      def version(self):          return self._version        @property      def cacheable(self):          """Deprecated. Use `skippable` attribute of a `Task^` instead."""          _warn_deprecated("cacheable", suggest="the skippable feature")          return self.properties.get("cacheable", False)        @cacheable.setter      def cacheable(self, val):          _warn_deprecated("cacheable", suggest="the skippable feature")        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def edit_in_progress(self):          return self._edit_in_progress        @edit_in_progress.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def edit_in_progress(self, val):          self._edit_in_progress = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def editor_id(self):          return self._editor_id        @editor_id.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def editor_id(self, val):          self._editor_id = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def editor_expiration_date(self):          return self._editor_expiration_date        @editor_expiration_date.setter  # type: ignore      @_self_setter(_MANAGER_NAME)      def editor_expiration_date(self, val):          self._editor_expiration_date = val        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def job_ids(self):          """List of the jobs having edited this data node."""          return [edit.get("job_id") for edit in self.edits if edit.get("job_id")]        @property      def properties(self):          """Dictionary of custom properties."""          self._properties = _Reloader()._reload(self._MANAGER_NAME, self)._properties          return self._properties        def _get_user_properties(self) -> Dict[str, Any]:          """Get user properties."""          return {key: value for key, value in self.properties.items() if key not in self._TAIPY_PROPERTIES}        def __eq__(self, other):          return self.id == other.id        def __ne__(self, other):          return not self == other        def __hash__(self):          return hash(self.id)        def __getstate__(self):          return vars(self)        def __setstate__(self, state):          vars(self).update(state)        def __getattr__(self, attribute_name):          protected_attribute_name = _validate_id(attribute_name)          if protected_attribute_name in self._properties:              return self._properties[protected_attribute_name]          raise AttributeError(f"{attribute_name} is not an attribute of data node {self.id}")        def __get_last_modified_datetime(self) -> Optional[datetime]:          path = self._properties.get(self.__PATH_KEY, None)          if path and os.path.isfile(path):              return datetime.fromtimestamp(os.path.getmtime(path))            last_modified_datetime = None          if path and os.path.isdir(path):              for filename in os.listdir(path):                  filepath = os.path.join(path, filename)                  if os.path.isfile(filepath):                      file_mtime = datetime.fromtimestamp(os.path.getmtime(filepath))                        if last_modified_datetime is None or file_mtime > last_modified_datetime:                          last_modified_datetime = file_mtime            return last_modified_datetime        @classmethod      @abstractmethod      def storage_type(cls) -> str:          raise NotImplementedError        def read_or_raise(self) -> Any:          """Read the data referenced by this data node.            Returns:              The data referenced by this data node.            Raises:              NoData^: If the data has not been written yet.          """          if not self.last_edit_date:              raise NoData(f"Data node {self.id} from config {self.config_id} has not been written yet.")          return self._read()        def read(self) -> Any:          """Read the data referenced by this data node.            Returns:              The data referenced by this data node. None if the data has not been written yet.          """          try:              return self.read_or_raise()          except NoData:              self.__logger.warning(                  f"Data node {self.id} from config {self.config_id} is being read but has never been " f"written."              )              return None        def append(self, data, job_id: Optional[JobId] = None, **kwargs: Dict[str, Any]):          """Append some data to this data node.            Parameters:              data (Any): The data to write to this data node.              job_id (JobId^): An optional identifier of the writer.              **kwargs (dict[str, any]): Extra information to attach to the edit document                  corresponding to this write.          """          from ._data_manager_factory import _DataManagerFactory            self._append(data)          self.track_edit(job_id=job_id, **kwargs)          self.unlock_edit()          _DataManagerFactory._build_manager()._set(self)        def write(self, data, job_id: Optional[JobId] = None, **kwargs: Dict[str, Any]):          """Write some data to this data node.            Parameters:              data (Any): The data to write to this data node.              job_id (JobId^): An optional identifier of the writer.              **kwargs (dict[str, any]): Extra information to attach to the edit document                  corresponding to this write.          """          from ._data_manager_factory import _DataManagerFactory            self._write(data)          self.track_edit(job_id=job_id, **kwargs)          self.unlock_edit()          _DataManagerFactory._build_manager()._set(self)        def track_edit(self, **options):          """Creates and adds a new entry in the edits attribute without writing the data.            Parameters:              options (dict[str, any)): track `timestamp`, `comments`, `job_id`. The others are user-custom, users can                  use options to attach any information to an external edit of a data node.          """          edit = {}          for k, v in options.items():              if v is not None:                  edit[k] = v          if "timestamp" not in edit:              edit["timestamp"] = datetime.now()          self.last_edit_date = edit.get("timestamp")          self._edits.append(edit)        def lock_edit(self, editor_id: Optional[str] = None):          """Lock the data node modification.            Note:              The data node can be unlocked with the method `(DataNode.)unlock_edit()^`.            Parameters:              editor_id (Optional[str]): The editor's identifier.          """          if editor_id:              if (                  self.edit_in_progress                  and self.editor_id != editor_id                  and self.editor_expiration_date                  and self.editor_expiration_date > datetime.now()              ):                  raise DataNodeIsBeingEdited(self.id, self._editor_id)              self.editor_id = editor_id  # type: ignore              self.editor_expiration_date = datetime.now() + timedelta(minutes=self.__EDIT_TIMEOUT)  # type: ignore          else:              self.editor_id = None  # type: ignore              self.editor_expiration_date = None  # type: ignore          self.edit_in_progress = True  # type: ignore        def unlock_edit(self, editor_id: Optional[str] = None):          """Unlocks the data node modification.            Note:              The data node can be locked with the method `(DataNode.)lock_edit()^`.            Parameters:              editor_id (Optional[str]): The editor's identifier.          """          if (              editor_id              and self.editor_id != editor_id              and self.editor_expiration_date              and self.editor_expiration_date > datetime.now()          ):              raise DataNodeIsBeingEdited(self.id, self._editor_id)          else:              self.editor_id = None  # type: ignore              self.editor_expiration_date = None  # type: ignore              self.edit_in_progress = False  # type: ignore        def filter(self, operators: Union[List, Tuple], join_operator=JoinOperator.AND):          """Read and filter the data referenced by this data node.            The data is filtered by the provided list of 3-tuples (key, value, `Operator^`).            If multiple filter operators are provided, filtered data will be joined based on the          join operator (*AND* or *OR*).            Parameters:              operators (Union[List[Tuple], Tuple]): A 3-element tuple or a list of 3-element tuples,                  each is in the form of (key, value, `Operator^`).              join_operator (JoinOperator^): The operator used to join the multiple filter                  3-tuples.          Returns:              The filtered data.          Raises:              NotImplementedError: If the data type is not supported.          """          data = self._read()          return _FilterDataNode._filter(data, operators, join_operator)        def __getitem__(self, item):          data = self._read()          return _FilterDataNode._filter_by_key(data, item)        @abstractmethod      def _read(self):          raise NotImplementedError        @abstractmethod      def _append(self, data):          raise NotImplementedError        @abstractmethod      def _write(self, data):          raise NotImplementedError        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def is_ready_for_reading(self) -> bool:          """Indicate if this data node is ready for reading.            Returns:              False if the data is locked for modification or if the data has never been written.                  True otherwise.          """          if self._edit_in_progress:              return False          if not self._last_edit_date:              # Never been written so it is not up-to-date              return False          return True        @property  # type: ignore      @_self_reload(_MANAGER_NAME)      def is_valid(self) -> bool:          """Indicate if this data node is valid.            Returns:              False if the data ever been written or the expiration date has passed.
              True otherwise.          """          if not self._last_edit_date:              # Never been written so it is not valid              return False          if not self._validity_period:              # No validity period and has already been written, so it is valid              return True          if datetime.now() > self.expiration_date:              # expiration_date has been passed              return False          return True        @property      def is_up_to_date(self) -> bool:          """Indicate if this data node is up-to-date.            Returns:              False if a preceding data node has been updated before the selected data node              or the selected data is invalid.
              True otherwise.          """            from ..scenario.scenario import Scenario          from ..taipy import get_parents            parent_scenarios: Set[Scenario] = get_parents(self)["scenario"]  # type: ignore          for parent_scenario in parent_scenarios:              for ancestor_node in nx.ancestors(parent_scenario._build_dag(), self):                  if (                      isinstance(ancestor_node, DataNode)                      and ancestor_node.last_edit_date                      and ancestor_node.last_edit_date > self.last_edit_date                  ):                      return False          return self.is_valid        @staticmethod      def _class_map():          def all_subclasses(cls):              subclasses = set(cls.__subclasses__())              for s in cls.__subclasses__():                  subclasses.update(all_subclasses(s))              return subclasses            class_map = {}          for c in all_subclasses(DataNode):              try:                  if c.storage_type() is not None:                      class_map[c.storage_type()] = c              except NotImplementedError:                  pass            return class_map        def get_label(self) -> str:          """Returns the data node simple label prefixed by its owner label.            Returns:              The label of the data node as a string.          """          return self._get_label()        def get_simple_label(self) -> str:          """Returns the data node simple label.            Returns:              The simple label of the data node as a string.          """          return self._get_simple_label()      @_make_event.register(DataNode)  def make_event_for_datanode(      data_node: DataNode,      operation: EventOperation,      /,      attribute_name: Optional[str] = None,      attribute_value: Optional[Any] = None,      **kwargs,  ) -> Event:      metadata = {"config_id": data_node.config_id, "version": data_node._version, **kwargs}      return Event(          entity_type=EventEntityType.DATA_NODE,          entity_id=data_node.id,          operation=operation,          attribute_name=attribute_name,          attribute_value=attribute_value,          metadata=metadata,      )


import dataclasses  import json  import os  from datetime import date, datetime, timedelta  from enum import Enum  from os.path import isfile  from pydoc import locate  from typing import Any, Dict, List, Optional, Set    from taipy.config.common.scope import Scope    from .._backup._backup import _replace_in_backup_file  from .._entity._reload import _self_reload  from .._version._version_manager_factory import _VersionManagerFactory  from ._abstract_file import _AbstractFileDataNode  from .data_node import DataNode  from .data_node_id import DataNodeId, Edit      class JSONDataNode(DataNode, _AbstractFileDataNode):      """Data Node stored as a JSON file.        Attributes:          config_id (str): Identifier of the data node configuration. This string must be a valid              Python identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or `None`.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          path (str): The path to the JSON file.          encoder (json.JSONEncoder): The JSON encoder that is used to write into the JSON file.          decoder (json.JSONDecoder): The JSON decoder that is used to read from the JSON file.          properties (dict[str, Any]): A dictionary of additional properties. The _properties_              must have a _"default_path"_ or _"path"_ entry with the path of the JSON file:                - _"default_path"_ `(str)`: The default path of the CSV file.\n              - _"encoding"_ `(str)`: The encoding of the CSV file. The default value is `utf-8`.\n              - _"default_data"_: The default data of the data nodes instantiated from this json data node.\n      """        __STORAGE_TYPE = "json"      __DEFAULT_DATA_KEY = "default_data"      __DEFAULT_PATH_KEY = "default_path"      __PATH_KEY = "path"      __ENCODING_KEY = "encoding"      _ENCODER_KEY = "encoder"      _DECODER_KEY = "decoder"      _REQUIRED_PROPERTIES: List[str] = []        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: Optional[List[Edit]] = None,          version: Optional[str] = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties: Optional[Dict] = None,      ):          if properties is None:              properties = {}            default_value = properties.pop(self.__DEFAULT_DATA_KEY, None)            if self.__ENCODING_KEY not in properties.keys():              properties[self.__ENCODING_KEY] = "utf-8"            super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              **properties,          )          self._path = properties.get(self.__PATH_KEY, properties.get(self.__DEFAULT_PATH_KEY))          if not self._path:              self._path = self._build_path(self.storage_type())          properties[self.__PATH_KEY] = self._path            self._decoder = self._properties.get(self._DECODER_KEY, _DefaultJSONDecoder)          self._encoder = self._properties.get(self._ENCODER_KEY, _DefaultJSONEncoder)            if default_value is not None and not os.path.exists(self._path):              self._write(default_value)              self._last_edit_date = datetime.now()              self._edits.append(                  Edit(                      {                          "timestamp": self._last_edit_date,                          "writer_identifier": "TAIPY",                          "comments": "Default data written.",                      }                  )              )            if not self._last_edit_date and isfile(self._path):  # type: ignore              self._last_edit_date = datetime.now()            self._TAIPY_PROPERTIES.update(              {                  self.__PATH_KEY,                  self.__DEFAULT_PATH_KEY,                  self.__ENCODING_KEY,                  self.__DEFAULT_DATA_KEY,                  self._ENCODER_KEY,                  self._DECODER_KEY,              }          )        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def path(self):          return self._path        @path.setter      def path(self, value):          tmp_old_path = self._path          self._path = value          self.properties[self.__PATH_KEY] = value          _replace_in_backup_file(old_file_path=tmp_old_path, new_file_path=self._path)        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def encoder(self):          return self._encoder        @encoder.setter      def encoder(self, encoder: json.JSONEncoder):          self.properties[self._ENCODER_KEY] = encoder        @property  # type: ignore      @_self_reload(DataNode._MANAGER_NAME)      def decoder(self):          return self._decoder        @decoder.setter      def decoder(self, decoder: json.JSONDecoder):          self.properties[self._DECODER_KEY] = decoder        def _read(self):          with open(self._path, "r", encoding=self.properties[self.__ENCODING_KEY]) as f:              return json.load(f, cls=self._decoder)        def _append(self, data: Any):          with open(self._path, "r+", encoding=self.properties[self.__ENCODING_KEY]) as f:              file_data = json.load(f, cls=self._decoder)              if isinstance(file_data, List):                  if isinstance(data, List):                      file_data.extend(data)                  else:                      file_data.append(data)              elif isinstance(data, Dict):                  file_data.update(data)                f.seek(0)              json.dump(file_data, f, indent=4, cls=self._encoder)        def _write(self, data: Any):          with open(self._path, "w", encoding=self.properties[self.__ENCODING_KEY]) as f:  # type: ignore              json.dump(data, f, indent=4, cls=self._encoder)      class _DefaultJSONEncoder(json.JSONEncoder):      def default(self, o):          if isinstance(o, Enum):              return {                  "__type__": f"Enum-{o.__class__.__module__}-{o.__class__.__qualname__}-{o.name}",                  "__value__": o.value,              }            if isinstance(o, (datetime, date)):              return {"__type__": "Datetime", "__value__": o.isoformat()}            if dataclasses.is_dataclass(o):              return {                  "__type__": f"dataclass-{o.__class__.__module__}-{o.__class__.__qualname__}",                  "__value__": dataclasses.asdict(o),              }            return super().default(o)      class _DefaultJSONDecoder(json.JSONDecoder):      def __init__(self, *args, **kwargs):          json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)        def object_hook(self, source):          if _type := source.get("__type__"):              if _type.startswith("Enum"):                  _, module, classname, name = _type.split("-")                  _enum_class = locate(f"{module}.{classname}")                  return _enum_class[name]                if _type == "Datetime":                  return datetime.fromisoformat(source.get("__value__"))                if _type.startswith("dataclass"):                  _, module, classname = _type.split("-")                  _data_class = locate(f"{module}.{classname}")                  return _data_class(**source.get("__value__"))            return source


from dataclasses import dataclass  from typing import Any, Dict, List, Optional    from sqlalchemy import JSON, Boolean, Column, Enum, Float, String, Table, UniqueConstraint    from taipy.config.common.scope import Scope    from .._repository._base_taipy_model import _BaseModel  from .._repository.db._sql_base_model import mapper_registry  from .data_node_id import Edit      @mapper_registry.mapped  @dataclass  class _DataNodeModel(_BaseModel):      __table__ = Table(          "data_node",          mapper_registry.metadata,          Column("id", String, primary_key=True),          Column("config_id", String),          Column("scope", Enum(Scope)),          Column("storage_type", String),          Column("owner_id", String),          Column("parent_ids", JSON),          Column("last_edit_date", String),          Column("edits", JSON),          Column("version", String),          Column("validity_days", Float),          Column("validity_seconds", Float),          Column("edit_in_progress", Boolean),          Column("editor_id", String),          Column("editor_expiration_date", String),          Column("data_node_properties", JSON),      )      __table_args__ = (UniqueConstraint("config_id", "owner_id", name="_config_owner_uc"),)        id: str      config_id: str      scope: Scope      storage_type: str      owner_id: Optional[str]      parent_ids: List[str]      last_edit_date: Optional[str]      edits: List[Edit]      version: str      validity_days: Optional[float]      validity_seconds: Optional[float]      edit_in_progress: bool      editor_id: Optional[str]      editor_expiration_date: Optional[str]      data_node_properties: Dict[str, Any]        @staticmethod      def from_dict(data: Dict[str, Any]):          return _DataNodeModel(              id=data["id"],              config_id=data["config_id"],              scope=Scope._from_repr(data["scope"]),              storage_type=data["storage_type"],              owner_id=data.get("owner_id"),              parent_ids=data.get("parent_ids", []),              last_edit_date=data.get("last_edit_date"),              edits=_BaseModel._deserialize_attribute(data["edits"]),              version=data["version"],              validity_days=data["validity_days"],              validity_seconds=data["validity_seconds"],              edit_in_progress=bool(data.get("edit_in_progress", False)),              editor_id=data.get("editor_id", None),              editor_expiration_date=data.get("editor_expiration_date"),              data_node_properties=_BaseModel._deserialize_attribute(data["data_node_properties"]),          )        def to_list(self):          return [              self.id,              self.config_id,              repr(self.scope),              self.storage_type,              self.owner_id,              _BaseModel._serialize_attribute(self.parent_ids),              self.last_edit_date,              _BaseModel._serialize_attribute(self.edits),              self.version,              self.validity_days,              self.validity_seconds,              self.edit_in_progress,              self.editor_id,              self.editor_expiration_date,              _BaseModel._serialize_attribute(self.data_node_properties),          ]


from datetime import datetime, timedelta  from typing import Any, Dict, List, Optional, Set    from taipy.config.common.scope import Scope    from .._version._version_manager_factory import _VersionManagerFactory  from .data_node import DataNode  from .data_node_id import DataNodeId, Edit    in_memory_storage: Dict[str, Any] = {}      class InMemoryDataNode(DataNode):      """Data Node stored in memory.        Warning:          This Data Node implementation is not compatible with a parallel execution of taipy tasks,          but only with a task executor in development mode. The purpose of `InMemoryDataNode` is to be used          for development or debugging.        Attributes:          config_id (str): Identifier of the data node configuration. It must be a valid Python              identifier.          scope (Scope^): The scope of this data node.          id (str): The unique identifier of this data node.          owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or              `None`.          parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.          last_edit_date (datetime): The date and time of the last modification.          edits (List[Edit^]): The ordered list of edits for that job.          version (str): The string indicates the application version of the data node to instantiate. If not provided,              the current version is used.          validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for              which the data node can be considered up-to-date. Once the validity period has passed, the data node is              considered stale and relevant tasks will run even if they are skippable (see the              [Task management page](../core/entities/task-mgt.md) for more details).              If _validity_period_ is set to `None`, the data node is always up-to-date.          edit_in_progress (bool): True if a task computing the data node has been submitted              and not completed yet. False otherwise.          editor_id (Optional[str]): The identifier of the user who is currently editing the data node.          editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.          properties (dict[str, Any]): A dictionary of additional properties. When creating an              _In Memory_ data node, if the _properties_ dictionary contains a _"default_data"_              entry, the data node is automatically written with the corresponding _"default_data"_              value.      """        __STORAGE_TYPE = "in_memory"      __DEFAULT_DATA_VALUE = "default_data"      _REQUIRED_PROPERTIES: List[str] = []        def __init__(          self,          config_id: str,          scope: Scope,          id: Optional[DataNodeId] = None,          owner_id: Optional[str] = None,          parent_ids: Optional[Set[str]] = None,          last_edit_date: Optional[datetime] = None,          edits: List[Edit] = None,          version: str = None,          validity_period: Optional[timedelta] = None,          edit_in_progress: bool = False,          editor_id: Optional[str] = None,          editor_expiration_date: Optional[datetime] = None,          properties=None,      ):          if properties is None:              properties = {}          default_value = properties.pop(self.__DEFAULT_DATA_VALUE, None)          super().__init__(              config_id,              scope,              id,              owner_id,              parent_ids,              last_edit_date,              edits,              version or _VersionManagerFactory._build_manager()._get_latest_version(),              validity_period,              edit_in_progress,              editor_id,              editor_expiration_date,              **properties          )          if default_value is not None and self.id not in in_memory_storage:              self._write(default_value)              self._last_edit_date = datetime.now()              self._edits.append(                  Edit(                      {                          "timestamp": self._last_edit_date,                          "writer_identifier": "TAIPY",                          "comments": "Default data written.",                      }                  )              )            self._TAIPY_PROPERTIES.update({self.__DEFAULT_DATA_VALUE})        @classmethod      def storage_type(cls) -> str:          return cls.__STORAGE_TYPE        def _read(self):          return in_memory_storage.get(self.id)        def _write(self, data):          in_memory_storage[self.id] = data


#  # 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 .._repository._filesystem_repository import _FileSystemRepository  from ._data_converter import _DataNodeConverter  from ._data_model import _DataNodeModel      class _DataFSRepository(_FileSystemRepository):      def __init__(self):          super().__init__(model_type=_DataNodeModel, converter=_DataNodeConverter, dir_name="data_nodes")