generic.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. # Copyright 2021-2024 Avaiga Private Limited
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  4. # the License. You may obtain a copy of the License at
  5. #
  6. # http://www.apache.org/licenses/LICENSE-2.0
  7. #
  8. # Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  9. # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  10. # specific language governing permissions and limitations under the License.
  11. from datetime import datetime, timedelta
  12. from typing import Any, Dict, List, Optional, Set
  13. from taipy.config.common.scope import Scope
  14. from .._version._version_manager_factory import _VersionManagerFactory
  15. from ..exceptions.exceptions import MissingReadFunction, MissingRequiredProperty, MissingWriteFunction
  16. from .data_node import DataNode
  17. from .data_node_id import DataNodeId, Edit
  18. class GenericDataNode(DataNode):
  19. """Generic Data Node that uses custom read and write functions.
  20. The read and write function for this data node type can be implemented is Python.
  21. Attributes:
  22. config_id (str): Identifier of the data node configuration. It must be a valid Python
  23. identifier.
  24. scope (Scope^): The scope of this data node.
  25. id (str): The unique identifier of the data node.
  26. owner_id (str): The identifier of the owner (sequence_id, scenario_id, cycle_id) or
  27. `None`.
  28. parent_ids (Optional[Set[str]]): The identifiers of the parent tasks or `None`.
  29. last_edit_date (datetime): The date and time of the last modification.
  30. edits (List[Edit^]): The ordered list of edits for that job.
  31. version (str): The string indicates the application version of the data node to instantiate. If not provided,
  32. the current version is used.
  33. validity_period (Optional[timedelta]): The duration implemented as a timedelta since the last edit date for
  34. which the data node can be considered up-to-date. Once the validity period has passed, the data node is
  35. considered stale and relevant tasks will run even if they are skippable (see the
  36. [Task management page](../core/entities/task-mgt.md) for more details).
  37. If _validity_period_ is set to `None`, the data node is always up-to-date.
  38. edit_in_progress (bool): True if a task computing the data node has been submitted
  39. and not completed yet. False otherwise.
  40. editor_id (Optional[str]): The identifier of the user who is currently editing the data node.
  41. editor_expiration_date (Optional[datetime]): The expiration date of the editor lock.
  42. properties (dict[str, Any]): A dictionary of additional properties. Note that the
  43. _properties_ parameter must at least contain an entry for either _"read_fct"_ or
  44. _"write_fct"_ representing the read and write functions.
  45. Entries for _"read_fct_args"_ and _"write_fct_args"_ respectively represent
  46. potential parameters for the _"read_fct"_ and _"write_fct"_ functions.
  47. """
  48. __STORAGE_TYPE = "generic"
  49. _OPTIONAL_READ_FUNCTION_PROPERTY = "read_fct"
  50. __READ_FUNCTION_ARGS_PROPERTY = "read_fct_args"
  51. _OPTIONAL_WRITE_FUNCTION_PROPERTY = "write_fct"
  52. __WRITE_FUNCTION_ARGS_PROPERTY = "write_fct_args"
  53. _REQUIRED_PROPERTIES: List[str] = []
  54. _REQUIRED_AT_LEAST_ONE_PROPERTY: List[str] = [_OPTIONAL_READ_FUNCTION_PROPERTY, _OPTIONAL_WRITE_FUNCTION_PROPERTY]
  55. def __init__(
  56. self,
  57. config_id: str,
  58. scope: Scope,
  59. id: Optional[DataNodeId] = None,
  60. owner_id: Optional[str] = None,
  61. parent_ids: Optional[Set[str]] = None,
  62. last_edit_date: Optional[datetime] = None,
  63. edits: List[Edit] = None,
  64. version: str = None,
  65. validity_period: Optional[timedelta] = None,
  66. edit_in_progress: bool = False,
  67. editor_id: Optional[str] = None,
  68. editor_expiration_date: Optional[datetime] = None,
  69. properties: Dict = None,
  70. ):
  71. if properties is None:
  72. properties = {}
  73. if missing := set(self._REQUIRED_PROPERTIES) - set(properties.keys()):
  74. raise MissingRequiredProperty(
  75. f"The following properties {', '.join(missing)} were not informed and are required."
  76. )
  77. missing_optional_fcts = set(self._REQUIRED_AT_LEAST_ONE_PROPERTY) - set(properties.keys())
  78. if len(missing_optional_fcts) == len(self._REQUIRED_AT_LEAST_ONE_PROPERTY):
  79. raise MissingRequiredProperty(
  80. f"None of the following properties {', '.join(missing)} were informed"
  81. "and at least one must be populated."
  82. )
  83. for missing_optional_fct in missing_optional_fcts:
  84. properties[missing_optional_fct] = None
  85. super().__init__(
  86. config_id,
  87. scope,
  88. id,
  89. owner_id,
  90. parent_ids,
  91. last_edit_date,
  92. edits,
  93. version or _VersionManagerFactory._build_manager()._get_latest_version(),
  94. validity_period,
  95. edit_in_progress,
  96. editor_id,
  97. editor_expiration_date,
  98. **properties,
  99. )
  100. if not self._last_edit_date:
  101. self._last_edit_date = datetime.now()
  102. self._TAIPY_PROPERTIES.update(
  103. {
  104. self.__READ_FUNCTION_ARGS_PROPERTY,
  105. self.__WRITE_FUNCTION_ARGS_PROPERTY,
  106. self._OPTIONAL_READ_FUNCTION_PROPERTY,
  107. self._OPTIONAL_WRITE_FUNCTION_PROPERTY,
  108. }
  109. )
  110. @classmethod
  111. def storage_type(cls) -> str:
  112. return cls.__STORAGE_TYPE
  113. def _read(self):
  114. if read_fct := self.properties[self._OPTIONAL_READ_FUNCTION_PROPERTY]:
  115. if read_fct_args := self.properties.get(self.__READ_FUNCTION_ARGS_PROPERTY, None):
  116. if not isinstance(read_fct_args, list):
  117. return read_fct(*[read_fct_args])
  118. return read_fct(*read_fct_args)
  119. return read_fct()
  120. raise MissingReadFunction(f"The read function is not defined in data node config {self.config_id}.")
  121. def _write(self, data: Any):
  122. if write_fct := self.properties[self._OPTIONAL_WRITE_FUNCTION_PROPERTY]:
  123. if write_fct_args := self.properties.get(self.__WRITE_FUNCTION_ARGS_PROPERTY, None):
  124. if not isinstance(write_fct_args, list):
  125. return write_fct(data, *[write_fct_args])
  126. return write_fct(data, *write_fct_args)
  127. return write_fct(data)
  128. raise MissingWriteFunction(f"The write function is not defined in data node config {self.config_id}.")