datamint 1.7.6__py3-none-any.whl → 1.9.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.

@@ -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:
@@ -8,7 +8,7 @@ import asyncio
8
8
  import aiohttp
9
9
  from medimgkit.dicom_utils import anonymize_dicom, to_bytesio, is_dicom, is_dicom_report
10
10
  from medimgkit import dicom_utils, standardize_mimetype
11
- from medimgkit.io_utils import is_io_object
11
+ from medimgkit.io_utils import is_io_object, peek
12
12
  from medimgkit.format_detection import guess_typez, guess_extension, DEFAULT_MIME_TYPE
13
13
  from medimgkit.nifti_utils import DEFAULT_NIFTI_MIME, NIFTI_MIMES
14
14
  import pydicom
@@ -103,16 +103,22 @@ class RootAPIHandler(BaseAPIHandler):
103
103
  filename = os.path.basename(name)
104
104
  _LOGGER.debug(f"File name '{filename}' mimetype: {mimetype}")
105
105
 
106
- if anonymize:
107
- if is_a_dicom_file == True or is_dicom(file_path):
108
- ds = pydicom.dcmread(file_path)
106
+ if is_a_dicom_file == True or is_dicom(file_path):
107
+ if tags is None:
108
+ tags = []
109
+ else:
110
+ tags = list(tags)
111
+ ds = pydicom.dcmread(file_path)
112
+ if anonymize:
109
113
  _LOGGER.info(f"Anonymizing {file_path}")
110
114
  ds = anonymize_dicom(ds, retain_codes=anonymize_retain_codes)
111
- # make the dicom `ds` object a file-like object in order to avoid unnecessary disk writes
112
- f = to_bytesio(ds, name)
113
- else:
114
- _LOGGER.warning(f"File {file_path} is not a dicom file. Skipping anonymization.")
115
- f = _open_io(file_path)
115
+ lat = dicom_utils.get_dicom_laterality(ds)
116
+ if lat == 'L':
117
+ tags.append("left")
118
+ elif lat == 'R':
119
+ tags.append("right")
120
+ # make the dicom `ds` object a file-like object in order to avoid unnecessary disk writes
121
+ f = to_bytesio(ds, name)
116
122
  else:
117
123
  f = _open_io(file_path)
118
124
 
@@ -437,7 +443,7 @@ class RootAPIHandler(BaseAPIHandler):
437
443
  if assemble_dicoms:
438
444
  files_path, assembled = self._assemble_dicoms(files_path)
439
445
  assemble_dicoms = assembled
440
-
446
+
441
447
  if segmentation_files is not None:
442
448
  if assemble_dicoms:
443
449
  raise NotImplementedError("Segmentation files cannot be uploaded when assembling dicoms yet.")
@@ -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.6
3
+ Version: 1.9.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.2)
22
+ Requires-Dist: medimgkit (>=0.4.4)
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
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=03eDLFYHzaQi-cv5aonKj7j0KowGK6sU5F_TCx3tRlU,59183
7
+ datamint/apihandler/root_api_handler.py,sha256=_vgIjqt7jb1yUkhyMez1Tg-vs4xmExby4TqWnfQG4OI,59320
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.6.dist-info/METADATA,sha256=xq4h5h5nYUIFRNpKleMaEkIrmuHbnLhIdJwm9ucwd8g,4100
26
- datamint-1.7.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
- datamint-1.7.6.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
28
- datamint-1.7.6.dist-info/RECORD,,
25
+ datamint-1.9.0.dist-info/METADATA,sha256=M1oMNcVYjTVV8tfZcxxcPFijib04QC8Zz9VBQLR7tXg,4100
26
+ datamint-1.9.0.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
27
+ datamint-1.9.0.dist-info/entry_points.txt,sha256=mn5H6jPjO-rY0W0CAZ6Z_KKWhMLvyVaSpoqk77jlTI4,145
28
+ datamint-1.9.0.dist-info/RECORD,,