test_scenario_manager.py 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600
  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, timedelta
  12. from typing import Callable, Iterable, Optional
  13. from unittest import mock
  14. from unittest.mock import ANY, patch
  15. import freezegun
  16. import pytest
  17. from taipy.common.config import Config
  18. from taipy.core import Job
  19. from taipy.core import taipy as tp
  20. from taipy.core._orchestrator._orchestrator import _Orchestrator
  21. from taipy.core._version._version_manager import _VersionManager
  22. from taipy.core.common import _utils
  23. from taipy.core.common._utils import _Subscriber
  24. from taipy.core.common.frequency import Frequency
  25. from taipy.core.common.scope import Scope
  26. from taipy.core.config.scenario_config import ScenarioConfig
  27. from taipy.core.cycle._cycle_manager import _CycleManager
  28. from taipy.core.data._data_manager import _DataManager
  29. from taipy.core.data._data_manager_factory import _DataManagerFactory
  30. from taipy.core.data.in_memory import InMemoryDataNode
  31. from taipy.core.exceptions.exceptions import (
  32. DeletingPrimaryScenario,
  33. DifferentScenarioConfigs,
  34. InsufficientScenarioToCompare,
  35. NonExistingComparator,
  36. NonExistingScenario,
  37. NonExistingScenarioConfig,
  38. NonExistingTask,
  39. SequenceTaskConfigDoesNotExistInSameScenarioConfig,
  40. UnauthorizedTagError,
  41. )
  42. from taipy.core.job._job_manager import _JobManager
  43. from taipy.core.reason import EntityDoesNotExist, ReasonCollection, WrongConfigType
  44. from taipy.core.scenario._scenario_duplicator import _ScenarioDuplicator
  45. from taipy.core.scenario._scenario_manager import _ScenarioManager
  46. from taipy.core.scenario._scenario_manager_factory import _ScenarioManagerFactory
  47. from taipy.core.scenario.scenario import Scenario
  48. from taipy.core.scenario.scenario_id import ScenarioId
  49. from taipy.core.sequence._sequence_manager import _SequenceManager
  50. from taipy.core.task._task_manager import _TaskManager
  51. from taipy.core.task.task import Task
  52. from taipy.core.task.task_id import TaskId
  53. from tests.core.utils.NotifyMock import NotifyMock
  54. def test_save_and_get_scenario(cycle):
  55. scenario_id_1 = ScenarioId("scenario_id_1")
  56. scenario_1 = Scenario("scenario_name_1", [], {}, [], scenario_id_1)
  57. input_dn_2 = InMemoryDataNode("foo", Scope.SCENARIO)
  58. output_dn_2 = InMemoryDataNode("bar", Scope.SCENARIO)
  59. additional_dn_2 = InMemoryDataNode("zyx", Scope.SCENARIO)
  60. task_name_2 = "task_2"
  61. task_2 = Task(task_name_2, {}, print, [input_dn_2], [output_dn_2], TaskId("task_id_2"))
  62. scenario_id_2 = ScenarioId("scenario_id_2")
  63. scenario_2 = Scenario(
  64. "scenario_name_2",
  65. [task_2],
  66. {},
  67. [additional_dn_2],
  68. scenario_id_2,
  69. datetime.now(),
  70. True,
  71. cycle,
  72. sequences={"sequence_2": {"tasks": [task_2]}},
  73. )
  74. additional_dn_3 = InMemoryDataNode("baz", Scope.SCENARIO)
  75. task_name_3 = "task_3"
  76. task_3 = Task(task_name_3, {}, print, id=TaskId("task_id_3"))
  77. scenario_3_with_same_id = Scenario(
  78. "scenario_name_3",
  79. [task_3],
  80. {},
  81. [additional_dn_3],
  82. scenario_id_1,
  83. datetime.now(),
  84. False,
  85. cycle,
  86. sequences={"sequence_3": {}},
  87. )
  88. # No existing scenario
  89. assert len(_ScenarioManager._get_all()) == 0
  90. assert _ScenarioManager._get(scenario_id_1) is None
  91. assert _ScenarioManager._get(scenario_1) is None
  92. assert _ScenarioManager._get(scenario_id_2) is None
  93. assert _ScenarioManager._get(scenario_2) is None
  94. # Save one scenario. We expect to have only one scenario stored
  95. _ScenarioManager._repository._save(scenario_1)
  96. assert len(_ScenarioManager._get_all()) == 1
  97. assert _ScenarioManager._get(scenario_id_1).id == scenario_1.id
  98. assert _ScenarioManager._get(scenario_id_1).config_id == scenario_1.config_id
  99. assert len(_ScenarioManager._get(scenario_id_1).tasks) == 0
  100. assert len(_ScenarioManager._get(scenario_id_1).additional_data_nodes) == 0
  101. assert len(_ScenarioManager._get(scenario_id_1).data_nodes) == 0
  102. assert len(_ScenarioManager._get(scenario_id_1).sequences) == 0
  103. assert _ScenarioManager._get(scenario_1).id == scenario_1.id
  104. assert _ScenarioManager._get(scenario_1).config_id == scenario_1.config_id
  105. assert len(_ScenarioManager._get(scenario_1).tasks) == 0
  106. assert len(_ScenarioManager._get(scenario_1).additional_data_nodes) == 0
  107. assert len(_ScenarioManager._get(scenario_1).data_nodes) == 0
  108. assert len(_ScenarioManager._get(scenario_1).sequences) == 0
  109. assert _ScenarioManager._get(scenario_id_2) is None
  110. assert _ScenarioManager._get(scenario_2) is None
  111. # Save a second scenario. Now, we expect to have a total of two scenarios stored
  112. _DataManagerFactory._build_manager()._repository._save(input_dn_2)
  113. _DataManagerFactory._build_manager()._repository._save(output_dn_2)
  114. _TaskManager._repository._save(task_2)
  115. _CycleManager._repository._save(cycle)
  116. _ScenarioManager._repository._save(scenario_2)
  117. _DataManager._repository._save(additional_dn_2)
  118. assert len(_ScenarioManager._get_all()) == 2
  119. assert _ScenarioManager._get(scenario_id_1).id == scenario_1.id
  120. assert _ScenarioManager._get(scenario_id_1).config_id == scenario_1.config_id
  121. assert len(_ScenarioManager._get(scenario_id_1).tasks) == 0
  122. assert len(_ScenarioManager._get(scenario_id_1).additional_data_nodes) == 0
  123. assert len(_ScenarioManager._get(scenario_id_1).data_nodes) == 0
  124. assert len(_ScenarioManager._get(scenario_id_1).sequences) == 0
  125. assert _ScenarioManager._get(scenario_1).id == scenario_1.id
  126. assert _ScenarioManager._get(scenario_1).config_id == scenario_1.config_id
  127. assert len(_ScenarioManager._get(scenario_1).tasks) == 0
  128. assert len(_ScenarioManager._get(scenario_1).additional_data_nodes) == 0
  129. assert len(_ScenarioManager._get(scenario_1).data_nodes) == 0
  130. assert len(_ScenarioManager._get(scenario_1).sequences) == 0
  131. assert _ScenarioManager._get(scenario_id_2).id == scenario_2.id
  132. assert _ScenarioManager._get(scenario_id_2).config_id == scenario_2.config_id
  133. assert len(_ScenarioManager._get(scenario_id_2).tasks) == 1
  134. assert len(_ScenarioManager._get(scenario_id_2).additional_data_nodes) == 1
  135. assert len(_ScenarioManager._get(scenario_id_2).data_nodes) == 3
  136. assert len(_ScenarioManager._get(scenario_id_2).sequences) == 1
  137. assert _ScenarioManager._get(scenario_2).id == scenario_2.id
  138. assert _ScenarioManager._get(scenario_2).config_id == scenario_2.config_id
  139. assert len(_ScenarioManager._get(scenario_2).tasks) == 1
  140. assert len(_ScenarioManager._get(scenario_2).additional_data_nodes) == 1
  141. assert len(_ScenarioManager._get(scenario_2).data_nodes) == 3
  142. assert len(_ScenarioManager._get(scenario_2).sequences) == 1
  143. assert _TaskManager._get(task_2.id).id == task_2.id
  144. assert _ScenarioManager._get(scenario_id_2).cycle == cycle
  145. assert _ScenarioManager._get(scenario_2).cycle == cycle
  146. assert _CycleManager._get(cycle.id).id == cycle.id
  147. # We save the first scenario again. We expect nothing to change
  148. _ScenarioManager._update(scenario_1)
  149. assert len(_ScenarioManager._get_all()) == 2
  150. assert _ScenarioManager._get(scenario_id_1).id == scenario_1.id
  151. assert _ScenarioManager._get(scenario_id_1).config_id == scenario_1.config_id
  152. assert len(_ScenarioManager._get(scenario_id_1).tasks) == 0
  153. assert len(_ScenarioManager._get(scenario_id_1).additional_data_nodes) == 0
  154. assert len(_ScenarioManager._get(scenario_id_1).data_nodes) == 0
  155. assert len(_ScenarioManager._get(scenario_id_1).sequences) == 0
  156. assert _ScenarioManager._get(scenario_1).id == scenario_1.id
  157. assert _ScenarioManager._get(scenario_1).config_id == scenario_1.config_id
  158. assert len(_ScenarioManager._get(scenario_1).tasks) == 0
  159. assert len(_ScenarioManager._get(scenario_1).additional_data_nodes) == 0
  160. assert len(_ScenarioManager._get(scenario_1).data_nodes) == 0
  161. assert len(_ScenarioManager._get(scenario_1).sequences) == 0
  162. assert _ScenarioManager._get(scenario_id_2).id == scenario_2.id
  163. assert _ScenarioManager._get(scenario_id_2).config_id == scenario_2.config_id
  164. assert len(_ScenarioManager._get(scenario_id_2).tasks) == 1
  165. assert len(_ScenarioManager._get(scenario_id_2).additional_data_nodes) == 1
  166. assert len(_ScenarioManager._get(scenario_id_2).data_nodes) == 3
  167. assert len(_ScenarioManager._get(scenario_id_2).sequences) == 1
  168. assert _ScenarioManager._get(scenario_2).id == scenario_2.id
  169. assert _ScenarioManager._get(scenario_2).config_id == scenario_2.config_id
  170. assert len(_ScenarioManager._get(scenario_2).tasks) == 1
  171. assert len(_ScenarioManager._get(scenario_2).additional_data_nodes) == 1
  172. assert len(_ScenarioManager._get(scenario_2).data_nodes) == 3
  173. assert len(_ScenarioManager._get(scenario_2).sequences) == 1
  174. assert _TaskManager._get(task_2.id).id == task_2.id
  175. assert _CycleManager._get(cycle.id).id == cycle.id
  176. # We save a third scenario with same id as the first one.
  177. # We expect the first scenario to be updated
  178. _DataManager._repository._save(additional_dn_3)
  179. _TaskManager._repository._save(task_3)
  180. _TaskManager._repository._save(scenario_2.tasks[task_name_2])
  181. _ScenarioManager._repository._save(scenario_3_with_same_id)
  182. assert len(_ScenarioManager._get_all()) == 2
  183. assert _ScenarioManager._get(scenario_id_1).id == scenario_1.id
  184. assert _ScenarioManager._get(scenario_id_1).config_id == scenario_3_with_same_id.config_id
  185. assert len(_ScenarioManager._get(scenario_id_1).tasks) == 1
  186. assert len(_ScenarioManager._get(scenario_id_1).additional_data_nodes) == 1
  187. assert len(_ScenarioManager._get(scenario_id_1).data_nodes) == 1
  188. assert len(_ScenarioManager._get(scenario_id_1).sequences) == 1
  189. assert _ScenarioManager._get(scenario_id_1).cycle == cycle
  190. assert _ScenarioManager._get(scenario_1).id == scenario_1.id
  191. assert _ScenarioManager._get(scenario_1).config_id == scenario_3_with_same_id.config_id
  192. assert len(_ScenarioManager._get(scenario_1).tasks) == 1
  193. assert len(_ScenarioManager._get(scenario_1).additional_data_nodes) == 1
  194. assert len(_ScenarioManager._get(scenario_1).data_nodes) == 1
  195. assert len(_ScenarioManager._get(scenario_1).sequences) == 1
  196. assert _ScenarioManager._get(scenario_1).cycle == cycle
  197. assert _ScenarioManager._get(scenario_id_2).id == scenario_2.id
  198. assert _ScenarioManager._get(scenario_id_2).config_id == scenario_2.config_id
  199. assert len(_ScenarioManager._get(scenario_id_2).tasks) == 1
  200. assert len(_ScenarioManager._get(scenario_id_2).additional_data_nodes) == 1
  201. assert len(_ScenarioManager._get(scenario_id_2).data_nodes) == 3
  202. assert len(_ScenarioManager._get(scenario_id_2).sequences) == 1
  203. assert _ScenarioManager._get(scenario_2).id == scenario_2.id
  204. assert _ScenarioManager._get(scenario_2).config_id == scenario_2.config_id
  205. assert len(_ScenarioManager._get(scenario_2).tasks) == 1
  206. assert len(_ScenarioManager._get(scenario_2).additional_data_nodes) == 1
  207. assert len(_ScenarioManager._get(scenario_2).data_nodes) == 3
  208. assert len(_ScenarioManager._get(scenario_2).sequences) == 1
  209. assert _TaskManager._get(task_2.id).id == task_2.id
  210. def test_raise_sequence_task_configs_not_in_scenario_config():
  211. data_node = Config.configure_pickle_data_node("temp")
  212. task_config_1 = Config.configure_task("task_1", print, output=[data_node])
  213. task_config_2 = Config.configure_task("task_2", print, input=[data_node])
  214. scenario_config_1 = Config.configure_scenario("scenario_1")
  215. scenario_config_1.add_sequences({"sequence_0": []})
  216. _ScenarioManager._create(scenario_config_1)
  217. scenario_config_1.add_sequences({"sequence_1": [task_config_1]})
  218. with pytest.raises(SequenceTaskConfigDoesNotExistInSameScenarioConfig) as err:
  219. _ScenarioManager._create(scenario_config_1)
  220. assert err.value.args == ([task_config_1.id], "sequence_1", scenario_config_1.id)
  221. scenario_config_1._tasks = [task_config_1]
  222. _ScenarioManager._create(scenario_config_1)
  223. scenario_config_1.add_sequences({"sequence_2": [task_config_1]})
  224. _ScenarioManager._create(scenario_config_1)
  225. scenario_config_1.add_sequences({"sequence_3": [task_config_1, task_config_2]})
  226. with pytest.raises(SequenceTaskConfigDoesNotExistInSameScenarioConfig) as err:
  227. _ScenarioManager._create(scenario_config_1)
  228. assert err.value.args == ([task_config_2.id], "sequence_3", scenario_config_1.id)
  229. scenario_config_1._tasks = [task_config_1, task_config_2]
  230. _ScenarioManager._create(scenario_config_1)
  231. def test_get_all_on_multiple_versions_environment():
  232. # Create 5 scenarios with 2 versions each
  233. # Only version 1.0 has the scenario with config_id = "config_id_1"
  234. # Only version 2.0 has the scenario with config_id = "config_id_6"
  235. for version in range(1, 3):
  236. for i in range(5):
  237. _ScenarioManager._repository._save(
  238. Scenario(f"config_id_{i+version}", [], {}, [], ScenarioId(f"id{i}_v{version}"), version=f"{version}.0")
  239. )
  240. _VersionManager._set_experiment_version("1.0")
  241. assert len(_ScenarioManager._get_all()) == 5
  242. assert len(_ScenarioManager._get_all_by(filters=[{"version": "1.0", "config_id": "config_id_1"}])) == 1
  243. assert len(_ScenarioManager._get_all_by(filters=[{"version": "1.0", "config_id": "config_id_6"}])) == 0
  244. _VersionManager._set_experiment_version("2.0")
  245. assert len(_ScenarioManager._get_all()) == 5
  246. assert len(_ScenarioManager._get_all_by(filters=[{"version": "2.0", "config_id": "config_id_1"}])) == 0
  247. assert len(_ScenarioManager._get_all_by(filters=[{"version": "2.0", "config_id": "config_id_6"}])) == 1
  248. _VersionManager._set_development_version("1.0")
  249. assert len(_ScenarioManager._get_all()) == 5
  250. assert len(_ScenarioManager._get_all_by(filters=[{"version": "1.0", "config_id": "config_id_1"}])) == 1
  251. assert len(_ScenarioManager._get_all_by(filters=[{"version": "1.0", "config_id": "config_id_6"}])) == 0
  252. _VersionManager._set_development_version("2.0")
  253. assert len(_ScenarioManager._get_all()) == 5
  254. assert len(_ScenarioManager._get_all_by(filters=[{"version": "2.0", "config_id": "config_id_1"}])) == 0
  255. assert len(_ScenarioManager._get_all_by(filters=[{"version": "2.0", "config_id": "config_id_6"}])) == 1
  256. def test_create_scenario_does_not_modify_config():
  257. creation_date_1 = datetime.now()
  258. name_1 = "name_1"
  259. scenario_config = Config.configure_scenario("sc", None, None, Frequency.DAILY)
  260. assert scenario_config.properties.get("name") is None
  261. assert len(scenario_config.properties) == 0
  262. scenario = _ScenarioManager._create(scenario_config, creation_date=creation_date_1, name=name_1)
  263. assert len(scenario_config.properties) == 0
  264. assert len(scenario.properties) == 1
  265. assert scenario.properties.get("name") == name_1
  266. assert scenario.name == name_1
  267. scenario.properties["foo"] = "bar"
  268. _ScenarioManager._update(scenario)
  269. assert len(scenario_config.properties) == 0
  270. assert len(scenario.properties) == 2
  271. assert scenario.properties.get("foo") == "bar"
  272. assert scenario.properties.get("name") == name_1
  273. assert scenario.name == name_1
  274. scenario_2 = _ScenarioManager._create(scenario_config, creation_date=creation_date_1)
  275. assert scenario_2.name is None
  276. def test_create_and_delete_scenario():
  277. creation_date_1 = datetime.now()
  278. creation_date_2 = creation_date_1 + timedelta(minutes=10)
  279. name_1 = "name_1"
  280. _ScenarioManager._delete_all()
  281. assert len(_ScenarioManager._get_all()) == 0
  282. scenario_config = Config.configure_scenario("sc", None, None, Frequency.DAILY)
  283. scenario_1 = _ScenarioManager._create(scenario_config, creation_date=creation_date_1, name=name_1)
  284. assert scenario_1.config_id == "sc"
  285. assert scenario_1.sequences == {}
  286. assert scenario_1.tasks == {}
  287. assert scenario_1.additional_data_nodes == {}
  288. assert scenario_1.data_nodes == {}
  289. assert scenario_1.cycle.frequency == Frequency.DAILY
  290. assert scenario_1.is_primary
  291. assert scenario_1.cycle.creation_date == creation_date_1
  292. assert scenario_1.cycle.start_date.date() == creation_date_1.date()
  293. assert scenario_1.cycle.end_date.date() == creation_date_1.date()
  294. assert scenario_1.creation_date == creation_date_1
  295. assert scenario_1.name == name_1
  296. assert scenario_1.properties["name"] == name_1
  297. assert scenario_1.tags == set()
  298. cycle_id_1 = scenario_1.cycle.id
  299. assert _CycleManager._get(cycle_id_1).id == cycle_id_1
  300. _ScenarioManager._delete(scenario_1.id)
  301. assert _ScenarioManager._get(scenario_1.id) is None
  302. assert _CycleManager._get(cycle_id_1) is None
  303. # Recreate scenario_1
  304. scenario_1 = _ScenarioManager._create(scenario_config, creation_date=creation_date_1, name=name_1)
  305. scenario_2 = _ScenarioManager._create(scenario_config, creation_date=creation_date_2)
  306. assert scenario_2.config_id == "sc"
  307. assert scenario_2.sequences == {}
  308. assert scenario_2.tasks == {}
  309. assert scenario_2.additional_data_nodes == {}
  310. assert scenario_2.data_nodes == {}
  311. assert scenario_2.cycle.frequency == Frequency.DAILY
  312. assert not scenario_2.is_primary
  313. assert scenario_2.cycle.creation_date == creation_date_1
  314. assert scenario_2.cycle.start_date.date() == creation_date_2.date()
  315. assert scenario_2.cycle.end_date.date() == creation_date_2.date()
  316. assert scenario_2.properties.get("name") is None
  317. assert scenario_2.tags == set()
  318. assert scenario_1 != scenario_2
  319. assert scenario_1.cycle == scenario_2.cycle
  320. assert len(_ScenarioManager._get_all()) == 2
  321. with pytest.raises(DeletingPrimaryScenario):
  322. _ScenarioManager._delete(
  323. scenario_1.id,
  324. )
  325. _ScenarioManager._delete(
  326. scenario_2.id,
  327. )
  328. assert len(_ScenarioManager._get_all()) == 1
  329. _ScenarioManager._delete(scenario_1.id)
  330. assert len(_ScenarioManager._get_all()) == 0
  331. def test_can_create():
  332. dn_config = Config.configure_in_memory_data_node("dn", 10)
  333. task_config = Config.configure_task("task", print, [dn_config])
  334. scenario_config = Config.configure_scenario("sc", {task_config}, [], Frequency.DAILY)
  335. reasons = _ScenarioManager._can_create()
  336. assert bool(reasons) is True
  337. assert reasons._reasons == {}
  338. reasons = _ScenarioManager._can_create(scenario_config)
  339. assert bool(reasons) is True
  340. assert reasons._reasons == {}
  341. _ScenarioManager._create(scenario_config)
  342. reasons = _ScenarioManager._can_create(task_config)
  343. assert bool(reasons) is False
  344. assert reasons._reasons[task_config.id] == {WrongConfigType(task_config.id, ScenarioConfig.__name__)}
  345. assert str(list(reasons._reasons[task_config.id])[0]) == "Object 'task' must be a valid ScenarioConfig"
  346. with pytest.raises(AttributeError):
  347. _ScenarioManager._create(task_config)
  348. reasons = _ScenarioManager._can_create(1)
  349. assert bool(reasons) is False
  350. assert reasons._reasons["1"] == {WrongConfigType(1, ScenarioConfig.__name__)}
  351. assert str(list(reasons._reasons["1"])[0]) == "Object '1' must be a valid ScenarioConfig"
  352. with pytest.raises(AttributeError):
  353. _ScenarioManager._create(1)
  354. def test_is_deletable():
  355. assert len(_ScenarioManager._get_all()) == 0
  356. scenario_config = Config.configure_scenario("sc", None, None, Frequency.DAILY)
  357. creation_date = datetime.now()
  358. scenario_1_primary = _ScenarioManager._create(scenario_config, creation_date=creation_date, name="1")
  359. scenario_2 = _ScenarioManager._create(scenario_config, creation_date=creation_date, name="2")
  360. rc = _ScenarioManager._is_deletable("some_scenario")
  361. assert not rc
  362. assert "Entity 'some_scenario' does not exist in the repository." in rc.reasons
  363. assert len(_ScenarioManager._get_all()) == 2
  364. assert scenario_1_primary.is_primary
  365. assert not _ScenarioManager._is_deletable(scenario_1_primary)
  366. assert not _ScenarioManager._is_deletable(scenario_1_primary.id)
  367. assert not scenario_2.is_primary
  368. assert _ScenarioManager._is_deletable(scenario_2)
  369. assert _ScenarioManager._is_deletable(scenario_2.id)
  370. _ScenarioManager._hard_delete(scenario_2.id)
  371. del scenario_2
  372. assert len(_ScenarioManager._get_all()) == 1
  373. assert scenario_1_primary.is_primary
  374. assert _ScenarioManager._is_deletable(scenario_1_primary)
  375. assert _ScenarioManager._is_deletable(scenario_1_primary.id)
  376. def test_assign_scenario_as_parent_of_task_and_additional_data_nodes():
  377. dn_config_1 = Config.configure_data_node("dn_1", "in_memory", scope=Scope.GLOBAL)
  378. dn_config_2 = Config.configure_data_node("dn_2", "in_memory", scope=Scope.GLOBAL)
  379. dn_config_3 = Config.configure_data_node("dn_3", "in_memory", scope=Scope.SCENARIO)
  380. additional_dn_config_1 = Config.configure_data_node("additional_dn_1", "in_memory", scope=Scope.GLOBAL)
  381. additional_dn_config_2 = Config.configure_data_node("additional_dn_2", "in_memory", scope=Scope.SCENARIO)
  382. task_config_1 = Config.configure_task("task_1", print, [dn_config_1], [dn_config_2])
  383. task_config_2 = Config.configure_task("task_2", print, [dn_config_2], [dn_config_3])
  384. task_config_3 = Config.configure_task("task_3", print, [dn_config_2], [dn_config_3])
  385. scenario_config_1 = Config.configure_scenario(
  386. "scenario_1", [task_config_1, task_config_2], [additional_dn_config_1, additional_dn_config_2]
  387. )
  388. scenario_config_1.add_sequences({"sequence_1": [task_config_1, task_config_2]})
  389. scenario_config_2 = Config.configure_scenario(
  390. "scenario_2", [task_config_1, task_config_2, task_config_3], [additional_dn_config_1, additional_dn_config_2]
  391. )
  392. scenario_config_2.add_sequences(
  393. {"sequence_1": [task_config_1, task_config_2], "sequence_2": [task_config_1, task_config_3]}
  394. )
  395. scenario_1 = _ScenarioManager._create(scenario_config_1)
  396. sequence_1_s1 = scenario_1.sequences["sequence_1"]
  397. assert all(sequence.parent_ids == {scenario_1.id} for sequence in scenario_1.sequences.values())
  398. tasks = scenario_1.tasks.values()
  399. assert all(task.parent_ids == {scenario_1.id, sequence_1_s1.id} for task in tasks)
  400. data_nodes = {}
  401. for task in tasks:
  402. data_nodes.update(task.data_nodes)
  403. assert data_nodes["dn_1"].parent_ids == {scenario_1.tasks["task_1"].id}
  404. assert data_nodes["dn_2"].parent_ids == {scenario_1.tasks["task_1"].id, scenario_1.tasks["task_2"].id}
  405. assert data_nodes["dn_3"].parent_ids == {scenario_1.tasks["task_2"].id}
  406. additional_data_nodes = scenario_1.additional_data_nodes
  407. assert additional_data_nodes["additional_dn_1"].parent_ids == {scenario_1.id}
  408. assert additional_data_nodes["additional_dn_2"].parent_ids == {scenario_1.id}
  409. scenario_2 = _ScenarioManager._create(scenario_config_2)
  410. sequence_1_s2 = scenario_2.sequences["sequence_1"]
  411. sequence_2_s2 = scenario_2.sequences["sequence_2"]
  412. assert all(sequence.parent_ids == {scenario_2.id} for sequence in scenario_2.sequences.values())
  413. assert scenario_1.tasks["task_1"] == scenario_2.tasks["task_1"]
  414. assert scenario_1.tasks["task_1"].parent_ids == {
  415. scenario_1.id,
  416. sequence_1_s1.id,
  417. scenario_2.id,
  418. sequence_1_s2.id,
  419. sequence_2_s2.id,
  420. }
  421. assert scenario_1.tasks["task_2"].parent_ids == {scenario_1.id, sequence_1_s1.id}
  422. assert scenario_2.tasks["task_2"].parent_ids == {scenario_2.id, sequence_1_s2.id}
  423. assert scenario_2.tasks["task_3"].parent_ids == {scenario_2.id, sequence_2_s2.id}
  424. additional_data_nodes = scenario_2.additional_data_nodes
  425. assert additional_data_nodes["additional_dn_1"].parent_ids == {scenario_1.id, scenario_2.id}
  426. assert additional_data_nodes["additional_dn_2"].parent_ids == {scenario_2.id}
  427. _ScenarioManager._hard_delete(scenario_1.id)
  428. _ScenarioManager._hard_delete(scenario_2.id)
  429. _TaskManager._delete_all()
  430. _DataManager._delete_all()
  431. dn_config_1 = Config.configure_data_node("dn_1", "in_memory", scope=Scope.GLOBAL)
  432. dn_config_2 = Config.configure_data_node("dn_2", "in_memory", scope=Scope.GLOBAL)
  433. dn_config_3 = Config.configure_data_node("dn_3", "in_memory", scope=Scope.GLOBAL)
  434. additional_dn_config_1 = Config.configure_data_node("additional_dn_1", "in_memory", scope=Scope.GLOBAL)
  435. additional_dn_config_2 = Config.configure_data_node("additional_dn_2", "in_memory", scope=Scope.GLOBAL)
  436. task_config_1 = Config.configure_task("task_1", print, [dn_config_1], [dn_config_2])
  437. task_config_2 = Config.configure_task("task_2", print, [dn_config_2], [dn_config_3])
  438. task_config_3 = Config.configure_task("task_3", print, [dn_config_2], [dn_config_3])
  439. scenario_config_1 = Config.configure_scenario(
  440. "scenario_1", [task_config_1, task_config_2], [additional_dn_config_1, additional_dn_config_2]
  441. )
  442. scenario_config_1.add_sequences({"sequence_1": [task_config_1, task_config_2]})
  443. scenario_config_2 = Config.configure_scenario(
  444. "scenario_2", [task_config_1, task_config_2, task_config_3], [additional_dn_config_1, additional_dn_config_2]
  445. )
  446. scenario_config_2.add_sequences(
  447. {"sequence_1": [task_config_1, task_config_2], "sequence_2": [task_config_1, task_config_3]}
  448. )
  449. scenario_1 = _ScenarioManager._create(scenario_config_1)
  450. sequence_1_s1 = scenario_1.sequences["sequence_1"]
  451. assert scenario_1.sequences["sequence_1"].parent_ids == {scenario_1.id}
  452. tasks = scenario_1.tasks.values()
  453. assert all(task.parent_ids == {scenario_1.id, sequence_1_s1.id} for task in tasks)
  454. data_nodes = {}
  455. for task in tasks:
  456. data_nodes.update(task.data_nodes)
  457. assert data_nodes["dn_1"].parent_ids == {scenario_1.tasks["task_1"].id}
  458. assert data_nodes["dn_2"].parent_ids == {scenario_1.tasks["task_1"].id, scenario_1.tasks["task_2"].id}
  459. assert data_nodes["dn_3"].parent_ids == {scenario_1.tasks["task_2"].id}
  460. additional_data_nodes = scenario_1.additional_data_nodes
  461. assert additional_data_nodes["additional_dn_1"].parent_ids == {scenario_1.id}
  462. assert additional_data_nodes["additional_dn_2"].parent_ids == {scenario_1.id}
  463. scenario_2 = _ScenarioManager._create(scenario_config_2)
  464. sequence_1_s2 = scenario_2.sequences["sequence_1"]
  465. sequence_2_s2 = scenario_2.sequences["sequence_2"]
  466. assert scenario_1.sequences["sequence_1"].parent_ids == {scenario_1.id}
  467. assert scenario_2.sequences["sequence_1"].parent_ids == {scenario_2.id}
  468. assert scenario_2.sequences["sequence_2"].parent_ids == {scenario_2.id}
  469. tasks = {**scenario_1.tasks, **scenario_2.tasks}
  470. assert tasks["task_1"].parent_ids == {
  471. scenario_1.id,
  472. scenario_2.id,
  473. sequence_1_s1.id,
  474. sequence_1_s2.id,
  475. sequence_2_s2.id,
  476. }
  477. assert tasks["task_2"].parent_ids == {scenario_1.id, scenario_2.id, sequence_1_s1.id, sequence_1_s2.id}
  478. assert tasks["task_3"].parent_ids == {scenario_2.id, sequence_2_s2.id}
  479. additional_data_nodes = scenario_2.additional_data_nodes
  480. assert additional_data_nodes["additional_dn_1"].parent_ids == {scenario_1.id, scenario_2.id}
  481. assert additional_data_nodes["additional_dn_2"].parent_ids == {scenario_1.id, scenario_2.id}
  482. def mult_by_2(nb: int):
  483. return nb * 2
  484. def mult_by_3(nb: int):
  485. return nb * 3
  486. def mult_by_4(nb: int):
  487. return nb * 4
  488. def test_scenario_manager_only_creates_data_node_once():
  489. # dn_1 ---> mult_by_2 ---> dn_2 ---> mult_by_3 ---> dn_6
  490. # dn_1 ---> mult_by_4 ---> dn_4
  491. dn_config_1 = Config.configure_data_node("foo", "in_memory", Scope.GLOBAL, default_data=1)
  492. dn_config_2 = Config.configure_data_node("bar", "in_memory", Scope.CYCLE, default_data=0)
  493. dn_config_6 = Config.configure_data_node("baz", "in_memory", Scope.CYCLE, default_data=0)
  494. dn_config_4 = Config.configure_data_node("qux", "in_memory", Scope.SCENARIO, default_data=0)
  495. task_mult_by_2_config = Config.configure_task("mult_by_2", mult_by_2, [dn_config_1], dn_config_2)
  496. task_mult_by_3_config = Config.configure_task("mult_by_3", mult_by_3, [dn_config_2], dn_config_6)
  497. task_mult_by_4_config = Config.configure_task("mult_by_4", mult_by_4, [dn_config_1], dn_config_4)
  498. scenario_config = Config.configure_scenario(
  499. "awesome_scenario", [task_mult_by_2_config, task_mult_by_3_config, task_mult_by_4_config], None, Frequency.DAILY
  500. )
  501. scenario_config.add_sequences(
  502. {"by_6": [task_mult_by_2_config, task_mult_by_3_config], "by_4": [task_mult_by_4_config]}
  503. )
  504. assert len(_DataManager._get_all()) == 0
  505. assert len(_TaskManager._get_all()) == 0
  506. assert len(_SequenceManager._get_all()) == 0
  507. assert len(_ScenarioManager._get_all()) == 0
  508. assert len(_CycleManager._get_all()) == 0
  509. scenario_1 = _ScenarioManager._create(scenario_config)
  510. assert len(_DataManager._get_all()) == 4
  511. assert len(_TaskManager._get_all()) == 3
  512. assert len(_SequenceManager._get_all()) == 2
  513. assert len(_ScenarioManager._get_all()) == 1
  514. assert scenario_1.foo.read() == 1
  515. assert scenario_1.bar.read() == 0
  516. assert scenario_1.baz.read() == 0
  517. assert scenario_1.qux.read() == 0
  518. assert scenario_1.by_6._get_sorted_tasks()[0][0].config_id == task_mult_by_2_config.id
  519. assert scenario_1.by_6._get_sorted_tasks()[1][0].config_id == task_mult_by_3_config.id
  520. assert scenario_1.by_4._get_sorted_tasks()[0][0].config_id == task_mult_by_4_config.id
  521. assert scenario_1.tasks.keys() == {task_mult_by_2_config.id, task_mult_by_3_config.id, task_mult_by_4_config.id}
  522. scenario_1_sorted_tasks = scenario_1._get_sorted_tasks()
  523. expected = [{task_mult_by_2_config.id, task_mult_by_4_config.id}, {task_mult_by_3_config.id}]
  524. for i, list_tasks_by_level in enumerate(scenario_1_sorted_tasks):
  525. assert {t.config_id for t in list_tasks_by_level} == expected[i]
  526. assert scenario_1.cycle.frequency == Frequency.DAILY
  527. _ScenarioManager._create(scenario_config)
  528. assert len(_DataManager._get_all()) == 5
  529. assert len(_TaskManager._get_all()) == 4
  530. assert len(_SequenceManager._get_all()) == 4
  531. assert len(_ScenarioManager._get_all()) == 2
  532. def test_notification_subscribe(mocker):
  533. mocker.patch("taipy.core._entity._reload._Reloader._reload", side_effect=lambda m, o: o)
  534. scenario_config = Config.configure_scenario(
  535. "awesome_scenario",
  536. [
  537. Config.configure_task(
  538. "mult_by_2",
  539. mult_by_2,
  540. [Config.configure_data_node("foo", "pickle", Scope.SCENARIO, default_data=1)],
  541. Config.configure_data_node("bar", "pickle", Scope.SCENARIO, default_data=0),
  542. )
  543. ],
  544. )
  545. scenario = _ScenarioManager._create(scenario_config)
  546. notify_1 = NotifyMock(scenario)
  547. notify_2 = NotifyMock(scenario)
  548. mocker.patch.object(
  549. _utils,
  550. "_load_fct",
  551. side_effect=[
  552. notify_1,
  553. notify_1,
  554. notify_1,
  555. notify_1,
  556. notify_2,
  557. notify_2,
  558. notify_2,
  559. notify_2,
  560. ],
  561. )
  562. # test subscribing notification
  563. _ScenarioManager._subscribe(callback=notify_1, scenario=scenario)
  564. _ScenarioManager._submit(scenario)
  565. notify_1.assert_called_3_times()
  566. notify_1.reset()
  567. # test unsubscribing notification
  568. # test notif subscribe only on new jobs
  569. _ScenarioManager._unsubscribe(callback=notify_1, scenario=scenario)
  570. _ScenarioManager._subscribe(callback=notify_2, scenario=scenario)
  571. _ScenarioManager._submit(scenario)
  572. notify_1.assert_not_called()
  573. notify_2.assert_called_3_times()
  574. def test_notification_subscribe_multiple_params(mocker):
  575. mocker.patch("taipy.core._entity._reload._Reloader._reload", side_effect=lambda m, o: o)
  576. scenario_config = Config.configure_scenario(
  577. "awesome_scenario",
  578. [
  579. Config.configure_task(
  580. "mult_by_2",
  581. mult_by_2,
  582. [Config.configure_data_node("foo", "in_memory", Scope.SCENARIO, default_data=1)],
  583. Config.configure_data_node("bar", "in_memory", Scope.SCENARIO, default_data=0),
  584. )
  585. ],
  586. )
  587. notify = mocker.Mock()
  588. scenario = _ScenarioManager._create(scenario_config)
  589. _ScenarioManager._subscribe(callback=notify, params=["foobar", 123, 1.2], scenario=scenario)
  590. mocker.patch.object(_ScenarioManager, "_get", return_value=scenario)
  591. _ScenarioManager._submit(scenario)
  592. notify.assert_called_with("foobar", 123, 1.2, scenario, ANY)
  593. def notify_multi_param(param, *args):
  594. assert len(param) == 3
  595. def notify1(*args, **kwargs): ...
  596. def notify2(*args, **kwargs): ...
  597. def test_notification_unsubscribe(mocker):
  598. mocker.patch("taipy.core._entity._reload._Reloader._reload", side_effect=lambda m, o: o)
  599. scenario_config = Config.configure_scenario(
  600. "awesome_scenario",
  601. [
  602. Config.configure_task(
  603. "mult_by_2",
  604. mult_by_2,
  605. [Config.configure_data_node("foo", "in_memory", Scope.SCENARIO, default_data=1)],
  606. Config.configure_data_node("bar", "in_memory", Scope.SCENARIO, default_data=0),
  607. )
  608. ],
  609. )
  610. scenario = _ScenarioManager._create(scenario_config)
  611. notify_1 = notify1
  612. notify_2 = notify2
  613. # test subscribing notification
  614. _ScenarioManager._subscribe(callback=notify_1, scenario=scenario)
  615. _ScenarioManager._unsubscribe(callback=notify_1, scenario=scenario)
  616. _ScenarioManager._subscribe(callback=notify_2, scenario=scenario)
  617. _ScenarioManager._submit(scenario.id)
  618. with pytest.raises(ValueError):
  619. _ScenarioManager._unsubscribe(callback=notify_1, scenario=scenario)
  620. _ScenarioManager._unsubscribe(callback=notify_2, scenario=scenario)
  621. def test_notification_unsubscribe_multi_param():
  622. scenario_config = Config.configure_scenario(
  623. "awesome_scenario",
  624. [
  625. Config.configure_task(
  626. "mult_by_2",
  627. mult_by_2,
  628. [Config.configure_data_node("foo", "in_memory", Scope.SCENARIO, default_data=1)],
  629. Config.configure_data_node("bar", "in_memory", Scope.SCENARIO, default_data=0),
  630. )
  631. ],
  632. )
  633. scenario = _ScenarioManager._create(scenario_config)
  634. # test subscribing notification
  635. _ScenarioManager._subscribe(callback=notify_multi_param, params=["foobar", 123, 0], scenario=scenario)
  636. _ScenarioManager._subscribe(callback=notify_multi_param, params=["foobar", 123, 1], scenario=scenario)
  637. _ScenarioManager._subscribe(callback=notify_multi_param, params=["foobar", 123, 2], scenario=scenario)
  638. assert len(scenario.subscribers) == 3
  639. # if no params are passed, removes the first occurrence of the subscriber when there's more than one copy
  640. scenario.unsubscribe(notify_multi_param)
  641. assert len(scenario.subscribers) == 2
  642. assert _Subscriber(notify_multi_param, ["foobar", 123, 0]) not in scenario.subscribers
  643. # If params are passed, find the corresponding pair of callback and params to remove
  644. scenario.unsubscribe(notify_multi_param, ["foobar", 123, 2])
  645. assert len(scenario.subscribers) == 1
  646. assert _Subscriber(notify_multi_param, ["foobar", 123, 2]) not in scenario.subscribers
  647. # If params are passed but is not on the list of subscribers, throws a ValueErrors
  648. with pytest.raises(ValueError):
  649. scenario.unsubscribe(notify_multi_param, ["foobar", 123, 10000])
  650. def test_scenario_notification_subscribe_all():
  651. scenario_config = Config.configure_scenario(
  652. "awesome_scenario",
  653. [
  654. Config.configure_task(
  655. "mult_by_2",
  656. mult_by_2,
  657. [Config.configure_data_node("foo", "in_memory", Scope.SCENARIO, default_data=1)],
  658. Config.configure_data_node("bar", "in_memory", Scope.SCENARIO, default_data=0),
  659. )
  660. ],
  661. )
  662. other_scenario_config = Config.configure_scenario(
  663. "other_scenario",
  664. [
  665. Config.configure_task(
  666. "other_mult_by_2_2",
  667. mult_by_2,
  668. [Config.configure_data_node("other_foo", "in_memory", Scope.SCENARIO, default_data=1)],
  669. Config.configure_data_node("other_bar", "in_memory", Scope.SCENARIO, default_data=0),
  670. )
  671. ],
  672. )
  673. scenario = _ScenarioManager._create(scenario_config)
  674. other_scenario = _ScenarioManager._create(other_scenario_config)
  675. notify_1 = NotifyMock(scenario)
  676. _ScenarioManager._subscribe(notify_1)
  677. assert len(_ScenarioManager._get(scenario.id).subscribers) == 1
  678. assert len(_ScenarioManager._get(other_scenario.id).subscribers) == 1
  679. def test_is_promotable_to_primary_scenario():
  680. assert len(_ScenarioManager._get_all()) == 0
  681. scenario_config = Config.configure_scenario("sc", set(), set(), Frequency.DAILY)
  682. creation_date = datetime.now()
  683. scenario_1 = _ScenarioManager._create(scenario_config, creation_date=creation_date, name="1") # primary scenario
  684. scenario_2 = _ScenarioManager._create(scenario_config, creation_date=creation_date, name="2")
  685. assert len(_ScenarioManager._get_all()) == 2
  686. assert scenario_1.is_primary
  687. assert not _ScenarioManager._is_promotable_to_primary(scenario_1)
  688. assert not _ScenarioManager._is_promotable_to_primary(scenario_1.id)
  689. assert not scenario_2.is_primary
  690. assert _ScenarioManager._is_promotable_to_primary(scenario_2)
  691. assert _ScenarioManager._is_promotable_to_primary(scenario_2.id)
  692. _ScenarioManager._set_primary(scenario_2)
  693. assert len(_ScenarioManager._get_all()) == 2
  694. assert not scenario_1.is_primary
  695. assert _ScenarioManager._is_promotable_to_primary(scenario_1)
  696. assert _ScenarioManager._is_promotable_to_primary(scenario_1.id)
  697. assert scenario_2.is_primary
  698. assert not _ScenarioManager._is_promotable_to_primary(scenario_2)
  699. assert not _ScenarioManager._is_promotable_to_primary(scenario_2.id)
  700. def test_get_set_primary_scenario():
  701. cycle_1 = _CycleManager._create(Frequency.DAILY, name="foo")
  702. scenario_1 = Scenario("sc_1", [], {}, ScenarioId("sc_1"), is_primary=False, cycle=cycle_1)
  703. scenario_2 = Scenario("sc_2", [], {}, ScenarioId("sc_2"), is_primary=False, cycle=cycle_1)
  704. _ScenarioManager._delete_all()
  705. _CycleManager._delete_all()
  706. assert len(_ScenarioManager._get_all()) == 0
  707. assert len(_CycleManager._get_all()) == 0
  708. _CycleManager._repository._save(cycle_1)
  709. _ScenarioManager._repository._save(scenario_1)
  710. _ScenarioManager._repository._save(scenario_2)
  711. assert len(_ScenarioManager._get_primary_scenarios()) == 0
  712. assert len(_ScenarioManager._get_all_by_cycle(cycle_1)) == 2
  713. _ScenarioManager._set_primary(scenario_1)
  714. assert len(_ScenarioManager._get_primary_scenarios()) == 1
  715. assert len(_ScenarioManager._get_all_by_cycle(cycle_1)) == 2
  716. assert _ScenarioManager._get_primary(cycle_1) == scenario_1
  717. _ScenarioManager._set_primary(scenario_2)
  718. assert len(_ScenarioManager._get_primary_scenarios()) == 1
  719. assert len(_ScenarioManager._get_all_by_cycle(cycle_1)) == 2
  720. assert _ScenarioManager._get_primary(cycle_1) == scenario_2
  721. def test_get_primary_scenarios_sorted():
  722. scenario_1_cfg = Config.configure_scenario(id="scenario_1", frequency=Frequency.DAILY)
  723. scenario_2_cfg = Config.configure_scenario(id="scenario_2", frequency=Frequency.DAILY)
  724. not_primary_scenario = _ScenarioManager._create(scenario_1_cfg, name="not_primary_scenario")
  725. now = datetime.now()
  726. scenario_1 = _ScenarioManager._create(scenario_1_cfg, now, "B_scenario")
  727. scenario_2 = _ScenarioManager._create(scenario_2_cfg, now + timedelta(days=2), "A_scenario")
  728. scenario_3 = _ScenarioManager._create(scenario_2_cfg, now + timedelta(days=4), "C_scenario")
  729. scenario_4 = _ScenarioManager._create(scenario_2_cfg, now + timedelta(days=3), "D_scenario")
  730. _ScenarioManager._set_primary(scenario_1)
  731. scenario_1.tags = ["banana", "kiwi"]
  732. _ScenarioManager._set_primary(scenario_2)
  733. scenario_2.tags = ["apple", "banana"]
  734. _ScenarioManager._set_primary(scenario_3)
  735. scenario_3.tags = ["banana", "kiwi"]
  736. _ScenarioManager._set_primary(scenario_4)
  737. all_scenarios = tp.get_scenarios()
  738. assert not_primary_scenario in all_scenarios
  739. primary_scenarios = _ScenarioManager._get_primary_scenarios()
  740. assert not_primary_scenario not in primary_scenarios
  741. primary_scenarios_sorted_by_name = [scenario_2, scenario_1, scenario_3, scenario_4]
  742. assert primary_scenarios_sorted_by_name == _ScenarioManager._sort_scenarios(
  743. primary_scenarios, descending=False, sort_key="name"
  744. )
  745. scenarios_with_same_config_id = [scenario_2, scenario_3, scenario_4]
  746. scenarios_with_same_config_id.sort(key=lambda x: x.id)
  747. primary_scenarios_sorted_by_config_id = [
  748. scenario_1,
  749. scenarios_with_same_config_id[0],
  750. scenarios_with_same_config_id[1],
  751. scenarios_with_same_config_id[2],
  752. ]
  753. assert primary_scenarios_sorted_by_config_id == _ScenarioManager._sort_scenarios(
  754. primary_scenarios, descending=False, sort_key="config_id"
  755. )
  756. scenarios_sorted_by_id = [scenario_1, scenario_2, scenario_3, scenario_4]
  757. scenarios_sorted_by_id.sort(key=lambda x: x.id)
  758. assert scenarios_sorted_by_id == _ScenarioManager._sort_scenarios(
  759. primary_scenarios, descending=False, sort_key="id"
  760. )
  761. primary_scenarios_sorted_by_creation_date = [scenario_1, scenario_2, scenario_4, scenario_3]
  762. assert primary_scenarios_sorted_by_creation_date == _ScenarioManager._sort_scenarios(
  763. primary_scenarios, descending=False, sort_key="creation_date"
  764. )
  765. scenarios_with_same_tags = [scenario_1, scenario_3]
  766. scenarios_with_same_tags.sort(key=lambda x: x.id)
  767. primary_scenarios_sorted_by_tags = [
  768. scenario_4,
  769. scenario_2,
  770. scenarios_with_same_tags[0],
  771. scenarios_with_same_tags[1],
  772. ]
  773. assert primary_scenarios_sorted_by_tags == _ScenarioManager._sort_scenarios(
  774. primary_scenarios, descending=False, sort_key="tags"
  775. )
  776. primary_scenarios_sorted_by_name_descending_order = [scenario_4, scenario_3, scenario_1, scenario_2]
  777. assert primary_scenarios_sorted_by_name_descending_order == _ScenarioManager._sort_scenarios(
  778. primary_scenarios, descending=True, sort_key="name"
  779. )
  780. def test_hard_delete_one_single_scenario_with_scenario_data_nodes():
  781. dn_input_config = Config.configure_data_node("my_input", "in_memory", scope=Scope.SCENARIO, default_data="testing")
  782. dn_output_config = Config.configure_data_node("my_output", "in_memory", scope=Scope.SCENARIO)
  783. task_config = Config.configure_task("task_config", print, dn_input_config, dn_output_config)
  784. scenario_config = Config.configure_scenario("scenario_config", [task_config])
  785. scenario_config.add_sequences({"sequence_config": [task_config]})
  786. scenario = _ScenarioManager._create(scenario_config)
  787. _ScenarioManager._submit(scenario.id)
  788. assert len(_ScenarioManager._get_all()) == 1
  789. assert len(_SequenceManager._get_all()) == 1
  790. assert len(_TaskManager._get_all()) == 1
  791. assert len(_DataManager._get_all()) == 2
  792. assert len(_JobManager._get_all()) == 1
  793. _ScenarioManager._hard_delete(scenario.id)
  794. assert len(_ScenarioManager._get_all()) == 0
  795. assert len(_SequenceManager._get_all()) == 0
  796. assert len(_TaskManager._get_all()) == 0
  797. assert len(_DataManager._get_all()) == 0
  798. assert len(_JobManager._get_all()) == 0
  799. def test_hard_delete_one_scenario_among_two_with_scenario_data_nodes():
  800. dn_input_config = Config.configure_data_node("my_input", "in_memory", scope=Scope.SCENARIO, default_data="testing")
  801. dn_output_config = Config.configure_data_node("my_output", "in_memory", scope=Scope.SCENARIO)
  802. task_config = Config.configure_task("task_config", print, dn_input_config, dn_output_config)
  803. scenario_config = Config.configure_scenario("scenario_config", [task_config])
  804. scenario_config.add_sequences({"sequence_config": [task_config]})
  805. scenario_1 = _ScenarioManager._create(scenario_config)
  806. scenario_2 = _ScenarioManager._create(scenario_config)
  807. _ScenarioManager._submit(scenario_1.id)
  808. _ScenarioManager._submit(scenario_2.id)
  809. assert len(_ScenarioManager._get_all()) == 2
  810. assert len(_SequenceManager._get_all()) == 2
  811. assert len(_TaskManager._get_all()) == 2
  812. assert len(_DataManager._get_all()) == 4
  813. assert len(_JobManager._get_all()) == 2
  814. _ScenarioManager._hard_delete(scenario_1.id)
  815. assert len(_ScenarioManager._get_all()) == 1
  816. assert len(_SequenceManager._get_all()) == 1
  817. assert len(_TaskManager._get_all()) == 1
  818. assert len(_DataManager._get_all()) == 2
  819. assert len(_JobManager._get_all()) == 1
  820. assert _ScenarioManager._get(scenario_2.id) is not None
  821. def test_hard_delete_one_scenario_among_two_with_cycle_data_nodes():
  822. dn_input_config = Config.configure_data_node("my_input", "in_memory", scope=Scope.CYCLE, default_data="testing")
  823. dn_output_config = Config.configure_data_node("my_output", "in_memory", scope=Scope.CYCLE)
  824. task_config = Config.configure_task("task_config", print, dn_input_config, dn_output_config)
  825. scenario_config = Config.configure_scenario("scenario_config", [task_config])
  826. scenario_config.add_sequences({"sequence_config": [task_config]})
  827. scenario_1 = _ScenarioManager._create(scenario_config)
  828. scenario_2 = _ScenarioManager._create(scenario_config)
  829. _ScenarioManager._submit(scenario_1.id)
  830. _ScenarioManager._submit(scenario_2.id)
  831. assert len(_ScenarioManager._get_all()) == 2
  832. assert len(_SequenceManager._get_all()) == 2
  833. assert len(_TaskManager._get_all()) == 1
  834. assert len(_DataManager._get_all()) == 2
  835. assert len(_JobManager._get_all()) == 2
  836. _ScenarioManager._hard_delete(scenario_1.id)
  837. assert len(_ScenarioManager._get_all()) == 1
  838. assert len(_SequenceManager._get_all()) == 1
  839. assert len(_TaskManager._get_all()) == 1
  840. assert len(_DataManager._get_all()) == 2
  841. assert len(_JobManager._get_all()) == 2
  842. assert _ScenarioManager._get(scenario_2.id) is not None
  843. def test_hard_delete_shared_entities():
  844. dn_config_1 = Config.configure_data_node("my_input_1", "in_memory", scope=Scope.CYCLE, default_data="testing")
  845. dn_config_2 = Config.configure_data_node("my_input_2", "in_memory", scope=Scope.SCENARIO, default_data="testing")
  846. dn_config_3 = Config.configure_data_node("my_input_3", "in_memory", scope=Scope.GLOBAL, default_data="testing")
  847. dn_config_4 = Config.configure_data_node("my_input_4", "in_memory", scope=Scope.GLOBAL, default_data="testing")
  848. task_config_1 = Config.configure_task("task_config_1", print, dn_config_1, dn_config_2)
  849. task_config_2 = Config.configure_task("task_config_2", print, dn_config_2, dn_config_3)
  850. task_config_3 = Config.configure_task("task_config_3", print, dn_config_3, dn_config_4) # scope = global
  851. task_config_4 = Config.configure_task("task_config_4", print, dn_config_1) # scope = cycle
  852. scenario_config_1 = Config.configure_scenario(
  853. "scenario_config_1",
  854. [task_config_1, task_config_2, task_config_3, task_config_4],
  855. frequency=Frequency.WEEKLY,
  856. )
  857. scenario_config_1.add_sequences(
  858. {
  859. "sequence_config_1": [task_config_1, task_config_2],
  860. "sequence_config_2": [task_config_1, task_config_2],
  861. "sequence_config_3": [task_config_3],
  862. "sequence_config_4": [task_config_4],
  863. }
  864. )
  865. scenario_1 = _ScenarioManager._create(scenario_config_1)
  866. scenario_2 = _ScenarioManager._create(scenario_config_1)
  867. scenario_1.submit()
  868. scenario_2.submit()
  869. assert len(_CycleManager._get_all()) == 1
  870. assert len(_ScenarioManager._get_all()) == 2
  871. assert len(_SequenceManager._get_all()) == 8
  872. assert len(_TaskManager._get_all()) == 6
  873. assert len(_DataManager._get_all()) == 5
  874. assert len(_JobManager._get_all()) == 8
  875. _ScenarioManager._hard_delete(scenario_2.id)
  876. assert len(_CycleManager._get_all()) == 1
  877. assert len(_ScenarioManager._get_all()) == 1
  878. assert len(_SequenceManager._get_all()) == 4
  879. assert len(_TaskManager._get_all()) == 4
  880. assert len(_DataManager._get_all()) == 4
  881. assert len(_JobManager._get_all()) == 6
  882. def test_is_submittable():
  883. assert len(_ScenarioManager._get_all()) == 0
  884. dn_config = Config.configure_in_memory_data_node("dn", 10)
  885. task_config = Config.configure_task("task", print, [dn_config])
  886. scenario_config = Config.configure_scenario("sc", {task_config}, set(), Frequency.DAILY)
  887. scenario = _ScenarioManager._create(scenario_config)
  888. rc = _ScenarioManager._is_submittable("some_scenario")
  889. assert not rc
  890. assert "Entity 'some_scenario' does not exist in the repository." in rc.reasons
  891. assert len(_ScenarioManager._get_all()) == 1
  892. assert _ScenarioManager._is_submittable(scenario)
  893. assert _ScenarioManager._is_submittable(scenario.id)
  894. assert not _ScenarioManager._is_submittable("Scenario_temp")
  895. scenario.dn.edit_in_progress = True
  896. assert not _ScenarioManager._is_submittable(scenario)
  897. assert not _ScenarioManager._is_submittable(scenario.id)
  898. scenario.dn.edit_in_progress = False
  899. assert _ScenarioManager._is_submittable(scenario)
  900. assert _ScenarioManager._is_submittable(scenario.id)
  901. def test_submit():
  902. data_node_1 = InMemoryDataNode("foo", Scope.SCENARIO, "s1")
  903. data_node_2 = InMemoryDataNode("bar", Scope.SCENARIO, "s2")
  904. data_node_3 = InMemoryDataNode("baz", Scope.SCENARIO, "s3")
  905. data_node_4 = InMemoryDataNode("qux", Scope.SCENARIO, "s4")
  906. data_node_5 = InMemoryDataNode("quux", Scope.SCENARIO, "s5")
  907. data_node_6 = InMemoryDataNode("quuz", Scope.SCENARIO, "s6")
  908. data_node_7 = InMemoryDataNode("corge", Scope.SCENARIO, "s7")
  909. data_node_8 = InMemoryDataNode("fum", Scope.SCENARIO, "s8")
  910. task_1 = Task(
  911. "grault",
  912. {},
  913. print,
  914. [data_node_1, data_node_2],
  915. [data_node_3, data_node_4],
  916. TaskId("t1"),
  917. )
  918. task_2 = Task("garply", {}, print, [data_node_3], [data_node_5], TaskId("t2"))
  919. task_3 = Task("waldo", {}, print, [data_node_5, data_node_4], [data_node_6], TaskId("t3"))
  920. task_4 = Task("fred", {}, print, [data_node_4], [data_node_7], TaskId("t4"))
  921. task_5 = Task("thud", {}, print, [data_node_6], [data_node_8], TaskId("t5"))
  922. scenario = Scenario(
  923. "scenario_name",
  924. [task_5, task_4, task_2, task_1, task_3],
  925. {},
  926. [],
  927. ScenarioId("sce_id"),
  928. )
  929. class MockOrchestrator(_Orchestrator):
  930. submit_calls = []
  931. @classmethod
  932. def _lock_dn_output_and_create_job(
  933. cls,
  934. task: Task,
  935. submit_id: str,
  936. submit_entity_id: str,
  937. callbacks: Optional[Iterable[Callable]] = None,
  938. force: bool = False,
  939. ) -> Job:
  940. cls.submit_calls.append(task.id)
  941. return super()._lock_dn_output_and_create_job(task, submit_id, submit_entity_id, callbacks, force)
  942. with patch("taipy.core.task._task_manager._TaskManager._orchestrator", new=MockOrchestrator):
  943. with pytest.raises(NonExistingScenario):
  944. _ScenarioManager._submit(scenario.id)
  945. with pytest.raises(NonExistingScenario):
  946. _ScenarioManager._submit(scenario)
  947. # scenario and sequence do exist, but tasks does not exist.
  948. # We expect an exception to be raised
  949. _ScenarioManager._repository._save(scenario)
  950. with pytest.raises(NonExistingTask):
  951. _ScenarioManager._submit(scenario.id)
  952. with pytest.raises(NonExistingTask):
  953. _ScenarioManager._submit(scenario)
  954. # scenario, sequence, and tasks do exist.
  955. # We expect all the tasks to be submitted once,
  956. # and respecting specific constraints on the order
  957. _DataManager._repository._save(data_node_1)
  958. _DataManager._repository._save(data_node_2)
  959. _DataManager._repository._save(data_node_3)
  960. _DataManager._repository._save(data_node_4)
  961. _DataManager._repository._save(data_node_5)
  962. _DataManager._repository._save(data_node_6)
  963. _DataManager._repository._save(data_node_7)
  964. _DataManager._repository._save(data_node_8)
  965. _TaskManager._repository._save(task_1)
  966. _TaskManager._repository._save(task_2)
  967. _TaskManager._repository._save(task_3)
  968. _TaskManager._repository._save(task_4)
  969. _TaskManager._repository._save(task_5)
  970. _ScenarioManager._submit(scenario.id)
  971. submit_calls = _TaskManager._orchestrator().submit_calls
  972. assert len(submit_calls) == 5
  973. assert set(submit_calls) == {task_1.id, task_2.id, task_4.id, task_3.id, task_5.id}
  974. assert submit_calls.index(task_2.id) < submit_calls.index(task_3.id)
  975. assert submit_calls.index(task_1.id) < submit_calls.index(task_3.id)
  976. assert submit_calls.index(task_1.id) < submit_calls.index(task_2.id)
  977. assert submit_calls.index(task_1.id) < submit_calls.index(task_4.id)
  978. _ScenarioManager._submit(scenario)
  979. submit_calls = _TaskManager._orchestrator().submit_calls
  980. assert len(submit_calls) == 10
  981. assert set(submit_calls) == {task_1.id, task_2.id, task_4.id, task_3.id, task_5.id}
  982. assert submit_calls.index(task_2.id) < submit_calls.index(task_3.id)
  983. assert submit_calls.index(task_1.id) < submit_calls.index(task_3.id)
  984. assert submit_calls.index(task_1.id) < submit_calls.index(task_2.id)
  985. assert submit_calls.index(task_1.id) < submit_calls.index(task_4.id)
  986. def my_print(a, b):
  987. print(a + b) # noqa: T201
  988. def test_submit_task_with_input_dn_wrong_file_path(caplog):
  989. csv_dn_cfg = Config.configure_csv_data_node("wrong_csv_file_path", default_path="wrong_path.csv")
  990. pickle_dn_cfg = Config.configure_pickle_data_node("wrong_pickle_file_path", default_path="wrong_path.pickle")
  991. parquet_dn_cfg = Config.configure_parquet_data_node("wrong_parquet_file_path", default_path="wrong_path.parquet")
  992. json_dn_cfg = Config.configure_parquet_data_node("wrong_json_file_path", default_path="wrong_path.json")
  993. task_cfg = Config.configure_task("task", my_print, [csv_dn_cfg, pickle_dn_cfg], parquet_dn_cfg)
  994. task_2_cfg = Config.configure_task("task2", my_print, [csv_dn_cfg, parquet_dn_cfg], json_dn_cfg)
  995. scenario_cfg = Config.configure_scenario("scenario", [task_cfg, task_2_cfg])
  996. sc_manager = _ScenarioManagerFactory._build_manager()
  997. scenario = sc_manager._create(scenario_cfg)
  998. sc_manager._submit(scenario)
  999. stdout = caplog.text
  1000. expected_outputs = [
  1001. f"{input_dn.id} cannot be read because it has never been written. Hint: The data node may refer to a wrong "
  1002. f"path : {input_dn.path} "
  1003. for input_dn in scenario.get_inputs()
  1004. ]
  1005. not_expected_outputs = [
  1006. f"{input_dn.id} cannot be read because it has never been written. Hint: The data node may refer to a wrong "
  1007. f"path : {input_dn.path} "
  1008. for input_dn in scenario.data_nodes.values()
  1009. if input_dn not in scenario.get_inputs()
  1010. ]
  1011. assert all(expected_output in stdout for expected_output in expected_outputs)
  1012. assert all(expected_output not in stdout for expected_output in not_expected_outputs)
  1013. def test_submit_task_with_one_input_dn_wrong_file_path(caplog):
  1014. csv_dn_cfg = Config.configure_csv_data_node("wrong_csv_file_path", default_path="wrong_path.csv")
  1015. pickle_dn_cfg = Config.configure_pickle_data_node("wrong_pickle_file_path", default_data="value")
  1016. parquet_dn_cfg = Config.configure_parquet_data_node("wrong_parquet_file_path", default_path="wrong_path.parquet")
  1017. json_dn_cfg = Config.configure_parquet_data_node("wrong_json_file_path", default_path="wrong_path.json")
  1018. task_cfg = Config.configure_task("task", my_print, [csv_dn_cfg, pickle_dn_cfg], parquet_dn_cfg)
  1019. task_2_cfg = Config.configure_task("task2", my_print, [csv_dn_cfg, parquet_dn_cfg], json_dn_cfg)
  1020. scenario_cfg = Config.configure_scenario("scenario", [task_cfg, task_2_cfg])
  1021. sce_manager = _ScenarioManagerFactory._build_manager()
  1022. scenario = sce_manager._create(scenario_cfg)
  1023. sce_manager._submit(scenario)
  1024. stdout = caplog.text
  1025. expected_outputs = [
  1026. f"{input_dn.id} cannot be read because it has never been written. Hint: The data node may refer to a wrong "
  1027. f"path : {input_dn.path} "
  1028. for input_dn in scenario.get_inputs()
  1029. if input_dn.config_id == "wrong_csv_file_path"
  1030. ]
  1031. not_expected_outputs = [
  1032. f"{input_dn.id} cannot be read because it has never been written. Hint: The data node may refer to a wrong "
  1033. f"path : {input_dn.path} "
  1034. for input_dn in scenario.data_nodes.values()
  1035. if input_dn.config_id != "wrong_csv_file_path"
  1036. ]
  1037. assert all(expected_output in stdout for expected_output in expected_outputs)
  1038. assert all(expected_output not in stdout for expected_output in not_expected_outputs)
  1039. def subtraction(n1, n2):
  1040. return n1 - n2
  1041. def addition(n1, n2):
  1042. return n1 + n2
  1043. def test_scenarios_comparison():
  1044. scenario_config = Config.configure_scenario(
  1045. "Awesome_scenario",
  1046. [
  1047. Config.configure_task(
  1048. "mult_by_2",
  1049. mult_by_2,
  1050. [Config.configure_data_node("foo", "in_memory", Scope.SCENARIO, default_data=1)],
  1051. Config.configure_data_node("bar", "in_memory", Scope.SCENARIO, default_data=0),
  1052. )
  1053. ],
  1054. comparators={"bar": [subtraction], "foo": [subtraction, addition]},
  1055. )
  1056. assert scenario_config.comparators is not None
  1057. scenario_1 = _ScenarioManager._create(scenario_config)
  1058. scenario_2 = _ScenarioManager._create(scenario_config)
  1059. with pytest.raises(InsufficientScenarioToCompare):
  1060. _ScenarioManager._compare(scenario_1, data_node_config_id="bar")
  1061. scenario_3 = Scenario("awesome_scenario_config", [], {})
  1062. with pytest.raises(DifferentScenarioConfigs):
  1063. _ScenarioManager._compare(scenario_1, scenario_3, data_node_config_id="bar")
  1064. _ScenarioManager._submit(scenario_1.id)
  1065. _ScenarioManager._submit(scenario_2.id)
  1066. bar_comparison = _ScenarioManager._compare(scenario_1, scenario_2, data_node_config_id="bar")["bar"]
  1067. assert bar_comparison["subtraction"] == 0
  1068. foo_comparison = _ScenarioManager._compare(scenario_1, scenario_2, data_node_config_id="foo")["foo"]
  1069. assert len(foo_comparison.keys()) == 2
  1070. assert foo_comparison["addition"] == 2
  1071. assert foo_comparison["subtraction"] == 0
  1072. assert len(_ScenarioManager._compare(scenario_1, scenario_2).keys()) == 2
  1073. with pytest.raises(NonExistingScenarioConfig):
  1074. _ScenarioManager._compare(scenario_3, scenario_3)
  1075. with pytest.raises(NonExistingComparator):
  1076. _ScenarioManager._compare(scenario_1, scenario_2, data_node_config_id="abc")
  1077. def test_tags():
  1078. cycle_1 = _CycleManager._create(Frequency.DAILY, name="today", creation_date=datetime.now())
  1079. cycle_2 = _CycleManager._create(
  1080. Frequency.DAILY,
  1081. name="tomorrow",
  1082. creation_date=datetime.now() + timedelta(days=1),
  1083. )
  1084. cycle_3 = _CycleManager._create(
  1085. Frequency.DAILY,
  1086. name="yesterday",
  1087. creation_date=datetime.now() + timedelta(days=-1),
  1088. )
  1089. scenario_no_tag = Scenario("scenario_no_tag", [], {}, [], ScenarioId("scenario_no_tag"), cycle=cycle_1)
  1090. scenario_1_tag = Scenario(
  1091. "scenario_1_tag",
  1092. [],
  1093. {},
  1094. [],
  1095. ScenarioId("scenario_1_tag"),
  1096. cycle=cycle_1,
  1097. tags={"fst"},
  1098. )
  1099. scenario_2_tags = Scenario(
  1100. "scenario_2_tags",
  1101. [],
  1102. {},
  1103. [],
  1104. ScenarioId("scenario_2_tags"),
  1105. cycle=cycle_2,
  1106. tags={"fst", "scd"},
  1107. )
  1108. # Test has_tag
  1109. assert len(scenario_no_tag.tags) == 0
  1110. assert not scenario_no_tag.has_tag("fst")
  1111. assert not scenario_no_tag.has_tag("scd")
  1112. assert len(scenario_1_tag.tags) == 1
  1113. assert scenario_1_tag.has_tag("fst")
  1114. assert not scenario_1_tag.has_tag("scd")
  1115. assert len(scenario_2_tags.tags) == 2
  1116. assert scenario_2_tags.has_tag("fst")
  1117. assert scenario_2_tags.has_tag("scd")
  1118. # test get and update serialize/deserialize tags
  1119. _ScenarioManager._repository._save(scenario_no_tag)
  1120. _ScenarioManager._repository._save(scenario_1_tag)
  1121. _ScenarioManager._repository._save(scenario_2_tags)
  1122. assert len(_ScenarioManager._get(ScenarioId("scenario_no_tag")).tags) == 0
  1123. assert not _ScenarioManager._get(ScenarioId("scenario_no_tag")).has_tag("fst")
  1124. assert not _ScenarioManager._get(ScenarioId("scenario_no_tag")).has_tag("scd")
  1125. assert len(_ScenarioManager._get(ScenarioId("scenario_1_tag")).tags) == 1
  1126. assert "fst" in _ScenarioManager._get(ScenarioId("scenario_1_tag")).tags
  1127. assert "scd" not in _ScenarioManager._get(ScenarioId("scenario_1_tag")).tags
  1128. assert len(_ScenarioManager._get(ScenarioId("scenario_2_tags")).tags) == 2
  1129. assert "fst" in _ScenarioManager._get(ScenarioId("scenario_2_tags")).tags
  1130. assert "scd" in _ScenarioManager._get(ScenarioId("scenario_2_tags")).tags
  1131. # Test tag & untag
  1132. _ScenarioManager._tag(scenario_no_tag, "thd") # add new tag
  1133. _ScenarioManager._untag(scenario_1_tag, "NOT_EXISTING_TAG") # remove not existing tag does nothing
  1134. _ScenarioManager._untag(scenario_1_tag, "fst") # remove `fst` tag
  1135. assert len(scenario_no_tag.tags) == 1
  1136. assert not scenario_no_tag.has_tag("fst")
  1137. assert not scenario_no_tag.has_tag("scd")
  1138. assert scenario_no_tag.has_tag("thd")
  1139. assert len(scenario_1_tag.tags) == 0
  1140. assert not scenario_1_tag.has_tag("fst")
  1141. assert not scenario_1_tag.has_tag("scd")
  1142. assert not scenario_1_tag.has_tag("thd")
  1143. assert len(scenario_2_tags.tags) == 2
  1144. assert scenario_2_tags.has_tag("fst")
  1145. assert scenario_2_tags.has_tag("scd")
  1146. assert not scenario_2_tags.has_tag("thd")
  1147. _ScenarioManager._untag(scenario_no_tag, "thd")
  1148. _ScenarioManager._tag(scenario_1_tag, "fst")
  1149. # test getters
  1150. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "fst") == []
  1151. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "scd") == []
  1152. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "thd") == []
  1153. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "fst") == [scenario_2_tags]
  1154. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "scd") == [scenario_2_tags]
  1155. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "thd") == []
  1156. assert _ScenarioManager._get_all_by_cycle_tag(cycle_1, "fst") == [scenario_1_tag]
  1157. assert _ScenarioManager._get_all_by_cycle_tag(cycle_1, "scd") == []
  1158. assert _ScenarioManager._get_all_by_cycle_tag(cycle_1, "thd") == []
  1159. assert len(_ScenarioManager._get_all_by_tag("NOT_EXISTING")) == 0
  1160. assert scenario_1_tag in _ScenarioManager._get_all_by_tag("fst")
  1161. assert scenario_2_tags in _ScenarioManager._get_all_by_tag("fst")
  1162. assert _ScenarioManager._get_all_by_tag("scd") == [scenario_2_tags]
  1163. assert len(_ScenarioManager._get_all_by_tag("thd")) == 0
  1164. # test tag cycle mgt
  1165. _ScenarioManager._tag(scenario_no_tag, "fst") # tag sc_no_tag with fst should not affect sc_1_tag and sc_2_tags
  1166. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "fst") == []
  1167. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "scd") == []
  1168. assert _ScenarioManager._get_all_by_cycle_tag(cycle_3, "thd") == []
  1169. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "fst") == [scenario_2_tags]
  1170. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "scd") == [scenario_2_tags]
  1171. assert _ScenarioManager._get_all_by_cycle_tag(cycle_2, "thd") == []
  1172. assert sorted([s.id for s in _ScenarioManager._get_all_by_cycle_tag(cycle_1, "fst")]) == sorted(
  1173. [s.id for s in [scenario_no_tag, scenario_1_tag]]
  1174. )
  1175. assert _ScenarioManager._get_all_by_cycle_tag(cycle_1, "scd") == []
  1176. assert _ScenarioManager._get_all_by_cycle_tag(cycle_1, "thd") == []
  1177. assert len(_ScenarioManager._get_all_by_tag("NOT_EXISTING")) == 0
  1178. assert len(_ScenarioManager._get_all_by_tag("fst")) == 3
  1179. assert scenario_2_tags in _ScenarioManager._get_all_by_tag("fst")
  1180. assert scenario_no_tag in _ScenarioManager._get_all_by_tag("fst")
  1181. assert _ScenarioManager._get_all_by_tag("scd") == [scenario_2_tags]
  1182. assert len(_ScenarioManager._get_all_by_tag("thd")) == 0
  1183. def test_authorized_tags():
  1184. scenario = Scenario("scenario_1", [], {"authorized_tags": ["foo", "bar"]}, [], ScenarioId("scenario_1"))
  1185. scenario_2_cfg = Config.configure_scenario("scenario_2", [], [], Frequency.DAILY, authorized_tags=["foo", "bar"])
  1186. scenario_2 = _ScenarioManager._create(scenario_2_cfg)
  1187. _ScenarioManager._repository._save(scenario)
  1188. assert len(scenario.tags) == 0
  1189. assert len(scenario_2.tags) == 0
  1190. with pytest.raises(UnauthorizedTagError):
  1191. _ScenarioManager._tag(scenario, "baz")
  1192. _ScenarioManager._tag(scenario_2, "baz")
  1193. assert len(scenario.tags) == 0
  1194. assert len(scenario_2.tags) == 0
  1195. _ScenarioManager._tag(scenario, "foo")
  1196. _ScenarioManager._tag(scenario_2, "foo")
  1197. assert len(scenario.tags) == 1
  1198. assert len(scenario_2.tags) == 1
  1199. _ScenarioManager._tag(scenario, "bar")
  1200. _ScenarioManager._tag(scenario_2, "bar")
  1201. assert len(scenario.tags) == 2
  1202. assert len(scenario_2.tags) == 2
  1203. _ScenarioManager._tag(scenario, "foo")
  1204. _ScenarioManager._tag(scenario_2, "foo")
  1205. assert len(scenario.tags) == 2
  1206. assert len(scenario_2.tags) == 2
  1207. _ScenarioManager._untag(scenario, "foo")
  1208. _ScenarioManager._untag(scenario_2, "foo")
  1209. assert len(scenario.tags) == 1
  1210. assert len(scenario_2.tags) == 1
  1211. def test_get_scenarios_by_config_id():
  1212. scenario_config_1 = Config.configure_scenario("s1", sequence_configs=[])
  1213. scenario_config_2 = Config.configure_scenario("s2", sequence_configs=[])
  1214. scenario_config_3 = Config.configure_scenario("s3", sequence_configs=[])
  1215. s_1_1 = _ScenarioManager._create(scenario_config_1)
  1216. s_1_2 = _ScenarioManager._create(scenario_config_1)
  1217. s_1_3 = _ScenarioManager._create(scenario_config_1)
  1218. assert len(_ScenarioManager._get_all()) == 3
  1219. s_2_1 = _ScenarioManager._create(scenario_config_2)
  1220. s_2_2 = _ScenarioManager._create(scenario_config_2)
  1221. assert len(_ScenarioManager._get_all()) == 5
  1222. s_3_1 = _ScenarioManager._create(scenario_config_3)
  1223. assert len(_ScenarioManager._get_all()) == 6
  1224. s1_scenarios = _ScenarioManager._get_by_config_id(scenario_config_1.id)
  1225. assert len(s1_scenarios) == 3
  1226. assert sorted([s_1_1.id, s_1_2.id, s_1_3.id]) == sorted([scenario.id for scenario in s1_scenarios])
  1227. s2_scenarios = _ScenarioManager._get_by_config_id(scenario_config_2.id)
  1228. assert len(s2_scenarios) == 2
  1229. assert sorted([s_2_1.id, s_2_2.id]) == sorted([scenario.id for scenario in s2_scenarios])
  1230. s3_scenarios = _ScenarioManager._get_by_config_id(scenario_config_3.id)
  1231. assert len(s3_scenarios) == 1
  1232. assert sorted([s_3_1.id]) == sorted([scenario.id for scenario in s3_scenarios])
  1233. def test_get_scenarios_by_config_id_in_multiple_versions_environment():
  1234. scenario_config_1 = Config.configure_scenario("s1", sequence_configs=[])
  1235. scenario_config_2 = Config.configure_scenario("s2", sequence_configs=[])
  1236. _VersionManager._set_experiment_version("1.0")
  1237. _ScenarioManager._create(scenario_config_1)
  1238. _ScenarioManager._create(scenario_config_1)
  1239. _ScenarioManager._create(scenario_config_1)
  1240. _ScenarioManager._create(scenario_config_2)
  1241. _ScenarioManager._create(scenario_config_2)
  1242. assert len(_ScenarioManager._get_by_config_id(scenario_config_1.id)) == 3
  1243. assert len(_ScenarioManager._get_by_config_id(scenario_config_2.id)) == 2
  1244. _VersionManager._set_experiment_version("2.0")
  1245. _ScenarioManager._create(scenario_config_1)
  1246. _ScenarioManager._create(scenario_config_1)
  1247. _ScenarioManager._create(scenario_config_1)
  1248. _ScenarioManager._create(scenario_config_2)
  1249. _ScenarioManager._create(scenario_config_2)
  1250. assert len(_ScenarioManager._get_by_config_id(scenario_config_1.id)) == 3
  1251. assert len(_ScenarioManager._get_by_config_id(scenario_config_2.id)) == 2
  1252. def test_filter_scenarios_by_creation_datetime():
  1253. scenario_config_1 = Config.configure_scenario("s1", sequence_configs=[])
  1254. with freezegun.freeze_time("2024-01-01"):
  1255. s_1_1 = _ScenarioManager._create(scenario_config_1)
  1256. with freezegun.freeze_time("2024-01-03"):
  1257. s_1_2 = _ScenarioManager._create(scenario_config_1)
  1258. with freezegun.freeze_time("2024-02-01"):
  1259. s_1_3 = _ScenarioManager._create(scenario_config_1)
  1260. all_scenarios = _ScenarioManager._get_all()
  1261. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1262. scenarios=all_scenarios,
  1263. created_start_time=datetime(2024, 1, 1),
  1264. created_end_time=datetime(2024, 1, 2),
  1265. )
  1266. assert len(filtered_scenarios) == 1
  1267. assert [s_1_1] == filtered_scenarios
  1268. # The start time is inclusive
  1269. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1270. scenarios=all_scenarios,
  1271. created_start_time=datetime(2024, 1, 1),
  1272. created_end_time=datetime(2024, 1, 3),
  1273. )
  1274. assert len(filtered_scenarios) == 1
  1275. assert [s_1_1] == filtered_scenarios
  1276. # The end time is exclusive
  1277. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1278. scenarios=all_scenarios,
  1279. created_start_time=datetime(2024, 1, 1),
  1280. created_end_time=datetime(2024, 1, 4),
  1281. )
  1282. assert len(filtered_scenarios) == 2
  1283. assert sorted([s_1_1.id, s_1_2.id]) == sorted([scenario.id for scenario in filtered_scenarios])
  1284. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1285. scenarios=all_scenarios,
  1286. created_start_time=datetime(2023, 1, 1),
  1287. created_end_time=datetime(2025, 1, 1),
  1288. )
  1289. assert len(filtered_scenarios) == 3
  1290. assert sorted([s_1_1.id, s_1_2.id, s_1_3.id]) == sorted([scenario.id for scenario in filtered_scenarios])
  1291. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1292. scenarios=all_scenarios,
  1293. created_start_time=datetime(2024, 2, 1),
  1294. )
  1295. assert len(filtered_scenarios) == 1
  1296. assert [s_1_3] == filtered_scenarios
  1297. filtered_scenarios = _ScenarioManager._filter_by_creation_time(
  1298. scenarios=all_scenarios,
  1299. created_end_time=datetime(2024, 1, 2),
  1300. )
  1301. assert len(filtered_scenarios) == 1
  1302. assert [s_1_1] == filtered_scenarios
  1303. def test_can_duplicate_scenario():
  1304. dn_config = Config.configure_pickle_data_node("dn")
  1305. task_config = Config.configure_task("task_1", print, [dn_config])
  1306. scenario_config = Config.configure_scenario("scenario_1", [task_config])
  1307. scenario = _ScenarioManager._create(scenario_config)
  1308. reasons = _ScenarioManager._can_duplicate(scenario)
  1309. assert bool(reasons)
  1310. assert reasons._reasons == {}
  1311. reasons = _ScenarioManager._can_duplicate(scenario.id)
  1312. assert bool(reasons)
  1313. assert reasons._reasons == {}
  1314. reasons = _ScenarioManager._can_duplicate("WRONG_ID")
  1315. assert not bool(reasons)
  1316. assert reasons._reasons["WRONG_ID"] == {EntityDoesNotExist("WRONG_ID")}
  1317. assert str(list(reasons._reasons["WRONG_ID"])[0]) == "Entity 'WRONG_ID' does not exist in the repository"
  1318. def test_duplicate_scenario():
  1319. scenario = Scenario("config_id", set(), {}, set(), ScenarioId("scenario_id"))
  1320. with mock.patch.object(_ScenarioManager, "_can_duplicate", return_value=ReasonCollection()) as mock_can:
  1321. with mock.patch.object(_ScenarioDuplicator, "duplicate") as mock_duplicate:
  1322. _ScenarioManager._duplicate(scenario)
  1323. mock_can.assert_called_once_with(scenario)
  1324. mock_duplicate.assert_called_once_with(None, None)
  1325. mock_duplicate.reset_mock()
  1326. mock_can.reset_mock()
  1327. new_date = datetime.now()
  1328. new_name = "new_name"
  1329. _ScenarioManager._duplicate(scenario, new_date, new_name)
  1330. mock_can.assert_called_once_with(scenario)
  1331. mock_duplicate.assert_called_once_with(new_date, new_name)