taipy.py 45 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. # Copyright 2021-2025 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
  12. from typing import Any, Callable, Dict, List, Literal, Optional, Set, Union, overload
  13. from taipy.common.logger._taipy_logger import _TaipyLogger
  14. from ._entity._entity import _Entity
  15. from ._version._version_manager_factory import _VersionManagerFactory
  16. from .common._check_instance import (
  17. _is_cycle,
  18. _is_data_node,
  19. _is_job,
  20. _is_scenario,
  21. _is_sequence,
  22. _is_submission,
  23. _is_task,
  24. )
  25. from .common._warnings import _warn_no_orchestrator_service
  26. from .common.scope import Scope
  27. from .config.data_node_config import DataNodeConfig
  28. from .config.scenario_config import ScenarioConfig
  29. from .cycle._cycle_manager_factory import _CycleManagerFactory
  30. from .cycle.cycle import Cycle
  31. from .cycle.cycle_id import CycleId
  32. from .data._data_manager_factory import _DataManagerFactory
  33. from .data.data_node import DataNode
  34. from .data.data_node_id import DataNodeId
  35. from .exceptions.exceptions import DataNodeConfigIsNotGlobal, ModelNotFound, NonExistingVersion
  36. from .job._job_manager_factory import _JobManagerFactory
  37. from .job.job import Job
  38. from .job.job_id import JobId
  39. from .orchestrator import Orchestrator
  40. from .reason import EntityDoesNotExist, EntityIsNotSubmittableEntity, ReasonCollection
  41. from .scenario._scenario_manager_factory import _ScenarioManagerFactory
  42. from .scenario.scenario import Scenario
  43. from .scenario.scenario_id import ScenarioId
  44. from .sequence._sequence_manager_factory import _SequenceManagerFactory
  45. from .sequence.sequence import Sequence
  46. from .sequence.sequence_id import SequenceId
  47. from .submission._submission_manager_factory import _SubmissionManagerFactory
  48. from .submission.submission import Submission, SubmissionId
  49. from .task._task_manager_factory import _TaskManagerFactory
  50. from .task.task import Task
  51. from .task.task_id import TaskId
  52. __logger = _TaipyLogger._get_logger()
  53. def set(entity: Union[DataNode, Task, Sequence, Scenario, Cycle, Submission]):
  54. """Save or update an entity.
  55. This function allows you to save or update an entity in Taipy.
  56. Arguments:
  57. entity (Union[DataNode^, Task^, Sequence^, Scenario^, Cycle^, Submission^]): The
  58. entity to save or update.
  59. """
  60. if isinstance(entity, Cycle):
  61. return _CycleManagerFactory._build_manager()._set(entity)
  62. if isinstance(entity, Scenario):
  63. return _ScenarioManagerFactory._build_manager()._set(entity)
  64. if isinstance(entity, Sequence):
  65. return _SequenceManagerFactory._build_manager()._set(entity)
  66. if isinstance(entity, Task):
  67. return _TaskManagerFactory._build_manager()._set(entity)
  68. if isinstance(entity, DataNode):
  69. return _DataManagerFactory._build_manager()._set(entity)
  70. if isinstance(entity, Submission):
  71. return _SubmissionManagerFactory._build_manager()._set(entity)
  72. def is_submittable(entity: Union[Scenario, ScenarioId, Sequence, SequenceId, Task, TaskId, str]) -> ReasonCollection:
  73. """Indicate if an entity can be submitted.
  74. This function checks if the given entity can be submitted for execution.
  75. Returns:
  76. True if the given entity can be submitted. False otherwise.
  77. """
  78. if isinstance(entity, Scenario):
  79. return _ScenarioManagerFactory._build_manager()._is_submittable(entity)
  80. if isinstance(entity, str) and entity.startswith(Scenario._ID_PREFIX):
  81. return _ScenarioManagerFactory._build_manager()._is_submittable(ScenarioId(entity))
  82. if isinstance(entity, Sequence):
  83. return _SequenceManagerFactory._build_manager()._is_submittable(entity)
  84. if isinstance(entity, str) and entity.startswith(Sequence._ID_PREFIX):
  85. return _SequenceManagerFactory._build_manager()._is_submittable(SequenceId(entity))
  86. if isinstance(entity, Task):
  87. return _TaskManagerFactory._build_manager()._is_submittable(entity)
  88. if isinstance(entity, str) and entity.startswith(Task._ID_PREFIX):
  89. return _TaskManagerFactory._build_manager()._is_submittable(TaskId(entity))
  90. return ReasonCollection()._add_reason(str(entity), EntityIsNotSubmittableEntity(str(entity)))
  91. def is_editable(
  92. entity: Union[
  93. DataNode,
  94. Task,
  95. Job,
  96. Sequence,
  97. Scenario,
  98. Cycle,
  99. Submission,
  100. DataNodeId,
  101. TaskId,
  102. JobId,
  103. SequenceId,
  104. ScenarioId,
  105. CycleId,
  106. SubmissionId,
  107. ],
  108. ) -> ReasonCollection:
  109. """Indicate if an entity can be edited.
  110. This function checks if the given entity can be edited.
  111. Returns:
  112. A ReasonCollection object that can function as a Boolean value,
  113. which is True if the given entity can be edited. False otherwise.
  114. """
  115. if isinstance(entity, Cycle):
  116. return _CycleManagerFactory._build_manager()._is_editable(entity)
  117. if isinstance(entity, str) and entity.startswith(Cycle._ID_PREFIX):
  118. return _CycleManagerFactory._build_manager()._is_editable(CycleId(entity))
  119. if isinstance(entity, Scenario):
  120. return _ScenarioManagerFactory._build_manager()._is_editable(entity)
  121. if isinstance(entity, str) and entity.startswith(Scenario._ID_PREFIX):
  122. return _ScenarioManagerFactory._build_manager()._is_editable(ScenarioId(entity))
  123. if isinstance(entity, Sequence):
  124. return _SequenceManagerFactory._build_manager()._is_editable(entity)
  125. if isinstance(entity, str) and entity.startswith(Sequence._ID_PREFIX):
  126. return _SequenceManagerFactory._build_manager()._is_editable(SequenceId(entity))
  127. if isinstance(entity, Task):
  128. return _TaskManagerFactory._build_manager()._is_editable(entity)
  129. if isinstance(entity, str) and entity.startswith(Task._ID_PREFIX):
  130. return _TaskManagerFactory._build_manager()._is_editable(TaskId(entity))
  131. if isinstance(entity, Job):
  132. return _JobManagerFactory._build_manager()._is_editable(entity)
  133. if isinstance(entity, str) and entity.startswith(Job._ID_PREFIX):
  134. return _JobManagerFactory._build_manager()._is_editable(JobId(entity))
  135. if isinstance(entity, DataNode):
  136. return _DataManagerFactory._build_manager()._is_editable(entity)
  137. if isinstance(entity, str) and entity.startswith(DataNode._ID_PREFIX):
  138. return _DataManagerFactory._build_manager()._is_editable(DataNodeId(entity))
  139. if isinstance(entity, Submission):
  140. return _SubmissionManagerFactory._build_manager()._is_editable(entity)
  141. if isinstance(entity, str) and entity.startswith(Submission._ID_PREFIX):
  142. return _SubmissionManagerFactory._build_manager()._is_editable(SequenceId(entity))
  143. return ReasonCollection()._add_reason(str(entity), EntityDoesNotExist(str(entity)))
  144. def is_readable(
  145. entity: Union[
  146. DataNode,
  147. Task,
  148. Job,
  149. Sequence,
  150. Scenario,
  151. Cycle,
  152. Submission,
  153. DataNodeId,
  154. TaskId,
  155. JobId,
  156. SequenceId,
  157. ScenarioId,
  158. CycleId,
  159. SubmissionId,
  160. ],
  161. ) -> ReasonCollection:
  162. """Indicate if an entity can be read.
  163. This function checks if the given entity can be read.
  164. Returns:
  165. A ReasonCollection object that can function as a Boolean value,
  166. which is True if the given entity can be read. False otherwise.
  167. """
  168. if isinstance(entity, Cycle):
  169. return _CycleManagerFactory._build_manager()._is_readable(entity)
  170. if isinstance(entity, str) and entity.startswith(Cycle._ID_PREFIX):
  171. return _CycleManagerFactory._build_manager()._is_readable(CycleId(entity))
  172. if isinstance(entity, Scenario):
  173. return _ScenarioManagerFactory._build_manager()._is_readable(entity)
  174. if isinstance(entity, str) and entity.startswith(Scenario._ID_PREFIX):
  175. return _ScenarioManagerFactory._build_manager()._is_readable(ScenarioId(entity))
  176. if isinstance(entity, Sequence):
  177. return _SequenceManagerFactory._build_manager()._is_readable(entity)
  178. if isinstance(entity, str) and entity.startswith(Sequence._ID_PREFIX):
  179. return _SequenceManagerFactory._build_manager()._is_readable(SequenceId(entity))
  180. if isinstance(entity, Task):
  181. return _TaskManagerFactory._build_manager()._is_readable(entity)
  182. if isinstance(entity, str) and entity.startswith(Task._ID_PREFIX):
  183. return _TaskManagerFactory._build_manager()._is_readable(TaskId(entity))
  184. if isinstance(entity, Job):
  185. return _JobManagerFactory._build_manager()._is_readable(entity)
  186. if isinstance(entity, str) and entity.startswith(Job._ID_PREFIX):
  187. return _JobManagerFactory._build_manager()._is_readable(JobId(entity))
  188. if isinstance(entity, DataNode):
  189. return _DataManagerFactory._build_manager()._is_readable(entity)
  190. if isinstance(entity, str) and entity.startswith(DataNode._ID_PREFIX):
  191. return _DataManagerFactory._build_manager()._is_readable(DataNodeId(entity))
  192. if isinstance(entity, Submission):
  193. return _SubmissionManagerFactory._build_manager()._is_readable(entity)
  194. if isinstance(entity, str) and entity.startswith(Submission._ID_PREFIX):
  195. return _SubmissionManagerFactory._build_manager()._is_readable(SequenceId(entity))
  196. return ReasonCollection()._add_reason(str(entity), EntityDoesNotExist(str(entity)))
  197. @_warn_no_orchestrator_service("The submitted entity will not be executed until the Orchestrator service is running.")
  198. def submit(
  199. entity: Union[Scenario, Sequence, Task],
  200. force: bool = False,
  201. wait: bool = False,
  202. timeout: Optional[Union[float, int]] = None,
  203. **properties,
  204. ) -> Submission:
  205. """Submit a scenario, sequence or task entity for execution.
  206. This function submits the given entity for execution and returns the created job(s).
  207. If the entity is a sequence or a scenario, all the tasks of the entity are
  208. submitted for execution.
  209. Arguments:
  210. entity (Union[Scenario^, Sequence^, Task^]): The scenario, sequence or task to submit.
  211. force (bool): If True, the execution is forced even if for skippable tasks.
  212. wait (bool): Wait for the orchestrated jobs created from the submission to be finished
  213. in asynchronous mode.
  214. timeout (Union[float, int]): The optional maximum number of seconds to wait
  215. for the jobs to be finished before returning.<br/>
  216. If not provided and *wait* is True, the function waits indefinitely.
  217. **properties (dict[str, any]): A key-worded variable length list of user additional arguments
  218. that will be stored within the `Submission^`. It can be accessed via `Submission.properties^`.
  219. Returns:
  220. The created `Submission^` containing the information about the submission.
  221. """
  222. if isinstance(entity, Scenario):
  223. return _ScenarioManagerFactory._build_manager()._submit(
  224. entity, force=force, wait=wait, timeout=timeout, **properties
  225. )
  226. if isinstance(entity, Sequence):
  227. return _SequenceManagerFactory._build_manager()._submit(
  228. entity, force=force, wait=wait, timeout=timeout, **properties
  229. )
  230. if isinstance(entity, Task):
  231. return _TaskManagerFactory._build_manager()._submit(
  232. entity, force=force, wait=wait, timeout=timeout, **properties
  233. )
  234. return None
  235. @overload
  236. def exists(entity_id: TaskId) -> ReasonCollection: ...
  237. @overload
  238. def exists(entity_id: DataNodeId) -> ReasonCollection: ...
  239. @overload
  240. def exists(entity_id: SequenceId) -> ReasonCollection: ...
  241. @overload
  242. def exists(entity_id: ScenarioId) -> ReasonCollection: ...
  243. @overload
  244. def exists(entity_id: CycleId) -> ReasonCollection: ...
  245. @overload
  246. def exists(entity_id: JobId) -> ReasonCollection: ...
  247. @overload
  248. def exists(entity_id: SubmissionId) -> ReasonCollection: ...
  249. @overload
  250. def exists(entity_id: str) -> ReasonCollection: ...
  251. def exists(
  252. entity_id: Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, CycleId, SubmissionId, str],
  253. ) -> ReasonCollection:
  254. """Check if an entity with the specified identifier exists.
  255. This function checks if an entity with the given identifier exists.
  256. It supports various types of entity identifiers, including `TaskId`,
  257. `DataNodeId`, `SequenceId`, `ScenarioId`, `JobId`, `CycleId`, `SubmissionId`, and string
  258. representations.
  259. Arguments:
  260. entity_id (Union[DataNodeId, TaskId, SequenceId, ScenarioId, JobId, CycleId, SubmissionId, str]): The
  261. identifier of the entity to check for existence.
  262. Returns:
  263. A ReasonCollection object that can function as a Boolean value,
  264. which is True if the given entity exists. False otherwise.
  265. Raises:
  266. ModelNotFound: If the entity's type cannot be determined.
  267. Note:
  268. The function performs checks for various entity types
  269. (`Job^`, `Cycle^`, `Scenario^`, `Sequence^`, `Task^`, `DataNode^`, `Submission^`)
  270. based on their respective identifier prefixes.
  271. """
  272. if _is_job(entity_id):
  273. return _JobManagerFactory._build_manager()._exists(JobId(entity_id))
  274. if _is_cycle(entity_id):
  275. return _CycleManagerFactory._build_manager()._exists(CycleId(entity_id))
  276. if _is_scenario(entity_id):
  277. return _ScenarioManagerFactory._build_manager()._exists(ScenarioId(entity_id))
  278. if _is_sequence(entity_id):
  279. return _SequenceManagerFactory._build_manager()._exists(SequenceId(entity_id))
  280. if _is_task(entity_id):
  281. return _TaskManagerFactory._build_manager()._exists(TaskId(entity_id))
  282. if _is_data_node(entity_id):
  283. return _DataManagerFactory._build_manager()._exists(DataNodeId(entity_id))
  284. if _is_submission(entity_id):
  285. return _SubmissionManagerFactory._build_manager()._exists(SubmissionId(entity_id))
  286. raise ModelNotFound("NOT_DETERMINED", entity_id)
  287. @overload
  288. def get(entity_id: TaskId) -> Task: ...
  289. @overload
  290. def get(entity_id: DataNodeId) -> DataNode: ...
  291. @overload
  292. def get(entity_id: SequenceId) -> Sequence: ...
  293. @overload
  294. def get(entity_id: ScenarioId) -> Scenario: ...
  295. @overload
  296. def get(entity_id: CycleId) -> Cycle: ...
  297. @overload
  298. def get(entity_id: JobId) -> Job: ...
  299. @overload
  300. def get(entity_id: SubmissionId) -> Submission: ...
  301. @overload
  302. def get(entity_id: str) -> Union[Task, DataNode, Sequence, Scenario, Job, Cycle, Submission]: ...
  303. def get(
  304. entity_id: Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, CycleId, SubmissionId, str],
  305. ) -> Union[Task, DataNode, Sequence, Scenario, Job, Cycle, Submission]:
  306. """Retrieve an entity by its unique identifier.
  307. This function allows you to retrieve an entity by specifying its identifier.
  308. The identifier must match the pattern of one of the supported entity types:
  309. `Task^`, `DataNode^`, `Sequence^`, `Job^`, `Cycle^`, `Submission^`, or `Scenario^`.
  310. Arguments:
  311. entity_id (Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, CycleId, str]):
  312. The identifier of the entity to retrieve.<br/>
  313. It should conform to the identifier pattern of one of the entities (`Task^`, `DataNode^`,
  314. `Sequence^`, `Job^`, `Cycle^` or `Scenario^`).
  315. Returns:
  316. The entity that corresponds to the provided identifier. Returns None if no matching entity is found.
  317. Raises:
  318. ModelNotFound^: If the provided *entity_id* does not match any known entity pattern.
  319. """
  320. if _is_job(entity_id):
  321. return _JobManagerFactory._build_manager()._get(JobId(entity_id))
  322. if _is_cycle(entity_id):
  323. return _CycleManagerFactory._build_manager()._get(CycleId(entity_id))
  324. if _is_scenario(entity_id):
  325. return _ScenarioManagerFactory._build_manager()._get(ScenarioId(entity_id))
  326. if _is_sequence(entity_id):
  327. return _SequenceManagerFactory._build_manager()._get(SequenceId(entity_id))
  328. if _is_task(entity_id):
  329. return _TaskManagerFactory._build_manager()._get(TaskId(entity_id))
  330. if _is_data_node(entity_id):
  331. return _DataManagerFactory._build_manager()._get(DataNodeId(entity_id))
  332. if _is_submission(entity_id):
  333. return _SubmissionManagerFactory._build_manager()._get(SubmissionId(entity_id))
  334. raise ModelNotFound("NOT_DETERMINED", entity_id)
  335. def get_tasks() -> List[Task]:
  336. """Retrieve a list of all existing tasks.
  337. This function returns a list of all tasks that currently exist.
  338. Returns:
  339. A list containing all the tasks.
  340. """
  341. return _TaskManagerFactory._build_manager()._get_all()
  342. def is_deletable(entity: Union[Scenario, Job, Submission, ScenarioId, JobId, SubmissionId]) -> ReasonCollection:
  343. """Check if a `Scenario^`, a `Job^` or a `Submission^` can be deleted.
  344. This function determines whether a scenario or a job can be safely
  345. deleted without causing conflicts or issues.
  346. Arguments:
  347. entity (Union[Scenario, Job, Submission, ScenarioId, JobId, SubmissionId]): The scenario,
  348. job or submission to check.
  349. Returns:
  350. A ReasonCollection object that can function as a Boolean value, which is True
  351. if the given scenario, job or submission can be deleted. False otherwise.
  352. """
  353. if isinstance(entity, Job):
  354. return _JobManagerFactory._build_manager()._is_deletable(entity)
  355. if isinstance(entity, str) and entity.startswith(Job._ID_PREFIX):
  356. return _JobManagerFactory._build_manager()._is_deletable(JobId(entity))
  357. if isinstance(entity, Scenario):
  358. return _ScenarioManagerFactory._build_manager()._is_deletable(entity)
  359. if isinstance(entity, str) and entity.startswith(Scenario._ID_PREFIX):
  360. return _ScenarioManagerFactory._build_manager()._is_deletable(ScenarioId(entity))
  361. if isinstance(entity, Submission):
  362. return _SubmissionManagerFactory._build_manager()._is_deletable(entity)
  363. if isinstance(entity, str) and entity.startswith(Submission._ID_PREFIX):
  364. return _SubmissionManagerFactory._build_manager()._is_deletable(SubmissionId(entity))
  365. return ReasonCollection()._add_reason(str(entity), EntityDoesNotExist(str(entity)))
  366. def delete(entity_id: Union[TaskId, DataNodeId, SequenceId, ScenarioId, JobId, CycleId, SubmissionId]):
  367. """Delete an entity and its nested entities.
  368. This function deletes the specified entity and recursively deletes all its nested entities.
  369. The behavior varies depending on the type of entity provided:
  370. - If a `CycleId` is provided, the nested scenarios, tasks, data nodes, and jobs are deleted.
  371. - If a `ScenarioId` is provided, the nested sequences, tasks, data nodes, submissions and jobs are deleted.
  372. If the scenario is primary, it can only be deleted if it is the only scenario in the cycle.
  373. In that case, its cycle is also deleted. Use the `is_deletable()^` function to check if
  374. the scenario can be deleted.
  375. - If a `SequenceId` is provided, the related jobs are deleted.
  376. - If a `TaskId` is provided, the related data nodes, and jobs are deleted.
  377. - If a `DataNodeId` is provided, the data node is deleted.
  378. - If a `SubmissionId` is provided, the related jobs are deleted.
  379. The submission can only be deleted if the execution has been finished.
  380. - If a `JobId` is provided, the job entity can only be deleted if the execution has been finished.
  381. Arguments:
  382. entity_id (Union[TaskId, DataNodeId, SequenceId, ScenarioId, SubmissionId, JobId, CycleId]):
  383. The identifier of the entity to delete.
  384. Raises:
  385. ModelNotFound^: No entity corresponds to the specified *entity_id*.
  386. """
  387. if _is_job(entity_id):
  388. job_manager = _JobManagerFactory._build_manager()
  389. return job_manager._delete(job_manager._get(JobId(entity_id)))
  390. if _is_cycle(entity_id):
  391. return _CycleManagerFactory._build_manager()._hard_delete(CycleId(entity_id))
  392. if _is_scenario(entity_id):
  393. return _ScenarioManagerFactory._build_manager()._hard_delete(ScenarioId(entity_id))
  394. if _is_sequence(entity_id):
  395. return _SequenceManagerFactory._build_manager()._hard_delete(SequenceId(entity_id))
  396. if _is_task(entity_id):
  397. return _TaskManagerFactory._build_manager()._hard_delete(TaskId(entity_id))
  398. if _is_data_node(entity_id):
  399. return _DataManagerFactory._build_manager()._delete(DataNodeId(entity_id))
  400. if _is_submission(entity_id):
  401. return _SubmissionManagerFactory._build_manager()._hard_delete(SubmissionId(entity_id))
  402. raise ModelNotFound("NOT_DETERMINED", entity_id)
  403. def get_scenarios(
  404. cycle: Optional[Cycle] = None,
  405. tag: Optional[str] = None,
  406. is_sorted: bool = False,
  407. descending: bool = False,
  408. created_start_time: Optional[datetime] = None,
  409. created_end_time: Optional[datetime] = None,
  410. sort_key: Literal["name", "id", "config_id", "creation_date", "tags"] = "name",
  411. ) -> List[Scenario]:
  412. """Retrieve a list of existing scenarios filtered by cycle or tag.
  413. This function allows you to retrieve a list of scenarios based on optional
  414. filtering criteria. If both a *cycle* and a *tag* are provided, the returned
  415. list contains scenarios that belong to the specified *cycle* and also
  416. have the specified *tag*.
  417. Arguments:
  418. cycle (Optional[Cycle^]): The optional `Cycle^` to filter scenarios by.
  419. tag (Optional[str]): The optional tag to filter scenarios by.
  420. is_sorted (bool): If True, sort the output list of scenarios using the sorting key.
  421. The default value is False.
  422. descending (bool): If True, sort the output list of scenarios in descending order.
  423. The default value is False.
  424. created_start_time (Optional[datetime]): The optional inclusive start date to filter scenarios by creation date.
  425. created_end_time (Optional[datetime]): The optional exclusive end date to filter scenarios by creation date.
  426. sort_key (Literal["name", "id", "creation_date", "tags"]): The optional sort_key to
  427. decide upon what key scenarios are sorted. The sorting is in increasing order for
  428. dates, in alphabetical order for name and id, and in lexicographical order for tags.
  429. The default value is "name".<br/>
  430. If an incorrect sorting key is provided, the scenarios are sorted by name.
  431. Returns:
  432. The list of scenarios filtered by cycle or tag.
  433. """
  434. scenario_manager = _ScenarioManagerFactory._build_manager()
  435. if not cycle and not tag:
  436. scenarios = scenario_manager._get_all()
  437. elif cycle and not tag:
  438. scenarios = scenario_manager._get_all_by_cycle(cycle)
  439. elif not cycle and tag:
  440. scenarios = scenario_manager._get_all_by_tag(tag)
  441. elif cycle and tag:
  442. scenarios = scenario_manager._get_all_by_cycle_tag(cycle, tag)
  443. else:
  444. scenarios = []
  445. if created_start_time or created_end_time:
  446. scenarios = scenario_manager._filter_by_creation_time(scenarios, created_start_time, created_end_time)
  447. if is_sorted:
  448. scenario_manager._sort_scenarios(scenarios, descending, sort_key)
  449. return scenarios
  450. def get_primary(cycle: Cycle) -> Optional[Scenario]:
  451. """Retrieve the primary scenario associated with a cycle.
  452. Arguments:
  453. cycle (Cycle^): The cycle for which to retrieve the primary scenario.
  454. Returns:
  455. The primary scenario of the given _cycle_. If the cycle has no
  456. primary scenario, this method returns None.
  457. """
  458. return _ScenarioManagerFactory._build_manager()._get_primary(cycle)
  459. def get_primary_scenarios(
  460. is_sorted: bool = False,
  461. descending: bool = False,
  462. created_start_time: Optional[datetime] = None,
  463. created_end_time: Optional[datetime] = None,
  464. sort_key: Literal["name", "id", "config_id", "creation_date", "tags"] = "name",
  465. ) -> List[Scenario]:
  466. """Retrieve a list of all primary scenarios.
  467. Arguments:
  468. is_sorted (bool): If True, sort the output list of scenarios using the sorting key.
  469. The default value is False.
  470. descending (bool): If True, sort the output list of scenarios in descending order.
  471. The default value is False.
  472. created_start_time (Optional[datetime]): The optional inclusive start date to filter scenarios by creation date.
  473. created_end_time (Optional[datetime]): The optional exclusive end date to filter scenarios by creation date.
  474. sort_key (Literal["name", "id", "creation_date", "tags"]): The optional sort_key to
  475. decide upon what key scenarios are sorted. The sorting is in increasing order for
  476. dates, in alphabetical order for name and id, and in lexicographical order for tags.
  477. The default value is "name".<br/>
  478. If an incorrect sorting key is provided, the scenarios are sorted by name.
  479. Returns:
  480. A list contains all primary scenarios.
  481. """
  482. scenario_manager = _ScenarioManagerFactory._build_manager()
  483. scenarios = scenario_manager._get_primary_scenarios()
  484. if created_start_time or created_end_time:
  485. scenarios = scenario_manager._filter_by_creation_time(scenarios, created_start_time, created_end_time)
  486. if is_sorted:
  487. scenario_manager._sort_scenarios(scenarios, descending, sort_key)
  488. return scenarios
  489. def is_promotable(scenario: Union[Scenario, ScenarioId]) -> ReasonCollection:
  490. """Determine if a scenario can be promoted to become a primary scenario.
  491. This function checks whether the given scenario is eligible to be promoted
  492. as a primary scenario.
  493. Arguments:
  494. scenario (Union[Scenario, ScenarioId]): The scenario to be evaluated for promotion.
  495. Returns:
  496. A ReasonCollection object that can function as a Boolean value,
  497. which is True if the given scenario can be promoted to be a primary scenario. False otherwise.
  498. """
  499. return _ScenarioManagerFactory._build_manager()._is_promotable_to_primary(scenario)
  500. def set_primary(scenario: Scenario):
  501. """Promote a scenario as the primary scenario of its cycle.
  502. This function promotes the given scenario as the primary scenario of its associated cycle.
  503. If the cycle already has a primary scenario, that scenario is demoted and is
  504. no longer considered the primary scenario for its cycle.
  505. Arguments:
  506. scenario (Scenario^): The scenario to promote as the new _primary_ scenario.
  507. """
  508. return _ScenarioManagerFactory._build_manager()._set_primary(scenario)
  509. def tag(scenario: Scenario, tag: str):
  510. """Add a tag to a scenario.
  511. This function adds a user-defined tag to the specified scenario.
  512. Arguments:
  513. scenario (Scenario^): The scenario to which the tag will be added.
  514. tag (str): The tag to apply to the scenario.
  515. """
  516. return _ScenarioManagerFactory._build_manager()._tag(scenario, tag)
  517. def untag(scenario: Scenario, tag: str):
  518. """Remove a tag from a scenario.
  519. This function removes a specified tag from the given scenario. If the scenario does
  520. not have the specified tag, it has no effect.
  521. Arguments:
  522. scenario (Scenario^): The scenario from which the tag will be removed.
  523. tag (str): The tag to remove from the scenario.
  524. """
  525. return _ScenarioManagerFactory._build_manager()._untag(scenario, tag)
  526. def compare_scenarios(*scenarios: Scenario, data_node_config_id: Optional[str] = None) -> Dict[str, Any]:
  527. """Compare the data nodes of several scenarios.
  528. You can specify which data node config identifier should the comparison be performed
  529. on.
  530. Arguments:
  531. *scenarios (*Scenario^): The list of the scenarios to compare.
  532. data_node_config_id (Optional[str]): The config identifier of the DataNode to perform
  533. the comparison on. <br/>
  534. If _data_node_config_id_ is not provided, the scenarios are
  535. compared on all defined comparators.<br/>
  536. Returns:
  537. The comparison results. The key is the data node config identifier used for
  538. comparison.
  539. Raises:
  540. InsufficientScenarioToCompare^: Raised when only one or no scenario for comparison
  541. is provided.
  542. NonExistingComparator^: Raised when the scenario comparator does not exist.
  543. DifferentScenarioConfigs^: Raised when the provided scenarios do not share the
  544. same scenario config.
  545. NonExistingScenarioConfig^: Raised when the scenario config of the provided
  546. scenarios could not be found.
  547. """
  548. return _ScenarioManagerFactory._build_manager()._compare(*scenarios, data_node_config_id=data_node_config_id)
  549. def subscribe_scenario(
  550. callback: Callable[[Scenario, Job], None],
  551. params: Optional[List[Any]] = None,
  552. scenario: Optional[Scenario] = None,
  553. ):
  554. """Subscribe a function to be called on job status change.
  555. The subscription is applied to all jobs created for the execution of _scenario_.
  556. If no scenario is provided, the subscription applies to all scenarios.
  557. Arguments:
  558. callback (Callable[[Scenario^, Job^], None]): The function to be called on
  559. status change.
  560. params (Optional[List[Any]]): The parameters to be passed to the _callback_.
  561. scenario (Optional[Scenario^]): The scenario to which the callback is applied.
  562. If None, the subscription is registered for all scenarios.
  563. Note:
  564. Notifications are applied only for jobs created **after** this subscription.
  565. """
  566. params = [] if params is None else params
  567. return _ScenarioManagerFactory._build_manager()._subscribe(callback, params, scenario)
  568. def unsubscribe_scenario(
  569. callback: Callable[[Scenario, Job], None], params: Optional[List[Any]] = None, scenario: Optional[Scenario] = None
  570. ):
  571. """Unsubscribe a function that is called when the status of a `Job^` changes.
  572. If no scenario is provided, the subscription is removed for all scenarios.
  573. Arguments:
  574. callback (Callable[[Scenario^, Job^], None]): The function to unsubscribe from.
  575. params (Optional[List[Any]]): The parameters to be passed to the callback.
  576. scenario (Optional[Scenario]): The scenario to unsubscribe from. If None, it
  577. applies to all scenarios.
  578. Note:
  579. The callback function will continue to be called for ongoing jobs.
  580. """
  581. return _ScenarioManagerFactory._build_manager()._unsubscribe(callback, params, scenario)
  582. def subscribe_sequence(
  583. callback: Callable[[Sequence, Job], None], params: Optional[List[Any]] = None, sequence: Optional[Sequence] = None
  584. ):
  585. """Subscribe a function to be called on job status change.
  586. The subscription is applied to all jobs created for the execution of _sequence_.
  587. Arguments:
  588. callback (Callable[[Sequence^, Job^], None]): The callable function to be called on
  589. status change.
  590. params (Optional[List[Any]]): The parameters to be passed to the _callback_.
  591. sequence (Optional[Sequence^]): The sequence to subscribe on. If None, the subscription
  592. is applied to all sequences.
  593. Note:
  594. Notifications are applied only for jobs created **after** this subscription.
  595. """
  596. return _SequenceManagerFactory._build_manager()._subscribe(callback, params, sequence)
  597. def unsubscribe_sequence(
  598. callback: Callable[[Sequence, Job], None], params: Optional[List[Any]] = None, sequence: Optional[Sequence] = None
  599. ) -> None:
  600. """Unsubscribe a function that is called when the status of a Job changes.
  601. Arguments:
  602. callback (Callable[[Sequence^, Job^], None]): The callable function to be called on
  603. status change.
  604. params (Optional[List[Any]]): The parameters to be passed to the _callback_.
  605. sequence (Optional[Sequence^]): The sequence to unsubscribe to. If None, it applies
  606. to all sequences.
  607. Note:
  608. The function will continue to be called for ongoing jobs.
  609. """
  610. return _SequenceManagerFactory._build_manager()._unsubscribe(callback, params, sequence)
  611. def get_sequences() -> List[Sequence]:
  612. """Return all existing sequences.
  613. Returns:
  614. The list of all sequences.
  615. """
  616. return _SequenceManagerFactory._build_manager()._get_all()
  617. def get_jobs() -> List[Job]:
  618. """Return all the existing jobs.
  619. Returns:
  620. The list of all jobs.
  621. """
  622. return _JobManagerFactory._build_manager()._get_all()
  623. def get_submissions() -> List[Submission]:
  624. """Return all the existing submissions.
  625. Returns:
  626. The list of all submissions.
  627. """
  628. return _SubmissionManagerFactory._build_manager()._get_all()
  629. def delete_job(job: Job, force: Optional[bool] = False):
  630. """Delete a job.
  631. This function deletes the specified job. If the job is not completed and
  632. *force* is not set to True, a `JobNotDeletedException^` may be raised.
  633. Arguments:
  634. job (Job^): The job to delete.
  635. force (Optional[bool]): If True, forces the deletion of _job_, even
  636. if it is not completed yet.
  637. Raises:
  638. JobNotDeletedException^: If the job is not finished.
  639. """
  640. return _JobManagerFactory._build_manager()._delete(job, force)
  641. def delete_jobs():
  642. """Delete all jobs."""
  643. return _JobManagerFactory._build_manager()._delete_all()
  644. def cancel_job(job: Union[str, Job]):
  645. """Cancel a job and set the status of the subsequent jobs to ABANDONED.
  646. This function cancels the specified job and sets the status of any subsequent jobs to ABANDONED.
  647. Arguments:
  648. job (Job^): The job to cancel.
  649. """
  650. _JobManagerFactory._build_manager()._cancel(job)
  651. def get_latest_job(task: Task) -> Optional[Job]:
  652. """Return the latest job of a task.
  653. This function retrieves the latest job associated with a task.
  654. Arguments:
  655. task (Task^): The task to retrieve the latest job from.
  656. Returns:
  657. The latest job created from _task_, or None if no job has been created from _task_.
  658. """
  659. return _JobManagerFactory._build_manager()._get_latest(task)
  660. def get_latest_submission(entity: Union[Scenario, Sequence, Task]) -> Optional[Submission]:
  661. """Return the latest submission of a scenario, sequence or task.
  662. This function retrieves the latest submission associated with a scenario, sequence or task.
  663. Arguments:
  664. entity (Union[Scenario^, Sequence^, Task^]): The scenario, sequence or task to
  665. retrieve the latest submission from.
  666. Returns:
  667. The latest submission created from _scenario_, _sequence_ and _task_, or None
  668. if no submission has been created from _scenario_, _sequence_ and _task_.
  669. """
  670. return _SubmissionManagerFactory._build_manager()._get_latest(entity)
  671. def get_data_nodes() -> List[DataNode]:
  672. """Return all the existing data nodes.
  673. Returns:
  674. The list of all data nodes.
  675. """
  676. return _DataManagerFactory._build_manager()._get_all()
  677. def get_cycles() -> List[Cycle]:
  678. """Return the list of all existing cycles.
  679. Returns:
  680. The list of all cycles.
  681. """
  682. return _CycleManagerFactory._build_manager()._get_all()
  683. def can_create(config: Optional[Union[ScenarioConfig, DataNodeConfig]] = None) -> ReasonCollection:
  684. """Indicate if a config section can be used to instantiate a scenario or a data node.
  685. If no config is provided, the function indicates if any scenario or data node config can be created.
  686. Returns:
  687. True if the given config can be created. False otherwise.
  688. """
  689. if isinstance(config, DataNodeConfig):
  690. return _DataManagerFactory._build_manager()._can_create(config)
  691. return _ScenarioManagerFactory._build_manager()._can_create(config)
  692. def create_scenario(
  693. config: ScenarioConfig,
  694. creation_date: Optional[datetime] = None,
  695. name: Optional[str] = None,
  696. ) -> Scenario:
  697. """Create and return a new scenario based on a scenario configuration.
  698. This function checks and locks the configuration, manages application's version,
  699. and creates a new scenario from the scenario configuration provided.
  700. If the scenario belongs to a cycle, the cycle (corresponding to the _creation_date_
  701. and the configuration frequency attribute) is created if it does not exist yet.
  702. Arguments:
  703. config (ScenarioConfig^): The scenario configuration used to create a new scenario.
  704. creation_date (Optional[datetime.datetime]): The creation date of the scenario.
  705. If None, the current date time is used.
  706. name (Optional[str]): The displayable name of the scenario.
  707. Returns:
  708. The new scenario.
  709. Raises:
  710. SystemExit: If the configuration check returns some errors.
  711. """
  712. Orchestrator._manage_version_and_block_config()
  713. return _ScenarioManagerFactory._build_manager()._create(config, creation_date, name)
  714. def create_global_data_node(config: DataNodeConfig) -> DataNode:
  715. """Create and return a new GLOBAL data node from a data node configuration.
  716. This function checks and locks the configuration, manages application's version,
  717. and creates the new data node from the data node configuration provided.
  718. Arguments:
  719. config (DataNodeConfig^): The data node configuration. It must have a `GLOBAL` scope.
  720. Returns:
  721. The new global data node.
  722. Raises:
  723. DataNodeConfigIsNotGlobal^: If the data node configuration does not have GLOBAL scope.
  724. SystemExit: If the configuration check returns some errors.
  725. """
  726. # Check if the data node config has GLOBAL scope
  727. if config.scope is not Scope.GLOBAL:
  728. raise DataNodeConfigIsNotGlobal(config.id)
  729. Orchestrator._manage_version_and_block_config()
  730. if dns := _DataManagerFactory._build_manager()._get_by_config_id(config.id):
  731. return dns[0]
  732. return _DataManagerFactory._build_manager()._create_and_set(config, None, None)
  733. def clean_all_entities(version_number: str) -> bool:
  734. """Deletes all entities associated with the specified version.
  735. This function cleans all entities, including jobs, submissions, scenarios, cycles, sequences, tasks, and data nodes.
  736. Arguments:
  737. version_number (str): The version number of the entities to be deleted.<br/>
  738. - If the specified version does not exist, the operation will be aborted, and False will be returned.
  739. Returns:
  740. True if the operation succeeded, False otherwise.
  741. """
  742. version_manager = _VersionManagerFactory._build_manager()
  743. try:
  744. version_number = version_manager._replace_version_number(version_number)
  745. except NonExistingVersion as e:
  746. __logger.warning(f"{e.message} Abort cleaning the entities of version '{version_number}'.")
  747. return False
  748. if not version_manager._is_deletable(version_number):
  749. reason_collection = version_manager._is_deletable(version_number)
  750. __logger.warning(f"Abort cleaning the entities of version '{version_number}'. {reason_collection.reasons}.")
  751. return False
  752. _JobManagerFactory._build_manager()._delete_by_version(version_number)
  753. _SubmissionManagerFactory._build_manager()._delete_by_version(version_number)
  754. _ScenarioManagerFactory._build_manager()._delete_by_version(version_number)
  755. _SequenceManagerFactory._build_manager()._delete_by_version(version_number)
  756. _TaskManagerFactory._build_manager()._delete_by_version(version_number)
  757. _DataManagerFactory._build_manager()._delete_by_version(version_number)
  758. version_manager._delete(version_number)
  759. return True
  760. def get_parents(
  761. entity: Union[TaskId, DataNodeId, SequenceId, Task, DataNode, Sequence], parent_dict=None
  762. ) -> Dict[str, Set[_Entity]]:
  763. """Get the parents of an entity from itself or its identifier.
  764. Arguments:
  765. entity (Union[TaskId, DataNodeId, SequenceId, Task, DataNode, Sequence]): The entity or its
  766. identifier to get the parents.
  767. Returns:
  768. The dictionary of all parent entities.
  769. They are grouped by their type (Scenario^, Sequences^, or tasks^) so each key corresponds
  770. to a level of the parents and the value is a set of the parent entities.
  771. An empty dictionary is returned if the entity does not have parents.<br/>
  772. Example: The following instruction returns all the scenarios that include the
  773. datanode identified by "my_datanode_id".
  774. `taipy.get_parents("id_of_my_datanode")["scenario"]`
  775. Raises:
  776. ModelNotFound^: If _entity_ does not match a correct entity pattern.
  777. """
  778. def update_parent_dict(parents_set, parent_dict):
  779. for k, value in parents_set.items():
  780. if k in parent_dict.keys():
  781. parent_dict[k].update(value)
  782. else:
  783. parent_dict[k] = value
  784. if isinstance(entity, str):
  785. entity = get(entity)
  786. parent_dict = parent_dict or {}
  787. if isinstance(entity, (Scenario, Cycle)):
  788. return parent_dict
  789. current_parent_dict: Dict[str, Set] = {}
  790. for parent in entity.parent_ids:
  791. parent_entity = get(parent)
  792. if parent_entity._MANAGER_NAME in current_parent_dict.keys():
  793. current_parent_dict[parent_entity._MANAGER_NAME].add(parent_entity)
  794. else:
  795. current_parent_dict[parent_entity._MANAGER_NAME] = {parent_entity}
  796. if isinstance(entity, Sequence):
  797. update_parent_dict(current_parent_dict, parent_dict)
  798. if isinstance(entity, Task):
  799. parent_entity_key_to_search_next = "scenario"
  800. update_parent_dict(current_parent_dict, parent_dict)
  801. for parent in parent_dict.get(parent_entity_key_to_search_next, []):
  802. get_parents(parent, parent_dict)
  803. if isinstance(entity, DataNode):
  804. parent_entity_key_to_search_next = "task"
  805. update_parent_dict(current_parent_dict, parent_dict)
  806. for parent in parent_dict.get(parent_entity_key_to_search_next, []):
  807. get_parents(parent, parent_dict)
  808. return parent_dict
  809. def get_cycles_scenarios() -> Dict[Optional[Cycle], List[Scenario]]:
  810. """Get the scenarios grouped by cycles.
  811. Returns:
  812. The dictionary of all cycles and their corresponding scenarios.
  813. """
  814. cycles_scenarios: Dict[Optional[Cycle], List[Scenario]] = {}
  815. for scenario in get_scenarios():
  816. if scenario.cycle in cycles_scenarios.keys():
  817. cycles_scenarios[scenario.cycle].append(scenario)
  818. else:
  819. cycles_scenarios[scenario.cycle] = [scenario]
  820. return cycles_scenarios
  821. def get_entities_by_config_id(
  822. config_id: str,
  823. ) -> Union[List, List[Task], List[DataNode], List[Sequence], List[Scenario]]:
  824. """Get the entities by its config id.
  825. Arguments:
  826. config_id (str): The config id of the entities
  827. Returns:
  828. The list of all entities by the config id.
  829. """
  830. if scenarios := _ScenarioManagerFactory._build_manager()._get_by_config_id(config_id):
  831. return scenarios
  832. if tasks := _TaskManagerFactory._build_manager()._get_by_config_id(config_id):
  833. return tasks
  834. if data_nodes := _DataManagerFactory._build_manager()._get_by_config_id(config_id):
  835. return data_nodes
  836. return scenarios
  837. def can_duplicate(entity: Union[str, Scenario]) -> ReasonCollection:
  838. """Indicate if a scenario can be duplicated.
  839. Arguments:
  840. entity (Union[str, Scenario]): The scenario or its id to check if it can be duplicated.
  841. Returns:
  842. True if the given scenario can be duplicated. False otherwise.
  843. """
  844. return _ScenarioManagerFactory._build_manager()._can_duplicate(entity)
  845. def duplicate_scenario(
  846. scenario: Scenario,
  847. new_creation_date: Optional[datetime] = None,
  848. new_name: Optional[str] = None,
  849. data_to_duplicate: Union[Set[str], bool] = True
  850. ) -> Scenario:
  851. """Duplicate an existing scenario and return a new scenario.
  852. This function duplicates the provided scenario, optionally setting a new creation
  853. date and name.
  854. If the scenario belongs to a cycle, the cycle (corresponding to the creation_date
  855. and the configuration frequency attribute) is created if it does not exist yet.
  856. The nested entities are duplicated or not depending on the creation date of the new
  857. scenario, its cycle, and the various data node scopes.
  858. !!! warning "Data and data nodes duplication"
  859. Note that for now, Taipy can only duplicate data for file-based data nodes. For
  860. other types of data nodes (sql, mongo, etc.), the new data nodes are created
  861. referencing the exact same data as the original data nodes. This can lead to
  862. conflicts if the data is modified in one of the scenarios.
  863. Users must ensure after a duplication that the data nodes' data are correctly
  864. set for the new scenario.
  865. For example, the table name of a SQL table data node must be manually updated to
  866. avoid conflicts.
  867. Arguments:
  868. scenario (Scenario): The scenario to duplicate.
  869. new_creation_date (Optional[datetime.datetime]): The creation date of the new scenario.
  870. If None, the current date and time is used.
  871. new_name (Optional[str]): The displayable name of the new scenario.
  872. data_to_duplicate (Union[Set[str], bool]): A set of data node configuration ids used
  873. to duplicate only the data nodes' data with the specified configuration ids.
  874. If True, all data nodes are duplicated. If False, no data nodes are duplicated.
  875. Returns:
  876. Scenario: The newly duplicated scenario.
  877. """
  878. return _ScenarioManagerFactory._build_manager()._duplicate(scenario, new_creation_date, new_name, data_to_duplicate)