entitysdk 0.4.0__tar.gz → 0.5.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.
Files changed (115) hide show
  1. {entitysdk-0.4.0/src/entitysdk.egg-info → entitysdk-0.5.0}/PKG-INFO +3 -4
  2. {entitysdk-0.4.0 → entitysdk-0.5.0}/README.md +2 -3
  3. entitysdk-0.5.0/src/entitysdk/downloaders/__init__.py +1 -0
  4. entitysdk-0.5.0/src/entitysdk/downloaders/emodel.py +29 -0
  5. entitysdk-0.5.0/src/entitysdk/downloaders/ion_channel_model.py +29 -0
  6. entitysdk-0.5.0/src/entitysdk/downloaders/memodel.py +42 -0
  7. entitysdk-0.5.0/src/entitysdk/downloaders/morphology.py +36 -0
  8. entitysdk-0.5.0/src/entitysdk/schemas/memodel.py +13 -0
  9. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/types.py +2 -0
  10. entitysdk-0.5.0/src/entitysdk/utils/filesystem.py +12 -0
  11. {entitysdk-0.4.0 → entitysdk-0.5.0/src/entitysdk.egg-info}/PKG-INFO +3 -4
  12. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk.egg-info/SOURCES.txt +13 -1
  13. entitysdk-0.5.0/tests/unit/downloaders/test_emodel.py +70 -0
  14. entitysdk-0.5.0/tests/unit/downloaders/test_ion_channel_model.py +72 -0
  15. entitysdk-0.5.0/tests/unit/downloaders/test_memodel.py +199 -0
  16. entitysdk-0.5.0/tests/unit/downloaders/test_morphology.py +68 -0
  17. entitysdk-0.5.0/tests/unit/utils/test_filesystem.py +6 -0
  18. {entitysdk-0.4.0 → entitysdk-0.5.0}/.github/workflows/sdist.yml +0 -0
  19. {entitysdk-0.4.0 → entitysdk-0.5.0}/.github/workflows/tox.yml +0 -0
  20. {entitysdk-0.4.0 → entitysdk-0.5.0}/.gitignore +0 -0
  21. {entitysdk-0.4.0 → entitysdk-0.5.0}/CHANGELOG.rst +0 -0
  22. {entitysdk-0.4.0 → entitysdk-0.5.0}/CONTRIBUTING.md +0 -0
  23. {entitysdk-0.4.0 → entitysdk-0.5.0}/LICENSE.txt +0 -0
  24. {entitysdk-0.4.0 → entitysdk-0.5.0}/examples/01_searching.ipynb +0 -0
  25. {entitysdk-0.4.0 → entitysdk-0.5.0}/examples/02_morphology.ipynb +0 -0
  26. {entitysdk-0.4.0 → entitysdk-0.5.0}/examples/03_contribution.ipynb +0 -0
  27. {entitysdk-0.4.0 → entitysdk-0.5.0}/examples/04_circuit.ipynb +0 -0
  28. {entitysdk-0.4.0 → entitysdk-0.5.0}/examples/05_simulation_campaign.ipynb +0 -0
  29. {entitysdk-0.4.0 → entitysdk-0.5.0}/pyproject.toml +0 -0
  30. {entitysdk-0.4.0 → entitysdk-0.5.0}/setup.cfg +0 -0
  31. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/__init__.py +0 -0
  32. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/client.py +0 -0
  33. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/common.py +0 -0
  34. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/config.py +0 -0
  35. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/core.py +0 -0
  36. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/exception.py +0 -0
  37. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/mixin.py +0 -0
  38. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/__init__.py +0 -0
  39. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/agent.py +0 -0
  40. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/asset.py +0 -0
  41. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/base.py +0 -0
  42. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/brain_location.py +0 -0
  43. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/brain_region.py +0 -0
  44. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/circuit.py +0 -0
  45. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/contribution.py +0 -0
  46. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/core.py +0 -0
  47. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/electrical_cell_recording.py +0 -0
  48. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/emodel.py +0 -0
  49. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/entity.py +0 -0
  50. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/etype.py +0 -0
  51. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/ion_channel_model.py +0 -0
  52. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/license.py +0 -0
  53. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/memodel.py +0 -0
  54. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/memodelcalibrationresult.py +0 -0
  55. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/morphology.py +0 -0
  56. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/mtype.py +0 -0
  57. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/response.py +0 -0
  58. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/scientific_artifact.py +0 -0
  59. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/simulation.py +0 -0
  60. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/simulation_campaign.py +0 -0
  61. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/subject.py +0 -0
  62. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/taxonomy.py +0 -0
  63. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/models/validation_result.py +0 -0
  64. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/result.py +0 -0
  65. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/route.py +0 -0
  66. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/schemas/__init__.py +0 -0
  67. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/schemas/asset.py +0 -0
  68. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/schemas/base.py +0 -0
  69. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/serdes.py +0 -0
  70. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/token_manager.py +0 -0
  71. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/util.py +0 -0
  72. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/utils/__init__.py +0 -0
  73. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk/utils/asset.py +0 -0
  74. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk.egg-info/dependency_links.txt +0 -0
  75. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk.egg-info/requires.txt +0 -0
  76. {entitysdk-0.4.0 → entitysdk-0.5.0}/src/entitysdk.egg-info/top_level.txt +0 -0
  77. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/__init__.py +0 -0
  78. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/integration/__init__.py +0 -0
  79. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/integration/conftest.py +0 -0
  80. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/integration/test_searching.py +0 -0
  81. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/__init__.py +0 -0
  82. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/conftest.py +0 -0
  83. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/__init__.py +0 -0
  84. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/.gitignore +0 -0
  85. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/circuit.json +0 -0
  86. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/electrical_cell_recording.json +0 -0
  87. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/ion_channel_model.json +0 -0
  88. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/memodel_calibration_result.json +0 -0
  89. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/reconstruction_morphology.json +0 -0
  90. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/simulation_campaign.json +0 -0
  91. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/data/validation_result.json +0 -0
  92. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_agent.py +0 -0
  93. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_asset.py +0 -0
  94. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_brain_region.py +0 -0
  95. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_circuit.py +0 -0
  96. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_contribution.py +0 -0
  97. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_electrical_cell_recording.py +0 -0
  98. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_init.py +0 -0
  99. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_ion_channel_model.py +0 -0
  100. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_memodel_calibration_result.py +0 -0
  101. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_morphology.py +0 -0
  102. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_simulation_campaign.py +0 -0
  103. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/models/test_validation_result.py +0 -0
  104. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_base.py +0 -0
  105. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_client.py +0 -0
  106. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_common.py +0 -0
  107. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_config.py +0 -0
  108. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_result.py +0 -0
  109. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_route.py +0 -0
  110. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_serdes.py +0 -0
  111. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_token_manager.py +0 -0
  112. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/test_util.py +0 -0
  113. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/util.py +0 -0
  114. {entitysdk-0.4.0 → entitysdk-0.5.0}/tests/unit/utils/test_asset.py +0 -0
  115. {entitysdk-0.4.0 → entitysdk-0.5.0}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entitysdk
3
- Version: 0.4.0
3
+ Version: 0.5.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>
@@ -66,14 +66,14 @@ client = Client(
66
66
  project_id=UUID("your-project-id"),
67
67
  virtual_lab_id=UUID("your-lab-id")
68
68
  ),
69
- environment="staging"
69
+ environment="staging",
70
+ token_manager=token
70
71
  )
71
72
 
72
73
  # Search for morphologies
73
74
  iterator = client.search_entity(
74
75
  entity_type=models.ReconstructionMorphology,
75
76
  query={"mtype__pref_label": "L5_TPC:A"},
76
- token=token,
77
77
  limit=1,
78
78
  )
79
79
  morphology = next(iterator)
@@ -84,7 +84,6 @@ client.upload_file(
84
84
  entity_type=models.ReconstructionMorphology,
85
85
  file_path="path/to/file.swc",
86
86
  file_content_type="application/swc",
87
- token="your-token"
88
87
  )
89
88
  ```
90
89
 
@@ -45,14 +45,14 @@ client = Client(
45
45
  project_id=UUID("your-project-id"),
46
46
  virtual_lab_id=UUID("your-lab-id")
47
47
  ),
48
- environment="staging"
48
+ environment="staging",
49
+ token_manager=token
49
50
  )
50
51
 
51
52
  # Search for morphologies
52
53
  iterator = client.search_entity(
53
54
  entity_type=models.ReconstructionMorphology,
54
55
  query={"mtype__pref_label": "L5_TPC:A"},
55
- token=token,
56
56
  limit=1,
57
57
  )
58
58
  morphology = next(iterator)
@@ -63,7 +63,6 @@ client.upload_file(
63
63
  entity_type=models.ReconstructionMorphology,
64
64
  file_path="path/to/file.swc",
65
65
  file_content_type="application/swc",
66
- token="your-token"
67
66
  )
68
67
  ```
69
68
 
@@ -0,0 +1 @@
1
+ """Downloaders module."""
@@ -0,0 +1,29 @@
1
+ """Download functions for EModel entities."""
2
+
3
+ from pathlib import Path
4
+
5
+ from entitysdk.client import Client
6
+ from entitysdk.models.emodel import EModel
7
+ from entitysdk.utils.filesystem import create_dir
8
+
9
+
10
+ def download_hoc(
11
+ client: Client,
12
+ emodel: EModel,
13
+ output_dir: str | Path,
14
+ ) -> Path:
15
+ """Download hoc file.
16
+
17
+ Args:
18
+ client (Client): EntitySDK client
19
+ emodel (EModel): EModel entitysdk object
20
+ output_dir (str or Path): directory to save the hoc file
21
+ """
22
+ output_dir = create_dir(output_dir)
23
+ asset = client.download_assets(
24
+ emodel,
25
+ selection={"content_type": "application/hoc"},
26
+ output_path=output_dir,
27
+ ).one()
28
+
29
+ return asset.output_path
@@ -0,0 +1,29 @@
1
+ """Download functions for IonChannelModel entities."""
2
+
3
+ from pathlib import Path
4
+
5
+ from entitysdk.client import Client
6
+ from entitysdk.models.ion_channel_model import IonChannelModel
7
+ from entitysdk.utils.filesystem import create_dir
8
+
9
+
10
+ def download_ion_channel_mechanism(
11
+ client: Client,
12
+ ion_channel_model: IonChannelModel,
13
+ output_dir: str | Path,
14
+ ) -> Path:
15
+ """Download one mechanism file.
16
+
17
+ Args:
18
+ client (Client): EntitySDK client
19
+ ion_channel_model (IonChannelModel): IonChannelModel entitysdk object
20
+ output_dir (str or Pathlib.Path): directory to save the mechanism file
21
+ """
22
+ output_dir = create_dir(output_dir)
23
+ asset = client.download_assets(
24
+ ion_channel_model,
25
+ selection={"content_type": "application/neuron-mod"},
26
+ output_path=output_dir,
27
+ ).one()
28
+
29
+ return asset.output_path
@@ -0,0 +1,42 @@
1
+ """Download functions for MEModel entities."""
2
+
3
+ from pathlib import Path
4
+ from typing import cast
5
+
6
+ from entitysdk.client import Client
7
+ from entitysdk.downloaders.emodel import download_hoc
8
+ from entitysdk.downloaders.ion_channel_model import download_ion_channel_mechanism
9
+ from entitysdk.downloaders.morphology import download_morphology
10
+ from entitysdk.models.emodel import EModel
11
+ from entitysdk.models.memodel import MEModel
12
+ from entitysdk.schemas.memodel import DownloadedMEModel
13
+ from entitysdk.utils.filesystem import create_dir
14
+
15
+
16
+ def download_memodel(client: Client, memodel: MEModel, output_dir=".") -> DownloadedMEModel:
17
+ """Download all assets needed to run an me-model: hoc, ion channel models, and morphology.
18
+
19
+ Args:
20
+ client (Client): EntitySDK client
21
+ memodel (MEModel): MEModel entitysdk object
22
+ output_dir (str): directory to save the downloaded files, defaults to current directory
23
+ """
24
+ # we have to get the emodel to get the ion channel models.
25
+ emodel = cast(
26
+ EModel,
27
+ client.get_entity(entity_id=memodel.emodel.id, entity_type=EModel), # type: ignore
28
+ )
29
+
30
+ hoc_path = download_hoc(client, emodel, Path(output_dir) / "hoc")
31
+ # only take .asc format for now.
32
+ # Will take specific format when morphology_format is integrated into MEModel
33
+ morphology_path = download_morphology(
34
+ client, memodel.morphology, Path(output_dir) / "morphology", "asc"
35
+ )
36
+ mechanisms_dir = create_dir(Path(output_dir) / "mechanisms")
37
+ for ic in emodel.ion_channel_models or []:
38
+ download_ion_channel_mechanism(client, ic, mechanisms_dir)
39
+
40
+ return DownloadedMEModel(
41
+ hoc_path=hoc_path, mechanisms_dir=mechanisms_dir, morphology_path=morphology_path
42
+ )
@@ -0,0 +1,36 @@
1
+ """Download functions for Morphology entities."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ from entitysdk.client import Client
7
+ from entitysdk.models.morphology import ReconstructionMorphology
8
+ from entitysdk.utils.filesystem import create_dir
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ def download_morphology(
14
+ client: Client,
15
+ morphology: ReconstructionMorphology,
16
+ output_dir: str | Path,
17
+ file_type: str,
18
+ ) -> Path:
19
+ """Download morphology file.
20
+
21
+ Args:
22
+ client (Client): EntitySDK client
23
+ morphology (ReconstructionMorphology): Morphology entitysdk object
24
+ output_dir (str or Path): directory to save the morphology file
25
+ file_type (str or None): type of the morphology file ('asc', 'swc' or 'h5').
26
+ Will take the first one if None.
27
+ """
28
+ output_dir = create_dir(output_dir)
29
+
30
+ asset = client.download_assets(
31
+ morphology,
32
+ selection={"content_type": f"application/{file_type}"},
33
+ output_path=output_dir,
34
+ ).one()
35
+
36
+ return asset.output_path
@@ -0,0 +1,13 @@
1
+ """MEModel related schemas."""
2
+
3
+ from pathlib import Path
4
+
5
+ from entitysdk.schemas.base import Schema
6
+
7
+
8
+ class DownloadedMEModel(Schema):
9
+ """Downloaded asset."""
10
+
11
+ hoc_path: Path
12
+ mechanisms_dir: Path
13
+ morphology_path: Path
@@ -1,10 +1,12 @@
1
1
  """Types definitions."""
2
2
 
3
+ import os
3
4
  import uuid
4
5
  from enum import StrEnum, auto
5
6
 
6
7
  ID = uuid.UUID
7
8
  Token = str
9
+ StrOrPath = str | os.PathLike[str]
8
10
 
9
11
 
10
12
  class DeploymentEnvironment(StrEnum):
@@ -0,0 +1,12 @@
1
+ """Utility functions for filesystem operations."""
2
+
3
+ from pathlib import Path
4
+
5
+ from entitysdk.types import StrOrPath
6
+
7
+
8
+ def create_dir(path: StrOrPath) -> Path:
9
+ """Create directory and parents if it doesn't already exist."""
10
+ path = Path(path)
11
+ path.mkdir(parents=True, exist_ok=True)
12
+ return path
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: entitysdk
3
- Version: 0.4.0
3
+ Version: 0.5.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>
@@ -66,14 +66,14 @@ client = Client(
66
66
  project_id=UUID("your-project-id"),
67
67
  virtual_lab_id=UUID("your-lab-id")
68
68
  ),
69
- environment="staging"
69
+ environment="staging",
70
+ token_manager=token
70
71
  )
71
72
 
72
73
  # Search for morphologies
73
74
  iterator = client.search_entity(
74
75
  entity_type=models.ReconstructionMorphology,
75
76
  query={"mtype__pref_label": "L5_TPC:A"},
76
- token=token,
77
77
  limit=1,
78
78
  )
79
79
  morphology = next(iterator)
@@ -84,7 +84,6 @@ client.upload_file(
84
84
  entity_type=models.ReconstructionMorphology,
85
85
  file_path="path/to/file.swc",
86
86
  file_content_type="application/swc",
87
- token="your-token"
88
87
  )
89
88
  ```
90
89
 
@@ -30,6 +30,11 @@ src/entitysdk.egg-info/SOURCES.txt
30
30
  src/entitysdk.egg-info/dependency_links.txt
31
31
  src/entitysdk.egg-info/requires.txt
32
32
  src/entitysdk.egg-info/top_level.txt
33
+ src/entitysdk/downloaders/__init__.py
34
+ src/entitysdk/downloaders/emodel.py
35
+ src/entitysdk/downloaders/ion_channel_model.py
36
+ src/entitysdk/downloaders/memodel.py
37
+ src/entitysdk/downloaders/morphology.py
33
38
  src/entitysdk/models/__init__.py
34
39
  src/entitysdk/models/agent.py
35
40
  src/entitysdk/models/asset.py
@@ -59,8 +64,10 @@ src/entitysdk/models/validation_result.py
59
64
  src/entitysdk/schemas/__init__.py
60
65
  src/entitysdk/schemas/asset.py
61
66
  src/entitysdk/schemas/base.py
67
+ src/entitysdk/schemas/memodel.py
62
68
  src/entitysdk/utils/__init__.py
63
69
  src/entitysdk/utils/asset.py
70
+ src/entitysdk/utils/filesystem.py
64
71
  tests/__init__.py
65
72
  tests/integration/__init__.py
66
73
  tests/integration/conftest.py
@@ -77,6 +84,10 @@ tests/unit/test_serdes.py
77
84
  tests/unit/test_token_manager.py
78
85
  tests/unit/test_util.py
79
86
  tests/unit/util.py
87
+ tests/unit/downloaders/test_emodel.py
88
+ tests/unit/downloaders/test_ion_channel_model.py
89
+ tests/unit/downloaders/test_memodel.py
90
+ tests/unit/downloaders/test_morphology.py
80
91
  tests/unit/models/__init__.py
81
92
  tests/unit/models/test_agent.py
82
93
  tests/unit/models/test_asset.py
@@ -98,4 +109,5 @@ tests/unit/models/data/memodel_calibration_result.json
98
109
  tests/unit/models/data/reconstruction_morphology.json
99
110
  tests/unit/models/data/simulation_campaign.json
100
111
  tests/unit/models/data/validation_result.json
101
- tests/unit/utils/test_asset.py
112
+ tests/unit/utils/test_asset.py
113
+ tests/unit/utils/test_filesystem.py
@@ -0,0 +1,70 @@
1
+ import uuid
2
+
3
+ from entitysdk.downloaders.emodel import download_hoc
4
+ from entitysdk.models.emodel import EModel
5
+
6
+
7
+ def _mock_asset_response(asset_id):
8
+ return {
9
+ "id": str(asset_id),
10
+ "path": "foo.hoc",
11
+ "full_path": "foo.hoc",
12
+ "is_directory": False,
13
+ "content_type": "application/hoc",
14
+ "size": 100,
15
+ "status": "created",
16
+ "meta": {},
17
+ "sha256_digest": "sha256_digest",
18
+ }
19
+
20
+
21
+ def test_download_hoc(
22
+ tmp_path,
23
+ client,
24
+ httpx_mock,
25
+ api_url,
26
+ request_headers,
27
+ ):
28
+ """Test downloading a hoc file from an EModel entity."""
29
+ emodel_id = uuid.uuid4()
30
+ asset_id = uuid.uuid4()
31
+ hierarchy_id = uuid.uuid4()
32
+
33
+ httpx_mock.add_response(
34
+ method="GET",
35
+ url=f"{api_url}/emodel/{emodel_id}/assets/{asset_id}",
36
+ match_headers=request_headers,
37
+ json=_mock_asset_response(asset_id) | {"path": "foo.hoc"},
38
+ )
39
+ httpx_mock.add_response(
40
+ method="GET",
41
+ url=f"{api_url}/emodel/{emodel_id}/assets/{asset_id}/download",
42
+ match_headers=request_headers,
43
+ content="foo",
44
+ )
45
+
46
+ emodel = EModel(
47
+ id=emodel_id,
48
+ name="foo",
49
+ species={"name": "foo", "taxonomy_id": "bar"},
50
+ brain_region={
51
+ "name": "foo",
52
+ "annotation_value": 997,
53
+ "acronym": "bar",
54
+ "parent_structure_id": None,
55
+ "hierarchy_id": str(hierarchy_id),
56
+ "color_hex_triplet": "#FFFFFF",
57
+ },
58
+ iteration="foofoo",
59
+ score=42,
60
+ seed=0,
61
+ assets=[_mock_asset_response(asset_id)],
62
+ )
63
+
64
+ output_path = download_hoc(
65
+ client=client,
66
+ emodel=emodel,
67
+ output_dir=tmp_path,
68
+ )
69
+
70
+ assert output_path.is_file()
@@ -0,0 +1,72 @@
1
+ import uuid
2
+
3
+ from entitysdk.downloaders.ion_channel_model import download_ion_channel_mechanism
4
+ from entitysdk.models.ion_channel_model import IonChannelModel, NeuronBlock
5
+
6
+
7
+ def _mock_asset_response(asset_id):
8
+ return {
9
+ "id": str(asset_id),
10
+ "path": "foo.mod",
11
+ "full_path": "foo.mod",
12
+ "is_directory": False,
13
+ "content_type": "application/neuron-mod",
14
+ "size": 100,
15
+ "status": "created",
16
+ "meta": {},
17
+ "sha256_digest": "sha256_digest",
18
+ }
19
+
20
+
21
+ def test_download_ion_channel_model(
22
+ tmp_path,
23
+ client,
24
+ httpx_mock,
25
+ api_url,
26
+ request_headers,
27
+ ):
28
+ """Test downloading an ion channel model file from an EModel entity."""
29
+ model_id = uuid.uuid4()
30
+ asset_id = uuid.uuid4()
31
+ hierarchy_id = uuid.uuid4()
32
+
33
+ httpx_mock.add_response(
34
+ method="GET",
35
+ url=f"{api_url}/ion-channel-model/{model_id}/assets/{asset_id}",
36
+ match_headers=request_headers,
37
+ json=_mock_asset_response(asset_id) | {"path": "foo.mod"},
38
+ )
39
+ httpx_mock.add_response(
40
+ method="GET",
41
+ url=f"{api_url}/ion-channel-model/{model_id}/assets/{asset_id}/download",
42
+ match_headers=request_headers,
43
+ content="foo",
44
+ )
45
+
46
+ ic_model = IonChannelModel(
47
+ id=model_id,
48
+ name="foo",
49
+ nmodl_suffix="Ca_HVA",
50
+ description="foo description",
51
+ species={"name": "foo", "taxonomy_id": "bar"},
52
+ brain_region={
53
+ "name": "foo",
54
+ "annotation_value": 997,
55
+ "acronym": "bar",
56
+ "parent_structure_id": None,
57
+ "hierarchy_id": str(hierarchy_id),
58
+ "color_hex_triplet": "#FFFFFF",
59
+ },
60
+ is_temperature_dependent=False,
61
+ temperature_celsius=34,
62
+ neuron_block=NeuronBlock(),
63
+ assets=[_mock_asset_response(asset_id)],
64
+ )
65
+
66
+ output_path = download_ion_channel_mechanism(
67
+ client=client,
68
+ ion_channel_model=ic_model,
69
+ output_dir=tmp_path,
70
+ )
71
+
72
+ assert output_path.is_file()
@@ -0,0 +1,199 @@
1
+ import os
2
+ import uuid
3
+
4
+ from entitysdk.downloaders.memodel import download_memodel
5
+ from entitysdk.models.emodel import EModel
6
+ from entitysdk.models.memodel import MEModel
7
+ from entitysdk.models.morphology import ReconstructionMorphology
8
+ from entitysdk.types import ValidationStatus
9
+
10
+
11
+ def _mock_morph_asset_response(asset_id):
12
+ """Mock response for morphology asset."""
13
+ return {
14
+ "id": str(asset_id),
15
+ "path": "foo.asc",
16
+ "full_path": "foo.asc",
17
+ "is_directory": False,
18
+ "content_type": "application/asc",
19
+ "size": 100,
20
+ "status": "created",
21
+ "meta": {},
22
+ "sha256_digest": "sha256_digest",
23
+ }
24
+
25
+
26
+ def _mock_ic_asset_response(asset_id):
27
+ """Mock response for ion channel model asset."""
28
+ return {
29
+ "id": str(asset_id),
30
+ "path": "foo.mod",
31
+ "full_path": "foo.mod",
32
+ "is_directory": False,
33
+ "content_type": "application/neuron-mod",
34
+ "size": 100,
35
+ "status": "created",
36
+ "meta": {},
37
+ "sha256_digest": "sha256_digest",
38
+ }
39
+
40
+
41
+ def _mock_emodel_asset_response(asset_id):
42
+ """Mock response for emodel asset."""
43
+ return {
44
+ "id": str(asset_id),
45
+ "path": "foo.hoc",
46
+ "full_path": "foo.hoc",
47
+ "is_directory": False,
48
+ "content_type": "application/hoc",
49
+ "size": 100,
50
+ "status": "created",
51
+ "meta": {},
52
+ "sha256_digest": "sha256_digest",
53
+ }
54
+
55
+
56
+ def test_download_memodel(
57
+ tmp_path,
58
+ client,
59
+ httpx_mock,
60
+ api_url,
61
+ request_headers,
62
+ ):
63
+ """Test downloading all memodel-related files from an MEModel entity."""
64
+ memodel_id = uuid.uuid4()
65
+ morph_id = uuid.uuid4()
66
+ emodel_id = uuid.uuid4()
67
+ ic_model_id = uuid.uuid4()
68
+ morph_asset_id = uuid.uuid4()
69
+ emodel_asset_id = uuid.uuid4()
70
+ ic_asset_id = uuid.uuid4()
71
+ hierarchy_id = uuid.uuid4()
72
+
73
+ httpx_mock.add_response(
74
+ method="GET",
75
+ url=f"{api_url}/reconstruction-morphology/{morph_id}/assets/{morph_asset_id}",
76
+ match_headers=request_headers,
77
+ json=_mock_morph_asset_response(morph_id) | {"path": "foo.asc"},
78
+ )
79
+ httpx_mock.add_response(
80
+ method="GET",
81
+ url=f"{api_url}/reconstruction-morphology/{morph_id}/assets/{morph_asset_id}/download",
82
+ match_headers=request_headers,
83
+ content="foo",
84
+ )
85
+ httpx_mock.add_response(
86
+ method="GET",
87
+ url=f"{api_url}/emodel/{emodel_id}/assets/{emodel_asset_id}",
88
+ match_headers=request_headers,
89
+ json=_mock_emodel_asset_response(emodel_asset_id) | {"path": "foo.hoc"},
90
+ )
91
+ httpx_mock.add_response(
92
+ method="GET",
93
+ url=f"{api_url}/emodel/{emodel_id}/assets/{emodel_asset_id}/download",
94
+ match_headers=request_headers,
95
+ content="foo",
96
+ )
97
+ httpx_mock.add_response(
98
+ method="GET",
99
+ url=f"{api_url}/ion-channel-model/{ic_model_id}/assets/{ic_asset_id}",
100
+ match_headers=request_headers,
101
+ json=_mock_ic_asset_response(ic_asset_id) | {"path": "foo.mod"},
102
+ )
103
+ httpx_mock.add_response(
104
+ method="GET",
105
+ url=f"{api_url}/ion-channel-model/{ic_model_id}/assets/{ic_asset_id}/download",
106
+ match_headers=request_headers,
107
+ content="foo",
108
+ )
109
+
110
+ httpx_mock.add_response(
111
+ method="GET",
112
+ url=f"{api_url}/emodel/{emodel_id}",
113
+ match_headers=request_headers,
114
+ json={
115
+ "id": str(emodel_id),
116
+ "name": "foo",
117
+ "species": {"name": "foo", "taxonomy_id": "bar"},
118
+ "brain_region": {
119
+ "name": "foo",
120
+ "annotation_value": 997,
121
+ "acronym": "bar",
122
+ "parent_structure_id": None,
123
+ "hierarchy_id": str(hierarchy_id),
124
+ "color_hex_triplet": "#FFFFFF",
125
+ },
126
+ "iteration": "foofoo",
127
+ "score": 42,
128
+ "seed": 0,
129
+ "ion_channel_models": [
130
+ {
131
+ "id": str(ic_model_id),
132
+ "name": "foo",
133
+ "nmodl_suffix": "Ca_HVA",
134
+ "description": "foo description",
135
+ "species": {"name": "foo", "taxonomy_id": "bar"},
136
+ "brain_region": {
137
+ "name": "foo",
138
+ "annotation_value": 997,
139
+ "acronym": "bar",
140
+ "parent_structure_id": None,
141
+ "hierarchy_id": str(hierarchy_id),
142
+ "color_hex_triplet": "#FFFFFF",
143
+ },
144
+ "is_temperature_dependent": False,
145
+ "temperature_celsius": 34,
146
+ "neuron_block": {},
147
+ "assets": [_mock_ic_asset_response(ic_asset_id)],
148
+ }
149
+ ],
150
+ "assets": [_mock_emodel_asset_response(emodel_asset_id)],
151
+ },
152
+ )
153
+
154
+ memodel = MEModel(
155
+ id=memodel_id,
156
+ name="foo",
157
+ species={"name": "foo", "taxonomy_id": "bar"},
158
+ brain_region={
159
+ "name": "foo",
160
+ "annotation_value": 997,
161
+ "acronym": "bar",
162
+ "parent_structure_id": None,
163
+ "hierarchy_id": str(hierarchy_id),
164
+ "color_hex_triplet": "#FFFFFF",
165
+ },
166
+ validation_status=ValidationStatus.done,
167
+ morphology=ReconstructionMorphology(
168
+ id=morph_id, name="foo", assets=[_mock_morph_asset_response(morph_asset_id)]
169
+ ),
170
+ emodel=EModel(
171
+ id=emodel_id,
172
+ name="foo",
173
+ species={"name": "foo", "taxonomy_id": "bar"},
174
+ brain_region={
175
+ "name": "foo",
176
+ "annotation_value": 997,
177
+ "acronym": "bar",
178
+ "parent_structure_id": None,
179
+ "hierarchy_id": str(hierarchy_id),
180
+ "color_hex_triplet": "#FFFFFF",
181
+ },
182
+ iteration="foofoo",
183
+ score=42,
184
+ seed=0,
185
+ # assets=[
186
+ # _mock_emodel_asset_response(emodel_asset_id)
187
+ # ]
188
+ ),
189
+ )
190
+
191
+ downloaded_memodel = download_memodel(
192
+ client=client,
193
+ memodel=memodel,
194
+ output_dir=tmp_path,
195
+ )
196
+ assert downloaded_memodel.hoc_path.is_file()
197
+ assert downloaded_memodel.morphology_path.is_file()
198
+ assert downloaded_memodel.mechanisms_dir.is_dir()
199
+ assert len(os.listdir(downloaded_memodel.mechanisms_dir)) == 1
@@ -0,0 +1,68 @@
1
+ import uuid
2
+
3
+ import pytest
4
+
5
+ from entitysdk.downloaders.morphology import download_morphology
6
+ from entitysdk.exception import IteratorResultError
7
+ from entitysdk.models.morphology import ReconstructionMorphology
8
+
9
+
10
+ def _mock_asset_response(asset_id):
11
+ return {
12
+ "id": str(asset_id),
13
+ "path": "foo.asc",
14
+ "full_path": "foo.asc",
15
+ "is_directory": False,
16
+ "content_type": "application/asc",
17
+ "size": 100,
18
+ "status": "created",
19
+ "meta": {},
20
+ "sha256_digest": "sha256_digest",
21
+ }
22
+
23
+
24
+ def test_download_morphology(
25
+ tmp_path,
26
+ client,
27
+ httpx_mock,
28
+ api_url,
29
+ request_headers,
30
+ ):
31
+ """Test downloading a morphology file from a Morphology entity."""
32
+ morph_id = uuid.uuid4()
33
+ asset_id = uuid.uuid4()
34
+
35
+ httpx_mock.add_response(
36
+ method="GET",
37
+ url=f"{api_url}/reconstruction-morphology/{morph_id}/assets/{asset_id}",
38
+ match_headers=request_headers,
39
+ json=_mock_asset_response(asset_id) | {"path": "foo.asc"},
40
+ )
41
+ httpx_mock.add_response(
42
+ method="GET",
43
+ url=f"{api_url}/reconstruction-morphology/{morph_id}/assets/{asset_id}/download",
44
+ match_headers=request_headers,
45
+ content="foo",
46
+ )
47
+
48
+ morphology = ReconstructionMorphology(
49
+ id=morph_id, name="foo", assets=[_mock_asset_response(asset_id)]
50
+ )
51
+
52
+ output_path = download_morphology(
53
+ client=client,
54
+ morphology=morphology,
55
+ output_dir=tmp_path,
56
+ file_type="asc",
57
+ )
58
+
59
+ assert output_path.is_file()
60
+
61
+ # should raise when the file type is not present in the morphology assets
62
+ with pytest.raises(IteratorResultError, match="Iterable is empty."):
63
+ output_path = download_morphology(
64
+ client=client,
65
+ morphology=morphology,
66
+ output_dir=tmp_path,
67
+ file_type="swc",
68
+ )
@@ -0,0 +1,6 @@
1
+ from entitysdk.utils.filesystem import create_dir
2
+
3
+
4
+ def test_create_dir(tmp_path):
5
+ """Test creating a directory with create_dir function."""
6
+ assert create_dir(tmp_path / "test_dir").is_dir() is True
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes