datamint 1.7.5__py3-none-any.whl → 1.8.0__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.

Potentially problematic release.


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

@@ -16,6 +16,7 @@ import json
16
16
  from deprecated import deprecated
17
17
  from pathlib import Path
18
18
  from tqdm.auto import tqdm
19
+ from medimgkit.nifti_utils import DEFAULT_NIFTI_MIME
19
20
 
20
21
  _LOGGER = logging.getLogger(__name__)
21
22
  _USER_LOGGER = logging.getLogger('user_logger')
@@ -280,7 +281,7 @@ class AnnotationAPIHandler(BaseAPIHandler):
280
281
  with open(file_path, 'rb') as f:
281
282
  filename = os.path.basename(file_path)
282
283
  form = aiohttp.FormData()
283
- form.add_field('file', f, filename=filename, content_type='application/x-nifti')
284
+ form.add_field('file', f, filename=filename, content_type=DEFAULT_NIFTI_MIME)
284
285
  if model_id is not None:
285
286
  form.add_field('model_id', model_id) # Add model_id if provided
286
287
  if worklist_id is not None:
@@ -119,9 +119,6 @@ class BaseAPIHandler:
119
119
  if method != 'GET':
120
120
  curl_command.extend(['-X', method])
121
121
 
122
- # Add URL
123
- curl_command.append(f"'{url}'")
124
-
125
122
  # Add headers
126
123
  for key, value in headers.items():
127
124
  if key.lower() == 'apikey':
@@ -132,6 +129,8 @@ class BaseAPIHandler:
132
129
  if params:
133
130
  param_str = '&'.join([f"{k}={v}" for k, v in params.items()])
134
131
  url = f"{url}?{param_str}"
132
+ # Add URL
133
+ curl_command.append(f"'{url}'")
135
134
 
136
135
  # Add data
137
136
  if data:
@@ -7,9 +7,10 @@ import logging
7
7
  import asyncio
8
8
  import aiohttp
9
9
  from medimgkit.dicom_utils import anonymize_dicom, to_bytesio, is_dicom, is_dicom_report
10
- from medimgkit import dicom_utils
10
+ from medimgkit import dicom_utils, standardize_mimetype
11
11
  from medimgkit.io_utils import is_io_object
12
12
  from medimgkit.format_detection import guess_typez, guess_extension, DEFAULT_MIME_TYPE
13
+ from medimgkit.nifti_utils import DEFAULT_NIFTI_MIME, NIFTI_MIMES
13
14
  import pydicom
14
15
  from pathlib import Path
15
16
  from datetime import date
@@ -88,12 +89,17 @@ class RootAPIHandler(BaseAPIHandler):
88
89
  is_a_dicom_file = None
89
90
  if mimetype is None:
90
91
  mimetype_list, ext = guess_typez(file_path, use_magic=True)
91
- mimetype = mimetype_list[-1]
92
- if mimetype == 'application/gzip':
93
- # Special case for gzipped NIfTI files
92
+ for mime in mimetype_list:
93
+ if mime in NIFTI_MIMES:
94
+ mimetype = DEFAULT_NIFTI_MIME
95
+ break
96
+ else:
94
97
  if ext == '.nii.gz' or name.lower().endswith('nii.gz'):
95
- mimetype = 'image/x.nifti'
98
+ mimetype = DEFAULT_NIFTI_MIME
99
+ else:
100
+ mimetype = mimetype_list[-1] if mimetype_list else DEFAULT_MIME_TYPE
96
101
 
102
+ mimetype = standardize_mimetype(mimetype)
97
103
  filename = os.path.basename(name)
98
104
  _LOGGER.debug(f"File name '{filename}' mimetype: {mimetype}")
99
105
 
@@ -5,7 +5,7 @@ from humanize import naturalsize
5
5
  import logging
6
6
  from pathlib import Path
7
7
  import sys
8
- from medimgkit.dicom_utils import is_dicom
8
+ from medimgkit.dicom_utils import is_dicom, detect_dicomdir, parse_dicomdir_files
9
9
  import fnmatch
10
10
  from typing import Generator, Optional, Any
11
11
  from collections import defaultdict
@@ -35,7 +35,7 @@ def _read_segmentation_names(segmentation_names_path: str | Path) -> dict:
35
35
  if segmentation_names_path.suffix in ['.yaml', '.yml']:
36
36
  with open(segmentation_names_path, 'r') as f:
37
37
  metadata = yaml.safe_load(f)
38
- elif segmentation_names_path.suffix in ['.csv', '.tsv']:
38
+ elif segmentation_names_path.suffix in ['.csv', '.tsv', '.txt']:
39
39
  df = pd.read_csv(segmentation_names_path,
40
40
  header=None,
41
41
  index_col=0,
@@ -124,6 +124,22 @@ def walk_to_depth(path: str | Path,
124
124
  depth: int,
125
125
  exclude_pattern: str | None = None) -> Generator[Path, None, None]:
126
126
  path = Path(path)
127
+
128
+ # Check for DICOMDIR first at current directory level
129
+ dicomdir_path = detect_dicomdir(path)
130
+ if dicomdir_path is not None:
131
+ try:
132
+ _USER_LOGGER.info(f"Found DICOMDIR file at {path}. Using it as authoritative source for file listing.")
133
+ dicom_files = parse_dicomdir_files(dicomdir_path)
134
+ # Yield all DICOM files from DICOMDIR and return early
135
+ for dicom_file in dicom_files:
136
+ yield dicom_file
137
+ return
138
+ except Exception as e:
139
+ _USER_LOGGER.warning(f"Failed to parse DICOMDIR at {path}: {e}. Falling back to directory scan.")
140
+ # Continue with regular directory scanning below
141
+
142
+ # Regular directory scanning
127
143
  for child in path.iterdir():
128
144
  if _is_system_file(child):
129
145
  continue
@@ -386,6 +402,46 @@ def _collect_metadata_files(files_path: list[str], auto_detect_json: bool) -> tu
386
402
  return metadata_files, filtered_files_path
387
403
 
388
404
 
405
+ def _get_files_from_path(path: str | Path,
406
+ recursive_depth: Optional[int] = None,
407
+ exclude_pattern: Optional[str] = None,
408
+ include_extensions: Optional[list[str]] = None,
409
+ exclude_extensions: Optional[list[str]] = None) -> list[str]:
410
+ """
411
+ Get files from a path with recursive DICOMDIR detection and parsing.
412
+
413
+ Args:
414
+ path: Path to search for files
415
+ recursive_depth: Depth for recursive search (None for no recursion, -1 for unlimited)
416
+ exclude_pattern: Pattern to exclude directories
417
+ include_extensions: File extensions to include
418
+ exclude_extensions: File extensions to exclude
419
+
420
+ Returns:
421
+ List of file paths as strings
422
+ """
423
+ path = Path(path)
424
+
425
+ if path.is_file():
426
+ return [str(path)]
427
+
428
+ try:
429
+ if recursive_depth is None:
430
+ recursive_depth = 0
431
+ elif recursive_depth < 0:
432
+ recursive_depth = MAX_RECURSION_LIMIT
433
+ else:
434
+ recursive_depth = min(MAX_RECURSION_LIMIT, recursive_depth)
435
+
436
+ file_paths = walk_to_depth(path, recursive_depth, exclude_pattern)
437
+ filtered_files = filter_files(file_paths, include_extensions, exclude_extensions)
438
+ return [str(f) for f in filtered_files]
439
+
440
+ except Exception as e:
441
+ _LOGGER.error(f'Error in recursive search: {e}')
442
+ raise
443
+
444
+
389
445
  def _parse_args() -> tuple[Any, list[str], Optional[list[dict]], Optional[list[str]]]:
390
446
  parser = argparse.ArgumentParser(
391
447
  description='DatamintAPI command line tool for uploading DICOM files and other resources')
@@ -433,7 +489,7 @@ def _parse_args() -> tuple[Any, list[str], Optional[list[dict]], Optional[list[s
433
489
  required=False,
434
490
  help='Path to a yaml or csv file containing the segmentation names.' +
435
491
  ' If yaml, the file may contain two keys: "segmentation_names" and "class_names".'
436
- ' If csv, the file should contain the following columns:'
492
+ ' If csv, the file should be in itk-snap label export format, i.e, it should contain the following columns (with no header):'
437
493
  ' index, r, g, b, ..., name')
438
494
  parser.add_argument('--yes', action='store_true',
439
495
  help='Automatically answer yes to all prompts')
@@ -482,18 +538,13 @@ def _parse_args() -> tuple[Any, list[str], Optional[list[dict]], Optional[list[s
482
538
  if args.recursive is not None:
483
539
  _USER_LOGGER.warning("Recursive flag ignored. Specified path is a file.")
484
540
  else:
485
- try:
486
- recursive_depth = 0 if args.recursive is None else args.recursive
487
- if recursive_depth < 0:
488
- recursive_depth = MAX_RECURSION_LIMIT
489
- else:
490
- recursive_depth = min(MAX_RECURSION_LIMIT, recursive_depth)
491
- file_path = walk_to_depth(args.path, recursive_depth, args.exclude)
492
- file_path = filter_files(file_path, args.include_extensions, args.exclude_extensions)
493
- file_path = list(map(str, file_path)) # from Path to str
494
- except Exception as e:
495
- _LOGGER.error(f'Error in recursive search: {e}')
496
- raise e
541
+ file_path = _get_files_from_path(
542
+ path=args.path,
543
+ recursive_depth=args.recursive,
544
+ exclude_pattern=args.exclude,
545
+ include_extensions=args.include_extensions,
546
+ exclude_extensions=args.exclude_extensions
547
+ )
497
548
 
498
549
  if len(file_path) == 0:
499
550
  raise ValueError(f"No valid file was found in {args.path}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: datamint
3
- Version: 1.7.5
3
+ Version: 1.8.0
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
@@ -19,7 +19,7 @@ Requires-Dist: humanize (>=4.0.0,<5.0.0)
19
19
  Requires-Dist: lazy-loader (>=0.3.0)
20
20
  Requires-Dist: lightning
21
21
  Requires-Dist: matplotlib
22
- Requires-Dist: medimgkit (>=0.2.1)
22
+ Requires-Dist: medimgkit (>=0.3.1)
23
23
  Requires-Dist: nest-asyncio (>=1.0.0,<2.0.0)
24
24
  Requires-Dist: nibabel (>=4.0.0)
25
25
  Requires-Dist: numpy
@@ -1,13 +1,13 @@
1
1
  datamint/__init__.py,sha256=7rKCCsaa4RBRTIfuHB708rai1xwDHLtkFNFJGKYG5D4,757
2
- datamint/apihandler/annotation_api_handler.py,sha256=HnWiG2ebq08mdaazTXVbkuwvh6fmKIKt8uqAOf3Y1jU,57013
2
+ datamint/apihandler/annotation_api_handler.py,sha256=ZJJD_Op8eDtGcpDZOS1DRjqyDUdD3UxvtuNJK9FaPOk,57063
3
3
  datamint/apihandler/api_handler.py,sha256=cdVSddrFCKlF_BJ81LO1aJ0OP49rssjpNEFzJ6Q7YyY,384
4
- datamint/apihandler/base_api_handler.py,sha256=HSMi8vN-xU0LLUEh3fgcuO7mXpMsX0wrA5nO9FVvrA4,12207
4
+ datamint/apihandler/base_api_handler.py,sha256=GQuJS3FFjxPi-2vkJtbHeNKywoa-PWa3Qvw3NGRzxug,12206
5
5
  datamint/apihandler/dto/annotation_dto.py,sha256=qId1RK1VO7dXrvGJ7dqJ31jBQB7Z8yy5x0tLSiMxTB4,7105
6
6
  datamint/apihandler/exp_api_handler.py,sha256=hFUgUgBc5rL7odK7gTW3MnrvMY1pVfJUpUdzRNobMQE,6226
7
- datamint/apihandler/root_api_handler.py,sha256=8VanqWbcLLkwQ0y2Z_a5Mqr5knplRCUSi5iRVQeHFzU,58901
7
+ datamint/apihandler/root_api_handler.py,sha256=03eDLFYHzaQi-cv5aonKj7j0KowGK6sU5F_TCx3tRlU,59183
8
8
  datamint/client_cmd_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  datamint/client_cmd_tools/datamint_config.py,sha256=md7dnWrbl10lPtXKbmD9yo6onLJsajeG8Vz0ZWH1v4M,8181
10
- datamint/client_cmd_tools/datamint_upload.py,sha256=890USkrtaH23mNjTRYVHWce2q9jSmkUNHIP_e8fnJRM,29502
10
+ datamint/client_cmd_tools/datamint_upload.py,sha256=J62xBDTaLxwBFWDmuXhzgbmZfPWaAZLr7CKsbqgTI7U,31389
11
11
  datamint/configs.py,sha256=Bdp6NydYwyCJ2dk19_gf_o3M2ZyQOmMHpLi8wEWNHUk,1426
12
12
  datamint/dataset/__init__.py,sha256=4PlUKSvVhdfQvvuq8jQXrkdqnot-iTTizM3aM1vgSwg,47
13
13
  datamint/dataset/annotation.py,sha256=qN1IMjdfLD2ceQ6va3l76jOXA8Vb_c-eBk1oWQu6hW0,7994
@@ -22,7 +22,7 @@ datamint/logging.yaml,sha256=a5dsATpul7QHeUHB2TjABFjWaPXBMbO--dgn8GlRqwk,483
22
22
  datamint/utils/logging_utils.py,sha256=DvoA35ATYG3JTwfXEXYawDyKRfHeCrH0a9czfkmz8kM,1851
23
23
  datamint/utils/torchmetrics.py,sha256=lwU0nOtsSWfebyp7dvjlAggaqXtj5ohSEUXOg3L0hJE,2837
24
24
  datamint/utils/visualization.py,sha256=yaUVAOHar59VrGUjpAWv5eVvQSfztFG0eP9p5Vt3l-M,4470
25
- datamint-1.7.5.dist-info/METADATA,sha256=vWoLCzDqK33Lzb0DRTLLRAYpYJRX6xmv0p30Lo46Hwg,4100
26
- datamint-1.7.5.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
- datamint-1.7.5.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
28
- datamint-1.7.5.dist-info/RECORD,,
25
+ datamint-1.8.0.dist-info/METADATA,sha256=wkOXaU-02ah0wNosREQqODM-H9NmdEw1L3sQ3F9gFW8,4100
26
+ datamint-1.8.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
+ datamint-1.8.0.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
28
+ datamint-1.8.0.dist-info/RECORD,,