def testMakeDicomItem(self): admin, user = self.users # create a collection, folder, and item collection = Collection().createCollection('collection2', admin, public=True) folder = Folder().createFolder(collection, 'folder2', parentType='collection', public=True) item = Item().createItem('item2', admin, folder) # Upload files self._uploadDicomFiles(item, admin) # Check the endpoint 'parseDicom' for an admin user dicomItem = Item().load(item['_id'], force=True) dicomItem = self._purgeDicomItem(dicomItem) path = '/item/%s/parseDicom' % dicomItem.get('_id') resp = self.request(path=path, method='POST', user=admin) self.assertStatusOk(resp) dicomItem = Item().load(item['_id'], force=True) self.assertIn('dicom', dicomItem) self.assertHasKeys(dicomItem['dicom'], ['meta', 'files']) # Check the endpoint 'parseDicom' for an non admin user dicomItem = Item().load(item['_id'], force=True) dicomItem = self._purgeDicomItem(dicomItem) path = '/item/%s/parseDicom' % dicomItem.get('_id') resp = self.request(path=path, method='POST', user=user) self.assertStatus(resp, 403)
def testAnnotationCopy(self, server, admin): publicFolder = utilities.namedFolder(admin, 'Public') # create annotation on an item itemSrc = Item().createItem('sample', admin, publicFolder) annot = Annotation().createAnnotation(itemSrc, admin, sampleAnnotation) assert Annotation().load(annot['_id'], user=admin) is not None # Create a new item itemDest = Item().createItem('sample', admin, publicFolder) # Copy the annotation from one item to an other resp = server.request(path='/annotation/{}/copy'.format(annot['_id']), method='POST', user=admin, params={'itemId': itemDest.get('_id')}) assert utilities.respStatus(resp) == 200 itemDest = Item().load(itemDest.get('_id'), level=AccessType.READ) # Check if the annotation is in the destination item resp = server.request(path='/annotation', method='GET', user=admin, params={ 'itemId': itemDest.get('_id'), 'name': 'sample' }) assert utilities.respStatus(resp) == 200 assert resp.json is not None
def _virtualItemPosition(self, event): params = event.info['params'] response = _virtualChildItemsFind(self, params) if response is None: return # This is not a virtual folder child listing request q, sort, user, limit, offset = response itemId = event.info['id'] item = Item().load(itemId, user=user, level=AccessType.READ) if not len(sort): raise RestException('Invalid sort mode.') filters = [] for idx in range(len(sort) + 1): dir = '$lt' if sort[min(idx, len(sort) - 1)][1] == SortDir.ASCENDING else '$gt' filter = {} for idx2 in range(idx): filter[sort[idx2][0]] = item.get(sort[idx2][0]) if idx < len(sort): filter[sort[idx][0]] = {dir: item.get(sort[idx][0])} else: filter['_id'] = {dir: item['_id']} filters.append(filter) q = {'$and': [q, {'$or': filters}]} items = Item().findWithPermissions(q, sort=sort, user=user, level=AccessType.READ, limit=limit, offset=offset) event.preventDefault().addResponse(items.count())
def _updateJob(event): """ Called when a job is saved, updated, or removed. If this is a large image job and it is ended, clean up after it. """ from girder.plugins.jobs.constants import JobStatus from girder.plugins.jobs.models.job import Job job = event.info[ 'job'] if event.name == 'jobs.job.update.after' else event.info meta = job.get('meta', {}) if (meta.get('creator') != 'large_image' or not meta.get('itemId') or meta.get('task') != 'createImageItem'): return status = job['status'] if event.name == 'model.job.remove' and status not in (JobStatus.ERROR, JobStatus.CANCELED, JobStatus.SUCCESS): status = JobStatus.CANCELED if status not in (JobStatus.ERROR, JobStatus.CANCELED, JobStatus.SUCCESS): return item = Item().load(meta['itemId'], force=True) if not item or 'largeImage' not in item: return if item.get('largeImage', {}).get('expected'): # We can get a SUCCESS message before we get the upload message, so # don't clear the expected status on success. if status != JobStatus.SUCCESS: del item['largeImage']['expected'] notify = item.get('largeImage', {}).get('notify') msg = None if notify: del item['largeImage']['notify'] if status == JobStatus.SUCCESS: msg = 'Large image created' elif status == JobStatus.CANCELED: msg = 'Large image creation canceled' else: # ERROR msg = 'FAILED: Large image creation failed' msg += ' for item %s' % item['name'] if (status in (JobStatus.ERROR, JobStatus.CANCELED) and 'largeImage' in item): del item['largeImage'] Item().save(item) if msg and event.name != 'model.job.remove': Job().updateJob(job, progressMessage=msg) if notify: Notification().createNotification( type='large_image.finished_image_item', data={ 'job_id': job['_id'], 'item_id': item['_id'], 'success': status == JobStatus.SUCCESS, 'status': status }, user={'_id': job.get('userId')}, expires=datetime.datetime.utcnow() + datetime.timedelta(seconds=30))
def _postUpload(event): """ Called when a file is uploaded. We check the parent item to see if it is expecting a large image upload, and if so we register this file as the result image. """ fileObj = event.info['file'] # There may not be an itemId (on thumbnails, for instance) if not fileObj.get('itemId'): return item = Item().load(fileObj['itemId'], force=True, exc=True) if item.get( 'largeImage', {}).get('expected') and (fileObj['name'].endswith('.tiff') or fileObj.get('mimeType') == 'image/tiff'): if fileObj.get('mimeType') != 'image/tiff': fileObj['mimeType'] = 'image/tiff' File().save(fileObj) del item['largeImage']['expected'] item['largeImage']['fileId'] = fileObj['_id'] item['largeImage']['sourceName'] = 'tiff' if fileObj['name'].endswith('.geo.tiff'): item['largeImage']['sourceName'] = 'gdal' Item().save(item)
def checkForLargeImageFiles(event): file = event.info possible = False mimeType = file.get('mimeType') if mimeType in ('image/tiff', 'image/x-tiff', 'image/x-ptif'): possible = True exts = [ext.split()[0] for ext in file.get('exts')] if set(exts[-2:]).intersection({ 'svs', 'ptif', 'tif', 'tiff', 'ndpi', 'mrxs', 'nc', 'ntf', 'nitf', 'scn' }): possible = True if not file.get('itemId') or not possible: return if not Setting().get(constants.PluginSettings.LARGE_IMAGE_AUTO_SET): return item = Item().load(file['itemId'], force=True, exc=False) if not item or item.get('largeImage'): return try: ImageItem().createImageItem(item, file, createJob=False) except Exception: # We couldn't automatically set this as a large image logger.info('Saved file %s cannot be automatically used as a ' 'largeImage' % str(file['_id']))
def testFileProcessHandler(self): admin, user = self.users # Create a collection, folder, and item collection = Collection().createCollection('collection1', admin, public=True) folder = Folder().createFolder(collection, 'folder1', parentType='collection', public=True) item = Item().createItem('item1', admin, folder) # Upload non-DICOM files self._uploadNonDicomFiles(item, admin) nonDicomItem = Item().load(item['_id'], force=True) self.assertIsNone(nonDicomItem.get('dicom')) # Upload DICOM files self._uploadDicomFiles(item, admin) # Check if the 'dicomItem' is well processed dicomItem = Item().load(item['_id'], force=True) self.assertIn('dicom', dicomItem) self.assertHasKeys(dicomItem['dicom'], ['meta', 'files']) # Check if the files list contain the good keys and all the file are well sorted for i in range(0, 4): self.assertTrue('_id' in dicomItem['dicom']['files'][i]) self.assertTrue('name' in dicomItem['dicom']['files'][i]) self.assertEqual(dicomItem['dicom']['files'][i]['name'], 'dicomFile{}.dcm'.format(i)) self.assertTrue('SeriesNumber' in dicomItem['dicom']['files'][i]['dicom']) self.assertTrue('InstanceNumber' in dicomItem['dicom']['files'][i]['dicom']) self.assertTrue('SliceLocation' in dicomItem['dicom']['files'][i]['dicom']) # Check the common metadata self.assertIsNotNone(dicomItem['dicom']['meta'])
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if token and 'authorizedUploadId' in token: user = User().load(token['userId'], force=True) item = Item().load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] Item().save(item) text = mail_utils.renderTemplate( 'authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) Token().remove(token)
def testCreationOnUpload(self): resp = self.request( path='/file', method='POST', user=self.admin, params={ 'parentType': 'folder', 'parentId': self.publicFolder['_id'], 'name': 'test.png', 'size': len(self.image), 'reference': json.dumps({ 'thumbnail': { 'width': 100 } }) }) self.assertStatusOk(resp) resp = self.request( path='/file/chunk', method='POST', user=self.admin, body=self.image, params={ 'offset': 0, 'uploadId': resp.json['_id'] }, type='image/png') self.assertStatusOk(resp) self.assertIn('itemId', resp.json) itemId = resp.json['itemId'] start = time.time() while time.time() - start < 15: # Wait for thumbnail creation item = Item().load(itemId, force=True) if item.get('_thumbnails'): break time.sleep(0.1) self.assertEqual(len(item['_thumbnails']), 1) file = File().load(item['_thumbnails'][0], force=True) with File().open(file) as fh: self.assertEqual(fh.read(2), b'\xff\xd8') # jpeg magic number
def _uploadComplete(event): """ Called after an upload finishes. We check if our current token is a special authorized upload token, and if so, delete it. TODO we could alternatively keep a reference count inside each token that authorized more than a single upload at a time, and just decrement it here. """ token = getCurrentToken() if token and 'authorizedUploadId' in token: user = User().load(token['userId'], force=True) item = Item().load(event.info['file']['itemId'], force=True) # Save the metadata on the item item['description'] = token['authorizedUploadDescription'] item['authorizedUploadEmail'] = token['authorizedUploadEmail'] Item().save(item) text = mail_utils.renderTemplate('authorized_upload.uploadFinished.mako', { 'itemId': item['_id'], 'itemName': item['name'], 'itemDescription': item.get('description', '') }) mail_utils.sendEmail(to=user['email'], subject='Authorized upload complete', text=text) Token().remove(token)
def file_upload_handler(event): file = event.info item_id = file.get('itemId') if item_id is None: return item = Item().load(item_id, force=True) if item and item.get('geometa') is None: if create_geometa(item, file): events.trigger(GEOMETA_CREATION_EVENT, info=event.info)
def testMetadataHandler(self, server, fsAssetstore, admin): file = utilities.uploadExternalFile('data/Easy1.png.sha512', admin, fsAssetstore) item = Item().load(file['itemId'], user=admin) utilities.uploadTestFile('sample.meta', admin, fsAssetstore, reference=json.dumps({ 'identifier': 'NotItemMetadataForItem', 'userId': str(admin['_id']), 'itemId': str(item['_id']), 'fileId': str(file['_id']), })) item = Item().load(file['itemId'], user=admin) assert item.get('meta', {}).get('sample') is None utilities.uploadTestFile('sample.meta', admin, fsAssetstore, reference=json.dumps({ 'identifier': 'NotItemMetadata', 'userId': str(admin['_id']), 'itemId': str(item['_id']), 'fileId': str(file['_id']), })) starttime = time.time() while time.time() < starttime + 10: item = Item().load(file['itemId'], user=admin) if 'sample' in item.get('meta', {}): break time.sleep(0.1) item = Item().load(file['itemId'], user=admin) assert item['meta']['sample'] == 'value' assert item['meta']['complex']['key1'] == 'value1'
def callback(event): job = event.info['job'] if job['kwargs'].get('fileId') != str(file_id): return SUCCESS = JobStatus.SUCCESS ERROR = JobStatus.ERROR CANCELED = JobStatus.CANCELED if job['status'] == SUCCESS: item_id = job['kwargs']['attachToId'] item = Item().load(item_id, user=user, level=AccessType.READ) thumbnails = item.get("_thumbnails", []) if len(thumbnails) > 0: thumbnail_id = thumbnails.pop() # remove old thumbnails if len(thumbnails) > 0: Item().update({'_id': item_id}, {'$set': { '_thumbnails': [thumbnail_id] }}) for thumb_id in thumbnails: file = File().load(thumb_id, user=user, level=AccessType.WRITE) File().remove(file) query = {'_id': model_id} updates = {} updates.setdefault('$set', {})[prop_name] = thumbnail_id update_result = super(Base, self).update(query, updates) if update_result.matched_count == 0: raise ValidationException('Invalid id (%s)' % model_id) if job['status'] in [SUCCESS, ERROR, CANCELED]: events.unbind('jobs.job.update.after', str(file_id)) return
def checkForLargeImageFiles(event): file = event.info possible = False mimeType = file.get('mimeType') if mimeType in girder_tilesource.KnownMimeTypes: possible = True exts = [ext.split()[0] for ext in file.get('exts') if ext] if set(exts[-2:]).intersection(girder_tilesource.KnownExtensions): possible = True if not file.get('itemId') or not possible: return if not Setting().get(constants.PluginSettings.LARGE_IMAGE_AUTO_SET): return item = Item().load(file['itemId'], force=True, exc=False) if not item or item.get('largeImage'): return try: ImageItem().createImageItem(item, file, createJob=False) except Exception: # We couldn't automatically set this as a large image girder.logger.info( 'Saved file %s cannot be automatically used as a largeImage' % str(file['_id']))
def _handleUpload(event): upload, file = event.info['upload'], event.info['file'] try: reference = json.loads(upload.get('reference')) except (TypeError, ValueError): return if isinstance(reference, dict) and 'interactive_thumbnail' in reference: item = Item().load(file['itemId'], force=True, exc=True) file['interactive_thumbnails_uid'] = file['name'] file['attachedToId'] = item['_id'] file['attachedToType'] = 'item' file['itemId'] = None File().save(file) if not item.get('hasInteractiveThumbnail'): Item().update({'_id': item['_id']}, {'$set': { 'hasInteractiveThumbnail': True }}, multi=False)
def testDicomViewer(self): admin, user = self.users # create a collection, folder, and item collection = Collection().createCollection('collection', admin, public=True) folder = Folder().createFolder(collection, 'folder', parentType='collection', public=True) item = Item().createItem('item', admin, folder) # test initial values path = '/item/%s/dicom' % item.get('_id') resp = self.request(path=path, user=admin) self.assertStatusOk(resp) self.assertEqual(resp.json, []) path = os.path.join(os.path.split(__file__)[0], 'test.dcm') with open(path, 'rb') as fp: Upload().uploadFromFile(fp, 25640, 'test.dcm', 'item', item, admin) path = os.path.join(os.path.split(__file__)[0], 'not-dicom.dcm') with open(path, 'rb') as fp: Upload().uploadFromFile(fp, 7590, 'not-dicom.dcm', 'item', item, admin) # test dicom endpoint start = time.time() while True: try: path = '/item/%s/dicom' % item.get('_id') resp = self.request(path=path, user=admin) break except AssertionError: if time.time() - start > 15: raise time.sleep(0.5) self.assertStatusOk(resp) # one dicom file found files = resp.json self.assertEqual(len(files), 1) # dicom tags present file = files[0] dicom = file['dicom'] self.assertEqual(bool(dicom), True) # dicom tags correct self.assertEqual(dicom['Rows'], 80) self.assertEqual(dicom['Columns'], 150) # test filters path = '/item/%s/dicom' % item.get('_id') resp = self.request(path=path, user=admin, params=dict(filters='Rows')) self.assertStatusOk(resp) dicom = resp.json[0]['dicom'] self.assertEqual(dicom['Rows'], 80) self.assertEqual(dicom.get('Columns'), None) # test non-admin force path = '/item/%s/dicom' % item.get('_id') resp = self.request(path=path, user=user, params=dict(force=True)) self.assertStatus(resp, 403)
def testItemAnnotationEndpoints(self, server, user, admin): publicFolder = utilities.namedFolder(admin, 'Public') # create two annotations on an item itemSrc = Item().createItem('sample', admin, publicFolder) annot = Annotation().createAnnotation(itemSrc, admin, sampleAnnotation) annot = Annotation().setPublic(annot, False, True) Annotation().createAnnotation(itemSrc, admin, sampleAnnotationEmpty) # Get all annotations for that item as the user resp = server.request(path='/annotation/item/{}'.format( itemSrc['_id']), user=user) assert utilities.respStatus(resp) == 200 assert len(resp.json) == 1 assert len(resp.json[0]['annotation']['elements']) == 0 # Get all annotations for that item as the admin resp = server.request(path='/annotation/item/{}'.format( itemSrc['_id']), user=admin) assert utilities.respStatus(resp) == 200 annotList = resp.json assert len(annotList) == 2 assert (annotList[0]['annotation']['elements'][0]['center'] == annot['annotation']['elements'][0]['center']) assert len(annotList[1]['annotation']['elements']) == 0 # Create a new item itemDest = Item().createItem('sample', admin, publicFolder) # Set the annotations on the new item resp = server.request(path='/annotation/item/{}'.format( itemDest['_id']), method='POST', user=admin, type='application/json', body=json.dumps(annotList)) assert utilities.respStatus(resp) == 200 assert resp.json == 2 # Check if the annotations are in the destination item resp = server.request(path='/annotation', method='GET', user=admin, params={ 'itemId': itemDest.get('_id'), 'name': 'sample' }) assert utilities.respStatus(resp) == 200 assert resp.json is not None # Check failure conditions resp = server.request(path='/annotation/item/{}'.format( itemDest['_id']), method='POST', user=admin, type='application/json', body=json.dumps(['not an object'])) assert utilities.respStatus(resp) == 400 resp = server.request(path='/annotation/item/{}'.format( itemDest['_id']), method='POST', user=admin, type='application/json', body=json.dumps([{ 'key': 'not an annotation' }])) assert utilities.respStatus(resp) == 400 # Delete annotations resp = server.request(path='/annotation/item/{}'.format( itemDest['_id']), method='DELETE', user=None) assert utilities.respStatus(resp) == 401 resp = server.request(path='/annotation/item/{}'.format( itemDest['_id']), method='DELETE', user=admin) assert utilities.respStatus(resp) == 200 assert resp.json == 2
def itemIsWebsafeVideo(item: Item) -> bool: return item.get("meta", {}).get("codec") == "h264"