large-image-source-dicom 1.27.5.dev4__tar.gz → 1.30.7.dev10__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.
Files changed (41) hide show
  1. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/PKG-INFO +10 -2
  2. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/README.rst +20 -40
  3. large_image_source_dicom-1.30.7.dev10/docs/dicomweb_assetstore.rst +8 -0
  4. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/__init__.py +14 -16
  5. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/assetstore/dicomweb_assetstore_adapter.py +70 -31
  6. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/assetstore/rest.py +4 -30
  7. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/girder_source.py +1 -2
  8. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/main.js +1 -2
  9. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/templates/dicomwebAssetstoreImportButton.pug +1 -1
  10. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/views/DICOMwebImportView.js +10 -4
  11. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom.egg-info/PKG-INFO +10 -2
  12. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom.egg-info/SOURCES.txt +1 -2
  13. large_image_source_dicom-1.30.7.dev10/large_image_source_dicom.egg-info/requires.txt +12 -0
  14. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/setup.py +10 -4
  15. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/test_dicom/test_web_client.py +2 -2
  16. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/test_dicom/web_client_specs/dicomWebSpec.js +2 -2
  17. large-image-source-dicom-1.27.5.dev4/large_image_source_dicom/web_client/models/AssetstoreModel.js +0 -20
  18. large-image-source-dicom-1.27.5.dev4/large_image_source_dicom/web_client/routes.js +0 -17
  19. large-image-source-dicom-1.27.5.dev4/large_image_source_dicom.egg-info/requires.txt +0 -5
  20. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/LICENSE +0 -0
  21. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/assetstore/__init__.py +0 -0
  22. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/dicom_metadata.py +0 -0
  23. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/dicom_tags.py +0 -0
  24. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/dicomweb_utils.py +0 -0
  25. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/girder_plugin.py +0 -0
  26. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/constants.js +0 -0
  27. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/package.json +0 -0
  28. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/templates/assetstoreImport.pug +0 -0
  29. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/templates/dicomwebAssetstoreCreate.pug +0 -0
  30. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/templates/dicomwebAssetstoreEditFields.pug +0 -0
  31. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/templates/dicomwebAssetstoreMixins.pug +0 -0
  32. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/views/AssetstoresView.js +0 -0
  33. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/views/AuthOptions.js +0 -0
  34. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/views/EditAssetstoreWidget.js +0 -0
  35. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom/web_client/views/NewAssetstoreWidget.js +0 -0
  36. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom.egg-info/dependency_links.txt +0 -0
  37. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom.egg-info/entry_points.txt +0 -0
  38. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/large_image_source_dicom.egg-info/top_level.txt +0 -0
  39. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/pyproject.toml +0 -0
  40. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/setup.cfg +0 -0
  41. {large-image-source-dicom-1.27.5.dev4 → large_image_source_dicom-1.30.7.dev10}/test_dicom/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: large-image-source-dicom
3
- Version: 1.27.5.dev4
3
+ Version: 1.30.7.dev10
4
4
  Summary: A DICOM tilesource for large_image.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -15,9 +15,17 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Requires-Python: >=3.8
19
- Provides-Extra: girder
20
+ Description-Content-Type: text/x-rst
20
21
  License-File: LICENSE
22
+ Requires-Dist: large-image>=1.30.7.dev10
23
+ Requires-Dist: wsidicom!=0.21.3,>=0.9.0
24
+ Requires-Dist: pydicom<3; python_version < "3.10"
25
+ Requires-Dist: pydicom; python_version >= "3.10"
26
+ Provides-Extra: girder
27
+ Requires-Dist: girder-large-image>=1.30.7.dev10; extra == "girder"
28
+ Requires-Dist: girder>=3.2.3; extra == "girder"
21
29
 
22
30
  A DICOM tilesource for large_image.
23
31
 
@@ -15,7 +15,7 @@ Large Image
15
15
  :target: https://codecov.io/github/girder/large_image?branch=master
16
16
  :alt: codecov.io
17
17
 
18
- .. |doi-badge| image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.4723355-blue
18
+ .. |doi-badge| image:: https://img.shields.io/badge/DOI-10.5281%2Fzenodo.4723355-blue.svg
19
19
  :target: https://zenodo.org/badge/latestdoi/45569214
20
20
 
21
21
  .. |pypi-badge| image:: https://img.shields.io/pypi/v/large-image.svg?logo=python&logoColor=white
@@ -39,7 +39,7 @@ Highlights
39
39
  Installation
40
40
  ------------
41
41
 
42
- In addition to installing the ``large-image`` package, you'll need at least one tile source (a ``large-image-source-xxx`` package). You can install everything from the main project with one of these commands:
42
+ In addition to installing the base ``large-image`` package, you'll need at least one tile source which corresponds to your target file format(s) (a ``large-image-source-xxx`` package). You can install everything from the main project with one of these commands:
43
43
 
44
44
  Pip
45
45
  ~~~
@@ -52,7 +52,7 @@ Install all tile sources on linux::
52
52
 
53
53
  pip install large-image[all] --find-links https://girder.github.io/large_image_wheels
54
54
 
55
- Install all tile sources and all Girder plugins on linux::
55
+ When using large-image with an instance of `Girder`_, install all tile sources and all Girder plugins on linux::
56
56
 
57
57
  pip install large-image[all] girder-large-image-annotation[tasks] --find-links https://girder.github.io/large_image_wheels
58
58
 
@@ -60,8 +60,9 @@ Install all tile sources and all Girder plugins on linux::
60
60
  Conda
61
61
  ~~~~~
62
62
 
63
- Conda makes dependency management a bit easier if not on Linux. Some of the source modules are available on conda-forge. You can install the following::
63
+ Conda makes dependency management a bit easier if not on Linux. The base module, converter module, and two of the source modules are available on conda-forge. You can install the following::
64
64
 
65
+ conda install -c conda-forge large-image
65
66
  conda install -c conda-forge large-image-source-gdal
66
67
  conda install -c conda-forge large-image-source-tiff
67
68
  conda install -c conda-forge large-image-converter
@@ -118,7 +119,7 @@ Large Image consists of several Python modules designed to work together. These
118
119
 
119
120
  - ``large-image-source-deepzoom``: A tile source for reading Deepzoom tiles.
120
121
 
121
- - ``large-image-source-dicom``: A tile source for reading DICOM WSI images.
122
+ - ``large-image-source-dicom``: A tile source for reading DICOM Whole Slide Images (WSI).
122
123
 
123
124
  - ``large-image-source-gdal``: A tile source for reading geotiff files via GDAL. This handles source data with more complex transforms than the mapnik tile source.
124
125
 
@@ -128,62 +129,41 @@ Large Image consists of several Python modules designed to work together. These
128
129
 
129
130
  - ``large-image-source-nd2``: A tile source for reading nd2 (NIS Element) images.
130
131
 
131
- - ``large-image-source-ometiff``: A tile source using the tiff library that can handle some multi-frame OMETiff files.
132
+ - ``large-image-source-ometiff``: A tile source using the tiff library that can handle most multi-frame OMETiff files that are compliant with the specification.
132
133
 
133
134
  - ``large-image-source-openjpeg``: A tile source using the Glymur library to read jp2 (JPEG 2000) files.
134
135
 
135
136
  - ``large-image-source-openslide``: A tile source using the OpenSlide library. This works with svs, ndpi, Mirax, tiff, vms, and other file formats.
136
137
 
137
- - ``large-image-source-pil``: A tile source for small images via the Python Imaging Library (Pillow).
138
+ - ``large-image-source-pil``: A tile source for small images via the Python Imaging Library (Pillow). By default, the maximum size is 4096, but the maximum size can be configured.
138
139
 
139
140
  - ``large-image-source-tiff``: A tile source for reading pyramidal tiff files in common compression formats.
140
141
 
141
142
  - ``large-image-source-tifffile``: A tile source using the tifffile library that can handle a wide variety of tiff-like files.
142
143
 
143
- - ``large-image-source-vips``: A tile source for reading any files handled by libvips. This also can be used for writing tiled images from numpy arrays.
144
+ - ``large-image-source-vips``: A tile source for reading any files handled by libvips. This also can be used for writing tiled images from numpy arrays (up to 4 dimensions).
144
145
 
145
- - ``large-image-source-zarr``: A tile source using the zarr library that can handle OME-Zarr (OME-NGFF) files as well as some other zarr files.
146
+ - ``large-image-source-zarr``: A tile source using the zarr library that can handle OME-Zarr (OME-NGFF) files as well as some other zarr files. This can also be used for writing N-dimensional tiled images from numpy arrays. Written images can be saved as any supported format.
146
147
 
147
148
  - ``large-image-source-test``: A tile source that generates test tiles, including a simple fractal pattern. Useful for testing extreme zoom levels.
148
149
 
149
- - ``large-image-source-dummy``: A tile source that does nothing.
150
+ - ``large-image-source-dummy``: A tile source that does nothing. This is an absolutely minimal implementation of a tile source used for testing. If you want to create a custom tile source, start with this implementation.
150
151
 
151
- Most tile sources can be used with girder-large-image. You can specific an extras_require of ``girder`` to include ``girder-large-image`` with the source.
152
152
 
153
- - As a Girder plugin:
153
+ As a `Girder`_ plugin, ``large-image`` adds end points to access all of the image formats it can read both to get metadata and to act as a tile server.
154
+ In the Girder UI, ``large-image`` shows images on item pages, and can show thumbnails in item lists when browsing folders.
155
+ There is also cache management to balance memory use and speed of response in Girder when ``large-image`` is used as a tile server.
154
156
 
155
- - ``girder-large-image``: Large Image as a Girder_ 3.x plugin.
156
- You can specify extras_require of ``tasks`` to install a Girder Worker task that can convert otherwise unreadable images to pyramidal tiff files.
157
+ Most tile sources can be used with Girder Large Image. You can specify an extras_require of ``girder`` to install the following packages:
157
158
 
158
- - ``girder-large-image-annotation``: Annotations for large images as a Girder_ 3.x plugin.
159
+ - ``girder-large-image``: Large Image as a Girder 3.x plugin.
160
+ You can install ``large-image[tasks]`` to install a Girder Worker task that can convert otherwise unreadable images to pyramidal tiff files.
161
+
162
+ - ``girder-large-image-annotation``: Adds models to the Girder database for supporting annotating large images. These annotations can be rendered on images. Annotations can include polygons, points, image overlays, and other types. Each annotation can have a label and metadata.
159
163
 
160
164
  - ``large-image-tasks``: A utility for running the converter via Girder Worker.
161
165
  You can specify an extras_require of ``girder`` to include modules needed to work with the Girder remote worker or ``worker`` to include modules needed on the remote side of the Girder remote worker. If neither is specified, some conversion tasks can be run using Girder local jobs.
162
166
 
163
167
 
164
- Developer Installation
165
- ----------------------
166
-
167
- To install all packages from source, clone the repository::
168
-
169
- git clone https://github.com/girder/large_image.git
170
- cd large_image
171
-
172
- Install all packages and dependencies::
173
-
174
- pip install -e . -r requirements-dev.txt
175
-
176
- If you aren't developing with Girder 3, you can skip installing those components. Use ``requirements-dev-core.txt`` instead of ``requirements-dev.txt``::
177
-
178
- pip install -e . -r requirements-dev-core.txt
179
-
180
-
181
- Tile source prerequisites
182
- =========================
183
-
184
- Many tile sources have complex prerequisites. These can be installed directly using your system's package manager or from some prebuilt Python wheels for Linux. The prebuilt wheels are not official packages, but they can be used by instructing pip to use them by preference::
185
-
186
- pip install -e . -r requirements-dev.txt --find-links https://girder.github.io/large_image_wheels
187
-
188
168
 
189
- .. _Girder: https://github.com/girder/girder
169
+ .. _Girder: https://girder.readthedocs.io/en/latest/
@@ -0,0 +1,8 @@
1
+ DICOMweb Assetstore
2
+ ===================
3
+
4
+ The DICOM source also provides a Girder Assetstore plugin for accessing data on DICOMweb servers. This is available if the package is installed in the same python environment as Girder and the Girder client is built after installation.
5
+
6
+ A DICOMweb assetstore can be added through the Girder Admin Console by selecting the "Create new DICOMweb assetstore" button. It requires an appropriate URL; for those using DCM4CHEE, this might be something like "https://some.server.com/dcm4chee-arc/aets/DCM4CHEE/rs". Check with the DICOMweb provider for appropriate QIDO and WADO prefixes.
7
+
8
+ Existing images have to be imported from the assetstore via the standard Girder import process. A json specification to filter the import can be added. A common filter is to only import Slide Modality images via ``{ "ModalitiesInStudy": "SM" }``.
@@ -186,9 +186,8 @@ class DICOMFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
186
186
  if isinstance(path, dict):
187
187
  # Use the DICOMweb open method
188
188
  return self._open_wsi_dicomweb(path)
189
- else:
190
- # Use the regular open method
191
- return wsidicom.WsiDicom.open(path)
189
+ # Use the regular open method
190
+ return wsidicom.WsiDicom.open(path)
192
191
 
193
192
  def _open_wsi_dicomweb(self, info):
194
193
  # These are the required keys in the info dict
@@ -333,22 +332,21 @@ class DICOMFileTileSource(FileTileSource, metaclass=LruCacheMetaclass):
333
332
  study_uid = self._dicom.uids.study_instance
334
333
  series_uid = self._dicom.uids.series_instance
335
334
  return get_dicomweb_metadata(client, study_uid, series_uid)
336
- else:
337
- # Find the first volume instance and extract the metadata
338
- volume = None
339
- for level in self._dicom.pyramids[0]:
340
- for ds in level.datasets:
341
- if ds.image_type.value == 'VOLUME':
342
- volume = ds
343
- break
344
-
345
- if volume:
335
+ # Find the first volume instance and extract the metadata
336
+ volume = None
337
+ for level in self._dicom.pyramids[0]:
338
+ for ds in level.datasets:
339
+ if ds.image_type.value == 'VOLUME':
340
+ volume = ds
346
341
  break
347
342
 
348
- if not volume:
349
- return None
343
+ if volume:
344
+ break
345
+
346
+ if not volume:
347
+ return None
350
348
 
351
- return extract_dicom_metadata(volume)
349
+ return extract_dicom_metadata(volume)
352
350
 
353
351
  @methodcache()
354
352
  def getTile(self, x, y, z, pilImageAllowed=False, numpyAllowed=False, **kwargs):
@@ -1,3 +1,5 @@
1
+ import json
2
+
1
3
  import cherrypy
2
4
  import requests
3
5
  from large_image_source_dicom.dicom_tags import dicom_key_to_tag
@@ -157,10 +159,9 @@ class DICOMwebAssetstoreAdapter(AbstractAssetstoreAdapter):
157
159
  # Skip over the whole chunk...
158
160
  bytes_read += len(chunk)
159
161
  continue
160
- else:
161
- # Discard all bytes before the offset
162
- chunk = chunk[bytes_needed:]
163
- bytes_read += bytes_needed
162
+ # Discard all bytes before the offset
163
+ chunk = chunk[bytes_needed:]
164
+ bytes_read += bytes_needed
164
165
 
165
166
  if endByte is not None and bytes_read + len(chunk) >= endByte:
166
167
  # We have reached the end... remove all bytes after endByte
@@ -377,48 +378,27 @@ class DICOMwebAssetstoreAdapter(AbstractAssetstoreAdapter):
377
378
  response = self._request_retrieve_instance(file, multipart=False)
378
379
  except requests.HTTPError:
379
380
  # If there is an HTTPError, the server might not accept single-part requests...
380
- return
381
+ return None
381
382
 
382
383
  if not self._is_singlepart_response(response):
383
384
  # Does not support single-part requests...
384
- return
385
+ return None
385
386
 
386
387
  content_length = response.headers.get('Content-Length')
387
388
  if not content_length:
388
389
  # The server did not return a Content-Length
389
- return
390
+ return None
390
391
 
391
392
  try:
392
393
  # The DICOM file size is equal to the Content-Length
393
394
  return int(content_length)
394
395
  except ValueError:
395
- return
396
-
397
- def importData(self, parent, parentType, params, progress, user, **kwargs):
398
- """
399
- Import DICOMweb WSI instances from a DICOMweb server.
400
-
401
- :param parent: The parent object to import into.
402
- :param parentType: The model type of the parent object.
403
- :type parentType: str
404
- :param params: Additional parameters required for the import process.
405
- This dictionary may include the following keys:
406
-
407
- :limit: (optional) limit the number of studies imported.
408
- :search_filters: (optional) a dictionary of additional search
409
- filters to use with dicomweb_client's `search_for_series()`
410
- function.
396
+ return None
411
397
 
412
- :type params: dict
413
- :param progress: Object on which to record progress if possible.
414
- :type progress: :py:class:`girder.utility.progress.ProgressContext`
415
- :param user: The Girder user performing the import.
416
- :type user: dict or None
417
- :return: a list of items that were created
418
- """
398
+ def _importData(self, parent, parentType, params, progress, user):
419
399
  if parentType not in ('folder', 'user', 'collection'):
420
400
  msg = f'Invalid parent type: {parentType}'
421
- raise RuntimeError(msg)
401
+ raise ValidationException(msg)
422
402
 
423
403
  limit = params.get('limit')
424
404
  search_filters = params.get('search_filters', {})
@@ -512,6 +492,65 @@ class DICOMwebAssetstoreAdapter(AbstractAssetstoreAdapter):
512
492
 
513
493
  return items
514
494
 
495
+ def importData(self, parent, parentType, params, progress, user, **kwargs):
496
+ """
497
+ Import DICOMweb WSI instances from a DICOMweb server.
498
+
499
+ :param parent: The parent object to import into.
500
+ :param parentType: The model type of the parent object.
501
+ :type parentType: str
502
+ :param params: Additional parameters required for the import process.
503
+ This dictionary may include the following keys:
504
+
505
+ :limit: (optional) limit the number of studies imported.
506
+ :filters: (optional) a dictionary/JSON string of additional search
507
+ filters to use with dicomweb_client's `search_for_series()`
508
+ function.
509
+
510
+ :type params: dict
511
+ :param progress: Object on which to record progress if possible.
512
+ :type progress: :py:class:`girder.utility.progress.ProgressContext`
513
+ :param user: The Girder user performing the import.
514
+ :type user: dict or None
515
+ :return: a list of items that were created
516
+ """
517
+ # Validate the parameters
518
+ limit = params.get('limit') or None
519
+ if limit is not None:
520
+ error_msg = f'Invalid limit: {limit}'
521
+ try:
522
+ limit = int(limit)
523
+ except ValueError:
524
+ raise ValidationException(error_msg)
525
+
526
+ if limit < 1:
527
+ raise ValidationException(error_msg)
528
+
529
+ search_filters = params.get('filters', {})
530
+ if isinstance(search_filters, str):
531
+ try:
532
+ search_filters = json.loads(search_filters)
533
+ except json.JSONDecodeError as e:
534
+ msg = f'Invalid filters: "{params.get("filters")}". {e}'
535
+ raise ValidationException(msg)
536
+
537
+ items = self._importData(
538
+ parent,
539
+ parentType,
540
+ {
541
+ 'limit': limit,
542
+ 'search_filters': search_filters,
543
+ },
544
+ progress,
545
+ user,
546
+ )
547
+
548
+ if not items:
549
+ msg = 'No studies matching the search filters were found'
550
+ raise ValidationException(msg)
551
+
552
+ return items
553
+
515
554
  @property
516
555
  def auth_session(self):
517
556
  return _create_auth_session(self.assetstore_meta)
@@ -1,15 +1,14 @@
1
- import json
2
-
3
1
  from girder.api import access
4
2
  from girder.api.describe import Description, autoDescribeRoute
5
3
  from girder.api.rest import Resource
6
4
  from girder.constants import TokenScope
7
5
  from girder.exceptions import RestException
8
6
  from girder.models.assetstore import Assetstore
9
- from girder.utility import assetstore_utilities
10
7
  from girder.utility.model_importer import ModelImporter
11
8
  from girder.utility.progress import ProgressContext
12
9
 
10
+ from .dicomweb_assetstore_adapter import DICOMwebAssetstoreAdapter
11
+
13
12
 
14
13
  class DICOMwebAssetstoreResource(Resource):
15
14
  def __init__(self):
@@ -34,39 +33,14 @@ class DICOMwebAssetstoreResource(Resource):
34
33
  parent = ModelImporter.model(destinationType).load(params['destinationId'], force=True,
35
34
  exc=True)
36
35
 
37
- limit = params.get('limit') or None
38
- if limit is not None:
39
- error_msg = 'Invalid limit'
40
- try:
41
- limit = int(limit)
42
- except ValueError:
43
- raise RestException(error_msg)
44
-
45
- if limit < 1:
46
- raise RestException(error_msg)
47
-
48
- try:
49
- search_filters = json.loads(params.get('filters') or '{}')
50
- except json.JSONDecodeError as e:
51
- msg = f'Invalid filters: {e}'
52
- raise RestException(msg)
53
-
54
- adapter = assetstore_utilities.getAssetstoreAdapter(assetstore)
55
- items = adapter.importData(
36
+ return DICOMwebAssetstoreAdapter(assetstore).importData(
56
37
  parent,
57
38
  destinationType,
58
- {
59
- 'limit': limit,
60
- 'search_filters': search_filters,
61
- },
39
+ params,
62
40
  progress,
63
41
  user,
64
42
  )
65
43
 
66
- if not items:
67
- msg = 'No studies matching the search filters were found'
68
- raise RestException(msg)
69
-
70
44
  @access.admin(scope=TokenScope.DATA_WRITE)
71
45
  @autoDescribeRoute(
72
46
  Description('Import references to DICOM objects from a DICOMweb server')
@@ -40,8 +40,7 @@ class DICOMGirderTileSource(DICOMFileTileSource, GirderTileSource):
40
40
  assetstore_type = assetstore['type'] if assetstore else None
41
41
  if assetstore_type == getattr(AssetstoreType, 'DICOMWEB', '__undefined__'):
42
42
  return self._getDICOMwebLargeImagePath(assetstore)
43
- else:
44
- return self._getFilesystemLargeImagePath()
43
+ return self._getFilesystemLargeImagePath()
45
44
 
46
45
  def _getFilesystemLargeImagePath(self):
47
46
  filelist = [
@@ -1,7 +1,6 @@
1
- import './routes';
2
-
3
1
  // Extends and overrides API
4
2
  import './constants';
3
+ import './views/DICOMwebImportView';
5
4
  import './views/AssetstoresView';
6
5
  import './views/EditAssetstoreWidget';
7
6
  import './views/NewAssetstoreWidget';
@@ -1,6 +1,6 @@
1
1
  .g-assetstore-import-button-container
2
2
  a.g-dwas-import-button.btn.btn-sm.btn-success(
3
- href=`#dicomweb_assetstore/${assetstore.get('_id')}/import`,
3
+ href=`#assetstore/${assetstore.get('_id')}/import`,
4
4
  title="Import references to DICOM objects from a DICOMweb server")
5
5
  i.icon-link-ext
6
6
  | Import data
@@ -5,6 +5,9 @@ import router from '@girder/core/router';
5
5
  import View from '@girder/core/views/View';
6
6
  import { restRequest } from '@girder/core/rest';
7
7
 
8
+ import { assetstoreImportViewMap } from '@girder/core/views/body/AssetstoresView';
9
+ import { AssetstoreType } from '@girder/core/constants';
10
+
8
11
  import DWASImportTemplate from '../templates/assetstoreImport.pug';
9
12
 
10
13
  const DICOMwebImportView = View.extend({
@@ -24,12 +27,12 @@ const DICOMwebImportView = View.extend({
24
27
  }
25
28
 
26
29
  this.$('.g-submit-dwas-import').addClass('disabled');
27
- this.model.off().on('g:imported', function () {
30
+ this.assetstore.off().on('g:imported', function () {
28
31
  router.navigate(destinationType + '/' + destinationId, { trigger: true });
29
32
  }, this).on('g:error', function (err) {
30
33
  this.$('.g-submit-dwas-import').removeClass('disabled');
31
34
  this.$('.g-validation-failed-message').html(err.responseJSON.message);
32
- }, this).dicomwebImport({
35
+ }, this).import({
33
36
  destinationId,
34
37
  destinationType,
35
38
  limit,
@@ -40,7 +43,7 @@ const DICOMwebImportView = View.extend({
40
43
  'click .g-open-browser': '_openBrowser'
41
44
  },
42
45
 
43
- initialize: function () {
46
+ initialize: function (settings) {
44
47
  this._browserWidgetView = new BrowserWidget({
45
48
  parentView: this,
46
49
  titleText: 'Destination',
@@ -75,12 +78,13 @@ const DICOMwebImportView = View.extend({
75
78
  }
76
79
  });
77
80
  });
81
+ this.assetstore = settings.assetstore;
78
82
  this.render();
79
83
  },
80
84
 
81
85
  render: function () {
82
86
  this.$el.html(DWASImportTemplate({
83
- assetstore: this.model
87
+ assetstore: this.assetstore
84
88
  }));
85
89
 
86
90
  return this;
@@ -91,4 +95,6 @@ const DICOMwebImportView = View.extend({
91
95
  }
92
96
  });
93
97
 
98
+ assetstoreImportViewMap[AssetstoreType.DICOMWEB] = DICOMwebImportView;
99
+
94
100
  export default DICOMwebImportView;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: large-image-source-dicom
3
- Version: 1.27.5.dev4
3
+ Version: 1.30.7.dev10
4
4
  Summary: A DICOM tilesource for large_image.
5
5
  Home-page: https://github.com/girder/large_image
6
6
  Author: Kitware, Inc.
@@ -15,9 +15,17 @@ Classifier: Programming Language :: Python :: 3.9
15
15
  Classifier: Programming Language :: Python :: 3.10
16
16
  Classifier: Programming Language :: Python :: 3.11
17
17
  Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
18
19
  Requires-Python: >=3.8
19
- Provides-Extra: girder
20
+ Description-Content-Type: text/x-rst
20
21
  License-File: LICENSE
22
+ Requires-Dist: large-image>=1.30.7.dev10
23
+ Requires-Dist: wsidicom!=0.21.3,>=0.9.0
24
+ Requires-Dist: pydicom<3; python_version < "3.10"
25
+ Requires-Dist: pydicom; python_version >= "3.10"
26
+ Provides-Extra: girder
27
+ Requires-Dist: girder-large-image>=1.30.7.dev10; extra == "girder"
28
+ Requires-Dist: girder>=3.2.3; extra == "girder"
21
29
 
22
30
  A DICOM tilesource for large_image.
23
31
 
@@ -2,6 +2,7 @@ LICENSE
2
2
  README.rst
3
3
  pyproject.toml
4
4
  setup.py
5
+ docs/dicomweb_assetstore.rst
5
6
  large_image_source_dicom/__init__.py
6
7
  large_image_source_dicom/dicom_metadata.py
7
8
  large_image_source_dicom/dicom_tags.py
@@ -20,8 +21,6 @@ large_image_source_dicom/assetstore/rest.py
20
21
  large_image_source_dicom/web_client/constants.js
21
22
  large_image_source_dicom/web_client/main.js
22
23
  large_image_source_dicom/web_client/package.json
23
- large_image_source_dicom/web_client/routes.js
24
- large_image_source_dicom/web_client/models/AssetstoreModel.js
25
24
  large_image_source_dicom/web_client/templates/assetstoreImport.pug
26
25
  large_image_source_dicom/web_client/templates/dicomwebAssetstoreCreate.pug
27
26
  large_image_source_dicom/web_client/templates/dicomwebAssetstoreEditFields.pug
@@ -0,0 +1,12 @@
1
+ large-image>=1.30.7.dev10
2
+ wsidicom!=0.21.3,>=0.9.0
3
+
4
+ [:python_version < "3.10"]
5
+ pydicom<3
6
+
7
+ [:python_version >= "3.10"]
8
+ pydicom
9
+
10
+ [girder]
11
+ girder-large-image>=1.30.7.dev10
12
+ girder>=3.2.3
@@ -20,8 +20,7 @@ def prerelease_local_scheme(version):
20
20
 
21
21
  if os.getenv('CIRCLE_BRANCH') in ('master', ):
22
22
  return ''
23
- else:
24
- return get_local_node_and_date(version)
23
+ return get_local_node_and_date(version)
25
24
 
26
25
 
27
26
  try:
@@ -41,11 +40,14 @@ entry_points = {
41
40
  ],
42
41
  }
43
42
 
43
+ girder_extras = [f'girder-large-image{limit_version}']
44
+
44
45
  if sys.version_info >= (3, 9):
45
46
  # For Python >= 3.9, include the DICOMweb plugin
46
47
  entry_points['girder.plugin'] = [
47
48
  'dicomweb = large_image_source_dicom.girder_plugin:DICOMwebPlugin',
48
49
  ]
50
+ girder_extras.append('girder>=3.2.3')
49
51
 
50
52
  setup(
51
53
  name='large-image-source-dicom',
@@ -53,6 +55,7 @@ setup(
53
55
  'fallback_version': '0.0.0'},
54
56
  description=description,
55
57
  long_description=long_description,
58
+ long_description_content_type='text/x-rst',
56
59
  license='Apache Software License 2.0',
57
60
  author='Kitware, Inc.',
58
61
  author_email='kitware@kitware.com',
@@ -65,13 +68,16 @@ setup(
65
68
  'Programming Language :: Python :: 3.10',
66
69
  'Programming Language :: Python :: 3.11',
67
70
  'Programming Language :: Python :: 3.12',
71
+ 'Programming Language :: Python :: 3.13',
68
72
  ],
69
73
  install_requires=[
70
74
  f'large-image{limit_version}',
71
- 'wsidicom>=0.9.0',
75
+ 'wsidicom>=0.9.0,!=0.21.3',
76
+ 'pydicom<3; python_version < "3.10"',
77
+ 'pydicom; python_version >= "3.10"',
72
78
  ],
73
79
  extras_require={
74
- 'girder': f'girder-large-image{limit_version}',
80
+ 'girder': girder_extras,
75
81
  },
76
82
  include_package_data=True,
77
83
  keywords='large_image, tile source',
@@ -12,8 +12,8 @@ pytestmark = [
12
12
  ]
13
13
 
14
14
 
15
- @pytest.mark.girder()
16
- @pytest.mark.girder_client()
15
+ @pytest.mark.girder
16
+ @pytest.mark.girder_client
17
17
  @pytest.mark.plugin('large_image')
18
18
  @pytest.mark.plugin('dicomweb')
19
19
  def testDICOMWebClient(boundServer, fsAssetstore, db):
@@ -143,7 +143,7 @@ describe('DICOMWeb assetstore', function () {
143
143
  });
144
144
 
145
145
  waitsFor(function () {
146
- return $('.g-validation-failed-message').html() === 'Invalid limit';
146
+ return $('.g-validation-failed-message').html() === 'Invalid limit: 1.3';
147
147
  }, 'Invalid limit check (float)');
148
148
 
149
149
  runs(function () {
@@ -156,7 +156,7 @@ describe('DICOMWeb assetstore', function () {
156
156
  });
157
157
 
158
158
  waitsFor(function () {
159
- return $('.g-validation-failed-message').html() === 'Invalid limit';
159
+ return $('.g-validation-failed-message').html() === 'Invalid limit: -1';
160
160
  }, 'Invalid limit check (negative)');
161
161
 
162
162
  runs(function () {
@@ -1,20 +0,0 @@
1
- import AssetstoreModel from '@girder/core/models/AssetstoreModel';
2
- import { restRequest } from '@girder/core/rest';
3
-
4
- /**
5
- * Extends the core assetstore model to add DICOMweb-specific functionality.
6
- */
7
- AssetstoreModel.prototype.dicomwebImport = function (params) {
8
- return restRequest({
9
- url: 'dicomweb_assetstore/' + this.get('_id') + '/import',
10
- type: 'POST',
11
- data: params,
12
- error: null
13
- }).done(() => {
14
- this.trigger('g:imported');
15
- }).fail((err) => {
16
- this.trigger('g:error', err);
17
- });
18
- };
19
-
20
- export default AssetstoreModel;
@@ -1,17 +0,0 @@
1
- import router from '@girder/core/router';
2
- import events from '@girder/core/events';
3
-
4
- import AssetstoreModel from './models/AssetstoreModel';
5
- import DICOMwebImportView from './views/DICOMwebImportView';
6
-
7
- router.route('dicomweb_assetstore/:id/import', 'dwasImport', function (id) {
8
- // Fetch the assetstore by id, then render the view.
9
- const assetstore = new AssetstoreModel({ _id: id });
10
- assetstore.once('g:fetched', function () {
11
- events.trigger('g:navigateTo', DICOMwebImportView, {
12
- model: assetstore
13
- });
14
- }).once('g:error', function () {
15
- router.navigate('assetstores', { trigger: true });
16
- }).fetch();
17
- });
@@ -1,5 +0,0 @@
1
- large-image>=1.27.5.dev4
2
- wsidicom>=0.9.0
3
-
4
- [girder]
5
- girder-large-image>=1.27.5.dev4