test_data_node.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  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. import os
  12. from datetime import datetime, timedelta
  13. from time import sleep
  14. from unittest import mock
  15. import pytest
  16. import taipy.core as tp
  17. from taipy.common.config import Config
  18. from taipy.common.config.common.scope import Scope
  19. from taipy.common.config.exceptions.exceptions import InvalidConfigurationId
  20. from taipy.core.data._data_manager import _DataManager
  21. from taipy.core.data._data_manager_factory import _DataManagerFactory
  22. from taipy.core.data.data_node import DataNode
  23. from taipy.core.data.data_node_id import (
  24. EDIT_COMMENT_KEY,
  25. EDIT_EDITOR_ID_KEY,
  26. EDIT_JOB_ID_KEY,
  27. EDIT_TIMESTAMP_KEY,
  28. DataNodeId,
  29. )
  30. from taipy.core.data.in_memory import InMemoryDataNode
  31. from taipy.core.exceptions.exceptions import DataNodeIsBeingEdited, NoData
  32. from taipy.core.job.job_id import JobId
  33. from taipy.core.task.task import Task
  34. from .utils import FakeDataNode
  35. def funct_a_b(input: str):
  36. print("task_a_b") # noqa: T201
  37. return "B"
  38. def funct_b_c(input: str):
  39. print("task_b_c") # noqa: T201
  40. return "C"
  41. def funct_b_d(input: str):
  42. print("task_b_d") # noqa: T201
  43. return "D"
  44. class TestDataNode:
  45. def test_dn_equals(self, data_node):
  46. data_manager = _DataManagerFactory()._build_manager()
  47. dn_id = data_node.id
  48. data_manager._set(data_node)
  49. # # To test if instance is same type
  50. task = Task("task", {}, print, [], [], dn_id)
  51. dn_2 = data_manager._get(dn_id)
  52. assert data_node == dn_2
  53. assert data_node != dn_id
  54. assert data_node != task
  55. def test_create_with_default_values(self):
  56. dn = DataNode("foo_bar")
  57. assert dn.config_id == "foo_bar"
  58. assert dn.scope == Scope.SCENARIO
  59. assert dn.id is not None
  60. assert dn.name is None
  61. assert dn.owner_id is None
  62. assert dn.parent_ids == set()
  63. assert dn.last_edit_date is None
  64. assert dn.job_ids == []
  65. assert not dn.is_ready_for_reading
  66. assert len(dn.properties) == 0
  67. def test_is_up_to_date_when_not_written(self):
  68. dn_confg_1 = Config.configure_in_memory_data_node("dn_1", default_data="a")
  69. dn_confg_2 = Config.configure_in_memory_data_node("dn_2")
  70. task_config_1 = Config.configure_task("t1", funct_a_b, [dn_confg_1], [dn_confg_2])
  71. scenario_config = Config.configure_scenario("sc", [task_config_1])
  72. scenario = tp.create_scenario(scenario_config)
  73. assert scenario.dn_1.is_up_to_date is True
  74. assert scenario.dn_2.is_up_to_date is False
  75. tp.submit(scenario)
  76. assert scenario.dn_1.is_up_to_date is True
  77. assert scenario.dn_2.is_up_to_date is True
  78. def test_create(self):
  79. a_date = datetime.now()
  80. dn = DataNode(
  81. "foo_bar",
  82. Scope.SCENARIO,
  83. DataNodeId("an_id"),
  84. "a_scenario_id",
  85. {"a_parent_id"},
  86. a_date,
  87. [{"job_id": "a_job_id"}],
  88. edit_in_progress=False,
  89. prop="erty",
  90. name="a name",
  91. )
  92. assert dn.config_id == "foo_bar"
  93. assert dn.scope == Scope.SCENARIO
  94. assert dn.id == "an_id"
  95. assert dn.name == "a name"
  96. assert dn.owner_id == "a_scenario_id"
  97. assert dn.parent_ids == {"a_parent_id"}
  98. assert dn.last_edit_date == a_date
  99. assert dn.job_ids == ["a_job_id"]
  100. assert dn.is_ready_for_reading
  101. assert len(dn.properties) == 2
  102. assert dn.properties == {"prop": "erty", "name": "a name"}
  103. with pytest.raises(InvalidConfigurationId):
  104. DataNode("foo bar")
  105. def test_read_write(self):
  106. dn = FakeDataNode("foo_bar")
  107. with pytest.raises(NoData):
  108. assert dn.read() is None
  109. dn.read_or_raise()
  110. assert dn.write_has_been_called == 0
  111. assert dn.read_has_been_called == 0
  112. assert not dn.is_ready_for_reading
  113. assert dn.last_edit_date is None
  114. assert dn.job_ids == []
  115. assert dn.edits == []
  116. dn.write("Any data")
  117. assert dn.write_has_been_called == 1
  118. assert dn.read_has_been_called == 0
  119. assert dn.last_edit_date is not None
  120. first_edition = dn.last_edit_date
  121. assert dn.is_ready_for_reading
  122. assert dn.job_ids == []
  123. assert len(dn.edits) == 1
  124. assert dn.get_last_edit()["timestamp"] == dn.last_edit_date
  125. sleep(0.1)
  126. dn.write("Any other data", job_id := JobId("a_job_id"))
  127. assert dn.write_has_been_called == 2
  128. assert dn.read_has_been_called == 0
  129. second_edition = dn.last_edit_date
  130. assert first_edition < second_edition
  131. assert dn.is_ready_for_reading
  132. assert dn.job_ids == [job_id]
  133. assert len(dn.edits) == 2
  134. assert dn.get_last_edit()["timestamp"] == dn.last_edit_date
  135. dn.read()
  136. assert dn.write_has_been_called == 2
  137. assert dn.read_has_been_called == 1
  138. second_edition = dn.last_edit_date
  139. assert first_edition < second_edition
  140. assert dn.is_ready_for_reading
  141. assert dn.job_ids == [job_id]
  142. def test_lock_initialization(self):
  143. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  144. assert not dn.edit_in_progress
  145. assert dn._editor_id is None
  146. assert dn._editor_expiration_date is None
  147. def test_locked_dn_unlockable_only_by_same_editor(self):
  148. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  149. dn.lock_edit("user_1")
  150. assert dn.edit_in_progress
  151. assert dn._editor_id == "user_1"
  152. assert dn._editor_expiration_date is not None
  153. with pytest.raises(DataNodeIsBeingEdited):
  154. dn.lock_edit("user_2")
  155. with pytest.raises(DataNodeIsBeingEdited):
  156. dn.unlock_edit("user_2")
  157. dn.unlock_edit("user_1")
  158. assert not dn.edit_in_progress
  159. assert dn._editor_id is None
  160. assert dn._editor_expiration_date is None
  161. def test_none_editor_can_lock_a_locked_dn(self):
  162. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  163. dn.lock_edit("user")
  164. assert dn.edit_in_progress
  165. assert dn._editor_id == "user"
  166. assert dn._editor_expiration_date is not None
  167. dn.lock_edit()
  168. assert dn.edit_in_progress
  169. assert dn._editor_id is None
  170. assert dn._editor_expiration_date is None
  171. def test_none_editor_can_unlock_a_locked_dn(self):
  172. dn = InMemoryDataNode("dn", Scope.SCENARIO)
  173. dn.lock_edit("user")
  174. assert dn.edit_in_progress
  175. assert dn._editor_id == "user"
  176. assert dn._editor_expiration_date is not None
  177. dn.unlock_edit()
  178. assert not dn.edit_in_progress
  179. assert dn._editor_id is None
  180. assert dn._editor_expiration_date is None
  181. dn.lock_edit()
  182. assert dn.edit_in_progress
  183. assert dn._editor_id is None
  184. assert dn._editor_expiration_date is None
  185. dn.unlock_edit()
  186. assert not dn.edit_in_progress
  187. assert dn._editor_id is None
  188. assert dn._editor_expiration_date is None
  189. def test_ready_for_reading(self):
  190. dn = InMemoryDataNode("foo_bar", Scope.CYCLE)
  191. assert dn.last_edit_date is None
  192. assert not dn.is_ready_for_reading
  193. assert dn.job_ids == []
  194. dn.lock_edit()
  195. assert dn.last_edit_date is None
  196. assert not dn.is_ready_for_reading
  197. assert dn.job_ids == []
  198. dn.unlock_edit()
  199. assert dn.last_edit_date is None
  200. assert not dn.is_ready_for_reading
  201. assert dn.job_ids == []
  202. dn.lock_edit()
  203. assert dn.last_edit_date is None
  204. assert not dn.is_ready_for_reading
  205. assert dn.job_ids == []
  206. dn.write("toto", job_id := JobId("a_job_id"))
  207. assert dn.last_edit_date is not None
  208. assert dn.is_ready_for_reading
  209. assert dn.job_ids == [job_id]
  210. def test_is_valid_no_validity_period(self):
  211. # Test Never been written
  212. dn = InMemoryDataNode("foo", Scope.SCENARIO, DataNodeId("id"), "name", "owner_id")
  213. assert not dn.is_valid
  214. # test has been written
  215. dn.write("My data")
  216. assert dn.is_valid
  217. def test_is_valid_with_30_min_validity_period(self):
  218. # Test Never been written
  219. dn = InMemoryDataNode(
  220. "foo", Scope.SCENARIO, DataNodeId("id"), "name", "owner_id", validity_period=timedelta(minutes=30)
  221. )
  222. assert dn.is_valid is False
  223. # Has been written less than 30 minutes ago
  224. dn.write("My data")
  225. assert dn.is_valid is True
  226. # Has been written more than 30 minutes ago
  227. dn.last_edit_date = datetime.now() + timedelta(days=-1)
  228. assert dn.is_valid is False
  229. def test_is_valid_with_5_days_validity_period(self):
  230. # Test Never been written
  231. dn = InMemoryDataNode("foo", Scope.SCENARIO, validity_period=timedelta(days=5))
  232. assert dn.is_valid is False
  233. # Has been written less than 30 minutes ago
  234. dn.write("My data")
  235. assert dn.is_valid is True
  236. # Has been written more than 30 minutes ago
  237. dn._last_edit_date = datetime.now() - timedelta(days=6)
  238. _DataManager()._set(dn)
  239. assert dn.is_valid is False
  240. def test_is_up_to_date(self, current_datetime):
  241. dn_confg_1 = Config.configure_in_memory_data_node("dn_1")
  242. dn_confg_2 = Config.configure_in_memory_data_node("dn_2")
  243. dn_confg_3 = Config.configure_in_memory_data_node("dn_3", scope=Scope.GLOBAL)
  244. task_config_1 = Config.configure_task("t1", print, [dn_confg_1], [dn_confg_2])
  245. task_config_2 = Config.configure_task("t2", print, [dn_confg_2], [dn_confg_3])
  246. scenario_config = Config.configure_scenario("sc", [task_config_1, task_config_2])
  247. scenario_1 = tp.create_scenario(scenario_config)
  248. assert len(_DataManager._get_all()) == 3
  249. dn_1_1 = scenario_1.data_nodes["dn_1"]
  250. dn_2_1 = scenario_1.data_nodes["dn_2"]
  251. dn_3_1 = scenario_1.data_nodes["dn_3"]
  252. assert dn_1_1.last_edit_date is None
  253. assert dn_2_1.last_edit_date is None
  254. assert dn_3_1.last_edit_date is None
  255. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  256. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  257. dn_3_1.last_edit_date = current_datetime + timedelta(3)
  258. assert dn_1_1.is_up_to_date
  259. assert dn_2_1.is_up_to_date
  260. assert dn_3_1.is_up_to_date
  261. dn_2_1.last_edit_date = current_datetime + timedelta(4)
  262. assert dn_1_1.is_up_to_date
  263. assert dn_2_1.is_up_to_date
  264. assert not dn_3_1.is_up_to_date
  265. dn_1_1.last_edit_date = current_datetime + timedelta(5)
  266. assert dn_1_1.is_up_to_date
  267. assert not dn_2_1.is_up_to_date
  268. assert not dn_3_1.is_up_to_date
  269. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  270. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  271. dn_3_1.last_edit_date = current_datetime + timedelta(3)
  272. def test_is_up_to_date_across_scenarios(self, current_datetime):
  273. dn_confg_1 = Config.configure_in_memory_data_node("dn_1", scope=Scope.SCENARIO)
  274. dn_confg_2 = Config.configure_in_memory_data_node("dn_2", scope=Scope.SCENARIO)
  275. dn_confg_3 = Config.configure_in_memory_data_node("dn_3", scope=Scope.GLOBAL)
  276. task_config_1 = Config.configure_task("t1", print, [dn_confg_1], [dn_confg_2])
  277. task_config_2 = Config.configure_task("t2", print, [dn_confg_2], [dn_confg_3])
  278. scenario_config = Config.configure_scenario("sc", [task_config_1, task_config_2])
  279. scenario_1 = tp.create_scenario(scenario_config)
  280. scenario_2 = tp.create_scenario(scenario_config)
  281. assert len(_DataManager._get_all()) == 5
  282. dn_1_1 = scenario_1.data_nodes["dn_1"]
  283. dn_2_1 = scenario_1.data_nodes["dn_2"]
  284. dn_1_2 = scenario_2.data_nodes["dn_1"]
  285. dn_2_2 = scenario_2.data_nodes["dn_2"]
  286. dn_3 = scenario_1.data_nodes["dn_3"]
  287. assert dn_3 == scenario_2.data_nodes["dn_3"]
  288. assert dn_1_1.last_edit_date is None
  289. assert dn_2_1.last_edit_date is None
  290. assert dn_1_2.last_edit_date is None
  291. assert dn_2_2.last_edit_date is None
  292. assert dn_3.last_edit_date is None
  293. dn_1_1.last_edit_date = current_datetime + timedelta(1)
  294. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  295. dn_1_2.last_edit_date = current_datetime + timedelta(3)
  296. dn_2_2.last_edit_date = current_datetime + timedelta(4)
  297. dn_3.last_edit_date = current_datetime + timedelta(5)
  298. assert dn_1_1.is_up_to_date
  299. assert dn_2_1.is_up_to_date
  300. assert dn_1_2.is_up_to_date
  301. assert dn_2_2.is_up_to_date
  302. assert dn_3.is_up_to_date
  303. dn_2_1.last_edit_date = current_datetime + timedelta(6)
  304. assert dn_1_1.is_up_to_date
  305. assert dn_2_1.is_up_to_date
  306. assert dn_1_2.is_up_to_date
  307. assert dn_2_2.is_up_to_date
  308. assert not dn_3.is_up_to_date
  309. dn_2_1.last_edit_date = current_datetime + timedelta(2)
  310. dn_2_2.last_edit_date = current_datetime + timedelta(6)
  311. assert dn_1_1.is_up_to_date
  312. assert dn_2_1.is_up_to_date
  313. assert dn_1_2.is_up_to_date
  314. assert dn_2_2.is_up_to_date
  315. assert not dn_3.is_up_to_date
  316. dn_2_2.last_edit_date = current_datetime + timedelta(4)
  317. dn_1_1.last_edit_date = current_datetime + timedelta(6)
  318. assert dn_1_1.is_up_to_date
  319. assert not dn_2_1.is_up_to_date
  320. assert dn_1_2.is_up_to_date
  321. assert dn_2_2.is_up_to_date
  322. assert not dn_3.is_up_to_date
  323. dn_1_2.last_edit_date = current_datetime + timedelta(6)
  324. assert dn_1_1.is_up_to_date
  325. assert not dn_2_1.is_up_to_date
  326. assert dn_1_2.is_up_to_date
  327. assert not dn_2_2.is_up_to_date
  328. assert not dn_3.is_up_to_date
  329. def test_do_not_recompute_data_node_valid_but_continue_sequence_execution(self):
  330. a = Config.configure_data_node("A", "pickle", default_data="A")
  331. b = Config.configure_data_node("B", "pickle")
  332. c = Config.configure_data_node("C", "pickle")
  333. d = Config.configure_data_node("D", "pickle")
  334. task_a_b = Config.configure_task("task_a_b", funct_a_b, input=a, output=b, skippable=True)
  335. task_b_c = Config.configure_task("task_b_c", funct_b_c, input=b, output=c)
  336. task_b_d = Config.configure_task("task_b_d", funct_b_d, input=b, output=d)
  337. scenario_cfg = Config.configure_scenario("scenario", [task_a_b, task_b_c, task_b_d])
  338. scenario = tp.create_scenario(scenario_cfg)
  339. scenario.submit()
  340. assert scenario.A.read() == "A"
  341. assert scenario.B.read() == "B"
  342. assert scenario.C.read() == "C"
  343. assert scenario.D.read() == "D"
  344. scenario.submit()
  345. assert len(tp.get_jobs()) == 6
  346. jobs_and_status = [(job.task.config_id, job.status) for job in tp.get_jobs()]
  347. assert ("task_a_b", tp.Status.COMPLETED) in jobs_and_status
  348. assert ("task_a_b", tp.Status.SKIPPED) in jobs_and_status
  349. assert ("task_b_c", tp.Status.COMPLETED) in jobs_and_status
  350. assert ("task_b_d", tp.Status.COMPLETED) in jobs_and_status
  351. def test_data_node_update_after_writing(self):
  352. dn = FakeDataNode("foo")
  353. _DataManager._set(dn)
  354. assert not _DataManager._get(dn.id).is_ready_for_reading
  355. dn.write("Any data")
  356. assert dn.is_ready_for_reading
  357. assert _DataManager._get(dn.id).is_ready_for_reading
  358. def test_expiration_date_raise_if_never_write(self):
  359. dn = FakeDataNode("foo")
  360. with pytest.raises(NoData):
  361. _ = dn.expiration_date
  362. def test_validity_null_if_never_write(self):
  363. dn = FakeDataNode("foo")
  364. assert dn.validity_period is None
  365. def test_auto_set_and_reload(self, current_datetime):
  366. dn_1 = InMemoryDataNode(
  367. "foo",
  368. scope=Scope.GLOBAL,
  369. id=DataNodeId("an_id"),
  370. owner_id=None,
  371. parent_ids=None,
  372. last_edit_date=current_datetime,
  373. edits=[{"job_id": "a_job_id"}],
  374. edit_in_progress=False,
  375. validity_period=None,
  376. properties={
  377. "name": "foo",
  378. },
  379. )
  380. dm = _DataManager()
  381. dm._set(dn_1)
  382. dn_2 = dm._get(dn_1)
  383. # auto set & reload on scope attribute
  384. assert dn_1.scope == Scope.GLOBAL
  385. assert dn_2.scope == Scope.GLOBAL
  386. dn_1.scope = Scope.CYCLE
  387. assert dn_1.scope == Scope.CYCLE
  388. assert dn_2.scope == Scope.CYCLE
  389. dn_2.scope = Scope.SCENARIO
  390. assert dn_1.scope == Scope.SCENARIO
  391. assert dn_2.scope == Scope.SCENARIO
  392. new_datetime = current_datetime + timedelta(1)
  393. new_datetime_1 = current_datetime + timedelta(3)
  394. # auto set & reload on last_edit_date attribute
  395. assert dn_1.last_edit_date == current_datetime
  396. assert dn_2.last_edit_date == current_datetime
  397. dn_1.last_edit_date = new_datetime_1
  398. assert dn_1.last_edit_date == new_datetime_1
  399. assert dn_2.last_edit_date == new_datetime_1
  400. dn_2.last_edit_date = new_datetime
  401. assert dn_1.last_edit_date == new_datetime
  402. assert dn_2.last_edit_date == new_datetime
  403. # auto set & reload on name attribute
  404. assert dn_1.name == "foo"
  405. assert dn_2.name == "foo"
  406. dn_1.name = "fed"
  407. assert dn_1.name == "fed"
  408. assert dn_2.name == "fed"
  409. dn_2.name = "def"
  410. assert dn_1.name == "def"
  411. assert dn_2.name == "def"
  412. # auto set & reload on parent_ids attribute (set() object does not have auto set yet)
  413. assert dn_1.parent_ids == set()
  414. assert dn_2.parent_ids == set()
  415. dn_1._parent_ids.update(["sc2"])
  416. _DataManager._set(dn_1)
  417. assert dn_1.parent_ids == {"sc2"}
  418. assert dn_2.parent_ids == {"sc2"}
  419. dn_2._parent_ids.clear()
  420. dn_2._parent_ids.update(["sc1"])
  421. _DataManager._set(dn_2)
  422. assert dn_1.parent_ids == {"sc1"}
  423. assert dn_2.parent_ids == {"sc1"}
  424. dn_2._parent_ids.clear()
  425. _DataManager._set(dn_2)
  426. # auto set & reload on edit_in_progress attribute
  427. assert not dn_2.edit_in_progress
  428. assert not dn_1.edit_in_progress
  429. dn_1.edit_in_progress = True
  430. assert dn_1.edit_in_progress
  431. assert dn_2.edit_in_progress
  432. dn_2.unlock_edit()
  433. assert not dn_1.edit_in_progress
  434. assert not dn_2.edit_in_progress
  435. dn_1.lock_edit()
  436. assert dn_1.edit_in_progress
  437. assert dn_2.edit_in_progress
  438. # auto set & reload on validity_period attribute
  439. time_period_1 = timedelta(1)
  440. time_period_2 = timedelta(5)
  441. assert dn_1.validity_period is None
  442. assert dn_2.validity_period is None
  443. dn_1.validity_period = time_period_1
  444. assert dn_1.validity_period == time_period_1
  445. assert dn_2.validity_period == time_period_1
  446. dn_2.validity_period = time_period_2
  447. assert dn_1.validity_period == time_period_2
  448. assert dn_2.validity_period == time_period_2
  449. dn_1.last_edit_date = new_datetime
  450. assert len(dn_1.job_ids) == 1
  451. assert len(dn_2.job_ids) == 1
  452. with dn_1 as dn:
  453. assert dn.config_id == "foo"
  454. assert dn.owner_id is None
  455. assert dn.scope == Scope.SCENARIO
  456. assert dn.last_edit_date == new_datetime
  457. assert dn.name == "def"
  458. assert dn.edit_in_progress
  459. assert dn.validity_period == time_period_2
  460. assert len(dn.job_ids) == 1
  461. assert dn._is_in_context
  462. new_datetime_2 = new_datetime + timedelta(5)
  463. dn.scope = Scope.CYCLE
  464. dn.last_edit_date = new_datetime_2
  465. dn.name = "abc"
  466. dn.edit_in_progress = False
  467. dn.validity_period = None
  468. assert dn.config_id == "foo"
  469. assert dn.owner_id is None
  470. assert dn.scope == Scope.SCENARIO
  471. assert dn.last_edit_date == new_datetime
  472. assert dn.name == "def"
  473. assert dn.edit_in_progress
  474. assert dn.validity_period == time_period_2
  475. assert len(dn.job_ids) == 1
  476. assert dn_1.config_id == "foo"
  477. assert dn_1.owner_id is None
  478. assert dn_1.scope == Scope.CYCLE
  479. assert dn_1.last_edit_date == new_datetime_2
  480. assert dn_1.name == "abc"
  481. assert not dn_1.edit_in_progress
  482. assert dn_1.validity_period is None
  483. assert not dn_1._is_in_context
  484. assert len(dn_1.job_ids) == 1
  485. def test_auto_set_and_reload_properties(self):
  486. dn_1 = InMemoryDataNode("foo", scope=Scope.GLOBAL, properties={"name": "def"})
  487. dm = _DataManager()
  488. dm._set(dn_1)
  489. dn_2 = dm._get(dn_1)
  490. # auto set & reload on properties attribute
  491. assert dn_1.properties == {"name": "def"}
  492. assert dn_2.properties == {"name": "def"}
  493. dn_1._properties["qux"] = 4
  494. assert dn_1.properties["qux"] == 4
  495. assert dn_2.properties["qux"] == 4
  496. assert dn_1.properties == {"qux": 4, "name": "def"}
  497. assert dn_2.properties == {"qux": 4, "name": "def"}
  498. dn_2._properties["qux"] = 5
  499. assert dn_1.properties["qux"] == 5
  500. assert dn_2.properties["qux"] == 5
  501. dn_1.properties["temp_key_1"] = "temp_value_1"
  502. dn_1.properties["temp_key_2"] = "temp_value_2"
  503. assert dn_1.properties == {
  504. "name": "def",
  505. "qux": 5,
  506. "temp_key_1": "temp_value_1",
  507. "temp_key_2": "temp_value_2",
  508. }
  509. assert dn_2.properties == {"name": "def", "qux": 5, "temp_key_1": "temp_value_1", "temp_key_2": "temp_value_2"}
  510. dn_1.properties.pop("temp_key_1")
  511. assert "temp_key_1" not in dn_1.properties.keys()
  512. assert "temp_key_1" not in dn_1.properties.keys()
  513. assert dn_1.properties == {"name": "def", "qux": 5, "temp_key_2": "temp_value_2"}
  514. assert dn_2.properties == {"name": "def", "qux": 5, "temp_key_2": "temp_value_2"}
  515. dn_2.properties.pop("temp_key_2")
  516. assert dn_1.properties == {"qux": 5, "name": "def"}
  517. assert dn_2.properties == {"qux": 5, "name": "def"}
  518. assert "temp_key_2" not in dn_1.properties.keys()
  519. assert "temp_key_2" not in dn_2.properties.keys()
  520. dn_1.properties["temp_key_3"] = 0
  521. assert dn_1.properties == {"qux": 5, "temp_key_3": 0, "name": "def"}
  522. assert dn_2.properties == {"qux": 5, "temp_key_3": 0, "name": "def"}
  523. dn_1.properties.update({"temp_key_3": 1})
  524. assert dn_1.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  525. assert dn_2.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  526. dn_1.properties.update({})
  527. assert dn_1.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  528. assert dn_2.properties == {"qux": 5, "temp_key_3": 1, "name": "def"}
  529. dn_1.properties["temp_key_4"] = 0
  530. dn_1.properties["temp_key_5"] = 0
  531. with dn_1 as dn:
  532. assert dn._is_in_context
  533. assert dn.properties["qux"] == 5
  534. assert dn.properties["temp_key_3"] == 1
  535. assert dn.properties["temp_key_4"] == 0
  536. assert dn.properties["temp_key_5"] == 0
  537. dn.properties["qux"] = 9
  538. dn.properties.pop("temp_key_3")
  539. dn.properties.pop("temp_key_4")
  540. dn.properties.update({"temp_key_4": 1})
  541. dn.properties.update({"temp_key_5": 2})
  542. dn.properties.pop("temp_key_5")
  543. dn.properties.update({})
  544. assert dn.properties["qux"] == 5
  545. assert dn.properties["temp_key_3"] == 1
  546. assert dn.properties["temp_key_4"] == 0
  547. assert dn.properties["temp_key_5"] == 0
  548. assert not dn_1._is_in_context
  549. assert dn_1.properties["qux"] == 9
  550. assert "temp_key_3" not in dn_1.properties.keys()
  551. assert dn_1.properties["temp_key_4"] == 1
  552. assert "temp_key_5" not in dn_1.properties.keys()
  553. def test_get_parents(self, data_node):
  554. with mock.patch("taipy.core.get_parents") as mck:
  555. data_node.get_parents()
  556. mck.assert_called_once_with(data_node)
  557. def test_data_node_with_env_variable_value_not_stored(self):
  558. dn_config = Config.configure_data_node("A", prop="ENV[FOO]")
  559. with mock.patch.dict(os.environ, {"FOO": "bar"}):
  560. dn = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  561. assert dn._properties.data["prop"] == "ENV[FOO]"
  562. assert dn.properties["prop"] == "bar"
  563. def test_path_populated_with_config_default_path(self):
  564. dn_config = Config.configure_data_node("data_node", "pickle", default_path="foo.p")
  565. assert dn_config.default_path == "foo.p"
  566. data_node = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  567. assert data_node.path == "foo.p"
  568. data_node.path = "baz.p"
  569. assert data_node.path == "baz.p"
  570. def test_edit_edit_tracking(self):
  571. dn_config = Config.configure_data_node("A")
  572. data_node = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  573. data_node.write(data="1", job_id="job_1")
  574. data_node.write(data="2", job_id="job_1")
  575. data_node.write(data="3", job_id="job_1")
  576. assert len(data_node.edits) == 3
  577. assert len(data_node.job_ids) == 3
  578. assert data_node.edits[-1] == data_node.get_last_edit()
  579. assert data_node.last_edit_date == data_node.get_last_edit().get("timestamp")
  580. date = datetime(2050, 1, 1, 12, 12)
  581. data_node.write(data="4", timestamp=date, message="This is a comment on this edit", env="staging")
  582. assert len(data_node.edits) == 4
  583. assert len(data_node.job_ids) == 3
  584. assert data_node.edits[-1] == data_node.get_last_edit()
  585. last_edit = data_node.get_last_edit()
  586. assert last_edit["message"] == "This is a comment on this edit"
  587. assert last_edit["env"] == "staging"
  588. assert last_edit["timestamp"] == date
  589. def test_label(self):
  590. a_date = datetime.now()
  591. dn = DataNode(
  592. "foo_bar",
  593. Scope.SCENARIO,
  594. DataNodeId("an_id"),
  595. "a_scenario_id",
  596. {"a_parent_id"},
  597. a_date,
  598. [{"job_id": "a_job_id"}],
  599. edit_in_progress=False,
  600. prop="erty",
  601. name="a name",
  602. )
  603. with mock.patch("taipy.core.get") as get_mck:
  604. class MockOwner:
  605. label = "owner_label"
  606. def get_label(self):
  607. return self.label
  608. get_mck.return_value = MockOwner()
  609. assert dn.get_label() == "owner_label > " + dn.name
  610. assert dn.get_simple_label() == dn.name
  611. def test_explicit_label(self):
  612. a_date = datetime.now()
  613. dn = DataNode(
  614. "foo_bar",
  615. Scope.SCENARIO,
  616. DataNodeId("an_id"),
  617. "a_scenario_id",
  618. {"a_parent_id"},
  619. a_date,
  620. [{"job_id": "a_job_id"}],
  621. edit_in_progress=False,
  622. label="a label",
  623. name="a name",
  624. )
  625. assert dn.get_label() == "a label"
  626. assert dn.get_simple_label() == "a label"
  627. def test_change_data_node_name(self):
  628. cgf = Config.configure_data_node("foo", scope=Scope.GLOBAL)
  629. dn = tp.create_global_data_node(cgf)
  630. dn.name = "bar"
  631. assert dn.name == "bar"
  632. # This new syntax will be the only one allowed: https://github.com/Avaiga/taipy-core/issues/806
  633. dn.properties["name"] = "baz"
  634. assert dn.name == "baz"
  635. def test_track_edit(self):
  636. dn_config = Config.configure_data_node("A")
  637. data_node = _DataManager._bulk_get_or_create([dn_config])[dn_config]
  638. before = datetime.now()
  639. data_node.track_edit(job_id="job_1")
  640. data_node.track_edit(editor_id="editor_1")
  641. data_node.track_edit(comment="This is a comment on this edit")
  642. data_node.track_edit(editor_id="editor_2", comment="This is another comment on this edit")
  643. data_node.track_edit(editor_id="editor_3", foo="bar")
  644. after = datetime.now()
  645. timestamp = datetime.now()
  646. data_node.track_edit(timestamp=timestamp)
  647. _DataManagerFactory._build_manager()._set(data_node)
  648. # To save the edits because track edit does not save the data node
  649. assert len(data_node.edits) == 6
  650. assert data_node.edits[-1] == data_node.get_last_edit()
  651. assert data_node.last_edit_date == data_node.get_last_edit().get(EDIT_TIMESTAMP_KEY)
  652. edit_0 = data_node.edits[0]
  653. assert len(edit_0) == 2
  654. assert edit_0[EDIT_JOB_ID_KEY] == "job_1"
  655. assert edit_0[EDIT_TIMESTAMP_KEY] >= before
  656. assert edit_0[EDIT_TIMESTAMP_KEY] <= after
  657. edit_1 = data_node.edits[1]
  658. assert len(edit_1) == 2
  659. assert edit_1[EDIT_EDITOR_ID_KEY] == "editor_1"
  660. assert edit_1[EDIT_TIMESTAMP_KEY] >= before
  661. assert edit_1[EDIT_TIMESTAMP_KEY] <= after
  662. edit_2 = data_node.edits[2]
  663. assert len(edit_2) == 2
  664. assert edit_2[EDIT_COMMENT_KEY] == "This is a comment on this edit"
  665. assert edit_2[EDIT_TIMESTAMP_KEY] >= before
  666. assert edit_2[EDIT_TIMESTAMP_KEY] <= after
  667. edit_3 = data_node.edits[3]
  668. assert len(edit_3) == 3
  669. assert edit_3[EDIT_EDITOR_ID_KEY] == "editor_2"
  670. assert edit_3[EDIT_COMMENT_KEY] == "This is another comment on this edit"
  671. assert edit_3[EDIT_TIMESTAMP_KEY] >= before
  672. assert edit_3[EDIT_TIMESTAMP_KEY] <= after
  673. edit_4 = data_node.edits[4]
  674. assert len(edit_4) == 3
  675. assert edit_4[EDIT_EDITOR_ID_KEY] == "editor_3"
  676. assert edit_4["foo"] == "bar"
  677. assert edit_4[EDIT_TIMESTAMP_KEY] >= before
  678. assert edit_4[EDIT_TIMESTAMP_KEY] <= after
  679. edit_5 = data_node.edits[5]
  680. assert len(edit_5) == 1
  681. assert edit_5[EDIT_TIMESTAMP_KEY] == timestamp
  682. def test_normalize_path(self):
  683. dn = DataNode(
  684. config_id="foo_bar",
  685. scope=Scope.SCENARIO,
  686. id=DataNodeId("an_id"),
  687. path=r"data\foo\bar.csv",
  688. )
  689. assert dn.config_id == "foo_bar"
  690. assert dn.scope == Scope.SCENARIO
  691. assert dn.id == "an_id"
  692. assert dn.properties["path"] == "data/foo/bar.csv"