From 836145f0d473a885c8df33c012558498988dec6c Mon Sep 17 00:00:00 2001 From: David Manthey Date: Tue, 23 Jan 2024 08:35:30 -0500 Subject: [PATCH] Add some folder API endpoints. --- wsi_deid/rest.py | 70 +++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/wsi_deid/rest.py b/wsi_deid/rest.py index b23cde7..d4bb569 100644 --- a/wsi_deid/rest.py +++ b/wsi_deid/rest.py @@ -304,6 +304,9 @@ def __init__(self, apiRoot): self.route('PUT', ('item', ':id', 'action', ':action'), self.itemAction) self.route('PUT', ('item', ':id', 'action', 'refile'), self.refileItem) self.route('POST', ('item', ':id', 'action', 'refile', ':tokenId'), self.refileItemFull) + self.route('PUT', ('folder', ':id', 'action', ':action'), self.folderAction) + self.route('POST', ('folder', ':id', 'action', 'refile', ':tokenId'), + self.refileFolderFull) self.route('PUT', ('action', 'bulkRefile'), self.refileItems) self.route('PUT', ('item', ':id', 'redactList'), self.setRedactList) self.route('GET', ('item', ':id', 'refileList'), self.getRefileList) @@ -673,6 +676,10 @@ def itemListAction(self, ids, action): return user = self.getCurrentUser() items = [Item().load(id=id, user=user, level=AccessType.READ) for id in ids] + self._itemListAction(action, items, user) + return len(items) + + def _itemListAction(self, action, items, user): actionfunc, actionargs, actname, pp = self._actionForItem(items[0], user, action) with ItemActionLock: ItemActionList.extend(items) @@ -680,7 +687,7 @@ def itemListAction(self, ids, action): with ProgressContext( True, user=user, title='%s items' % pp.capitalize(), message='%s %s' % (pp.capitalize(), items[0]['name']), - total=len(ids), current=0 + total=len(items), current=0 ) as ctx: try: for idx, item in enumerate(items): @@ -688,14 +695,14 @@ def itemListAction(self, ids, action): item, user, action) ctx.update( message='%s %s' % (pp.capitalize(), item['name']), - total=len(ids), current=idx) + total=len(items), current=idx) try: actionfunc(*actionargs) except Exception: logger.exception('Failed to %s item' % actname) ctx.update('Error %s %s' % (pp, item['name'])) raise - ctx.update(message='Done %s' % pp, total=len(ids), current=len(ids)) + ctx.update(message='Done %s' % pp, total=len(items), current=len(items)) except Exception: pass finally: @@ -703,6 +710,32 @@ def itemListAction(self, ids, action): for item in items: ItemActionList.remove(item) + @autoDescribeRoute( + Description('Perform an action on a folder of items.') + .modelParam('id', 'The folder ID', model=Folder, level=AccessType.READ) + .param('action', 'Action to perform on the item. One of process, ' + 'reject, quarantine, unquarantine, finish, ocr.', paramType='path', + enum=['process', 'reject', 'quarantine', 'unquarantine', 'finish', 'ocr']) + .param('limit', 'Maximum number of items in folder to process', + required=False, dataType='int') + .param('recurse', 'Return items recursively under this folder.', + dataType='boolean', default=False, required=False) + .errorResponse() + ) + @access.user + def folderAction(self, folder, action, limit=None, recurse=False): + setResponseTimeLimit(86400) + user = self.getCurrentUser() + text = '_recurse_:' if recurse else None + filters = {'largeImage.fileId': {'$exists': True}} + limit = int(limit or 0) + sort = [('lowerName', SortDir.ASCENDING)] + items = list(self._item_find( + folder['_id'], text=text, name=None, limit=limit, offset=0, + sort=sort, filters=filters)) + self._itemListAction(action, items, user) + return len(items) + @autoDescribeRoute( Description('Get the list of known and allowed image names for refiling.') .modelParam('id', model=Item, level=AccessType.READ) @@ -781,7 +814,7 @@ def refileItem(self, item, imageId, tokenId): return item @autoDescribeRoute( - Description('Perform an action on an item.') + Description('Refile an item with a specific token ID.') .responseClass('Item') # Allow all users to do redaction actions; change to WRITE otherwise .modelParam('id', model=Item, level=AccessType.READ) @@ -798,6 +831,35 @@ def refileItemFull(self, item, tokenId, refileData): item, user, tokenId, imageId, {imageId: {'fields': refileData}}) return item + @autoDescribeRoute( + Description('Refile items in a folder with a specific token ID.') + .modelParam('id', 'The folder ID', model=Folder, level=AccessType.READ) + .param('tokenId', 'The new tokenId', required=True) + .param('limit', 'Maximum number of items in folder to process', + required=False, dataType='int') + .param('recurse', 'Return items recursively under this folder.', + dataType='boolean', default=False, required=False) + ) + @access.user + def refileFolderFull(self, folder, tokenId, limit=None, recurse=False): + setResponseTimeLimit(86400) + user = self.getCurrentUser() + text = '_recurse_:' if recurse else None + filters = {'largeImage.fileId': {'$exists': True}} + sort = [('lowerName', SortDir.ASCENDING)] + processedImageIds = [] + limit = int(limit or 0) + for item in self._item_find( + folder['_id'], text=text, name=None, limit=limit, offset=0, + sort=sort, filters=filters): + imageId = TokenOnlyPrefix + tokenId + uploadInfo = item.get('wsi_uploadInfo') + if uploadInfo and TokenOnlyPrefix + imageId in uploadInfo: + imageId = TokenOnlyPrefix + imageId + item = process.refile_image(item, user, tokenId, imageId, uploadInfo) + processedImageIds += [imageId] + return processedImageIds + @autoDescribeRoute( Description('Refile multiple images at once.') .jsonParam('imageRefileData', 'Data used to refile images', paramType='body')