entitysdk 0.6.2__tar.gz → 0.7.0__tar.gz
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.
- {entitysdk-0.6.2/src/entitysdk.egg-info → entitysdk-0.7.0}/PKG-INFO +1 -1
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/client.py +31 -21
- entitysdk-0.7.0/src/entitysdk/dependencies/__init__.py +1 -0
- entitysdk-0.7.0/src/entitysdk/dependencies/entity.py +18 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/downloaders/emodel.py +1 -1
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/downloaders/ion_channel_model.py +1 -1
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/downloaders/morphology.py +1 -1
- entitysdk-0.7.0/src/entitysdk/downloaders/simulation.py +78 -0
- entitysdk-0.7.0/src/entitysdk/downloaders/simulation_result.py +61 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/exception.py +8 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/asset.py +3 -0
- entitysdk-0.7.0/src/entitysdk/schemas/asset.py +20 -0
- entitysdk-0.7.0/src/entitysdk/staging/__init__.py +7 -0
- entitysdk-0.7.0/src/entitysdk/staging/circuit.py +43 -0
- entitysdk-0.7.0/src/entitysdk/staging/simulation.py +142 -0
- entitysdk-0.7.0/src/entitysdk/staging/simulation_result.py +72 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/types.py +6 -0
- entitysdk-0.7.0/src/entitysdk/utils/io.py +16 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0/src/entitysdk.egg-info}/PKG-INFO +1 -1
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk.egg-info/SOURCES.txt +24 -0
- entitysdk-0.7.0/tests/unit/dependencies/test_entity.py +27 -0
- entitysdk-0.7.0/tests/unit/models/data/.gitignore +0 -0
- entitysdk-0.7.0/tests/unit/staging/__init__.py +0 -0
- entitysdk-0.7.0/tests/unit/staging/conftest.py +281 -0
- entitysdk-0.7.0/tests/unit/staging/data/SomaVoltRec 1.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/data/SomaVoltRec 2.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/data/circuit/circuit_config.json +36 -0
- entitysdk-0.7.0/tests/unit/staging/data/circuit/edges.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/data/circuit/nodes.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/data/node_sets.json +67 -0
- entitysdk-0.7.0/tests/unit/staging/data/simulation_config.json +128 -0
- entitysdk-0.7.0/tests/unit/staging/data/spike_replays.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/data/spikes.h5 +0 -0
- entitysdk-0.7.0/tests/unit/staging/test_simulation.py +98 -0
- entitysdk-0.7.0/tests/unit/staging/test_simulation_result.py +106 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_client.py +54 -2
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/utils/test_asset.py +5 -0
- entitysdk-0.6.2/src/entitysdk/schemas/asset.py +0 -13
- {entitysdk-0.6.2 → entitysdk-0.7.0}/.github/workflows/sdist.yml +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/.github/workflows/tox.yml +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/.gitignore +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/CHANGELOG.rst +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/CONTRIBUTING.md +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/LICENSE.txt +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/README.md +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/examples/01_searching.ipynb +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/examples/02_morphology.ipynb +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/examples/03_circuit.ipynb +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/examples/04_simulation_campaign.ipynb +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/examples/utils.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/pyproject.toml +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/setup.cfg +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/common.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/config.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/core.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/downloaders/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/downloaders/memodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/mixin.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/activity.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/agent.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/base.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/brain_location.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/brain_region.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/brain_region_hierarchy.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/circuit.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/classification.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/contribution.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/core.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/electrical_cell_recording.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/emodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/entity.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/etype.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/ion_channel_model.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/license.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/memodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/memodelcalibrationresult.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/morphology.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/mtype.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/response.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/scientific_artifact.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/simulation.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/simulation_campaign.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/simulation_execution.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/simulation_generation.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/simulation_result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/single_neuron_simulation.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/single_neuron_synaptome_simulation.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/subject.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/synaptome.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/taxonomy.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/models/validation_result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/route.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/schemas/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/schemas/base.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/schemas/memodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/serdes.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/token_manager.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/util.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/utils/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/utils/asset.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk/utils/filesystem.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk.egg-info/dependency_links.txt +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk.egg-info/requires.txt +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/src/entitysdk.egg-info/top_level.txt +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/integration/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/integration/conftest.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/integration/test_searching.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/conftest.py +0 -0
- {entitysdk-0.6.2/tests/unit/models → entitysdk-0.7.0/tests/unit/dependencies}/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/downloaders/test_emodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/downloaders/test_ion_channel_model.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/downloaders/test_memodel.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/downloaders/test_morphology.py +0 -0
- /entitysdk-0.6.2/tests/unit/models/data/.gitignore → /entitysdk-0.7.0/tests/unit/models/__init__.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/circuit.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/electrical_cell_recording.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/ion_channel_model.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/memodel_calibration_result.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/reconstruction_morphology.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/simulation_campaign.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/data/validation_result.json +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_agent.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_asset.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_brain_region.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_circuit.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_contribution.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_electrical_cell_recording.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_init.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_ion_channel_model.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_memodel_calibration_result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_morphology.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_simulation_campaign.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/models/test_validation_result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_base.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_common.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_config.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_result.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_route.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_serdes.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_token_manager.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/test_util.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/util.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tests/unit/utils/test_filesystem.py +0 -0
- {entitysdk-0.6.2 → entitysdk-0.7.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: entitysdk
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: Python library for interacting with the entitycore service
|
5
5
|
Author-email: Open Brain Institute <info@openbraininstitute.org>
|
6
6
|
Maintainer-email: Open Brain Institute <info@openbraininstitute.org>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
import io
|
4
4
|
import os
|
5
5
|
from pathlib import Path
|
6
|
-
from typing import Any, cast
|
6
|
+
from typing import Any, TypeVar, cast
|
7
7
|
|
8
8
|
import httpx
|
9
9
|
|
@@ -14,7 +14,7 @@ from entitysdk.models.asset import Asset, DetailedFileList, LocalAssetMetadata
|
|
14
14
|
from entitysdk.models.core import Identifiable
|
15
15
|
from entitysdk.models.entity import Entity
|
16
16
|
from entitysdk.result import IteratorResult
|
17
|
-
from entitysdk.schemas.asset import
|
17
|
+
from entitysdk.schemas.asset import DownloadedAssetFile
|
18
18
|
from entitysdk.token_manager import TokenFromValue, TokenManager
|
19
19
|
from entitysdk.types import ID, DeploymentEnvironment, Token
|
20
20
|
from entitysdk.util import (
|
@@ -24,6 +24,8 @@ from entitysdk.util import (
|
|
24
24
|
)
|
25
25
|
from entitysdk.utils.asset import filter_assets
|
26
26
|
|
27
|
+
TEntity = TypeVar("TEntity", bound=Entity)
|
28
|
+
|
27
29
|
|
28
30
|
class Client:
|
29
31
|
"""Client for entitysdk."""
|
@@ -94,9 +96,9 @@ class Client:
|
|
94
96
|
self,
|
95
97
|
entity_id: ID,
|
96
98
|
*,
|
97
|
-
entity_type: type[
|
99
|
+
entity_type: type[TEntity],
|
98
100
|
project_context: ProjectContext | None = None,
|
99
|
-
) ->
|
101
|
+
) -> TEntity:
|
100
102
|
"""Get entity from resource id.
|
101
103
|
|
102
104
|
Args:
|
@@ -357,28 +359,30 @@ class Client:
|
|
357
359
|
|
358
360
|
context = self._optional_user_context(override_context=project_context)
|
359
361
|
|
360
|
-
asset = None
|
362
|
+
asset = cast(Asset, asset_id) if isinstance(asset_id, Asset) else None
|
363
|
+
|
361
364
|
if not ignore_directory_name:
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
365
|
+
if asset is None:
|
366
|
+
asset_endpoint = route.get_assets_endpoint(
|
367
|
+
api_url=self.api_url,
|
368
|
+
entity_type=entity_type,
|
369
|
+
entity_id=cast(ID, entity_id),
|
370
|
+
asset_id=asset_id,
|
371
|
+
)
|
372
|
+
asset = core.get_entity(
|
373
|
+
asset_endpoint,
|
374
|
+
entity_type=Asset,
|
375
|
+
project_context=context,
|
376
|
+
http_client=self._http_client,
|
377
|
+
token=self._token_manager.get_token(),
|
378
|
+
)
|
375
379
|
|
376
380
|
output_path /= asset.path
|
377
381
|
|
378
382
|
contents = self.list_directory(
|
379
383
|
entity_id=entity_id,
|
380
384
|
entity_type=entity_type,
|
381
|
-
asset_id=asset_id,
|
385
|
+
asset_id=asset_id if isinstance(asset_id, ID) else asset.id,
|
382
386
|
project_context=project_context,
|
383
387
|
)
|
384
388
|
|
@@ -457,6 +461,7 @@ class Client:
|
|
457
461
|
Output file path.
|
458
462
|
"""
|
459
463
|
context = self._optional_user_context(override_context=project_context)
|
464
|
+
|
460
465
|
asset_endpoint = route.get_assets_endpoint(
|
461
466
|
api_url=self.api_url,
|
462
467
|
entity_type=entity_type,
|
@@ -498,6 +503,11 @@ class Client:
|
|
498
503
|
token=self._token_manager.get_token(),
|
499
504
|
)
|
500
505
|
|
506
|
+
@staticmethod
|
507
|
+
def select_assets(entity: Entity, selection: dict) -> IteratorResult:
|
508
|
+
"""Select assets from entity based on selection."""
|
509
|
+
return IteratorResult(filter_assets(entity.assets, selection))
|
510
|
+
|
501
511
|
def download_assets(
|
502
512
|
self,
|
503
513
|
entity_or_id: Entity | tuple[ID, type[Entity]],
|
@@ -520,9 +530,9 @@ class Client:
|
|
520
530
|
project_context=context,
|
521
531
|
)
|
522
532
|
|
523
|
-
return
|
533
|
+
return DownloadedAssetFile(
|
524
534
|
asset=asset,
|
525
|
-
|
535
|
+
path=path,
|
526
536
|
)
|
527
537
|
|
528
538
|
context = self._optional_user_context(override_context=project_context)
|
@@ -0,0 +1 @@
|
|
1
|
+
"""Dependencies."""
|
@@ -0,0 +1,18 @@
|
|
1
|
+
"""Entity dependencies."""
|
2
|
+
|
3
|
+
from entitysdk.exception import DependencyError
|
4
|
+
from entitysdk.models.entity import Entity
|
5
|
+
|
6
|
+
|
7
|
+
def ensure_has_id(model: Entity) -> Entity:
|
8
|
+
"""Ensure entity has id."""
|
9
|
+
if model.id is None:
|
10
|
+
raise DependencyError(f"Model has no id: {repr(model)}")
|
11
|
+
return model
|
12
|
+
|
13
|
+
|
14
|
+
def ensure_has_assets(model: Entity) -> Entity:
|
15
|
+
"""Ensure entity has assets."""
|
16
|
+
if not model.assets:
|
17
|
+
raise DependencyError(f"Model has no assets: {repr(model)}")
|
18
|
+
return model
|
@@ -0,0 +1,78 @@
|
|
1
|
+
"""Downloading functions for Simulation."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
import logging
|
5
|
+
from pathlib import Path
|
6
|
+
from typing import cast
|
7
|
+
|
8
|
+
from entitysdk.client import Client
|
9
|
+
from entitysdk.dependencies.entity import ensure_has_assets, ensure_has_id
|
10
|
+
from entitysdk.models import Simulation
|
11
|
+
from entitysdk.types import ID
|
12
|
+
|
13
|
+
L = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def download_simulation_config_content(client: Client, *, model: Simulation) -> dict:
|
17
|
+
"""Download the the simulation config json into a dictionary."""
|
18
|
+
ensure_has_id(model)
|
19
|
+
ensure_has_assets(model)
|
20
|
+
|
21
|
+
asset = client.select_assets(
|
22
|
+
model,
|
23
|
+
selection={"label": "sonata_simulation_config"},
|
24
|
+
).one()
|
25
|
+
|
26
|
+
json_content: bytes = client.download_content(
|
27
|
+
entity_id=cast(ID, model.id),
|
28
|
+
entity_type=Simulation,
|
29
|
+
asset_id=asset.id,
|
30
|
+
)
|
31
|
+
|
32
|
+
return json.loads(json_content)
|
33
|
+
|
34
|
+
|
35
|
+
def download_node_sets_file(client: Client, *, model: Simulation, output_path: Path) -> Path:
|
36
|
+
"""Download the node sets file from simulation's assets."""
|
37
|
+
ensure_has_id(model)
|
38
|
+
ensure_has_assets(model)
|
39
|
+
|
40
|
+
asset = client.select_assets(
|
41
|
+
model,
|
42
|
+
selection={"label": "custom_node_sets"},
|
43
|
+
).one()
|
44
|
+
|
45
|
+
path = client.download_file(
|
46
|
+
entity_id=cast(ID, model.id),
|
47
|
+
entity_type=Simulation,
|
48
|
+
asset_id=asset,
|
49
|
+
output_path=output_path,
|
50
|
+
)
|
51
|
+
|
52
|
+
L.info("Node sets file downloaded at %s", path)
|
53
|
+
|
54
|
+
return path
|
55
|
+
|
56
|
+
|
57
|
+
def download_spike_replay_files(
|
58
|
+
client: Client, *, model: Simulation, output_dir: Path
|
59
|
+
) -> list[Path]:
|
60
|
+
"""Download the spike replay files from simualtion's assets."""
|
61
|
+
ensure_has_id(model)
|
62
|
+
ensure_has_assets(model)
|
63
|
+
|
64
|
+
assets = client.select_assets(model, selection={"label": "replay_spikes"}).all()
|
65
|
+
|
66
|
+
spike_files: list[Path] = [
|
67
|
+
client.download_file(
|
68
|
+
entity_id=cast(ID, model.id),
|
69
|
+
entity_type=Simulation,
|
70
|
+
asset_id=asset,
|
71
|
+
output_path=output_dir / asset.path,
|
72
|
+
)
|
73
|
+
for asset in assets
|
74
|
+
]
|
75
|
+
|
76
|
+
L.info("Downloaded %d spike replay files: %s", len(spike_files), spike_files)
|
77
|
+
|
78
|
+
return spike_files
|
@@ -0,0 +1,61 @@
|
|
1
|
+
"""Downloading functions for SimulationResult."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import cast
|
6
|
+
|
7
|
+
from entitysdk.client import Client
|
8
|
+
from entitysdk.dependencies.entity import ensure_has_assets, ensure_has_id
|
9
|
+
from entitysdk.models import SimulationResult
|
10
|
+
from entitysdk.types import ID
|
11
|
+
|
12
|
+
L = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def download_spike_report_file(
|
16
|
+
client: Client, *, model: SimulationResult, output_path: Path
|
17
|
+
) -> Path:
|
18
|
+
"""Download spike report file from SimulationResult entity."""
|
19
|
+
ensure_has_id(model)
|
20
|
+
ensure_has_assets(model)
|
21
|
+
|
22
|
+
asset = client.select_assets(
|
23
|
+
model,
|
24
|
+
selection={"label": "spike_report"},
|
25
|
+
).one()
|
26
|
+
|
27
|
+
path = client.download_file(
|
28
|
+
entity_id=cast(ID, model.id),
|
29
|
+
entity_type=SimulationResult,
|
30
|
+
asset_id=asset,
|
31
|
+
output_path=output_path / asset.path if output_path.is_dir() else output_path,
|
32
|
+
)
|
33
|
+
L.info("Spike report file downloaded at %s", path)
|
34
|
+
return path
|
35
|
+
|
36
|
+
|
37
|
+
def download_voltage_report_files(
|
38
|
+
client: Client, *, model: SimulationResult, output_dir: Path
|
39
|
+
) -> list[Path]:
|
40
|
+
"""Download voltage report files from SimulationResult entity."""
|
41
|
+
ensure_has_id(model)
|
42
|
+
ensure_has_assets(model)
|
43
|
+
|
44
|
+
assets = client.select_assets(
|
45
|
+
model,
|
46
|
+
selection={"label": "voltage_report"},
|
47
|
+
).all()
|
48
|
+
|
49
|
+
files: list[Path] = [
|
50
|
+
client.download_file(
|
51
|
+
entity_id=cast(ID, model.id),
|
52
|
+
entity_type=SimulationResult,
|
53
|
+
asset_id=asset,
|
54
|
+
output_path=output_dir / asset.path,
|
55
|
+
)
|
56
|
+
for asset in assets
|
57
|
+
]
|
58
|
+
|
59
|
+
L.info("Downloaded voltage report files: %s", files)
|
60
|
+
|
61
|
+
return files
|
@@ -11,3 +11,11 @@ class RouteNotFoundError(EntitySDKError):
|
|
11
11
|
|
12
12
|
class IteratorResultError(EntitySDKError):
|
13
13
|
"""Raised when the result of an iterator is not as expected."""
|
14
|
+
|
15
|
+
|
16
|
+
class DependencyError(EntitySDKError):
|
17
|
+
"""Raised when a dependency check fails."""
|
18
|
+
|
19
|
+
|
20
|
+
class StagingError(EntitySDKError):
|
21
|
+
"""Raised when a staging operation has failed."""
|
@@ -8,11 +8,14 @@ from pydantic import ConfigDict, Field
|
|
8
8
|
|
9
9
|
from entitysdk.models.base import BaseModel
|
10
10
|
from entitysdk.models.core import Identifiable
|
11
|
+
from entitysdk.types import ID
|
11
12
|
|
12
13
|
|
13
14
|
class Asset(Identifiable):
|
14
15
|
"""Asset."""
|
15
16
|
|
17
|
+
id: ID
|
18
|
+
|
16
19
|
path: Annotated[
|
17
20
|
str,
|
18
21
|
Field(
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""Asset related schemas."""
|
2
|
+
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from entitysdk.models.asset import Asset
|
6
|
+
from entitysdk.schemas.base import Schema
|
7
|
+
|
8
|
+
|
9
|
+
class DownloadedAssetFile(Schema):
|
10
|
+
"""Downloaded asset file."""
|
11
|
+
|
12
|
+
asset: Asset
|
13
|
+
path: Path
|
14
|
+
|
15
|
+
|
16
|
+
class DownloadedAssetContent(Schema):
|
17
|
+
"""Downloaded asset content."""
|
18
|
+
|
19
|
+
asset: Asset
|
20
|
+
content: bytes
|
@@ -0,0 +1,7 @@
|
|
1
|
+
"""Staging functions."""
|
2
|
+
|
3
|
+
from entitysdk.staging.circuit import stage_circuit
|
4
|
+
from entitysdk.staging.simulation import stage_simulation
|
5
|
+
from entitysdk.staging.simulation_result import stage_simulation_result
|
6
|
+
|
7
|
+
__all__ = ["stage_circuit", "stage_simulation", "stage_simulation_result"]
|
@@ -0,0 +1,43 @@
|
|
1
|
+
"""Staging functions for Circuit."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from pathlib import Path
|
5
|
+
from typing import cast
|
6
|
+
|
7
|
+
from entitysdk.client import Client
|
8
|
+
from entitysdk.dependencies.entity import ensure_has_assets, ensure_has_id
|
9
|
+
from entitysdk.models import Circuit
|
10
|
+
from entitysdk.types import ID
|
11
|
+
|
12
|
+
L = logging.getLogger(__name__)
|
13
|
+
|
14
|
+
|
15
|
+
def stage_circuit(client: Client, *, model: Circuit, output_dir: Path) -> Path:
|
16
|
+
"""Stage a Circuit directory into output_dir."""
|
17
|
+
ensure_has_id(model)
|
18
|
+
ensure_has_assets(model)
|
19
|
+
|
20
|
+
asset = client.select_assets(
|
21
|
+
model,
|
22
|
+
selection={
|
23
|
+
"content_type": "application/vnd.directory",
|
24
|
+
"is_directory": True,
|
25
|
+
},
|
26
|
+
).one()
|
27
|
+
|
28
|
+
paths = client.download_directory(
|
29
|
+
entity_id=cast(ID, model.id),
|
30
|
+
entity_type=Circuit,
|
31
|
+
asset_id=asset,
|
32
|
+
output_path=output_dir,
|
33
|
+
ignore_directory_name=True,
|
34
|
+
)
|
35
|
+
|
36
|
+
L.debug("Downloaded circuit %s paths: %s", model.id, paths)
|
37
|
+
|
38
|
+
circuit_config_path = output_dir / "circuit_config.json"
|
39
|
+
assert circuit_config_path in paths
|
40
|
+
|
41
|
+
L.info("Circuit %s staged at %s", model.id, circuit_config_path)
|
42
|
+
|
43
|
+
return circuit_config_path
|
@@ -0,0 +1,142 @@
|
|
1
|
+
"""Staging functions for Simulation."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from copy import deepcopy
|
5
|
+
from pathlib import Path
|
6
|
+
|
7
|
+
from entitysdk.client import Client
|
8
|
+
from entitysdk.downloaders.simulation import (
|
9
|
+
download_node_sets_file,
|
10
|
+
download_simulation_config_content,
|
11
|
+
download_spike_replay_files,
|
12
|
+
)
|
13
|
+
from entitysdk.exception import StagingError
|
14
|
+
from entitysdk.models import Circuit, Simulation
|
15
|
+
from entitysdk.staging.circuit import stage_circuit
|
16
|
+
from entitysdk.types import StrOrPath
|
17
|
+
from entitysdk.utils.filesystem import create_dir
|
18
|
+
from entitysdk.utils.io import write_json
|
19
|
+
|
20
|
+
L = logging.getLogger(__name__)
|
21
|
+
|
22
|
+
DEFAULT_NODE_SETS_FILENAME = "node_sets.json"
|
23
|
+
DEFAULT_SIMULATION_CONFIG_FILENAME = "simulation_config.json"
|
24
|
+
DEFAULT_CIRCUIT_DIR = "circuit"
|
25
|
+
|
26
|
+
|
27
|
+
def stage_simulation(
|
28
|
+
client: Client,
|
29
|
+
*,
|
30
|
+
model: Simulation,
|
31
|
+
output_dir: StrOrPath,
|
32
|
+
circuit_config_path: Path | None = None,
|
33
|
+
override_results_dir: Path | None = None,
|
34
|
+
) -> Path:
|
35
|
+
"""Stage a simulation entity into output_dir.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
client: The client to use to stage the simulation.
|
39
|
+
model: The simulation entity to stage.
|
40
|
+
output_dir: The directory to stage the simulation into.
|
41
|
+
circuit_config_path: The path to the circuit config file.
|
42
|
+
If not provided, the circuit will be staged from metadata.
|
43
|
+
override_results_dir: Directory to update the simulation config section to point to.
|
44
|
+
|
45
|
+
Returns:
|
46
|
+
The path to the staged simulation config file.
|
47
|
+
"""
|
48
|
+
output_dir = create_dir(output_dir).resolve()
|
49
|
+
|
50
|
+
simulation_config: dict = download_simulation_config_content(client, model=model)
|
51
|
+
node_sets_file: Path = download_node_sets_file(
|
52
|
+
client,
|
53
|
+
model=model,
|
54
|
+
output_path=output_dir / DEFAULT_NODE_SETS_FILENAME,
|
55
|
+
)
|
56
|
+
spike_paths: list[Path] = download_spike_replay_files(
|
57
|
+
client,
|
58
|
+
model=model,
|
59
|
+
output_dir=output_dir,
|
60
|
+
)
|
61
|
+
if circuit_config_path is None:
|
62
|
+
L.info(
|
63
|
+
"Circuit config path was not provided. Circuit is going to be staged from metadata. "
|
64
|
+
"Circuit id to be staged: %s"
|
65
|
+
)
|
66
|
+
circuit_config_path = stage_circuit(
|
67
|
+
client,
|
68
|
+
model=client.get_entity(
|
69
|
+
entity_id=model.entity_id,
|
70
|
+
entity_type=Circuit,
|
71
|
+
),
|
72
|
+
output_dir=create_dir(output_dir / DEFAULT_CIRCUIT_DIR),
|
73
|
+
)
|
74
|
+
|
75
|
+
transformed_simulation_config: dict = _transform_simulation_config(
|
76
|
+
simulation_config=simulation_config,
|
77
|
+
circuit_config_path=circuit_config_path,
|
78
|
+
node_sets_path=node_sets_file,
|
79
|
+
spike_paths=spike_paths,
|
80
|
+
output_dir=output_dir,
|
81
|
+
override_results_dir=override_results_dir,
|
82
|
+
)
|
83
|
+
|
84
|
+
output_simulation_config_file = output_dir / DEFAULT_SIMULATION_CONFIG_FILENAME
|
85
|
+
|
86
|
+
write_json(
|
87
|
+
data=transformed_simulation_config,
|
88
|
+
path=output_simulation_config_file,
|
89
|
+
)
|
90
|
+
|
91
|
+
L.info("Staged Simulation %s at %s", model.id, output_dir)
|
92
|
+
|
93
|
+
return output_simulation_config_file
|
94
|
+
|
95
|
+
|
96
|
+
def _transform_simulation_config(
|
97
|
+
simulation_config: dict,
|
98
|
+
circuit_config_path: Path,
|
99
|
+
node_sets_path: Path,
|
100
|
+
spike_paths: list[Path],
|
101
|
+
output_dir: Path,
|
102
|
+
override_results_dir: Path | None,
|
103
|
+
) -> dict:
|
104
|
+
return simulation_config | {
|
105
|
+
"network": str(circuit_config_path),
|
106
|
+
"node_sets_file": str(node_sets_path.relative_to(output_dir)),
|
107
|
+
"inputs": _transform_inputs(simulation_config["inputs"], spike_paths),
|
108
|
+
"output": _transform_output(simulation_config["output"], override_results_dir),
|
109
|
+
}
|
110
|
+
|
111
|
+
|
112
|
+
def _transform_inputs(inputs: dict, spike_paths: list[Path]) -> dict:
|
113
|
+
expected_spike_filenames = {p.name for p in spike_paths}
|
114
|
+
|
115
|
+
transformed_inputs = deepcopy(inputs)
|
116
|
+
for values in transformed_inputs.values():
|
117
|
+
if values["input_type"] == "spikes":
|
118
|
+
path = Path(values["spike_file"]).name
|
119
|
+
|
120
|
+
if path not in expected_spike_filenames:
|
121
|
+
raise StagingError(
|
122
|
+
f"Spike file name in config is not present in spike asset file names.\n"
|
123
|
+
f"Config file name: {path}\n"
|
124
|
+
f"Asset file names: {expected_spike_filenames}"
|
125
|
+
)
|
126
|
+
|
127
|
+
values["spike_file"] = str(path)
|
128
|
+
L.debug("Spike file %s -> %s", values["spike_file"], path)
|
129
|
+
|
130
|
+
return transformed_inputs
|
131
|
+
|
132
|
+
|
133
|
+
def _transform_output(output: dict, override_results_dir: StrOrPath | None) -> dict:
|
134
|
+
if override_results_dir is None:
|
135
|
+
return output
|
136
|
+
|
137
|
+
path = Path(override_results_dir)
|
138
|
+
|
139
|
+
return {
|
140
|
+
"output_dir": str(path),
|
141
|
+
"spikes_file": str(path / "spikes.h5"),
|
142
|
+
}
|
@@ -0,0 +1,72 @@
|
|
1
|
+
"""Staging functions for SimulationResult."""
|
2
|
+
|
3
|
+
import logging
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from entitysdk.client import Client
|
7
|
+
from entitysdk.downloaders.simulation_result import (
|
8
|
+
download_spike_report_file,
|
9
|
+
download_voltage_report_files,
|
10
|
+
)
|
11
|
+
from entitysdk.models import Simulation, SimulationResult
|
12
|
+
from entitysdk.staging.simulation import stage_simulation
|
13
|
+
from entitysdk.types import StrOrPath
|
14
|
+
from entitysdk.utils.filesystem import create_dir
|
15
|
+
from entitysdk.utils.io import load_json
|
16
|
+
|
17
|
+
L = logging.getLogger(__name__)
|
18
|
+
|
19
|
+
|
20
|
+
DEFAULT_REPORTS_DIR_NAME = "output"
|
21
|
+
DEFAULT_SPIKE_FILE_NAME = "output/spikes.h5"
|
22
|
+
DEFAULT_SIMULATION_CONFIG_FILENAME = "simulation_config.json"
|
23
|
+
|
24
|
+
|
25
|
+
def stage_simulation_result(
|
26
|
+
client: Client,
|
27
|
+
*,
|
28
|
+
model: SimulationResult,
|
29
|
+
output_dir: StrOrPath,
|
30
|
+
simulation_config_file: StrOrPath | None = None,
|
31
|
+
) -> Path:
|
32
|
+
"""Stage a SimulationResult entity."""
|
33
|
+
output_dir: Path = create_dir(output_dir)
|
34
|
+
|
35
|
+
if simulation_config_file is None:
|
36
|
+
L.info(
|
37
|
+
"Simulation will be staged from simulation result's simulation_id %s",
|
38
|
+
model.simulation_id,
|
39
|
+
)
|
40
|
+
simulation_config_file = stage_simulation(
|
41
|
+
client,
|
42
|
+
model=client.get_entity(entity_id=model.simulation_id, entity_type=Simulation),
|
43
|
+
output_dir=output_dir,
|
44
|
+
)
|
45
|
+
else:
|
46
|
+
L.info(
|
47
|
+
"External simulation config provided at %s. Outputs will be staged relative to it.",
|
48
|
+
simulation_config_file,
|
49
|
+
)
|
50
|
+
|
51
|
+
config: dict = load_json(simulation_config_file)
|
52
|
+
reports_dir, spikes_file = _get_output_paths(config, Path(simulation_config_file).parent)
|
53
|
+
create_dir(reports_dir)
|
54
|
+
|
55
|
+
download_spike_report_file(
|
56
|
+
client,
|
57
|
+
model=model,
|
58
|
+
output_path=spikes_file,
|
59
|
+
)
|
60
|
+
download_voltage_report_files(
|
61
|
+
client,
|
62
|
+
model=model,
|
63
|
+
output_dir=reports_dir,
|
64
|
+
)
|
65
|
+
|
66
|
+
return Path(simulation_config_file)
|
67
|
+
|
68
|
+
|
69
|
+
def _get_output_paths(config: dict, output_dir: Path) -> tuple[Path, Path]:
|
70
|
+
reports_dir = output_dir / config["output"]["output_dir"]
|
71
|
+
spikes_file = output_dir / config["output"]["spikes_file"]
|
72
|
+
return reports_dir, spikes_file
|
@@ -0,0 +1,16 @@
|
|
1
|
+
"""IO utilities."""
|
2
|
+
|
3
|
+
import json
|
4
|
+
from pathlib import Path
|
5
|
+
|
6
|
+
from entitysdk.types import StrOrPath
|
7
|
+
|
8
|
+
|
9
|
+
def write_json(data: dict, path: StrOrPath, **json_kwargs) -> None:
|
10
|
+
"""Write dictionary to file as JSON."""
|
11
|
+
Path(path).write_text(json.dumps(data, **json_kwargs))
|
12
|
+
|
13
|
+
|
14
|
+
def load_json(path: StrOrPath) -> dict:
|
15
|
+
"""Load JSON file to dict."""
|
16
|
+
return json.loads(Path(path).read_bytes())
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: entitysdk
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: Python library for interacting with the entitycore service
|
5
5
|
Author-email: Open Brain Institute <info@openbraininstitute.org>
|
6
6
|
Maintainer-email: Open Brain Institute <info@openbraininstitute.org>
|