entitysdk 0.6.1__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.
Files changed (149) hide show
  1. {entitysdk-0.6.1/src/entitysdk.egg-info → entitysdk-0.7.0}/PKG-INFO +1 -1
  2. {entitysdk-0.6.1 → entitysdk-0.7.0}/examples/02_morphology.ipynb +18 -2
  3. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/client.py +35 -21
  4. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/core.py +20 -7
  5. entitysdk-0.7.0/src/entitysdk/dependencies/__init__.py +1 -0
  6. entitysdk-0.7.0/src/entitysdk/dependencies/entity.py +18 -0
  7. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/downloaders/emodel.py +1 -1
  8. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/downloaders/ion_channel_model.py +1 -1
  9. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/downloaders/morphology.py +1 -1
  10. entitysdk-0.7.0/src/entitysdk/downloaders/simulation.py +78 -0
  11. entitysdk-0.7.0/src/entitysdk/downloaders/simulation_result.py +61 -0
  12. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/exception.py +8 -0
  13. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/asset.py +4 -0
  14. entitysdk-0.7.0/src/entitysdk/schemas/asset.py +20 -0
  15. entitysdk-0.7.0/src/entitysdk/staging/__init__.py +7 -0
  16. entitysdk-0.7.0/src/entitysdk/staging/circuit.py +43 -0
  17. entitysdk-0.7.0/src/entitysdk/staging/simulation.py +142 -0
  18. entitysdk-0.7.0/src/entitysdk/staging/simulation_result.py +72 -0
  19. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/types.py +6 -0
  20. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/util.py +3 -0
  21. entitysdk-0.7.0/src/entitysdk/utils/io.py +16 -0
  22. {entitysdk-0.6.1 → entitysdk-0.7.0/src/entitysdk.egg-info}/PKG-INFO +1 -1
  23. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk.egg-info/SOURCES.txt +24 -0
  24. entitysdk-0.7.0/tests/unit/dependencies/test_entity.py +27 -0
  25. entitysdk-0.7.0/tests/unit/models/data/.gitignore +0 -0
  26. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_asset.py +1 -0
  27. entitysdk-0.7.0/tests/unit/staging/__init__.py +0 -0
  28. entitysdk-0.7.0/tests/unit/staging/conftest.py +281 -0
  29. entitysdk-0.7.0/tests/unit/staging/data/SomaVoltRec 1.h5 +0 -0
  30. entitysdk-0.7.0/tests/unit/staging/data/SomaVoltRec 2.h5 +0 -0
  31. entitysdk-0.7.0/tests/unit/staging/data/circuit/circuit_config.json +36 -0
  32. entitysdk-0.7.0/tests/unit/staging/data/circuit/edges.h5 +0 -0
  33. entitysdk-0.7.0/tests/unit/staging/data/circuit/nodes.h5 +0 -0
  34. entitysdk-0.7.0/tests/unit/staging/data/node_sets.json +67 -0
  35. entitysdk-0.7.0/tests/unit/staging/data/simulation_config.json +128 -0
  36. entitysdk-0.7.0/tests/unit/staging/data/spike_replays.h5 +0 -0
  37. entitysdk-0.7.0/tests/unit/staging/data/spikes.h5 +0 -0
  38. entitysdk-0.7.0/tests/unit/staging/test_simulation.py +98 -0
  39. entitysdk-0.7.0/tests/unit/staging/test_simulation_result.py +106 -0
  40. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_client.py +106 -3
  41. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/utils/test_asset.py +5 -0
  42. entitysdk-0.6.1/src/entitysdk/schemas/asset.py +0 -13
  43. {entitysdk-0.6.1 → entitysdk-0.7.0}/.github/workflows/sdist.yml +0 -0
  44. {entitysdk-0.6.1 → entitysdk-0.7.0}/.github/workflows/tox.yml +0 -0
  45. {entitysdk-0.6.1 → entitysdk-0.7.0}/.gitignore +0 -0
  46. {entitysdk-0.6.1 → entitysdk-0.7.0}/CHANGELOG.rst +0 -0
  47. {entitysdk-0.6.1 → entitysdk-0.7.0}/CONTRIBUTING.md +0 -0
  48. {entitysdk-0.6.1 → entitysdk-0.7.0}/LICENSE.txt +0 -0
  49. {entitysdk-0.6.1 → entitysdk-0.7.0}/README.md +0 -0
  50. {entitysdk-0.6.1 → entitysdk-0.7.0}/examples/01_searching.ipynb +0 -0
  51. {entitysdk-0.6.1 → entitysdk-0.7.0}/examples/03_circuit.ipynb +0 -0
  52. {entitysdk-0.6.1 → entitysdk-0.7.0}/examples/04_simulation_campaign.ipynb +0 -0
  53. {entitysdk-0.6.1 → entitysdk-0.7.0}/examples/utils.py +0 -0
  54. {entitysdk-0.6.1 → entitysdk-0.7.0}/pyproject.toml +0 -0
  55. {entitysdk-0.6.1 → entitysdk-0.7.0}/setup.cfg +0 -0
  56. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/__init__.py +0 -0
  57. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/common.py +0 -0
  58. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/config.py +0 -0
  59. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/downloaders/__init__.py +0 -0
  60. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/downloaders/memodel.py +0 -0
  61. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/mixin.py +0 -0
  62. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/__init__.py +0 -0
  63. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/activity.py +0 -0
  64. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/agent.py +0 -0
  65. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/base.py +0 -0
  66. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/brain_location.py +0 -0
  67. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/brain_region.py +0 -0
  68. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/brain_region_hierarchy.py +0 -0
  69. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/circuit.py +0 -0
  70. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/classification.py +0 -0
  71. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/contribution.py +0 -0
  72. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/core.py +0 -0
  73. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/electrical_cell_recording.py +0 -0
  74. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/emodel.py +0 -0
  75. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/entity.py +0 -0
  76. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/etype.py +0 -0
  77. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/ion_channel_model.py +0 -0
  78. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/license.py +0 -0
  79. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/memodel.py +0 -0
  80. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/memodelcalibrationresult.py +0 -0
  81. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/morphology.py +0 -0
  82. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/mtype.py +0 -0
  83. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/response.py +0 -0
  84. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/scientific_artifact.py +0 -0
  85. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/simulation.py +0 -0
  86. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/simulation_campaign.py +0 -0
  87. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/simulation_execution.py +0 -0
  88. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/simulation_generation.py +0 -0
  89. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/simulation_result.py +0 -0
  90. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/single_neuron_simulation.py +0 -0
  91. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/single_neuron_synaptome_simulation.py +0 -0
  92. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/subject.py +0 -0
  93. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/synaptome.py +0 -0
  94. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/taxonomy.py +0 -0
  95. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/models/validation_result.py +0 -0
  96. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/result.py +0 -0
  97. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/route.py +0 -0
  98. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/schemas/__init__.py +0 -0
  99. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/schemas/base.py +0 -0
  100. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/schemas/memodel.py +0 -0
  101. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/serdes.py +0 -0
  102. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/token_manager.py +0 -0
  103. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/utils/__init__.py +0 -0
  104. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/utils/asset.py +0 -0
  105. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk/utils/filesystem.py +0 -0
  106. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk.egg-info/dependency_links.txt +0 -0
  107. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk.egg-info/requires.txt +0 -0
  108. {entitysdk-0.6.1 → entitysdk-0.7.0}/src/entitysdk.egg-info/top_level.txt +0 -0
  109. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/__init__.py +0 -0
  110. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/integration/__init__.py +0 -0
  111. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/integration/conftest.py +0 -0
  112. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/integration/test_searching.py +0 -0
  113. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/__init__.py +0 -0
  114. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/conftest.py +0 -0
  115. {entitysdk-0.6.1/tests/unit/models → entitysdk-0.7.0/tests/unit/dependencies}/__init__.py +0 -0
  116. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/downloaders/test_emodel.py +0 -0
  117. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/downloaders/test_ion_channel_model.py +0 -0
  118. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/downloaders/test_memodel.py +0 -0
  119. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/downloaders/test_morphology.py +0 -0
  120. /entitysdk-0.6.1/tests/unit/models/data/.gitignore → /entitysdk-0.7.0/tests/unit/models/__init__.py +0 -0
  121. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/circuit.json +0 -0
  122. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/electrical_cell_recording.json +0 -0
  123. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/ion_channel_model.json +0 -0
  124. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/memodel_calibration_result.json +0 -0
  125. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/reconstruction_morphology.json +0 -0
  126. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/simulation_campaign.json +0 -0
  127. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/data/validation_result.json +0 -0
  128. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_agent.py +0 -0
  129. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_brain_region.py +0 -0
  130. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_circuit.py +0 -0
  131. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_contribution.py +0 -0
  132. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_electrical_cell_recording.py +0 -0
  133. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_init.py +0 -0
  134. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_ion_channel_model.py +0 -0
  135. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_memodel_calibration_result.py +0 -0
  136. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_morphology.py +0 -0
  137. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_simulation_campaign.py +0 -0
  138. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/models/test_validation_result.py +0 -0
  139. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_base.py +0 -0
  140. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_common.py +0 -0
  141. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_config.py +0 -0
  142. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_result.py +0 -0
  143. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_route.py +0 -0
  144. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_serdes.py +0 -0
  145. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_token_manager.py +0 -0
  146. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/test_util.py +0 -0
  147. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/util.py +0 -0
  148. {entitysdk-0.6.1 → entitysdk-0.7.0}/tests/unit/utils/test_filesystem.py +0 -0
  149. {entitysdk-0.6.1 → 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.6.1
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>
@@ -276,7 +276,8 @@
276
276
  " entity_id=registered.id,\n",
277
277
  " entity_type=ReconstructionMorphology,\n",
278
278
  " file_path=file1,\n",
279
- " file_content_type=\"application/h5\",\n",
279
+ " file_content_type=\"application/x-hdf5\",\n",
280
+ " asset_label=\"hdf5\",\n",
280
281
  " )\n",
281
282
  " rprint(asset1)\n",
282
283
  "\n",
@@ -289,6 +290,7 @@
289
290
  " file_content=buffer,\n",
290
291
  " file_name=\"buffer.h5\",\n",
291
292
  " file_content_type=\"application/swc\",\n",
293
+ " asset_label=\"swc\",\n",
292
294
  " )\n",
293
295
  " rprint(asset2)"
294
296
  ]
@@ -408,8 +410,22 @@
408
410
  }
409
411
  ],
410
412
  "metadata": {
413
+ "kernelspec": {
414
+ "display_name": "Python 3 (ipykernel)",
415
+ "language": "python",
416
+ "name": "python3"
417
+ },
411
418
  "language_info": {
412
- "name": "python"
419
+ "codemirror_mode": {
420
+ "name": "ipython",
421
+ "version": 3
422
+ },
423
+ "file_extension": ".py",
424
+ "mimetype": "text/x-python",
425
+ "name": "python",
426
+ "nbconvert_exporter": "python",
427
+ "pygments_lexer": "ipython3",
428
+ "version": "3.12.10"
413
429
  }
414
430
  },
415
431
  "nbformat": 4,
@@ -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 DownloadedAsset
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[Identifiable],
99
+ entity_type: type[TEntity],
98
100
  project_context: ProjectContext | None = None,
99
- ) -> Identifiable:
101
+ ) -> TEntity:
100
102
  """Get entity from resource id.
101
103
 
102
104
  Args:
@@ -214,6 +216,7 @@ class Client:
214
216
  file_content_type: str,
215
217
  file_name: str | None = None,
216
218
  file_metadata: dict | None = None,
219
+ asset_label: str | None = None,
217
220
  project_context: ProjectContext | None = None,
218
221
  ) -> Asset:
219
222
  """Upload asset to an existing entity's endpoint from a file path."""
@@ -229,6 +232,7 @@ class Client:
229
232
  file_name=file_name or path.name,
230
233
  content_type=file_content_type,
231
234
  metadata=file_metadata,
235
+ label=asset_label,
232
236
  )
233
237
  return core.upload_asset_file(
234
238
  url=url,
@@ -248,6 +252,7 @@ class Client:
248
252
  file_name: str,
249
253
  file_content_type: str,
250
254
  file_metadata: dict | None = None,
255
+ asset_label: str | None = None,
251
256
  project_context: ProjectContext | None = None,
252
257
  ) -> Asset:
253
258
  """Upload asset to an existing entity's endpoint from a file-like object."""
@@ -261,6 +266,7 @@ class Client:
261
266
  file_name=file_name,
262
267
  content_type=file_content_type,
263
268
  metadata=file_metadata or {},
269
+ label=asset_label,
264
270
  )
265
271
  context = self._required_user_context(override_context=project_context)
266
272
  return core.upload_asset_content(
@@ -353,28 +359,30 @@ class Client:
353
359
 
354
360
  context = self._optional_user_context(override_context=project_context)
355
361
 
356
- asset = None
362
+ asset = cast(Asset, asset_id) if isinstance(asset_id, Asset) else None
363
+
357
364
  if not ignore_directory_name:
358
- asset_endpoint = route.get_assets_endpoint(
359
- api_url=self.api_url,
360
- entity_type=entity_type,
361
- entity_id=entity_id,
362
- asset_id=asset_id,
363
- )
364
- asset = core.get_entity(
365
- asset_endpoint,
366
- entity_type=Asset,
367
- project_context=context,
368
- http_client=self._http_client,
369
- token=self._token_manager.get_token(),
370
- )
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
+ )
371
379
 
372
380
  output_path /= asset.path
373
381
 
374
382
  contents = self.list_directory(
375
383
  entity_id=entity_id,
376
384
  entity_type=entity_type,
377
- asset_id=asset_id,
385
+ asset_id=asset_id if isinstance(asset_id, ID) else asset.id,
378
386
  project_context=project_context,
379
387
  )
380
388
 
@@ -453,6 +461,7 @@ class Client:
453
461
  Output file path.
454
462
  """
455
463
  context = self._optional_user_context(override_context=project_context)
464
+
456
465
  asset_endpoint = route.get_assets_endpoint(
457
466
  api_url=self.api_url,
458
467
  entity_type=entity_type,
@@ -494,6 +503,11 @@ class Client:
494
503
  token=self._token_manager.get_token(),
495
504
  )
496
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
+
497
511
  def download_assets(
498
512
  self,
499
513
  entity_or_id: Entity | tuple[ID, type[Entity]],
@@ -516,9 +530,9 @@ class Client:
516
530
  project_context=context,
517
531
  )
518
532
 
519
- return DownloadedAsset(
533
+ return DownloadedAssetFile(
520
534
  asset=asset,
521
- output_path=path,
535
+ path=path,
522
536
  )
523
537
 
524
538
  context = self._optional_user_context(override_context=project_context)
@@ -1,6 +1,7 @@
1
1
  """Core SDK operations."""
2
2
 
3
3
  import io
4
+ import logging
4
5
  import os
5
6
  from collections.abc import Iterator
6
7
  from pathlib import Path
@@ -10,11 +11,13 @@ import httpx
10
11
 
11
12
  from entitysdk import serdes
12
13
  from entitysdk.common import ProjectContext
14
+ from entitysdk.exception import EntitySDKError
13
15
  from entitysdk.models.asset import Asset, DetailedFileList, LocalAssetMetadata
14
16
  from entitysdk.models.core import Identifiable
15
17
  from entitysdk.result import IteratorResult
16
18
  from entitysdk.util import make_db_api_request, stream_paginated_request
17
19
 
20
+ L = logging.getLogger(__name__)
18
21
  TIdentifiable = TypeVar("TIdentifiable", bound=Identifiable)
19
22
 
20
23
 
@@ -170,6 +173,7 @@ def upload_asset_content(
170
173
  url=url,
171
174
  method="POST",
172
175
  files=files,
176
+ data={"label": asset_metadata.label} if asset_metadata.label else None,
173
177
  project_context=project_context,
174
178
  token=token,
175
179
  http_client=http_client,
@@ -192,7 +196,7 @@ def upload_asset_directory(
192
196
  for concrete_path in paths.values():
193
197
  if not concrete_path.exists():
194
198
  msg = f"Path {concrete_path} does not exist"
195
- raise Exception(msg)
199
+ raise EntitySDKError(msg)
196
200
 
197
201
  response = make_db_api_request(
198
202
  url=url,
@@ -214,11 +218,20 @@ def upload_asset_directory(
214
218
  failed = {}
215
219
  for path, url in to_upload.items():
216
220
  with open(paths[Path(path)], "rb") as fd:
217
- response = http_client.request(
218
- method="PUT", url=url, content=fd, follow_redirects=True
219
- )
220
- if response.status_code != 200:
221
- failed[path] = url
221
+ try:
222
+ response = http_client.request(
223
+ method="PUT",
224
+ url=url,
225
+ content=fd,
226
+ follow_redirects=True,
227
+ timeout=20,
228
+ )
229
+ except httpx.HTTPError:
230
+ L.exception("Upload failed, will retry again")
231
+ failed[path] = url
232
+ else:
233
+ if response.status_code != 200:
234
+ failed[path] = url
222
235
  return failed
223
236
 
224
237
  to_upload = js["files"]
@@ -228,7 +241,7 @@ def upload_asset_directory(
228
241
  break
229
242
 
230
243
  if to_upload:
231
- raise Exception(f"Uploading these files failed: {to_upload}")
244
+ raise EntitySDKError(f"Uploading these files failed: {to_upload}")
232
245
 
233
246
  return serdes.deserialize_model(js["asset"], Asset)
234
247
 
@@ -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
@@ -26,4 +26,4 @@ def download_hoc(
26
26
  output_path=output_dir,
27
27
  ).one()
28
28
 
29
- return asset.output_path
29
+ return asset.path
@@ -26,4 +26,4 @@ def download_ion_channel_mechanism(
26
26
  output_path=output_dir,
27
27
  ).one()
28
28
 
29
- return asset.output_path
29
+ return asset.path
@@ -33,4 +33,4 @@ def download_morphology(
33
33
  output_path=output_dir,
34
34
  ).one()
35
35
 
36
- return asset.output_path
36
+ return asset.path
@@ -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(
@@ -93,6 +96,7 @@ class LocalAssetMetadata(BaseModel):
93
96
  description="The metadata of the asset.",
94
97
  ),
95
98
  ] = None
99
+ label: Annotated[str | None, Field(description="Optional asset label.")] = None
96
100
 
97
101
 
98
102
  class DetailedFile(BaseModel):
@@ -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