datamint 2.3.1__py3-none-any.whl → 2.3.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,19 +1,33 @@
1
1
  """Resource entity module for DataMint API."""
2
2
 
3
3
  from datetime import datetime
4
- from typing import Optional, Any
4
+ from typing import TYPE_CHECKING, Optional, Any, Sequence
5
5
  import logging
6
+
6
7
  from .base_entity import BaseEntity, MISSING_FIELD
7
- from pydantic import Field
8
+ from .cache_manager import CacheManager
9
+ from pydantic import PrivateAttr
10
+ from datamint.api.dto import AnnotationType
11
+ import webbrowser
12
+ from datamint.types import ImagingData
13
+
14
+ if TYPE_CHECKING:
15
+ from datamint.api.endpoints.resources_api import ResourcesApi
16
+ from .project import Project
17
+ from .annotation import Annotation
8
18
 
9
19
  logger = logging.getLogger(__name__)
10
20
 
21
+
22
+ _IMAGE_CACHEKEY = "image_data"
23
+
24
+
11
25
  class Resource(BaseEntity):
12
26
  """Represents a DataMint resource with all its properties and metadata.
13
-
27
+
14
28
  This class models a resource entity from the DataMint API, containing
15
29
  information about uploaded files, their metadata, and associated projects.
16
-
30
+
17
31
  Attributes:
18
32
  id: Unique identifier for the resource
19
33
  resource_uri: URI path to access the resource file
@@ -84,42 +98,145 @@ class Resource(BaseEntity):
84
98
  categories: Optional[Any] = None # TODO: Define proper type when spec available
85
99
  user_info: Optional[dict] = None
86
100
 
101
+ _api: 'ResourcesApi' = PrivateAttr()
102
+
103
+ def __init__(self, **data):
104
+ """Initialize the resource entity."""
105
+ super().__init__(**data)
106
+ self._cache: CacheManager[bytes] = CacheManager[bytes]('resources')
107
+
108
+ def fetch_file_data(
109
+ self,
110
+ auto_convert: bool = True,
111
+ save_path: str | None = None,
112
+ use_cache: bool = False,
113
+ ) -> bytes | ImagingData:
114
+ """Get the file data for this resource.
115
+
116
+ This method automatically caches the file data locally. On subsequent
117
+ calls, it checks the server for changes and uses cached data if unchanged.
118
+
119
+ Args:
120
+ use_cache: If True, uses cached data when available and valid
121
+ auto_convert: If True, automatically converts to appropriate format (pydicom.Dataset, PIL Image, etc.)
122
+ save_path: Optional path to save the file locally
123
+
124
+ Returns:
125
+ File data (format depends on auto_convert and file type)
126
+ """
127
+ # Version info for cache validation
128
+ version_info = self._generate_version_info()
129
+
130
+ # Try to get from cache
131
+ img_data = None
132
+ if use_cache:
133
+ img_data = self._cache.get(self.id, _IMAGE_CACHEKEY, version_info)
134
+ if img_data is not None:
135
+ logger.debug(f"Using cached image data for resource {self.id}")
136
+
137
+ if img_data is None:
138
+ # Fetch from server using download_resource_file
139
+ logger.debug(f"Fetching image data from server for resource {self.id}")
140
+ img_data = self._api.download_resource_file(
141
+ self,
142
+ save_path=save_path,
143
+ auto_convert=False
144
+ )
145
+ # Cache the data
146
+ if use_cache:
147
+ self._cache.set(self.id, _IMAGE_CACHEKEY, img_data, version_info)
148
+
149
+ if auto_convert:
150
+ try:
151
+ mimetype, ext = self._api._determine_mimetype(img_data, self)
152
+ img_data = self._api.convert_format(img_data,
153
+ mimetype=mimetype,
154
+ file_path=save_path)
155
+ except Exception as e:
156
+ logger.error(f"Failed to auto-convert resource {self.id}: {e}")
157
+
158
+ return img_data
159
+
160
+ def _generate_version_info(self) -> dict:
161
+ """Helper to generate version info for caching."""
162
+ return {
163
+ 'created_at': self.created_at,
164
+ 'deleted_at': self.deleted_at,
165
+ 'size': self.size,
166
+ }
167
+
168
+ def _save_into_cache(self, data: bytes) -> None:
169
+ """Helper to save raw data into cache."""
170
+ version_info = self._generate_version_info()
171
+ self._cache.set(self.id, _IMAGE_CACHEKEY, data, version_info)
172
+
173
+ def fetch_annotations(
174
+ self,
175
+ annotation_type: AnnotationType | str | None = None
176
+ ) -> Sequence['Annotation']:
177
+ """Get annotations associated with this resource."""
178
+
179
+ annotations = self._api.get_annotations(self)
180
+
181
+ if annotation_type:
182
+ annotation_type = AnnotationType(annotation_type)
183
+ annotations = [a for a in annotations if a.annotation_type == annotation_type]
184
+ return annotations
185
+
186
+ # def get_projects(
187
+ # self,
188
+ # ) -> Sequence['Project']:
189
+ # """Get all projects this resource belongs to.
190
+
191
+ # Returns:
192
+ # List of Project instances
193
+ # """
194
+ # return self._api.get_projects(self)
195
+
196
+
197
+ def invalidate_cache(self) -> None:
198
+ """Invalidate cached data for this resource.
199
+ """
200
+ # Invalidate all
201
+ self._cache.invalidate(self.id)
202
+ logger.debug(f"Invalidated all cache for resource {self.id}")
203
+
87
204
  @property
88
205
  def size_mb(self) -> float:
89
206
  """Get file size in megabytes.
90
-
207
+
91
208
  Returns:
92
209
  File size in MB rounded to 2 decimal places
93
210
  """
94
211
  return round(self.size / (1024 * 1024), 2)
95
-
212
+
96
213
  def is_dicom(self) -> bool:
97
214
  """Check if the resource is a DICOM file.
98
-
215
+
99
216
  Returns:
100
217
  True if the resource is a DICOM file, False otherwise
101
218
  """
102
219
  return self.mimetype == 'application/dicom' or self.storage == 'DicomResource'
103
-
104
- def get_project_names(self) -> list[str]:
105
- """Get list of project names this resource belongs to.
106
-
107
- Returns:
108
- List of project names
109
- """
110
- return [proj['name'] for proj in self.projects]
111
-
220
+
221
+ # def get_project_names(self) -> list[str]:
222
+ # """Get list of project names this resource belongs to.
223
+
224
+ # Returns:
225
+ # List of project names
226
+ # """
227
+ # return [proj['name'] for proj in self.projects] if self.projects != MISSING_FIELD else []
228
+
112
229
  def __str__(self) -> str:
113
230
  """String representation of the resource.
114
-
231
+
115
232
  Returns:
116
233
  Human-readable string describing the resource
117
234
  """
118
235
  return f"Resource(id='{self.id}', filename='{self.filename}', size={self.size_mb}MB)"
119
-
236
+
120
237
  def __repr__(self) -> str:
121
238
  """Detailed string representation of the resource.
122
-
239
+
123
240
  Returns:
124
241
  Detailed string representation for debugging
125
242
  """
@@ -128,3 +245,13 @@ class Resource(BaseEntity):
128
245
  f"modality='{self.modality}', status='{self.status}', "
129
246
  f"published={self.published})"
130
247
  )
248
+
249
+ @property
250
+ def url(self) -> str:
251
+ """Get the URL to access this resource in the DataMint web application."""
252
+ base_url = self._api.config.web_app_url
253
+ return f'{base_url}/resource/{self.id}'
254
+
255
+ def show(self) -> None:
256
+ """Open the resource in the default web browser."""
257
+ webbrowser.open(self.url)
datamint/types.py ADDED
@@ -0,0 +1,17 @@
1
+ from typing import TypeAlias, TYPE_CHECKING, Union
2
+
3
+ if TYPE_CHECKING:
4
+ import pydicom.dataset
5
+ from PIL import Image
6
+ import cv2
7
+ from nibabel.filebasedimages import FileBasedImage as nib_FileBasedImage
8
+
9
+ # Type alias for imaging formats
10
+ ImagingData: TypeAlias = (
11
+ Union[
12
+ 'pydicom.dataset.Dataset',
13
+ 'Image.Image',
14
+ 'cv2.VideoCapture',
15
+ 'nib_FileBasedImage'
16
+ ]
17
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: datamint
3
- Version: 2.3.1
3
+ Version: 2.3.3
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
@@ -15,6 +15,7 @@ Requires-Dist: Deprecated (>=1.2.0)
15
15
  Requires-Dist: aiohttp (>=3.0.0,<4.0.0)
16
16
  Requires-Dist: aioresponses (>=0.7.8,<0.8.0) ; extra == "dev"
17
17
  Requires-Dist: albumentations (>=2.0.0)
18
+ Requires-Dist: backports-strenum ; python_version < "3.11"
18
19
  Requires-Dist: datamintapi (==0.0.*)
19
20
  Requires-Dist: httpx
20
21
  Requires-Dist: humanize (>=4.0.0,<5.0.0)
@@ -1,39 +1,41 @@
1
1
  datamint/__init__.py,sha256=ucsnxrYClh6pdy7psRJXWam_9rjAQB4NXzvy7xLovmo,824
2
2
  datamint/api/__init__.py,sha256=7QYkmDBXbKh8-zchV7k6Lpolaw6h-IK6ezfXROIWh2A,43
3
- datamint/api/base_api.py,sha256=Iu9oJEZ8YlIF5xcbH_M0Lkb7t9ZDNFLzjJp9bPDHW1c,16628
4
- datamint/api/client.py,sha256=qL6kW1lUma98LieDEkhRdJAGRcsEFlaBSg2oQ3ZePYY,3466
5
- datamint/api/dto/__init__.py,sha256=KOSNl1axDDE5eBt68MmsgkyE0Ds_1DDzWUg73iyoWvc,281
6
- datamint/api/endpoints/__init__.py,sha256=uN2EYxSL6T5O5poVmQs8CQpyy3_MZzHfNyniORcvKUc,454
7
- datamint/api/endpoints/annotations_api.py,sha256=jhaWkLd01zKw-lVQYhBX288o9Ew2lQ-jmRP-0fz2fx0,48185
3
+ datamint/api/base_api.py,sha256=UmNEgcFt1czkqyVCM3t5P9UvA1F9AEZITPqdakojvJs,18810
4
+ datamint/api/client.py,sha256=ndKSj2QnveRNOtaQhE9qM4tCGtgrTxfInDy9FhdReCo,3922
5
+ datamint/api/dto/__init__.py,sha256=fUi901Zs-q5XHyWwZ4dMi2fEO8-CUEVEdYbpd17lahc,416
6
+ datamint/api/endpoints/__init__.py,sha256=wi4liAb5-wOohwyzKUD6TxHGeZmUPaZerFUGa2IUju4,529
7
+ datamint/api/endpoints/annotations_api.py,sha256=B21oGgNXO7xoir3ynNvhnbo1JyRvWZ9sbsc7VVjghhM,49713
8
+ datamint/api/endpoints/annotationsets_api.py,sha256=NIsPIjGGptiUBxHft-EhOMRG-DsQAthheVqd7ph0id4,409
8
9
  datamint/api/endpoints/channels_api.py,sha256=oQqxSw9DJzAqtVQI7-tc1llTdnsm-URx8jwtXNXnhio,867
9
10
  datamint/api/endpoints/datasetsinfo_api.py,sha256=WdzrUzK63w9gvAP6U--P65FbD-3X-jm9TPCcYnRNjas,597
10
11
  datamint/api/endpoints/models_api.py,sha256=tbVuajc-mCsIp5AKSCoq3uQRDWgKnJaIA6tf_ck8-XY,1502
11
- datamint/api/endpoints/projects_api.py,sha256=9tYIQsnMFOGTXrsoizweoWNqNue5907nbI6G9PAcYcA,7784
12
- datamint/api/endpoints/resources_api.py,sha256=Hd8sObIarvAdATS6qmAvT9EdXzPfruyMHnlGXcYkeMg,48320
12
+ datamint/api/endpoints/projects_api.py,sha256=C5IqKXaYRrg5LDBjIRSo4NBCvbN2619X3SSgKqGe8pc,7929
13
+ datamint/api/endpoints/resources_api.py,sha256=Ez-mGYElCpwHZ8T8NlqXH188mCFBZGfs8b320xa43tc,50142
13
14
  datamint/api/endpoints/users_api.py,sha256=pnkuTZ1B9Y0FtwwvXO8J64e02RSkRxnBmTl9UGSuC5I,1186
14
- datamint/api/entity_base_api.py,sha256=gPE28bwv7B6JngMk9szD2XwaVhB8OwB1HJjaMYD354k,12935
15
+ datamint/api/entity_base_api.py,sha256=-8SIt4M8P9G2b8SQznuWpFuFE8zEQjQxkRkw0s_w0Y4,11692
15
16
  datamint/apihandler/annotation_api_handler.py,sha256=W3vV4z3BqX1OQe1r7zr8dI-IVu4zUDxED4QttdiWV-E,57098
16
17
  datamint/apihandler/api_handler.py,sha256=mL0gMaWePYa7zwkw92E-VMK2WjpcPt7au0KqnmsWSYw,439
17
18
  datamint/apihandler/base_api_handler.py,sha256=Hqt3oUvXfEqF25DJkk0WOWAtNLnKaZRGtnCchKFA1ag,11669
18
19
  datamint/apihandler/dto/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- datamint/apihandler/dto/annotation_dto.py,sha256=KUeHbxLYols16q-ANNxC48eH4EA8Tc-nKmW_8xrqhy4,7119
20
+ datamint/apihandler/dto/annotation_dto.py,sha256=8gcTFsvbXQQU4QjdW6lcUI_De1fyNyF49_a2uIyBOpg,7220
20
21
  datamint/apihandler/exp_api_handler.py,sha256=hFUgUgBc5rL7odK7gTW3MnrvMY1pVfJUpUdzRNobMQE,6226
21
22
  datamint/apihandler/root_api_handler.py,sha256=jBof_XPTeq4o41CW-l-I5GHQKVa76kaX75RovS_qAM4,63384
22
23
  datamint/client_cmd_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
24
  datamint/client_cmd_tools/datamint_config.py,sha256=MpR5UHv_xpElOOYyEESBkDg2n3JjP_PNLI2jqmZgYQ8,16222
24
25
  datamint/client_cmd_tools/datamint_upload.py,sha256=rZEqBRZZDBFSlwMvIg_DbipqgBtQD31v7DC1C6tysqA,36058
25
- datamint/configs.py,sha256=Bdp6NydYwyCJ2dk19_gf_o3M2ZyQOmMHpLi8wEWNHUk,1426
26
+ datamint/configs.py,sha256=ArVD5QxuohLcM6NB0dMxcEfrD1-x5lPRDLcBdY8jhMU,1625
26
27
  datamint/dataset/__init__.py,sha256=4PlUKSvVhdfQvvuq8jQXrkdqnot-iTTizM3aM1vgSwg,47
27
28
  datamint/dataset/annotation.py,sha256=qN1IMjdfLD2ceQ6va3l76jOXA8Vb_c-eBk1oWQu6hW0,7994
28
- datamint/dataset/base_dataset.py,sha256=Za41Qf76nm3GmY7iA9mba_FGzzwF3gxqAwu4eY0_5b4,49479
29
- datamint/dataset/dataset.py,sha256=Oj_Xr6MaCdO2Nq16fFE345qoKMWaTzKk6M1G6ajSXMo,28959
30
- datamint/entities/__init__.py,sha256=tbHE7rZb0R9Hm-Dc8VWEq3PlRl7BYOzffumrV0ZdsMs,444
31
- datamint/entities/annotation.py,sha256=ochAEh_JqxAe_FyYTNUfPT47KiIAG7CkBTim52bu7M8,6636
32
- datamint/entities/base_entity.py,sha256=DniakCgJ-gV7Hz8VKQA_dRYTp4DU5rcjLBVOuD1aZuA,1902
29
+ datamint/dataset/base_dataset.py,sha256=2m_5nBayFCzXSjuPLb6mM9NLQ43wIUqeFUfm-6WVg_U,49827
30
+ datamint/dataset/dataset.py,sha256=c0887PcI6fEWG3FjM3gz_EYb9maH8v4ormZhm7vf4gE,28929
31
+ datamint/entities/__init__.py,sha256=ePkFeNATE5tJ9Lulsn2VTGWBJGwPtoRT1zsfbg1bkPw,504
32
+ datamint/entities/annotation.py,sha256=4qsm3yO8bTgu5SNxY91P0LGXx3d7mxF1OGWpMNir1CA,8956
33
+ datamint/entities/base_entity.py,sha256=QX381VdnrisQeQKg2kkHNDUvczYm03VsDHlHZQXr78U,3307
34
+ datamint/entities/cache_manager.py,sha256=ZN5_HeyM6c0ocWFsJuJEF4CkuOHr6RPErwhZNYYq7WU,10282
33
35
  datamint/entities/channel.py,sha256=9fl22eSx_ng98NosfQGs18cdaRdbeC3wXL61KhSg4Zo,1601
34
- datamint/entities/datasetinfo.py,sha256=O73Aq0tLflQomFzseful8a_cXqKdO9w2yP0p3zBcA-s,489
35
- datamint/entities/project.py,sha256=FNweb1Q0ZQkWfbyijoV-UyqUXthzookdcyO-ophAl0U,2676
36
- datamint/entities/resource.py,sha256=7YCVihswd-bH-2AH4aMPIddt5ejwRqRFQAszI_sTWaU,4882
36
+ datamint/entities/datasetinfo.py,sha256=yeGy5CVxqc7h0SFBJROr0UObeIMkOVZKwrOBsZ-iCEg,4151
37
+ datamint/entities/project.py,sha256=QQXBOe6FzupPrCunBcqOlY6d3uqxvnYvGeCq8hZy_U8,4192
38
+ datamint/entities/resource.py,sha256=Ai36-dJ9PhrRYho8QLRnySfsAPWPW8QpKMiVra_jCgA,9316
37
39
  datamint/entities/user.py,sha256=MREHDOsV9NOBEbXqiQ2ww6DmetN07CELId-ZQVpZCb8,620
38
40
  datamint/examples/__init__.py,sha256=zcYnd5nLVme9GCTPYH-1JpGo8xXK2WEYvhzcy_2alZc,39
39
41
  datamint/examples/example_projects.py,sha256=sU-Gxy7PPqA0WUfN-ZmXV-0YnwrnzpJ79lMXTJp2DzU,2804
@@ -42,10 +44,11 @@ datamint/experiment/__init__.py,sha256=5qQOMzoG17DEd1YnTF-vS0qiM-DGdbNh42EUo91CR
42
44
  datamint/experiment/_patcher.py,sha256=ZgbezoevAYhJsbiJTvWPALGTcUiMT371xddcTllt3H4,23296
43
45
  datamint/experiment/experiment.py,sha256=aHK9dRFdQTi569xgUg1KqlCZLHZpDmSH3g3ndPIZvXw,44546
44
46
  datamint/logging.yaml,sha256=tOMxtc2UmwlIMTK6ljtnBwTco1PNrPeq3mx2iMuSbiw,482
47
+ datamint/types.py,sha256=2OaY5QJvQIJKxyMNJYzxBksKCa9ZS2gb_ayJrByvu2Y,410
45
48
  datamint/utils/logging_utils.py,sha256=9pRoaPrWu2jOdDCiAoUsjEdP5ZwaealWL3hjUqFvx9g,4022
46
49
  datamint/utils/torchmetrics.py,sha256=lwU0nOtsSWfebyp7dvjlAggaqXtj5ohSEUXOg3L0hJE,2837
47
50
  datamint/utils/visualization.py,sha256=yaUVAOHar59VrGUjpAWv5eVvQSfztFG0eP9p5Vt3l-M,4470
48
- datamint-2.3.1.dist-info/METADATA,sha256=m77em4ywu5yOsk2pZV6dqnNo21UsxSaadPQPo2tNLSY,4203
49
- datamint-2.3.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
50
- datamint-2.3.1.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
51
- datamint-2.3.1.dist-info/RECORD,,
51
+ datamint-2.3.3.dist-info/METADATA,sha256=Cl4QTN2lWp65r0Sot2NP-n5h0A-1B-PqH0l_gT94XUY,4262
52
+ datamint-2.3.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
53
+ datamint-2.3.3.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
54
+ datamint-2.3.3.dist-info/RECORD,,