فهرست منبع

filter data to duplicate

jean-robin medori 2 ماه پیش
والد
کامیت
2d601338d8

+ 10 - 4
taipy/core/scenario/_scenario_manager.py

@@ -11,7 +11,7 @@
 
 
 from datetime import datetime
 from datetime import datetime
 from functools import partial
 from functools import partial
-from typing import Any, Callable, Dict, List, Literal, Optional, Union
+from typing import Any, Callable, Dict, List, Literal, Optional, Set, Union
 
 
 from taipy.common.config import Config
 from taipy.common.config import Config
 
 
@@ -525,7 +525,11 @@ class _ScenarioManager(_Manager[Scenario], _VersionMixin):
 
 
     @classmethod
     @classmethod
     def _duplicate(
     def _duplicate(
-        cls, scenario: Scenario, new_creation_date: Optional[datetime] = None, new_name: Optional[str] = None
+        cls,
+        scenario: Scenario,
+        new_creation_date: Optional[datetime] = None,
+        new_name: Optional[str] = None,
+        data_to_duplicate: Union[bool, Set[str]] = True
     ) -> Scenario:
     ) -> Scenario:
         """Create a duplicated scenario with its related entities.
         """Create a duplicated scenario with its related entities.
 
 
@@ -538,14 +542,16 @@ class _ScenarioManager(_Manager[Scenario], _VersionMixin):
                 If not provided, the current date and time is used.
                 If not provided, the current date and time is used.
             new_name (Optional[str]): The name of the new scenario. If not provided, the
             new_name (Optional[str]): The name of the new scenario. If not provided, the
                 name of the original scenario is used.
                 name of the original scenario is used.
-
+            data_to_duplicate (Union[Set[str], bool]): A set of data node configuration ids used
+                to duplicate only the data nodes' data with the specified configuration ids.
+                If True, all data nodes are duplicated. If False, no data nodes are duplicated.
         Returns:
         Returns:
             The newly created scenario.
             The newly created scenario.
         """
         """
         reasons = cls._can_duplicate(scenario)
         reasons = cls._can_duplicate(scenario)
         if not reasons:
         if not reasons:
             raise Exception(reasons.reasons)
             raise Exception(reasons.reasons)
-        return _ScenarioDuplicator(scenario).duplicate(new_creation_date, new_name)
+        return _ScenarioDuplicator(scenario, data_to_duplicate).duplicate(new_creation_date, new_name)
 
 
     @classmethod
     @classmethod
     def _can_duplicate(cls, scenario: Union[str, Scenario]) -> ReasonCollection:
     def _can_duplicate(cls, scenario: Union[str, Scenario]) -> ReasonCollection:

+ 23 - 6
taipy/core/scenario/scenario.py

@@ -431,26 +431,43 @@ class Scenario(_Entity, Submittable, _Labeled):
         self,
         self,
         new_creation_date: Optional[datetime] = None,
         new_creation_date: Optional[datetime] = None,
         new_name: Optional[str] = None,
         new_name: Optional[str] = None,
+        data_to_duplicate: Union[Set[str], bool] = True
     ) -> "Scenario":
     ) -> "Scenario":
         """Duplicate the scenario and return the new one.
         """Duplicate the scenario and return the new one.
 
 
-        This method duplicates the scenario, optionally setting a new creation date and name.
-        The nested tasks and data nodes are duplicated as well. If the scenario belongs to a
-        cycle, the cycle (corresponding to the creation_date and the configuration frequency
-        attribute) is created if it does not exist yet.
+        If the scenario belongs to a cycle, the cycle (corresponding to the creation_date
+        and the configuration frequency attribute) is created if it does not exist yet.
+
+        The nested entities are duplicated or not depending on the creation date of the new
+        scenario, its cycle, and the various data node scopes.
+
+        !!! warning "Data and data nodes duplication"
+
+            Note that for now, Taipy can only duplicate data for file-based data nodes. For
+            other types of data nodes (sql, mongo, etc.), the new data nodes are created
+            referencing the exact same data as the original data nodes. This can lead to
+            conflicts if the data is modified in one of the scenarios.
+
+            Users must ensure after a duplication that the data nodes' data are correctly
+            set for the new scenario.
+
+            For example, the table name of a SQL table data node must be manually updated to
+            avoid conflicts.
 
 
         Arguments:
         Arguments:
             new_creation_date (Optional[datetime.datetime]): The creation date of the new scenario.
             new_creation_date (Optional[datetime.datetime]): The creation date of the new scenario.
                 If None, the current date and time is used.
                 If None, the current date and time is used.
             new_name (Optional[str]): The displayable name of the new scenario.
             new_name (Optional[str]): The displayable name of the new scenario.
                 If None, the name of the current scenario is used.
                 If None, the name of the current scenario is used.
-
+            data_to_duplicate (Union[Set[str], bool]): A set of data node configuration ids used
+                to duplicate only the data nodes' data with the specified configuration ids.
+                If True, all data nodes are duplicated. If False, no data nodes are duplicated.
         Returns:
         Returns:
             Scenario: The newly duplicated scenario.
             Scenario: The newly duplicated scenario.
         """
         """
         from ._scenario_manager_factory import _ScenarioManagerFactory
         from ._scenario_manager_factory import _ScenarioManagerFactory
 
 
-        return _ScenarioManagerFactory._build_manager()._duplicate(self, new_creation_date, new_name)
+        return _ScenarioManagerFactory._build_manager()._duplicate(self, new_creation_date, new_name, data_to_duplicate)
 
 
     def set_primary(self) -> None:
     def set_primary(self) -> None:
         """Promote the scenario as the primary scenario of its cycle.
         """Promote the scenario as the primary scenario of its cycle.

+ 28 - 5
taipy/core/taipy.py

@@ -1082,23 +1082,46 @@ def can_duplicate(entity: Union[str, Scenario]) -> ReasonCollection:
 
 
 
 
 def duplicate_scenario(
 def duplicate_scenario(
-    scenario: Scenario, new_creation_date: Optional[datetime] = None, new_name: Optional[str] = None
+    scenario: Scenario,
+    new_creation_date: Optional[datetime] = None,
+    new_name: Optional[str] = None,
+    data_to_duplicate: Union[Set[str], bool] = True
 ) -> Scenario:
 ) -> Scenario:
     """Duplicate an existing scenario and return a new scenario.
     """Duplicate an existing scenario and return a new scenario.
 
 
-    This function duplicates the provided scenario, optionally setting a new creation date and name.
+    This function duplicates the provided scenario, optionally setting a new creation
+    date and name.
 
 
-    If the scenario belongs to a cycle, the cycle (corresponding to the creation_date and the configuration
-    frequency attribute) is created if it does not exist yet.
+    If the scenario belongs to a cycle, the cycle (corresponding to the creation_date
+    and the configuration frequency attribute) is created if it does not exist yet.
+
+    The nested entities are duplicated or not depending on the creation date of the new
+    scenario, its cycle, and the various data node scopes.
+
+    !!! warning "Data and data nodes duplication"
+
+        Note that for now, Taipy can only duplicate data for file-based data nodes. For
+        other types of data nodes (sql, mongo, etc.), the new data nodes are created
+        referencing the exact same data as the original data nodes. This can lead to
+        conflicts if the data is modified in one of the scenarios.
+
+        Users must ensure after a duplication that the data nodes' data are correctly
+        set for the new scenario.
+
+        For example, the table name of a SQL table data node must be manually updated to
+        avoid conflicts.
 
 
     Arguments:
     Arguments:
         scenario (Scenario): The scenario to duplicate.
         scenario (Scenario): The scenario to duplicate.
         new_creation_date (Optional[datetime.datetime]): The creation date of the new scenario.
         new_creation_date (Optional[datetime.datetime]): The creation date of the new scenario.
             If None, the current date and time is used.
             If None, the current date and time is used.
         new_name (Optional[str]): The displayable name of the new scenario.
         new_name (Optional[str]): The displayable name of the new scenario.
+        data_to_duplicate (Union[Set[str], bool]): A set of data node configuration ids used
+            to duplicate only the data nodes' data with the specified configuration ids.
+            If True, all data nodes are duplicated. If False, no data nodes are duplicated.
 
 
     Returns:
     Returns:
         Scenario: The newly duplicated scenario.
         Scenario: The newly duplicated scenario.
     """
     """
 
 
-    return _ScenarioManagerFactory._build_manager()._duplicate(scenario, new_creation_date, new_name)
+    return _ScenarioManagerFactory._build_manager()._duplicate(scenario, new_creation_date, new_name, data_to_duplicate)

+ 22 - 0
tests/core/scenario/test_scenario_duplicator.py

@@ -10,6 +10,8 @@
 # specific language governing permissions and limitations under the License.
 # specific language governing permissions and limitations under the License.
 
 
 from datetime import datetime, timedelta
 from datetime import datetime, timedelta
+from unittest import mock
+from unittest.mock import call
 
 
 from taipy import Config, Frequency, Scope, Sequence
 from taipy import Config, Frequency, Scope, Sequence
 from taipy.core.cycle._cycle_manager import _CycleManager
 from taipy.core.cycle._cycle_manager import _CycleManager
@@ -638,3 +640,23 @@ def test_duplicate_with_all_global_dn():
     assert len(dn_2._properties) == len(new_dn_2._properties)
     assert len(dn_2._properties) == len(new_dn_2._properties)
     for k, v in dn_2._properties.items():
     for k, v in dn_2._properties.items():
         assert new_dn_2._properties[k] == v
         assert new_dn_2._properties[k] == v
+
+
+def test_data_duplication():
+    dn_config_1 = Config.configure_pickle_data_node("dn_1")
+    dn_config_2 = Config.configure_json_data_node("dn_2")
+    dn_config_3 = Config.configure_generic_data_node("dn_3", read_fct=print)
+    dn_config_4 = Config.configure_in_memory_data_node("dn_4")
+    scenario_config_1 = Config.configure_scenario(
+        "scenario_1",
+        [],
+        additional_data_node_configs=[dn_config_1, dn_config_2, dn_config_3, dn_config_4])
+    scenario = _ScenarioManager._create(scenario_config_1)
+
+    with mock.patch("taipy.core.data._data_duplicator._DataDuplicator.duplicate_data") as mck:
+        new_scenario = _ScenarioDuplicator(scenario, True).duplicate(datetime.now(), "new")
+        mck.assert_has_calls([call(new_scenario.dn_1), call(new_scenario.dn_2)], any_order=True)
+
+    with mock.patch("taipy.core.data._data_duplicator._DataDuplicator.duplicate_data") as mck:
+        new_scenario = _ScenarioDuplicator(scenario, {"dn_1", "dn_3"}).duplicate(datetime.now(), "new")
+        mck.assert_called_once_with(new_scenario.dn_1)

+ 3 - 3
tests/core/test_taipy.py

@@ -888,10 +888,10 @@ class TestTaipy:
 
 
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
             tp.duplicate_scenario(scenario)
             tp.duplicate_scenario(scenario)
-            mck.assert_called_once_with(scenario, None, None)
+            mck.assert_called_once_with(scenario, None, None, True)
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
             tp.duplicate_scenario(scenario, datetime.datetime(2022, 2, 5))
             tp.duplicate_scenario(scenario, datetime.datetime(2022, 2, 5))
-            mck.assert_called_once_with(scenario, datetime.datetime(2022, 2, 5), None)
+            mck.assert_called_once_with(scenario, datetime.datetime(2022, 2, 5), None, True)
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
         with mock.patch("taipy.core.scenario._scenario_manager._ScenarioManager._duplicate") as mck:
             tp.duplicate_scenario(scenario, datetime.datetime(2022, 2, 5), "displayable_name")
             tp.duplicate_scenario(scenario, datetime.datetime(2022, 2, 5), "displayable_name")
-            mck.assert_called_once_with(scenario, datetime.datetime(2022, 2, 5), "displayable_name")
+            mck.assert_called_once_with(scenario, datetime.datetime(2022, 2, 5), "displayable_name", True)