diff --git a/import_tracker/__init__.py b/import_tracker/__init__.py
index ff3d0e4..110848e 100644
--- a/import_tracker/__init__.py
+++ b/import_tracker/__init__.py
@@ -1,6 +1,6 @@
import time
-from girder import plugin
+from girder import logger, plugin
from girder.api.describe import autoDescribeRoute
from girder.api.rest import boundHandler
from girder.constants import AccessType
@@ -162,12 +162,100 @@ def shouldImportFileWrapper(self, path, params):
AbstractAssetstoreAdapter.shouldImportFile = shouldImportFileWrapper
+def wrapDICOMImport(assetstoreResource):
+ baseImportData = assetstoreResource.importData
+ baseImportData.description.param(
+ 'excludeExisting',
+ 'If true, then a file with an import path that is already in the '
+ 'system is not imported, even if it is not in the destination '
+ 'hierarchy.', dataType='boolean', required=False, default=False)
+
+ @boundHandler(ctx=assetstoreResource)
+ @autoDescribeRoute(baseImportData.description)
+ def dwaImportDataWrapper(self, assetstore, destinationId, destinationType, filters,
+ progress, excludeExisting):
+
+ user = self.getCurrentUser()
+ params = {
+ 'destinationId': destinationId,
+ 'destinationType': destinationType,
+ 'filters': filters,
+ 'progress': str(progress).lower(),
+ }
+ if excludeExisting:
+ params['excludeExisting'] = str(excludeExisting).lower()
+
+ importRecord = AssetstoreImport().createAssetstoreImport(assetstore, params)
+ job = Job().createJob(
+ title=f'Import from {assetstore["name"]}',
+ type='assetstore_import',
+ public=False,
+ user=user,
+ kwargs=params,
+ )
+ job = Job().updateJob(job, '%s - Starting import from %s\n' % (
+ time.strftime('%Y-%m-%d %H:%M:%S'),
+ assetstore['name']
+ ), status=JobStatus.RUNNING)
+
+ try:
+ with ProgressContext(progress, user=user, title='Importing data') as ctx:
+ try:
+ jobRec = {
+ 'id': str(job['_id']),
+ 'count': 0,
+ 'skip': 0,
+ 'lastlog': time.time(),
+ 'logcount': 0,
+ }
+ self._importData(
+ assetstore,
+ params={
+ **params,
+ '_job': jobRec},
+ progress=ctx)
+
+ success = True
+ Job().updateJob(job, '%s - Finished. Checked %d, skipped %d\n' % (
+ time.strftime('%Y-%m-%d %H:%M:%S'),
+ jobRec['count'], jobRec['skip'],
+ ), status=JobStatus.SUCCESS)
+
+ except ImportTrackerCancelError:
+ Job().updateJob(job, '%s - Canceled' % (
+ time.strftime('%Y-%m-%d %H:%M:%S'),
+ ))
+ success = 'canceled'
+
+ except Exception as exc:
+ Job().updateJob(job, '%s - Failed with %s\n' % (
+ time.strftime('%Y-%m-%d %H:%M:%S'),
+ exc,
+ ), status=JobStatus.ERROR)
+ success = False
+
+ importRecord = AssetstoreImport().markEnded(importRecord, success)
+ return importRecord
+
+ for key in {'accessLevel', 'description', 'requiredScopes'}:
+ setattr(dwaImportDataWrapper, key, getattr(baseImportData, key))
+
+ assetstoreResource.importData = dwaImportDataWrapper
+ assetstoreResource.removeRoute('POST', (':id', 'import'))
+ assetstoreResource.route('POST', (':id', 'import'), assetstoreResource.importData)
+
+
class GirderPlugin(plugin.GirderPlugin):
DISPLAY_NAME = 'import_tracker'
CLIENT_SOURCE_PATH = 'web_client'
def load(self, info):
plugin.getPlugin('jobs').load(info)
+ try:
+ import large_image_source_dicom # noqa
+ plugin.getPlugin('dicomweb').load(info)
+ except ImportError:
+ pass
ModelImporter.registerModel(
'assetstoreImport', AssetstoreImport, 'import_tracker'
)
@@ -178,3 +266,8 @@ def load(self, info):
wrapImportData(info['apiRoot'].assetstore)
info['apiRoot'].folder.route('PUT', (':id', 'move'), moveFolder)
+
+ if hasattr(info['apiRoot'], 'dicomweb_assetstore'):
+ wrapDICOMImport(info['apiRoot'].dicomweb_assetstore)
+ else:
+ logger.info('dicomweb_assetstore not found')
diff --git a/import_tracker/web_client/main.js b/import_tracker/web_client/main.js
index 2e5c407..94b072a 100644
--- a/import_tracker/web_client/main.js
+++ b/import_tracker/web_client/main.js
@@ -1,3 +1,5 @@
+import $ from 'jquery';
+
import AssetstoreView from '@girder/core/views/body/AssetstoresView';
import FilesystemImportView from '@girder/core/views/body/FilesystemImportView';
import S3ImportView from '@girder/core/views/body/S3ImportView';
@@ -22,16 +24,23 @@ import './JobStatus';
wrap(AssetstoreView, 'render', function (render) {
// Call the underlying render function that we are wrapping
render.call(this);
-
- this.$el.find('.g-current-assetstores-container .g-body-title').after(
- 'View all past Imports'
- );
-
- // Inject new button into each assetstore
- const assetstores = this.collection.toArray();
- this.$('.g-assetstore-import-button-container').after(
- (i) => importDataButton({ importsPageLink: `#assetstore/${assetstores[i].id}/imports` })
- );
+ // defer adding buttons so optional plugins can render first.
+ window.setTimeout(() => {
+ this.$el.find('.g-current-assetstores-container .g-body-title').after(
+ 'View all past Imports'
+ );
+
+ // Inject new button into each assetstore
+ const assetstores = this.collection;
+ this.$('.g-assetstore-import-button-container').after(
+ function () {
+ // we can't just use the index of the after call, since not
+ // all assetstores will have import buttons.
+ const assetstore = assetstores.get($(this).closest('.g-assetstore-buttons').find('[cid]').attr('cid'));
+ return importDataButton({ importsPageLink: `#assetstore/${assetstore.id}/imports` });
+ }
+ );
+ }, 0);
});
// Add duplicate_files option to Import Asset form
diff --git a/import_tracker/web_client/templates/importList.pug b/import_tracker/web_client/templates/importList.pug
index 7147c4a..a8b2754 100644
--- a/import_tracker/web_client/templates/importList.pug
+++ b/import_tracker/web_client/templates/importList.pug
@@ -21,7 +21,8 @@ table.g-imports-list-table
-
var anyRegex = false;
var anyLeafed = false;
- var anyNoProgress;
+ var anyImportPath = false;
+ var anyNoProgress = false;
var otherParams = [];
var showCount = false;
for _import in imports
@@ -31,9 +32,11 @@ table.g-imports-list-table
- anyLeafed = true
if !_import.params.progress
- anyNoProgress = true
- - Object.keys(_import.params).forEach((key) => { if (['importPath', 'destinationId', 'destinationType', 'leafFoldersAsItems', 'progress', 'fileIncludeRegex', 'fileExcludeRegex'].indexOf(key) < 0 && otherParams.indexOf(key) < 0) { otherParams.push(key); } });
+ - Object.keys(_import.params).forEach((key) => { if (['importPath', 'destinationId', 'leafFoldersAsItems', 'progress', 'fileIncludeRegex', 'fileExcludeRegex'].indexOf(key) < 0 && otherParams.indexOf(key) < 0) { otherParams.push(key); } });
if _import._count
- - showCount = true;
+ - showCount = true
+ if _import.params.importPath
+ - anyImportPath = true
thead
tr
th Actions
@@ -42,7 +45,8 @@ table.g-imports-list-table
th Started
th Ended
th Assetstore Name
- th Import Path
+ if anyImportPath
+ th Import Path
//- th Destination Type
th Destination Path
if anyLeafed
@@ -59,11 +63,11 @@ table.g-imports-list-table
tr
td
if assetstoreExists[i]
- div(style='display: flex; justify-content: flex-end;')
+ div(style='display: flex; padding-left: 10px; padding-right: 10px;')
button.re-import-btn.btn.btn-sm.btn-success(index=i, disabled=(_import._destinationPath =='does not exist'))
i.icon-cw
| Re-Import
- button.re-import-edit-btn.btn.btn-sm.btn-primary(index=i, style='margin-left: 5px;', data-toggle='tooltip', title='Edit Import Parameters')
+ button.re-import-edit-btn.btn.btn-sm.btn-primary(index=i, style='margin-left: 1em;', data-toggle='tooltip', title='Edit Import Parameters')
i.icon-pencil
if showCount
td(data-id=_import._count, data-toggle='tooltip', title=_import._count !== 1 ? `Imported ${_import._count} times` : 'Imported once')
@@ -74,11 +78,9 @@ table.g-imports-list-table
span= _import.ended ? moment(_import.ended).format('YYYY-MM-DD HH:mm:ss.SSS') : ''
td(data-id=_import._assetstoreName, data-toggle='tooltip', title=_import._assetstoreName)
span= _import._assetstoreName
- td(data-id=_import.params.importPath, data-toggle='tooltip', title=_import.params.importPath)
- span= _import.params.importPath
- //-
- td
- span= _import.params.destinationType
+ if anyImportPath
+ td(data-id=_import.params.importPath, data-toggle='tooltip', title=_import.params.importPath)
+ span= _import.params.importPath
td(data-id=_import.params.destinationId, data-toggle='tooltip', title=_import._destinationPath + '\n' + _import.params.destinationId)
span= _import._destinationPath
if anyLeafed
diff --git a/import_tracker/web_client/views/importList.js b/import_tracker/web_client/views/importList.js
index edf8354..448d252 100644
--- a/import_tracker/web_client/views/importList.js
+++ b/import_tracker/web_client/views/importList.js
@@ -2,6 +2,7 @@ import $ from 'jquery';
import moment from 'moment';
import AssetstoreModel from '@girder/core/models/AssetstoreModel';
+import { AssetstoreType } from '@girder/core/constants';
import View from '@girder/core/views/View';
import router from '@girder/core/router';
import { restRequest } from '@girder/core/rest';
@@ -22,11 +23,20 @@ var importList = View.extend({
const assetstore = new AssetstoreModel({ _id: importEvent.assetstoreId });
const destType = importEvent.params.destinationType;
const destId = importEvent.params.destinationId;
+
assetstore.off('g:imported').on('g:imported', function () {
router.navigate(destType + '/' + destId, { trigger: true });
}, this).on('g:error', function (resp) {
this.$('.g-validation-failed-message').text(resp.responseJSON.message);
- }, this).import(importEvent.params);
+ }, this);
+
+ assetstore.once('g:fetched', () => {
+ if (assetstore.get('type') === AssetstoreType.DICOMWEB) {
+ assetstore.dicomwebImport(importEvent.params);
+ } else {
+ assetstore.import(importEvent.params);
+ }
+ }).fetch();
},
'click .re-import-edit-btn': function (e) {
const index = Number($(e.currentTarget).attr('index'));
@@ -35,10 +45,24 @@ var importList = View.extend({
return;
}
+ // Navigate to re-import page
+ const navigate = (assetstoreId, importId) => {
+ const assetstore = new AssetstoreModel({ _id: assetstoreId });
+ assetstore.once('g:fetched', () => {
+ if (assetstore.get('type') === AssetstoreType.DICOMWEB) {
+ // Avoid adding previous import data for DICOMweb imports by navigating to blank import
+ // TODO: Add DICOMweb-specific re-import view
+ router.navigate(`dicomweb_assetstore/${assetstoreId}/import`, { trigger: true });
+ } else {
+ router.navigate(`assetstore/${assetstoreId}/re-import/${importId}`, { trigger: true });
+ }
+ }).fetch();
+ };
+
const assetstoreId = importEvent.assetstoreId;
- const importId = importEvent._id;
+ const importId = importEvent._id; // Only individual imports have an _id
if (importId) {
- router.navigate(`assetstore/${assetstoreId}/re-import/${importId}`, { trigger: true });
+ navigate(assetstoreId, importId);
return;
}
@@ -53,7 +77,7 @@ var importList = View.extend({
i.params.destinationType === importEvent.params.destinationType
)[0]._id;
- router.navigate(`assetstore/${assetstoreId}/re-import/${importId}`, { trigger: true });
+ navigate(assetstoreId, importId);
});
}
},