Skip to content

Commit

Permalink
Merge pull request #1504 from willdunklin/amend-dicomweb-assetstore
Browse files Browse the repository at this point in the history
Make DICOMweb assetstore imports compatible with Girder generics
  • Loading branch information
manthey authored Apr 25, 2024
2 parents 6e91b8c + 73d4e1b commit 3b59156
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 100 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import json

import cherrypy
import requests
from large_image_source_dicom.dicom_tags import dicom_key_to_tag
Expand Down Expand Up @@ -394,31 +396,10 @@ def _infer_file_size_singlepart_content_length(self, file):
except ValueError:
return

def importData(self, parent, parentType, params, progress, user, **kwargs):
"""
Import DICOMweb WSI instances from a DICOMweb server.
:param parent: The parent object to import into.
:param parentType: The model type of the parent object.
:type parentType: str
:param params: Additional parameters required for the import process.
This dictionary may include the following keys:
:limit: (optional) limit the number of studies imported.
:search_filters: (optional) a dictionary of additional search
filters to use with dicomweb_client's `search_for_series()`
function.
:type params: dict
:param progress: Object on which to record progress if possible.
:type progress: :py:class:`girder.utility.progress.ProgressContext`
:param user: The Girder user performing the import.
:type user: dict or None
:return: a list of items that were created
"""
def _importData(self, parent, parentType, params, progress, user):
if parentType not in ('folder', 'user', 'collection'):
msg = f'Invalid parent type: {parentType}'
raise RuntimeError(msg)
raise ValidationException(msg)

limit = params.get('limit')
search_filters = params.get('search_filters', {})
Expand Down Expand Up @@ -512,6 +493,65 @@ def importData(self, parent, parentType, params, progress, user, **kwargs):

return items

def importData(self, parent, parentType, params, progress, user, **kwargs):
"""
Import DICOMweb WSI instances from a DICOMweb server.
:param parent: The parent object to import into.
:param parentType: The model type of the parent object.
:type parentType: str
:param params: Additional parameters required for the import process.
This dictionary may include the following keys:
:limit: (optional) limit the number of studies imported.
:filters: (optional) a dictionary/JSON string of additional search
filters to use with dicomweb_client's `search_for_series()`
function.
:type params: dict
:param progress: Object on which to record progress if possible.
:type progress: :py:class:`girder.utility.progress.ProgressContext`
:param user: The Girder user performing the import.
:type user: dict or None
:return: a list of items that were created
"""
# Validate the parameters
limit = params.get('limit') or None
if limit is not None:
error_msg = f'Invalid limit: {limit}'
try:
limit = int(limit)
except ValueError:
raise ValidationException(error_msg)

if limit < 1:
raise ValidationException(error_msg)

search_filters = params.get('filters', {})
if isinstance(search_filters, str):
try:
search_filters = json.loads(search_filters)
except json.JSONDecodeError as e:
msg = f'Invalid filters: "{params.get("filters")}". {e}'
raise ValidationException(msg)

items = self._importData(
parent,
parentType,
{
'limit': limit,
'search_filters': search_filters,
},
progress,
user,
)

if not items:
msg = 'No studies matching the search filters were found'
raise ValidationException(msg)

return items

@property
def auth_session(self):
return _create_auth_session(self.assetstore_meta)
Expand Down
34 changes: 4 additions & 30 deletions sources/dicom/large_image_source_dicom/assetstore/rest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import json

from girder.api import access
from girder.api.describe import Description, autoDescribeRoute
from girder.api.rest import Resource
from girder.constants import TokenScope
from girder.exceptions import RestException
from girder.models.assetstore import Assetstore
from girder.utility import assetstore_utilities
from girder.utility.model_importer import ModelImporter
from girder.utility.progress import ProgressContext

from .dicomweb_assetstore_adapter import DICOMwebAssetstoreAdapter


class DICOMwebAssetstoreResource(Resource):
def __init__(self):
Expand All @@ -34,39 +33,14 @@ def _importData(self, assetstore, params, progress):
parent = ModelImporter.model(destinationType).load(params['destinationId'], force=True,
exc=True)

limit = params.get('limit') or None
if limit is not None:
error_msg = 'Invalid limit'
try:
limit = int(limit)
except ValueError:
raise RestException(error_msg)

if limit < 1:
raise RestException(error_msg)

try:
search_filters = json.loads(params.get('filters') or '{}')
except json.JSONDecodeError as e:
msg = f'Invalid filters: {e}'
raise RestException(msg)

adapter = assetstore_utilities.getAssetstoreAdapter(assetstore)
items = adapter.importData(
return DICOMwebAssetstoreAdapter(assetstore).importData(
parent,
destinationType,
{
'limit': limit,
'search_filters': search_filters,
},
params,
progress,
user,
)

if not items:
msg = 'No studies matching the search filters were found'
raise RestException(msg)

@access.admin(scope=TokenScope.DATA_WRITE)
@autoDescribeRoute(
Description('Import references to DICOM objects from a DICOMweb server')
Expand Down
3 changes: 1 addition & 2 deletions sources/dicom/large_image_source_dicom/web_client/main.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import './routes';

// Extends and overrides API
import './constants';
import './views/DICOMwebImportView';
import './views/AssetstoresView';
import './views/EditAssetstoreWidget';
import './views/NewAssetstoreWidget';

This file was deleted.

17 changes: 0 additions & 17 deletions sources/dicom/large_image_source_dicom/web_client/routes.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.g-assetstore-import-button-container
a.g-dwas-import-button.btn.btn-sm.btn-success(
href=`#dicomweb_assetstore/${assetstore.get('_id')}/import`,
href=`#assetstore/${assetstore.get('_id')}/import`,
title="Import references to DICOM objects from a DICOMweb server")
i.icon-link-ext
| Import data
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import router from '@girder/core/router';
import View from '@girder/core/views/View';
import { restRequest } from '@girder/core/rest';

import { assetstoreImportViewMap } from '@girder/core/views/body/AssetstoresView';
import { AssetstoreType } from '@girder/core/constants';

import DWASImportTemplate from '../templates/assetstoreImport.pug';

const DICOMwebImportView = View.extend({
Expand All @@ -24,12 +27,12 @@ const DICOMwebImportView = View.extend({
}

this.$('.g-submit-dwas-import').addClass('disabled');
this.model.off().on('g:imported', function () {
this.assetstore.off().on('g:imported', function () {
router.navigate(destinationType + '/' + destinationId, { trigger: true });
}, this).on('g:error', function (err) {
this.$('.g-submit-dwas-import').removeClass('disabled');
this.$('.g-validation-failed-message').html(err.responseJSON.message);
}, this).dicomwebImport({
}, this).import({
destinationId,
destinationType,
limit,
Expand All @@ -40,7 +43,7 @@ const DICOMwebImportView = View.extend({
'click .g-open-browser': '_openBrowser'
},

initialize: function () {
initialize: function (settings) {
this._browserWidgetView = new BrowserWidget({
parentView: this,
titleText: 'Destination',
Expand Down Expand Up @@ -75,12 +78,13 @@ const DICOMwebImportView = View.extend({
}
});
});
this.assetstore = settings.assetstore;
this.render();
},

render: function () {
this.$el.html(DWASImportTemplate({
assetstore: this.model
assetstore: this.assetstore
}));

return this;
Expand All @@ -91,4 +95,6 @@ const DICOMwebImportView = View.extend({
}
});

assetstoreImportViewMap[AssetstoreType.DICOMWEB] = DICOMwebImportView;

export default DICOMwebImportView;
5 changes: 4 additions & 1 deletion sources/dicom/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,14 @@ def prerelease_local_scheme(version):
],
}

girder_extras = [f'girder-large-image{limit_version}']

if sys.version_info >= (3, 9):
# For Python >= 3.9, include the DICOMweb plugin
entry_points['girder.plugin'] = [
'dicomweb = large_image_source_dicom.girder_plugin:DICOMwebPlugin',
]
girder_extras.append('girder>=3.2.3')

setup(
name='large-image-source-dicom',
Expand All @@ -71,7 +74,7 @@ def prerelease_local_scheme(version):
'wsidicom>=0.9.0',
],
extras_require={
'girder': f'girder-large-image{limit_version}',
'girder': girder_extras,
},
include_package_data=True,
keywords='large_image, tile source',
Expand Down
4 changes: 2 additions & 2 deletions sources/dicom/test_dicom/web_client_specs/dicomWebSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ describe('DICOMWeb assetstore', function () {
});

waitsFor(function () {
return $('.g-validation-failed-message').html() === 'Invalid limit';
return $('.g-validation-failed-message').html() === 'Invalid limit: 1.3';
}, 'Invalid limit check (float)');

runs(function () {
Expand All @@ -156,7 +156,7 @@ describe('DICOMWeb assetstore', function () {
});

waitsFor(function () {
return $('.g-validation-failed-message').html() === 'Invalid limit';
return $('.g-validation-failed-message').html() === 'Invalid limit: -1';
}, 'Invalid limit check (negative)');

runs(function () {
Expand Down

0 comments on commit 3b59156

Please sign in to comment.