From e35c18fa6060a03c13fcfd262e9373faf0807f55 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Thu, 2 Jul 2020 10:03:49 -0400 Subject: [PATCH] ENH: Add LIMS support (See #93) This commit updates CellLocator and Slicer adding support for flags `--lims-specimen-id` and `--lims-base-url`. If `--lims-specimen-id` is provided, it is used: * At application startup time to (1) retrieve the corresponding annotation from LIMS and load it by using the `/specimen_metadata/view` endpoint. (2) enable the "Upload Annotation" button * After user initiate annotation upload to LIMS while using the `/specimen_metadata/store` endpoint. The "kind" parameter for both endpoints `/specimen_metadata/view` and `/specimen_metadata/store` is set to "IVSCC cell locations" IVSCC expected cell count is currently unsupported. For testing this functionality, a mock server as been implemented. Relevant instructions are available here: https://github.com/KitwareMedical/AllenInstituteMockLIMS List of Slicer changes: $ git shortlog 0661948e5..9b00dc521f --no-merges David Allemang (1): [cell-locator] ENH: Add command line args --lims-specimen-id and --lims-base-url for LIMS integration. --- CMakeLists.txt | 2 +- Modules/Scripted/Home/Home.py | 99 ++++++++++++++++++++++ Modules/Scripted/Home/Resources/UI/Home.ui | 16 ++++ 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b9e9880..f542d19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ if(NOT DEFINED slicersources_SOURCE_DIR) # Download Slicer sources and set variables slicersources_SOURCE_DIR and slicersources_BINARY_DIR FetchContent_Populate(slicersources GIT_REPOSITORY git://github.com/KitwareMedical/Slicer - GIT_TAG 0661948e52a7c809002954088bd3a8f847dc888d # cell-locator-v4.11.0-2018-12-19-0dc589ee5 + GIT_TAG 9b00dc521fa993d0c3f266890fdae90c3c9945ca # cell-locator-v4.11.0-2018-12-19-0dc589ee5 GIT_PROGRESS 1 ) else() diff --git a/Modules/Scripted/Home/Home.py b/Modules/Scripted/Home/Home.py index 017a8f1..3307850 100644 --- a/Modules/Scripted/Home/Home.py +++ b/Modules/Scripted/Home/Home.py @@ -1,7 +1,11 @@ import os import json import logging +import shutil import textwrap +import urllib +import urllib2 +import urlparse import vtk, qt, ctk, slicer @@ -124,6 +128,10 @@ def postStartupInitialization(): if annotationFilePath: self.loadAnnotationFile(annotationFilePath) + limsSpecimenID = slicer.app.commandOptions().limsSpecimenID + if limsSpecimenID: + self.loadLIMSSpecimen(limsSpecimenID) + qt.QTimer.singleShot(0, lambda: postStartupInitialization()) def isAnnotationSavingRequired(self): @@ -271,6 +279,96 @@ def loadAnnotationFile(self, filename): self.onAnnotationLoaded(loadedNode) return success + def loadAnnotationJson(self, body): + tempdir = os.path.join(slicer.app.temporaryPath, 'json') + tempfile = os.path.join(tempdir, 'annotation.json') + + if not (os.path.exists(tempdir) and os.path.isdir(tempdir)): + os.mkdir(tempdir) + + with open(tempfile, 'w') as f: + json.dump(body, f) + + self.loadAnnotationFile(tempfile) + self.MarkupsAnnotationNode.GetStorageNode().ResetFileNameList() + self.updateGUIFromMRML() + + shutil.rmtree(tempdir) + + def loadLIMSSpecimen(self, specimenID): + logging.info('Loading LIMS specimen id %s', specimenID) + + base = slicer.app.commandOptions().limsBaseURL or 'http://localhost:5000/' + path = '/specimen_metadata/view' + url = urlparse.urljoin(base, path) + + query = urllib.urlencode({ + 'kind': 'IVSCC cell locations', + 'specimen_id': specimenID + }) + + try: + res = urllib2.urlopen('%s?%s' % (url, query)) + except urllib2.URLError as e: + logging.error('Failed to connect to LIMS server.') + return + + if res.getcode() != 200: + logging.error('Failed to load specimen ID %s from LIMS server.', specimenID) + return + + body = json.loads(res.read()) + + self.loadAnnotationJson(body['data']['data']) + + def onUploadAnnotationButtonClicked(self): + logging.info('Upload Annotation Button Clicked') + + limsSpecimenID = slicer.app.commandOptions().limsSpecimenID + self.saveLIMSSpecimen(self.MarkupsAnnotationNode, limsSpecimenID) + + def saveLIMSSpecimen(self, annotationNode, specimenID): + logging.info('Saving LIMS specimen id %s', specimenID) + + data = self.saveAnnotationJson(annotationNode) + + base = slicer.app.commandOptions().limsBaseURL or 'http://localhost:5000/' + path = '/specimen_metadata/store' + url = urlparse.urljoin(base, path) + + body = json.dumps({ + 'kind': 'IVSCC cell locations', + 'specimen_id': specimenID, + 'data': data + }) + + try: + res = urllib2.urlopen(url, data=body) + except urllib2.URLError as e: + logging.error('Failed to connect to LIMS server') + return + + if res.getcode() != 200: + logging.error('Failed to store specimen ID %s to LIMS server.', specimenID) + + def saveAnnotationJson(self, annotationNode): + tempdir = os.path.join(slicer.app.temporaryPath, 'json') + tempfile = os.path.join(tempdir, 'annotation.json') + + if not (os.path.exists(tempdir) and os.path.isdir(tempdir)): + os.mkdir(tempdir) + + slicer.util.saveNode(annotationNode, tempfile) + self.logic.annotationStored(annotationNode) + self.updateGUIFromMRML() + + with open(tempfile, 'r') as f: + data = json.load(f) + + shutil.rmtree(tempdir) + + return data + def onAnnotationLoaded(self, newAnnotationNode): sliceNode = slicer.mrmlScene.GetNodeByID('vtkMRMLSliceNodeSlice') @@ -987,6 +1085,7 @@ def setupConnections(self): self.get('SaveAnnotationButton').connect("clicked()", self.onSaveAnnotationButtonClicked) self.get('SaveAsAnnotationButton').connect("clicked()", self.onSaveAsAnnotationButtonClicked) self.get('LoadAnnotationButton').connect("clicked()", self.onLoadAnnotationButtonClicked) + self.get('UploadAnnotationButton').connect("clicked()", self.onUploadAnnotationButtonClicked) self.get('ResetFieldOfViewButton').connect("clicked()", self.resetFieldOfView) self.get('ReferenceViewComboBox').connect("currentTextChanged(QString)", self.setReferenceView) diff --git a/Modules/Scripted/Home/Resources/UI/Home.ui b/Modules/Scripted/Home/Resources/UI/Home.ui index 5d99752..f6b4251 100644 --- a/Modules/Scripted/Home/Resources/UI/Home.ui +++ b/Modules/Scripted/Home/Resources/UI/Home.ui @@ -139,6 +139,22 @@ + + + + + + Save to LIMS + + + + :/Icons/save_icon.svg.png + + + + + +