datamint 2.2.1__tar.gz → 2.3.1__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.

Potentially problematic release.


This version of datamint might be problematic. Click here for more details.

Files changed (50) hide show
  1. {datamint-2.2.1 → datamint-2.3.1}/PKG-INFO +1 -1
  2. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/client.py +13 -5
  3. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/__init__.py +3 -1
  4. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/annotations_api.py +51 -5
  5. datamint-2.3.1/datamint/api/endpoints/models_api.py +47 -0
  6. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/project.py +3 -1
  7. {datamint-2.2.1 → datamint-2.3.1}/datamint/exceptions.py +22 -1
  8. {datamint-2.2.1 → datamint-2.3.1}/pyproject.toml +1 -1
  9. {datamint-2.2.1 → datamint-2.3.1}/README.md +0 -0
  10. {datamint-2.2.1 → datamint-2.3.1}/datamint/__init__.py +0 -0
  11. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/__init__.py +0 -0
  12. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/base_api.py +0 -0
  13. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/dto/__init__.py +0 -0
  14. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/channels_api.py +0 -0
  15. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/datasetsinfo_api.py +0 -0
  16. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/projects_api.py +0 -0
  17. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/resources_api.py +0 -0
  18. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/endpoints/users_api.py +0 -0
  19. {datamint-2.2.1 → datamint-2.3.1}/datamint/api/entity_base_api.py +0 -0
  20. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/annotation_api_handler.py +0 -0
  21. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/api_handler.py +0 -0
  22. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/base_api_handler.py +0 -0
  23. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/dto/__init__.py +0 -0
  24. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/dto/annotation_dto.py +0 -0
  25. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/exp_api_handler.py +0 -0
  26. {datamint-2.2.1 → datamint-2.3.1}/datamint/apihandler/root_api_handler.py +0 -0
  27. {datamint-2.2.1 → datamint-2.3.1}/datamint/client_cmd_tools/__init__.py +0 -0
  28. {datamint-2.2.1 → datamint-2.3.1}/datamint/client_cmd_tools/datamint_config.py +0 -0
  29. {datamint-2.2.1 → datamint-2.3.1}/datamint/client_cmd_tools/datamint_upload.py +0 -0
  30. {datamint-2.2.1 → datamint-2.3.1}/datamint/configs.py +0 -0
  31. {datamint-2.2.1 → datamint-2.3.1}/datamint/dataset/__init__.py +0 -0
  32. {datamint-2.2.1 → datamint-2.3.1}/datamint/dataset/annotation.py +0 -0
  33. {datamint-2.2.1 → datamint-2.3.1}/datamint/dataset/base_dataset.py +0 -0
  34. {datamint-2.2.1 → datamint-2.3.1}/datamint/dataset/dataset.py +0 -0
  35. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/__init__.py +0 -0
  36. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/annotation.py +0 -0
  37. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/base_entity.py +0 -0
  38. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/channel.py +0 -0
  39. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/datasetinfo.py +0 -0
  40. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/resource.py +0 -0
  41. {datamint-2.2.1 → datamint-2.3.1}/datamint/entities/user.py +0 -0
  42. {datamint-2.2.1 → datamint-2.3.1}/datamint/examples/__init__.py +0 -0
  43. {datamint-2.2.1 → datamint-2.3.1}/datamint/examples/example_projects.py +0 -0
  44. {datamint-2.2.1 → datamint-2.3.1}/datamint/experiment/__init__.py +0 -0
  45. {datamint-2.2.1 → datamint-2.3.1}/datamint/experiment/_patcher.py +0 -0
  46. {datamint-2.2.1 → datamint-2.3.1}/datamint/experiment/experiment.py +0 -0
  47. {datamint-2.2.1 → datamint-2.3.1}/datamint/logging.yaml +0 -0
  48. {datamint-2.2.1 → datamint-2.3.1}/datamint/utils/logging_utils.py +0 -0
  49. {datamint-2.2.1 → datamint-2.3.1}/datamint/utils/torchmetrics.py +0 -0
  50. {datamint-2.2.1 → datamint-2.3.1}/datamint/utils/visualization.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datamint
3
- Version: 2.2.1
3
+ Version: 2.3.1
4
4
  Summary: A library for interacting with the Datamint API, designed for efficient data management, processing and Deep Learning workflows.
5
5
  Requires-Python: >=3.10
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,10 +1,8 @@
1
1
  from typing import Optional
2
- import httpx
3
2
  from .base_api import ApiConfig
4
- from .endpoints import ProjectsApi, ResourcesApi, AnnotationsApi, ChannelsApi, UsersApi, DatasetsInfoApi
3
+ from .endpoints import ProjectsApi, ResourcesApi, AnnotationsApi, ChannelsApi, UsersApi, DatasetsInfoApi, ModelsApi
5
4
  import datamint.configs
6
5
  from datamint.exceptions import DatamintException
7
- import asyncio
8
6
 
9
7
 
10
8
  class Api:
@@ -18,7 +16,8 @@ class Api:
18
16
  'annotations': AnnotationsApi,
19
17
  'channels': ChannelsApi,
20
18
  'users': UsersApi,
21
- 'datasets': DatasetsInfoApi
19
+ 'datasets': DatasetsInfoApi,
20
+ 'models': ModelsApi
22
21
  }
23
22
 
24
23
  def __init__(self,
@@ -73,19 +72,28 @@ class Api:
73
72
  @property
74
73
  def projects(self) -> ProjectsApi:
75
74
  return self._get_endpoint('projects')
75
+
76
76
  @property
77
77
  def resources(self) -> ResourcesApi:
78
78
  return self._get_endpoint('resources')
79
+
79
80
  @property
80
81
  def annotations(self) -> AnnotationsApi:
81
82
  return self._get_endpoint('annotations')
83
+
82
84
  @property
83
85
  def channels(self) -> ChannelsApi:
84
86
  return self._get_endpoint('channels')
87
+
85
88
  @property
86
89
  def users(self) -> UsersApi:
87
90
  return self._get_endpoint('users')
91
+
88
92
  @property
89
93
  def _datasetsinfo(self) -> DatasetsInfoApi:
90
94
  """Internal property to access DatasetsInfoApi."""
91
- return self._get_endpoint('datasets')
95
+ return self._get_endpoint('datasets')
96
+
97
+ @property
98
+ def models(self) -> ModelsApi:
99
+ return self._get_endpoint('models')
@@ -6,6 +6,7 @@ from .projects_api import ProjectsApi
6
6
  from .resources_api import ResourcesApi
7
7
  from .users_api import UsersApi
8
8
  from .datasetsinfo_api import DatasetsInfoApi
9
+ from .models_api import ModelsApi
9
10
 
10
11
  __all__ = [
11
12
  'AnnotationsApi',
@@ -13,5 +14,6 @@ __all__ = [
13
14
  'ProjectsApi',
14
15
  'ResourcesApi',
15
16
  'UsersApi',
16
- 'DatasetsInfoApi'
17
+ 'DatasetsInfoApi',
18
+ 'ModelsApi',
17
19
  ]
@@ -3,6 +3,7 @@ import httpx
3
3
  from datetime import date
4
4
  import logging
5
5
  from ..entity_base_api import ApiConfig, CreatableEntityApi, DeletableEntityApi
6
+ from .models_api import ModelsApi
6
7
  from datamint.entities.annotation import Annotation
7
8
  from datamint.entities.resource import Resource
8
9
  from datamint.entities.project import Project
@@ -38,6 +39,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
38
39
  client: Optional HTTP client instance. If None, a new one will be created.
39
40
  """
40
41
  super().__init__(config, Annotation, 'annotations', client)
42
+ self._models_api = ModelsApi(config, client=client)
41
43
 
42
44
  def get_list(self,
43
45
  resource: str | Resource | None = None,
@@ -69,7 +71,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
69
71
 
70
72
  async def _upload_segmentations_async(self,
71
73
  resource: str | Resource,
72
- frame_index: int | Sequence [int] | None,
74
+ frame_index: int | Sequence[int] | None,
73
75
  file_path: str | np.ndarray,
74
76
  name: dict[int, str] | dict[tuple, str],
75
77
  imported_from: str | None = None,
@@ -78,7 +80,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
78
80
  worklist_id: str | None = None,
79
81
  model_id: str | None = None,
80
82
  transpose_segmentation: bool = False,
81
- upload_volume: bool | str = 'auto'
83
+ upload_volume: bool | str = 'auto',
82
84
  ) -> Sequence[str]:
83
85
  """
84
86
  Upload segmentations asynchronously.
@@ -397,6 +399,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
397
399
  worklist_id: str | None = None,
398
400
  model_id: str | None = None,
399
401
  transpose_segmentation: bool = False,
402
+ ai_model_name: str | None = None
400
403
  ) -> list[str]:
401
404
  """
402
405
  Upload segmentations to a resource.
@@ -425,6 +428,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
425
428
  worklist_id: The annotation worklist unique id.
426
429
  model_id: The model unique id.
427
430
  transpose_segmentation: Whether to transpose the segmentation or not.
431
+ ai_model_name: Optional AI model name to associate with the segmentation.
428
432
 
429
433
  Returns:
430
434
  List of segmentation unique ids.
@@ -453,6 +457,18 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
453
457
  if isinstance(file_path, str) and not os.path.exists(file_path):
454
458
  raise FileNotFoundError(f"File {file_path} not found.")
455
459
 
460
+ if ai_model_name is not None:
461
+ model_id = self._models_api.get_by_name(ai_model_name)
462
+ if model_id is None:
463
+ try:
464
+ available_models = [model['name'] for model in self._models_api.get_all()]
465
+ except Exception:
466
+ _LOGGER.warning("Could not fetch available AI models from the server.")
467
+ raise ValueError(f"AI model with name '{ai_model_name}' not found. ")
468
+ raise ValueError(f"AI model with name '{ai_model_name}' not found. " +
469
+ f"Available models: {available_models}")
470
+ model_id = model_id['id']
471
+
456
472
  # Handle NIfTI files specially - upload as single volume
457
473
  if isinstance(file_path, str) and (file_path.endswith('.nii') or file_path.endswith('.nii.gz')):
458
474
  _LOGGER.info(f"Uploading NIfTI segmentation file: {file_path}")
@@ -472,7 +488,7 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
472
488
  worklist_id=worklist_id,
473
489
  model_id=model_id,
474
490
  transpose_segmentation=transpose_segmentation,
475
- upload_volume=True
491
+ upload_volume=True,
476
492
  )
477
493
  return loop.run_until_complete(task)
478
494
 
@@ -486,8 +502,8 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
486
502
  raise ValueError("frame_index list contains duplicate values.")
487
503
 
488
504
  if isinstance(frame_index, Sequence) and len(frame_index) == 1:
489
- frame_index = frame_index[0]
490
-
505
+ frame_index = frame_index[0]
506
+
491
507
  nest_asyncio.apply()
492
508
  loop = asyncio.get_event_loop()
493
509
  task = self._upload_segmentations_async(
@@ -982,3 +998,33 @@ class AnnotationsApi(CreatableEntityApi[Annotation], DeletableEntityApi[Annotati
982
998
  ) -> None:
983
999
  """Alias for :py:meth:`download_multiple_files`"""
984
1000
  return self.download_multiple_files(annotations, save_paths)
1001
+
1002
+ def patch(self,
1003
+ annotation: str | Annotation,
1004
+ identifier: str) -> None:
1005
+ """
1006
+ Update the project assignment for an annotation.
1007
+
1008
+ Args:
1009
+ annotation: The annotation unique id or Annotation instance.
1010
+ identifier: The new identifier/label for the annotation.
1011
+
1012
+ Raises:
1013
+ DatamintException: If the update fails.
1014
+ """
1015
+ annotation_id = self._entid(annotation)
1016
+
1017
+ payload = {'identifier': identifier}
1018
+ # remove None values
1019
+ payload = {k: v for k, v in payload.items() if v is not None}
1020
+ if len(payload) == 0:
1021
+ _LOGGER.info("No fields to update for annotation patch.")
1022
+ return
1023
+
1024
+ resp = self._make_request('PATCH',
1025
+ f'{self.endpoint_base}/{annotation_id}',
1026
+ json=payload)
1027
+
1028
+ respdata = resp.json()
1029
+ if isinstance(respdata, dict) and 'error' in respdata:
1030
+ raise DatamintException(respdata['error'])
@@ -0,0 +1,47 @@
1
+ from typing import Sequence
2
+ from ..entity_base_api import ApiConfig, BaseApi
3
+ import httpx
4
+ from datamint.exceptions import EntityAlreadyExistsError
5
+
6
+
7
+ class ModelsApi(BaseApi):
8
+ """API handler for project-related endpoints."""
9
+
10
+ def __init__(self,
11
+ config: ApiConfig,
12
+ client: httpx.Client | None = None) -> None:
13
+ """Initialize the projects API handler.
14
+
15
+ Args:
16
+ config: API configuration containing base URL, API key, etc.
17
+ client: Optional HTTP client instance. If None, a new one will be created.
18
+ """
19
+ super().__init__(config, client=client)
20
+
21
+ def create(self,
22
+ name: str) -> dict:
23
+ json = {
24
+ 'name': name
25
+ }
26
+
27
+ try:
28
+ response = self._make_request('POST',
29
+ 'ai-models',
30
+ json=json)
31
+ return response.json()
32
+ except httpx.HTTPStatusError as e:
33
+ if e.response.status_code == 409:
34
+ raise EntityAlreadyExistsError('ai-model', {'name': name})
35
+ raise
36
+
37
+ def get_all(self) -> Sequence[dict]:
38
+ response = self._make_request('GET',
39
+ 'ai-models')
40
+ return response.json()
41
+
42
+ def get_by_name(self, name: str) -> dict | None:
43
+ models = self.get_all()
44
+ for model in models:
45
+ if model['name'] == name:
46
+ return model
47
+ return None
@@ -3,6 +3,7 @@
3
3
  from datetime import datetime
4
4
  import logging
5
5
  from .base_entity import BaseEntity, MISSING_FIELD
6
+ from typing import Any
6
7
 
7
8
  logger = logging.getLogger(__name__)
8
9
 
@@ -43,9 +44,10 @@ class Project(BaseEntity):
43
44
  resource_count: int
44
45
  annotated_resource_count: int
45
46
  description: str | None
46
- ai_model_id: str | None
47
47
  viewable_ai_segs: list | None
48
48
  editable_ai_segs: list | None
49
+ registered_model: Any | None = MISSING_FIELD
50
+ ai_model_id: str | None = MISSING_FIELD
49
51
  closed_resources_count: int = MISSING_FIELD
50
52
  resources_to_annotate_count: int = MISSING_FIELD
51
53
  most_recent_experiment: str | None = MISSING_FIELD # ISO timestamp string
@@ -28,4 +28,25 @@ class ResourceNotFoundError(DatamintException):
28
28
  self.params = params
29
29
 
30
30
  def __str__(self):
31
- return f"Resource '{self.resource_type}' not found for parameters: {self.params}"
31
+ return f"Resource '{self.resource_type}' not found for parameters: {self.params}"
32
+
33
+ # Already existing (e.g, creating a project with a name that already exists)
34
+ class EntityAlreadyExistsError(DatamintException):
35
+ """
36
+ Exception raised when trying to create an entity that already exists.
37
+ For instance, when creating a project with a name that already exists.
38
+ """
39
+
40
+ def __init__(self, entity_type: str, params: dict):
41
+ """Constructor.
42
+
43
+ Args:
44
+ entity_type: The type of entity that already exists.
45
+ params: Dict of params identifying the existing entity.
46
+ """
47
+ super().__init__()
48
+ self.entity_type = entity_type
49
+ self.params = params
50
+
51
+ def __str__(self) -> str:
52
+ return f"Entity '{self.entity_type}' already exists for parameters: {self.params}"
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "datamint"
3
3
  description = "A library for interacting with the Datamint API, designed for efficient data management, processing and Deep Learning workflows."
4
- version = "2.2.1"
4
+ version = "2.3.1"
5
5
  dynamic = ["dependencies"]
6
6
  requires-python = ">=3.10"
7
7
  readme = "README.md"
File without changes
File without changes
File without changes
File without changes