idmtools-platform-general 0.0.0.dev0__py3-none-any.whl → 0.0.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. idmtools_platform_file/__init__.py +18 -0
  2. idmtools_platform_file/assets/__init__.py +77 -0
  3. idmtools_platform_file/assets/_run.sh.jinja2 +47 -0
  4. idmtools_platform_file/assets/batch.sh.jinja2 +24 -0
  5. idmtools_platform_file/assets/run_simulation.sh +8 -0
  6. idmtools_platform_file/cli/__init__.py +5 -0
  7. idmtools_platform_file/cli/file.py +185 -0
  8. idmtools_platform_file/file_operations/__init__.py +4 -0
  9. idmtools_platform_file/file_operations/file_operations.py +298 -0
  10. idmtools_platform_file/file_operations/operations_interface.py +74 -0
  11. idmtools_platform_file/file_platform.py +288 -0
  12. idmtools_platform_file/platform_operations/__init__.py +5 -0
  13. idmtools_platform_file/platform_operations/asset_collection_operations.py +172 -0
  14. idmtools_platform_file/platform_operations/experiment_operations.py +314 -0
  15. idmtools_platform_file/platform_operations/json_metadata_operations.py +320 -0
  16. idmtools_platform_file/platform_operations/simulation_operations.py +212 -0
  17. idmtools_platform_file/platform_operations/suite_operations.py +243 -0
  18. idmtools_platform_file/platform_operations/utils.py +461 -0
  19. idmtools_platform_file/plugin_info.py +82 -0
  20. idmtools_platform_file/tools/__init__.py +4 -0
  21. idmtools_platform_file/tools/job_history.py +334 -0
  22. idmtools_platform_file/tools/status_report/__init__.py +4 -0
  23. idmtools_platform_file/tools/status_report/status_report.py +222 -0
  24. idmtools_platform_file/tools/status_report/utils.py +159 -0
  25. idmtools_platform_general-0.0.2.dist-info/METADATA +81 -0
  26. idmtools_platform_general-0.0.2.dist-info/RECORD +36 -0
  27. idmtools_platform_general-0.0.2.dist-info/entry_points.txt +6 -0
  28. idmtools_platform_general-0.0.2.dist-info/licenses/LICENSE.TXT +3 -0
  29. idmtools_platform_general-0.0.2.dist-info/top_level.txt +3 -0
  30. idmtools_platform_process/__init__.py +17 -0
  31. idmtools_platform_process/platform_operations/__init__.py +5 -0
  32. idmtools_platform_process/platform_operations/experiment_operations.py +53 -0
  33. idmtools_platform_process/plugin_info.py +80 -0
  34. idmtools_platform_process/process_platform.py +52 -0
  35. tests/input/hello.sh +2 -0
  36. idmtools_platform_general/__init__.py +0 -8
  37. idmtools_platform_general-0.0.0.dev0.dist-info/METADATA +0 -41
  38. idmtools_platform_general-0.0.0.dev0.dist-info/RECORD +0 -5
  39. idmtools_platform_general-0.0.0.dev0.dist-info/top_level.txt +0 -1
  40. {idmtools_platform_general-0.0.0.dev0.dist-info → idmtools_platform_general-0.0.2.dist-info}/WHEEL +0 -0
@@ -0,0 +1,74 @@
1
+ """
2
+ Here we implement the base operations interfaces for all file based platforms.
3
+
4
+ Copyright 2025, Gates Foundation. All rights reserved.
5
+ """
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass, field
8
+ from pathlib import Path
9
+ from typing import Type, Union, Any
10
+ from idmtools.core import ItemType
11
+ from idmtools.core.interfaces.ientity import IEntity
12
+ from idmtools.entities.simulation import Simulation
13
+
14
+
15
+ @dataclass
16
+ class IOperations(ABC):
17
+ """
18
+ Abstract base class defining platform-specific operations.
19
+
20
+ This interface should be implemented by platform FilePlatform or SlurmPlatform
21
+ to handle directory structure, job submission, and file linking.
22
+ """
23
+ platform: 'FilePlatform' # noqa: F821
24
+ platform_type: Type = field(default=None)
25
+
26
+ @abstractmethod
27
+ def get_directory(self, item: IEntity) -> Path:
28
+ """Get the directory of the given entity."""
29
+ pass
30
+
31
+ @abstractmethod
32
+ def get_directory_by_id(self, item_id: str, item_type: ItemType) -> Path:
33
+ """Get the directory by item id."""
34
+ pass
35
+
36
+ @abstractmethod
37
+ def make_command_executable(self, simulation: Simulation) -> None:
38
+ """Make command executable."""
39
+ pass
40
+
41
+ @abstractmethod
42
+ def mk_directory(self, item: IEntity, exist_ok: bool = False) -> None:
43
+ """Make a new directory."""
44
+ pass
45
+
46
+ @abstractmethod
47
+ def link_file(self, target: Union[Path, str], link: Union[Path, str]) -> None:
48
+ """Link files with symlink."""
49
+ pass
50
+
51
+ @abstractmethod
52
+ def link_dir(self, target: Union[Path, str], link: Union[Path, str]) -> None:
53
+ """Link directory with symlink."""
54
+ pass
55
+
56
+ @abstractmethod
57
+ def update_script_mode(self, script_path: Union[Path, str], mode: int) -> None:
58
+ """Update script mode."""
59
+ pass
60
+
61
+ @abstractmethod
62
+ def create_batch_file(self, item: IEntity, **kwargs) -> None:
63
+ """Create batch file."""
64
+ pass
65
+
66
+ @abstractmethod
67
+ def get_simulation_status(self, sim_id: str) -> Any:
68
+ """Get simulation status."""
69
+ pass
70
+
71
+ @abstractmethod
72
+ def create_file(self, file_path: str, content: str) -> None:
73
+ """Create file."""
74
+ pass
@@ -0,0 +1,288 @@
1
+ """
2
+ Here we implement the FilePlatform object.
3
+
4
+ Copyright 2025, Gates Foundation. All rights reserved.
5
+ """
6
+ import os
7
+ from pathlib import Path
8
+ from logging import getLogger
9
+ from typing import Union, List
10
+ from dataclasses import dataclass, field
11
+
12
+ from idmtools import IdmConfigParser
13
+ from idmtools.core import ItemType, EntityStatus, TRUTHY_VALUES
14
+ from idmtools.entities import Suite
15
+ from idmtools.entities.experiment import Experiment
16
+ from idmtools.entities.simulation import Simulation
17
+ from idmtools.entities.iplatform import IPlatform
18
+ from idmtools_platform_file.file_operations.file_operations import FileOperations
19
+ from idmtools_platform_file.platform_operations.asset_collection_operations import FilePlatformAssetCollectionOperations
20
+ from idmtools_platform_file.platform_operations.experiment_operations import FilePlatformExperimentOperations
21
+ from idmtools_platform_file.platform_operations.json_metadata_operations import JSONMetadataOperations
22
+ from idmtools_platform_file.platform_operations.simulation_operations import FilePlatformSimulationOperations
23
+ from idmtools_platform_file.platform_operations.suite_operations import FilePlatformSuiteOperations
24
+ from idmtools_platform_file.platform_operations.utils import FileExperiment, FileSimulation, FileSuite
25
+
26
+ logger = getLogger(__name__)
27
+ user_logger = getLogger('user')
28
+
29
+ op_defaults = dict(default=None, compare=False, metadata={"pickle_ignore": True})
30
+
31
+
32
+ @dataclass(repr=False)
33
+ class FilePlatform(IPlatform):
34
+ """
35
+ File Platform definition.
36
+ """
37
+ job_directory: str = field(default=None, metadata=dict(help="Job Directory"))
38
+ max_job: int = field(default=4, metadata=dict(help="Maximum number of jobs to run concurrently"))
39
+ run_sequence: bool = field(default=True, metadata=dict(help="Run jobs in sequence"))
40
+ sym_link: bool = field(default=True, metadata=dict(help="Use symbolic links"))
41
+ # Default retries for jobs
42
+ retries: int = field(default=1, metadata=dict(help="Number of retries for failed jobs"))
43
+ # number of MPI processes
44
+ ntasks: int = field(default=1,
45
+ metadata=dict(help="Number of MPI processes. If greater than 1, it triggers mpirun."))
46
+ # modules to be load
47
+ modules: list = field(default_factory=list, metadata=dict(help="Modules to load"))
48
+ # extra packages to install
49
+ extra_packages: list = field(default_factory=list, metadata=dict(help="Extra packages to install"))
50
+ maxlen: int = field(default=30, metadata=dict(help="Maximum length of suite/experiment name"))
51
+
52
+ _suites: FilePlatformSuiteOperations = field(**op_defaults, repr=False, init=False)
53
+ _experiments: FilePlatformExperimentOperations = field(**op_defaults, repr=False, init=False)
54
+ _simulations: FilePlatformSimulationOperations = field(**op_defaults, repr=False, init=False)
55
+ _assets: FilePlatformAssetCollectionOperations = field(**op_defaults, repr=False, init=False)
56
+ _metas: JSONMetadataOperations = field(**op_defaults, repr=False, init=False)
57
+ _op_client: FileOperations = field(**op_defaults, repr=False, init=False)
58
+
59
+ def __post_init__(self):
60
+ self.__init_interfaces()
61
+ self.supported_types = {ItemType.SUITE, ItemType.EXPERIMENT, ItemType.SIMULATION}
62
+ if self.job_directory is None:
63
+ raise ValueError("Job Directory is required.")
64
+ self.job_directory = os.path.abspath(self.job_directory)
65
+ self.name_directory = IdmConfigParser.get_option(None, "name_directory", 'True').lower() in TRUTHY_VALUES
66
+ self.sim_name_directory = IdmConfigParser.get_option(None, "sim_name_directory",
67
+ 'False').lower() in TRUTHY_VALUES
68
+ super().__post_init__()
69
+ self._object_cache_expiration = 600
70
+
71
+ def __init_interfaces(self):
72
+ self._suites = FilePlatformSuiteOperations(platform=self)
73
+ self._experiments = FilePlatformExperimentOperations(platform=self)
74
+ self._simulations = FilePlatformSimulationOperations(platform=self)
75
+ self._assets = FilePlatformAssetCollectionOperations(platform=self)
76
+ self._metas = JSONMetadataOperations(platform=self)
77
+ self._op_client = FileOperations(platform=self)
78
+
79
+ def post_setstate(self):
80
+ """
81
+ Utility function.
82
+ Returns: None
83
+ """
84
+ self.__init_interfaces()
85
+
86
+ def mk_directory(self, item: Union[Suite, Experiment, Simulation] = None, dest: Union[Path, str] = None,
87
+ exist_ok: bool = True) -> None:
88
+ """
89
+ Make a new directory.
90
+ Args:
91
+ item: Suite/Experiment/Simulation
92
+ dest: the folder path
93
+ exist_ok: True/False
94
+
95
+ Returns:
96
+ None
97
+ """
98
+ self._op_client.mk_directory(item, dest, exist_ok)
99
+
100
+ def get_directory(self, item: Union[Suite, Experiment, Simulation]) -> Path:
101
+ """
102
+ Get item's path.
103
+ Args:
104
+ item: Suite, Experiment, Simulation
105
+ Returns:
106
+ item file directory
107
+ """
108
+ return self._op_client.get_directory(item)
109
+
110
+ def get_directory_by_id(self, item_id: str, item_type: ItemType) -> Path:
111
+ """
112
+ Get item's path.
113
+ Args:
114
+ item_id: entity id (Suite, Experiment, Simulation)
115
+ item_type: the type of items (Suite, Experiment, Simulation)
116
+ Returns:
117
+ item file directory
118
+ """
119
+ return self._op_client.get_directory_by_id(item_id, item_type)
120
+
121
+ def create_batch_file(self, item: Union[Experiment, Simulation], **kwargs) -> None:
122
+ """
123
+ Create batch file.
124
+ Args:
125
+ item: the item to build batch file for
126
+ kwargs: keyword arguments used to expand functionality.
127
+ Returns:
128
+ None
129
+ """
130
+ self._op_client.create_batch_file(item, **kwargs)
131
+
132
+ @staticmethod
133
+ def update_script_mode(script_path: Union[Path, str], mode: int = 0o777) -> None:
134
+ """
135
+ Change file mode.
136
+ Args:
137
+ script_path: script path
138
+ mode: permission mode
139
+ Returns:
140
+ None
141
+ """
142
+ script_path = Path(script_path)
143
+ script_path.chmod(mode)
144
+
145
+ def flatten_item(self, item: object, raw: bool = False, **kwargs) -> List[object]:
146
+ """
147
+ Flatten an item: resolve the children until getting to the leaves.
148
+
149
+ For example, for an experiment, will return all the simulations.
150
+ For a suite, will return all the simulations contained in the suites experiments.
151
+
152
+ Args:
153
+ item: Which item to flatten
154
+ raw: If True, returns raw platform objects, False, return local objects
155
+ kwargs: Extra parameters for conversion
156
+
157
+ Returns:
158
+ List of leaf items, which can be from either the local platform or a File platform:
159
+ - Simulations (either local Simulation or FileSimulation),
160
+ """
161
+ # Return directly if item is already in leaf and raw = False
162
+ if not raw and (isinstance(item, Simulation) and not isinstance(item, FileSimulation)):
163
+ return [item]
164
+ # Handle platform object conversion if needed
165
+ if not isinstance(item, (FileSuite, FileExperiment, FileSimulation)):
166
+ return self.flatten_item(item.get_platform_object(), raw=raw, **kwargs)
167
+
168
+ # Process types (suites and experiments)
169
+ if isinstance(item, (FileSuite, FileExperiment)):
170
+ children = self._get_children_for_platform_item(item)
171
+ # Assign server experiment to child.experiment to avoid recreating child's parent
172
+ if isinstance(item, FileExperiment):
173
+ for child in children:
174
+ child.experiment = item
175
+ return [leaf
176
+ for child in children
177
+ for leaf in self.flatten_item(child, raw=raw, **kwargs)]
178
+
179
+ # Handle leaf types
180
+ if isinstance(item, FileSimulation):
181
+ self._ensure_simulation_experiment(item)
182
+
183
+ if not raw:
184
+ parent = item.experiment if isinstance(item, FileSimulation) else None
185
+ item = self._convert_platform_item_to_entity(item, parent=parent, **kwargs)
186
+
187
+ return [item]
188
+
189
+ def _ensure_simulation_experiment(self, simulation: FileSimulation) -> None:
190
+ """
191
+ Ensure the given simulation has a valid experiment attached.
192
+
193
+ If the simulation's 'experiment' attribute is missing or uninitialized,
194
+ fetch the experiment from the server using its ID and normalize it.
195
+
196
+ Args:
197
+ simulation (FileSimulation): The simulation object to validate.
198
+ Raises:
199
+ ValueError: If 'experiment_id' is missing or invalid.
200
+ """
201
+ try:
202
+ experiment = hasattr(simulation, "experiment")
203
+ except Exception:
204
+ experiment = None
205
+
206
+ if experiment is None:
207
+ experiment = self.get_item(simulation.experiment_id, item_type=ItemType.EXPERIMENT, raw=True)
208
+ simulation.experiment = experiment
209
+
210
+ def validate_item_for_analysis(self, item: Union[Simulation, FileSimulation], analyze_failed_items=False):
211
+ """
212
+ Check if item is valid for analysis.
213
+
214
+ Args:
215
+ item: which item to verify status
216
+ analyze_failed_items: bool
217
+
218
+ Returns: bool
219
+ """
220
+ result = False
221
+
222
+ # TODO: we may consolidate two cases into one
223
+ if isinstance(item, FileSimulation):
224
+ if item.status == EntityStatus.SUCCEEDED:
225
+ result = True
226
+ else:
227
+ if analyze_failed_items and item.status == EntityStatus.FAILED:
228
+ result = True
229
+ elif isinstance(item, Simulation):
230
+ if item.succeeded:
231
+ result = True
232
+ else:
233
+ if analyze_failed_items and item.status == EntityStatus.FAILED:
234
+ result = True
235
+ return result
236
+
237
+ def link_file(self, target: Union[Path, str], link: Union[Path, str]) -> None:
238
+ """
239
+ Link files.
240
+ Args:
241
+ target: the source file path
242
+ link: the file path
243
+ Returns:
244
+ None
245
+ """
246
+ self._op_client.link_file(target, link)
247
+
248
+ def link_dir(self, target: Union[Path, str], link: Union[Path, str]) -> None:
249
+ """
250
+ Link directory/files.
251
+ Args:
252
+ target: the source folder path.
253
+ link: the folder path
254
+ Returns:
255
+ None
256
+ """
257
+ self._op_client.link_dir(target, link)
258
+
259
+ def make_command_executable(self, simulation: Simulation) -> None:
260
+ """
261
+ Make simulation command executable.
262
+ Args:
263
+ simulation: idmtools Simulation
264
+ Returns:
265
+ None
266
+ """
267
+ self._op_client.make_command_executable(simulation)
268
+
269
+ def get_simulation_status(self, sim_id: str, **kwargs) -> EntityStatus:
270
+ """
271
+ Retrieve simulation status.
272
+ Args:
273
+ sim_id: Simulation ID
274
+ kwargs: keyword arguments used to expand functionality
275
+ Returns:
276
+ EntityStatus
277
+ """
278
+ return self._op_client.get_simulation_status(sim_id, **kwargs)
279
+
280
+ def entity_display_name(self, item: Union[Suite, Experiment, Simulation]) -> str:
281
+ """
282
+ Get display name for entity.
283
+ Args:
284
+ item: Suite, Experiment or Simulation
285
+ Returns:
286
+ str
287
+ """
288
+ return self._op_client.entity_display_name(item)
@@ -0,0 +1,5 @@
1
+ """
2
+ idmtools FilePlatform platform operations module.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
@@ -0,0 +1,172 @@
1
+ """
2
+ Here we implement the FilePlatform asset collection operations.
3
+
4
+ Copyright 2021, Bill & Melinda Gates Foundation. All rights reserved.
5
+ """
6
+ import shutil
7
+ from uuid import UUID
8
+ from pathlib import Path
9
+ from dataclasses import field, dataclass
10
+ from logging import getLogger
11
+ from typing import TYPE_CHECKING, Type, List, Dict, Union, Optional
12
+ from idmtools.core import ItemType
13
+ from idmtools.assets import AssetCollection, Asset
14
+ from idmtools.entities.experiment import Experiment
15
+ from idmtools.entities.simulation import Simulation
16
+ from idmtools.entities.iplatform_ops.iplatform_asset_collection_operations import IPlatformAssetCollectionOperations
17
+ from idmtools_platform_file.platform_operations.utils import FileSimulation, validate_file_copy_path_length, \
18
+ validate_file_path_length
19
+
20
+ if TYPE_CHECKING:
21
+ from idmtools_platform_file.file_platform import FilePlatform
22
+
23
+ logger = getLogger(__name__)
24
+ user_logger = getLogger("user")
25
+
26
+ EXCLUDE_FILES = ['_run.sh', 'metadata.json', 'stdout.txt', 'stderr.txt', 'status.txt', 'job_id.txt', 'job_status.txt']
27
+
28
+
29
+ @dataclass
30
+ class FilePlatformAssetCollectionOperations(IPlatformAssetCollectionOperations):
31
+ """
32
+ Provides AssetCollection Operations to FilePlatform.
33
+ """
34
+ platform: 'FilePlatform' # noqa F821
35
+ platform_type: Type = field(default=None)
36
+
37
+ def get(self, asset_collection_id: Optional[UUID], **kwargs) -> AssetCollection:
38
+ """
39
+ Get an asset collection by id.
40
+ Args:
41
+ asset_collection_id: id of asset collection
42
+ kwargs: keyword arguments used to expand functionality.
43
+ Returns:
44
+ AssetCollection
45
+ """
46
+ raise NotImplementedError("Get asset collection is not supported on FilePlatform.")
47
+
48
+ def platform_create(self, asset_collection: AssetCollection, **kwargs) -> AssetCollection:
49
+ """
50
+ Create AssetCollection.
51
+ Args:
52
+ asset_collection: AssetCollection to create
53
+ kwargs: keyword arguments used to expand functionality.
54
+ Returns:
55
+ AssetCollection
56
+ """
57
+ raise NotImplementedError("platform_create is not supported on FilePlatform.")
58
+
59
+ def link_common_assets(self, simulation: Simulation, common_asset_dir: Union[Path, str] = None) -> None:
60
+ """
61
+ Link directory/files.
62
+ Args:
63
+ simulation: Simulation
64
+ common_asset_dir: the common asset folder path
65
+ Returns:
66
+ None
67
+ """
68
+ if common_asset_dir is None:
69
+ common_asset_dir = Path(self.platform.get_directory(simulation.parent), 'Assets')
70
+ link_dir = Path(self.platform.get_directory(simulation), 'Assets')
71
+
72
+ # Copy common assets to simulation directory
73
+ self.platform.link_dir(common_asset_dir, link_dir)
74
+
75
+ @staticmethod
76
+ def _get_assets_from_dir(sim_dir: Path, files: List[str]) -> Dict[str, bytearray]:
77
+ ret = {}
78
+ for file in files:
79
+ asset_file = sim_dir / file
80
+ if asset_file.exists():
81
+ asset = Asset(absolute_path=asset_file.absolute())
82
+ ret[file] = bytearray(asset.bytes)
83
+ else:
84
+ raise RuntimeError(f"Couldn't find asset for path '{file}'.")
85
+ return ret
86
+
87
+ def get_assets(self, simulation: Union[Simulation, FileSimulation], files: List[str], **kwargs) -> Dict[str, bytearray]:
88
+ """
89
+ Get assets for simulation.
90
+ Args:
91
+ simulation: Simulation or FileSimulation
92
+ files: files to be retrieved
93
+ kwargs: keyword arguments used to expand functionality.
94
+ Returns:
95
+ Dict[str, bytearray]
96
+ """
97
+ if isinstance(simulation, (Simulation, FileSimulation)):
98
+ sim_dir = self.platform.get_directory_by_id(simulation.id, ItemType.SIMULATION)
99
+ return self._get_assets_from_dir(sim_dir, files)
100
+ else:
101
+ raise NotImplementedError(
102
+ f"get_assets() for items of type {type(simulation)} is not supported on FilePlatform.")
103
+
104
+ def list_assets(self, item: Union[Experiment, Simulation], exclude: List[str] = None, **kwargs) -> List[Asset]:
105
+ """
106
+ List assets for Experiment/Simulation.
107
+ Args:
108
+ item: Experiment/Simulation
109
+ exclude: list of file path
110
+ kwargs: keyword arguments used to expand functionality.
111
+ Returns:
112
+ list of Asset
113
+ """
114
+ exclude = exclude if exclude is not None else EXCLUDE_FILES
115
+ if isinstance(item, Experiment):
116
+ assets_dir = Path(self.platform.get_directory(item), 'Assets')
117
+ return AssetCollection.assets_from_directory(assets_dir, recursive=True)
118
+ elif isinstance(item, Simulation):
119
+ assets_dir = self.platform.get_directory(item)
120
+ asset_list = AssetCollection.assets_from_directory(assets_dir, recursive=True)
121
+ assets = [asset for asset in asset_list if asset.filename not in exclude]
122
+ return assets
123
+ else:
124
+ raise NotImplementedError("List assets for this item is not supported on FilePlatform.")
125
+
126
+ @staticmethod
127
+ def copy_asset(src: Union[Asset, Path, str], dest: Union[Path, str]) -> None:
128
+ """
129
+ Copy asset/file to destination.
130
+ Args:
131
+ src: the file content
132
+ dest: the file path
133
+ Returns:
134
+ None
135
+ """
136
+ if isinstance(src, Asset):
137
+ if src.absolute_path:
138
+ validate_file_copy_path_length(src.absolute_path, dest)
139
+ shutil.copy(src.absolute_path, dest)
140
+ elif src.content:
141
+ dest_filepath = Path(dest, src.filename)
142
+ validate_file_path_length(dest_filepath)
143
+ dest_filepath.write_bytes(src.bytes)
144
+ else:
145
+ validate_file_copy_path_length(src, dest)
146
+ shutil.copy(src, dest)
147
+
148
+ def dump_assets(self, item: Union[Experiment, Simulation], **kwargs) -> None:
149
+ """
150
+ Dump item's assets.
151
+ Args:
152
+ item: Experiment/Simulation
153
+ kwargs: keyword arguments used to expand functionality.
154
+ Returns:
155
+ None
156
+ """
157
+ if isinstance(item, Experiment):
158
+ self.pre_create(item.assets)
159
+ exp_asset_dir = Path(self.platform.get_directory(item), 'Assets')
160
+ self.platform.mk_directory(dest=exp_asset_dir)
161
+ for asset in item.assets:
162
+ self.platform.mk_directory(dest=exp_asset_dir.joinpath(asset.relative_path))
163
+ self.copy_asset(asset, exp_asset_dir.joinpath(asset.relative_path))
164
+ self.post_create(item.assets)
165
+ elif isinstance(item, Simulation):
166
+ self.pre_create(item.assets)
167
+ sim_dir = self.platform.get_directory(item)
168
+ for asset in item.assets:
169
+ self.copy_asset(asset, sim_dir)
170
+ self.post_create(item.assets)
171
+ else:
172
+ raise NotImplementedError(f"dump_assets() for item of type {type(item)} is not supported on FilePlatform.")