11import logging
2- from typing import Any , Generator , AsyncGenerator , Sequence
2+ from typing import Any , Generator , AsyncGenerator , Sequence , TYPE_CHECKING
33import httpx
44from dataclasses import dataclass
55from datamint .exceptions import DatamintException , ResourceNotFoundError
6+ from datamint .types import ImagingData
67import aiohttp
78import json
8- import pydicom .dataset
99from PIL import Image
1010import cv2
1111import nibabel as nib
12- from nibabel .filebasedimages import FileBasedImage as nib_FileBasedImage
1312from io import BytesIO
1413import gzip
1514import contextlib
1615import asyncio
17- from medimgkit .format_detection import GZIP_MIME_TYPES
16+ from medimgkit .format_detection import GZIP_MIME_TYPES , DEFAULT_MIME_TYPE , guess_typez , guess_extension
17+
18+ if TYPE_CHECKING :
19+ from datamint .api .client import Api
1820
1921logger = logging .getLogger (__name__ )
2022
2123# Generic type for entities
2224_PAGE_LIMIT = 5000
2325
24-
2526@dataclass
2627class ApiConfig :
2728 """Configuration for API client.
@@ -37,6 +38,15 @@ class ApiConfig:
3738 timeout : float = 30.0
3839 max_retries : int = 3
3940
41+ @property
42+ def web_app_url (self ) -> str :
43+ """Get the base URL for the web application."""
44+ if self .server_url .startswith ('http://localhost:3001' ):
45+ return 'http://localhost:3000'
46+ if self .server_url .startswith ('https://stagingapi.datamint.io' ):
47+ return 'https://staging.datamint.io'
48+ return 'https://app.datamint.io'
49+
4050
4151class BaseApi :
4252 """Base class for all API endpoint handlers."""
@@ -53,6 +63,7 @@ def __init__(self,
5363 self .config = config
5464 self .client = client or self ._create_client ()
5565 self .semaphore = asyncio .Semaphore (20 )
66+ self ._api_instance : 'Api | None' = None # Injected by Api class
5667
5768 def _create_client (self ) -> httpx .Client :
5869 """Create and configure HTTP client with authentication and timeouts."""
@@ -399,10 +410,30 @@ def _convert_array_response(self,
399410
400411 @staticmethod
401412 def convert_format (bytes_array : bytes ,
402- mimetype : str ,
413+ mimetype : str | None = None ,
403414 file_path : str | None = None
404- ) -> pydicom .dataset .Dataset | Image .Image | cv2 .VideoCapture | bytes | nib_FileBasedImage :
405- """ Convert the bytes array to the appropriate format based on the mimetype."""
415+ ) -> ImagingData | bytes :
416+ """ Convert the bytes array to the appropriate format based on the mimetype.
417+
418+ Args:
419+ bytes_array: Raw file content bytes
420+ mimetype: Optional MIME type of the content
421+ file_path: deprecated
422+
423+ Returns:
424+ Converted content in appropriate format (pydicom.Dataset, PIL Image, cv2.VideoCapture, ...)
425+
426+ Example:
427+ >>> fpath = 'path/to/file.dcm'
428+ >>> with open(fpath, 'rb') as f:
429+ ... dicom_bytes = f.read()
430+ >>> dicom = BaseApi.convert_format(dicom_bytes)
431+
432+ """
433+ if mimetype is None :
434+ mimetype , ext = BaseApi ._determine_mimetype (bytes_array )
435+ if mimetype is None :
436+ raise ValueError ("Could not determine mimetype from content." )
406437 content_io = BytesIO (bytes_array )
407438 if mimetype .endswith ('/dicom' ):
408439 return pydicom .dcmread (content_io )
@@ -429,3 +460,30 @@ def convert_format(bytes_array: bytes,
429460 return nib .Nifti1Image .from_stream (f )
430461
431462 raise ValueError (f"Unsupported mimetype: { mimetype } " )
463+
464+ @staticmethod
465+ def _determine_mimetype (content : bytes ,
466+ declared_mimetype : str | None = None ) -> tuple [str | None , str | None ]:
467+ """Infer MIME type and file extension from content and optional declared type.
468+
469+ Args:
470+ content: Raw file content bytes
471+ declared_mimetype: Optional MIME type declared by the source
472+
473+ Returns:
474+ Tuple of (inferred_mimetype, file_extension)
475+ """
476+ # Determine mimetype from file content
477+ mimetype_list , ext = guess_typez (content , use_magic = True )
478+ mimetype = mimetype_list [- 1 ]
479+
480+ # get mimetype from resource info if not detected
481+ if declared_mimetype is not None :
482+ if mimetype is None :
483+ mimetype = declared_mimetype
484+ ext = guess_extension (mimetype )
485+ elif mimetype == DEFAULT_MIME_TYPE :
486+ mimetype = declared_mimetype
487+ ext = guess_extension (mimetype )
488+
489+ return mimetype , ext
0 commit comments