class XNATSessionManager(object): def __init__(self, browser): self.browser = browser self.utils = XNATUtils() self.sessionFileName = os.path.join(self.utils.utilPath, 'SessionLog.txt') self.sessionArgs = None self.saveItem = None def startNewSession(self, sessionArgs): if not sessionArgs.__class__.__name__ == "XNATSessionArgs": raise NameError("You can only use XNATSessionArgs to start a new session.") self.sessionArgs = sessionArgs self.writeSession() def clearCurrentSession(self): #print(self.utils.lf() + "CLEARING CURRENT SESSION!") self.sessionArgs = None def writeSession(self): fileLines = [] for item in self.sessionArgs: fileLines.append("%s:\t\t%s\n"%(item, self.sessionArgs[item])) fileLines.append("\n\n") print(self.utils.lf() + "Session log file: %s"%(self.sessionFileName)) f = open(self.sessionFileName, 'a') f.writelines(fileLines) f.close() del fileLines
class XNATMRMLParser(object): """XNATMRMLParser is the class that parses and changes strings in a given .mrml file """ def __init__(self, browser=None): self.browser = browser self.utils = XNATUtils() self.useCache = True self.useCacheMsgBox = None self.resetUseCacheMsgBox() self.tempLocalFileMap = None self.tempNewFilename = None self.cacheList = None self.TESTWRITE = False def changeValues(self, filename, newFilename, replaceValues, otherReplaceValues, removeOriginalFile = False, debug = True): self.browser.updateStatus(["Changing values in the mrml.", "", ""]) #print (self.utils.lf() + " MRML PARSER CHANGE VALUES!") #======================================================================= # CONCATENATE ALL REPLACE VALUES #======================================================================= dicoms = [] compLines = [] if otherReplaceValues: replaceValues.update(otherReplaceValues) #======================================================================= # CREATE NEW MRML, BACKUP OLD #======================================================================= if filename == newFilename: bkpFN = filename.split(".")[0] + ".BKP" shutil.copy(filename,bkpFN) self.utils.removeFile(filename) slicer.app.processEvents() filename = bkpFN #======================================================================= # INIT XML PARSER #======================================================================= elementTree = ET.parse(codecs.open(filename, encoding="UTF-8")) root = elementTree.getroot() iterator = root.getiterator() for subelement in iterator: if subelement.keys(): for name, value in subelement.items(): #=========================================================== # if no strings to be changed, at least make sure filepaths are relative #=========================================================== if replaceValues == {}: if os.path.basename(os.path.dirname(value)).lower() == "data": #print self.utils.lf() + " CHANGING NAME WITH DATA FORMAT: %s\tOLD: %s\tNEW:%s"%(subelement.attrib[name], value, "./Data/" + os.path.basename(value)) subelement.attrib[name] = "./Data/%s"%(os.path.basename(value)) self.browser.updateStatus(["Writing a new element tree in the mrml.", "", ""]) #======================================================================= # write new mrml #======================================================================= elementTree.write(newFilename) ### For testing purposes ############################################################# #if self.TESTWRITE: # z = open(filename,"r") # oldlines = z.readlines() # z.close() # self.makeMrmlReadable(str(newFilename).split(".")[0]+"BEFORE", oldlines) # self.makeMrmlReadable(str(newFilename).split(".")[0]+"AFTER", lines) ###################################################################################### #======================================================================= # return the dicom files, if necessary #======================================================================= self.browser.updateStatus(["Done writing new mrml!", "", ""]) return {"dicoms": dicoms} def localizeRemoteMRMLLinks(self, filename, downloadRemoteLinks = True): """Used for the 'load' workflow. This changes remote URIs (ones with the http and https perefixes) . """ #======================================================================= # INIT DICTIONARY #======================================================================= self.cacheList = [] fileNameKey = "fileName" fileListMemberKey = "fileListMember" remoteLocalFileMap = {} compLines = [] #======================================================================= # BEGIN MRML PARSING #======================================================================= elementTree = ET.parse(codecs.open(filename, encoding="UTF-8")) root = elementTree.getroot() iterator = root.getiterator() for subelement in iterator: if subelement.keys(): for name, value in subelement.items(): #=========================================================== # if the URI has a remote prefix, begin algorithm #=========================================================== if ("http://" in value) or ("https://" in value): print self.utils.lf() + "\t\tName: '%s', Value: '%s'"%(name, value) #======================================================= # removes the https://www.host.*** prefix #======================================================= _localURI = self.utils.adjustPathSlashes("%s%s"%(self.utils.remoteFilePath, str(qt.QUrl(value).path()).split('archive')[1])) print (self.utils.lf() + "GIVEN URL: " + value + " INITIAL PATH VALUE: " + _localURI) #======================================================= # creates a new local URI based on the module cache paths #======================================================= tempFileInfo = XNATFileInfo(remoteURI = value, localURI = _localURI) newValue = tempFileInfo.localURI #======================================================= # special case for handling .raw files #======================================================= if _localURI.endswith("raw"): #special case newValue = os.path.join(os.path.join(tempFileInfo.localDirName, tempFileInfo.basenameNoExtension), tempFileInfo.basename) print (self.utils.lf() + "FOUND RAW: " + self.utils.adjustPathSlashes(newValue)) #======================================================= # makes sure path slashes are all forward #======================================================= newValue = self.utils.adjustPathSlashes(newValue) if "win" in sys.platform: newValue = newValue.replace('\\', '/') #======================================================= # if the value does not exist in the map... #======================================================= if not value in remoteLocalFileMap: #=================================================== # if no map value, create raw file map values #=================================================== print (self.utils.lf() + ("DICTIONARY: Adding new val.")) if tempFileInfo.localURI.endswith('raw'): print self.utils.lf() + "RAW EXTENSION" remoteLocalFileMap[value] = "%s/%s/%s"%(tempFileInfo.localDirName, os.path.basename(tempFileInfo.localURI).split(".")[0], os.path.basename(tempFileInfo.localURI)) remoteLocalFileMap[value + ".gz"] = "%s/%s%s"%(tempFileInfo.localDirName, os.path.basename(tempFileInfo.localURI).split(".")[0], ".raw.gz") #=================================================== # for nodes other than raw files, define its map value #=================================================== else: print self.utils.lf() + "OTHER EXTENSION" remoteLocalFileMap[value] = tempFileInfo.localURI tempFN = os.path.basename(tempFileInfo.localURI).split(".")[0] tempFolder = os.path.basename(os.path.dirname(tempFileInfo.localURI)) print self.utils.lf() + "TEMPFN %s TEMPFOLDER %s"%(tempFN, tempFolder) if tempFN == tempFolder: remoteLocalFileMap[value] = "%s%s"%(os.path.dirname(tempFileInfo.localURI), os.path.basename(tempFileInfo.localURI).split(".")[1]) #=================================================== # debugging calls #=================================================== print self.utils.lf() + "VALUE: %s and LOCAL %s"%(value, remoteLocalFileMap[value]) compLines.append("OLDWORD - READ1: '" + value + "'") compLines.append("NEWWORD - READ1: '" + remoteLocalFileMap[value]+ "'\n") #======================================================= # set new mrml values #======================================================= subelement.attrib[name] = remoteLocalFileMap[value] #=========================================================== # If there's a local prefix to the string, and it's in the data directory # of the scene... #=========================================================== elif os.path.basename(os.path.dirname(value)).lower() == 'data': ext = os.path.basename(value).split(".")[1] newValue = "./Data/%s"%(os.path.basename(value)) compLines.append("DATAOLD - READ2: '" + value + "'") compLines.append("DATANEW - READ2: '" + newValue + "'\n") subelement.attrib[name] = newValue #======================================================================= # GENERATE NEW FILENAME #======================================================================= newFilename = filename.replace("-remotized.mrml", self.utils.localizedMRMLExtension + ".mrml") newFilename = os.path.normpath(newFilename) #======================================================================= # CHANGES DIRECTOR CHARS FOR WINDOWS #======================================================================= if sys.platform.find("win") > -1: newFilename.replace('\\', '/') #======================================================================= # PRINT LINES FOR DEBUGGING #======================================================================= for line in compLines: print self.utils.lf() + "COMPLINE: " + line #======================================================================= # WRITE NEW MRML #======================================================================= slicer.app.processEvents() elementTree.write(newFilename) slicer.app.processEvents() #======================================================================= # MAKE, RETURN NEW FILENAMES #======================================================================= self.currLoadManager.newMRMLFile = newFilename print self.utils.lf() + "MRML PARSE FILENAME: " + filename print "*****************" + self.utils.lf() + "MRML PARSER: " + str(remoteLocalFileMap) return newFilename, remoteLocalFileMap def resetUseCacheMsgBox(self): """Message box workflow for using the cached files instead of redownloading.""" self.useCacheMsgBox = qt.QMessageBox() self.useCacheMsgBox.setText("It appears that some of the DICOM images in this scene are locally cached. "+ "Would you like the widget to use them when possible?"); self.useCacheMsgBox.setInformativeText("NOTE: This will potentially save you load time."); self.useCacheMsgBox.setStandardButtons(qt.QMessageBox.Yes | qt.QMessageBox.No) self.useCacheMsgBox.setDefaultButton(qt.QMessageBox.Yes) def toggleUseCache(self, button): """Toggles the "use cache" yes/no window.""" newDict = {} if button.text.lower().find('yes') > -1: self.useCache = True elif button.text.lower().find('no') > -1: self.useCache = False text = "User opted to use cache." if self.useCache else "User selected to opt out of using cache." print self.utils.lf() + (text) for item in self.cacheList: self.tempLocalFileMap[str(item)] = None self.downloadRemoteURIs(self.tempLocalFileMap, self.tempNewFilename) def makeMrmlReadable(self, filename, lines = None): """Makes MRML files more readable to humans (i.e. linebreaks). """ if not lines: z = open(filename,"r") lines = z.readlines() z.close() f = open(filename,'w' ) for line in lines: words = line.split() for word in words: word = word.rstrip() if len(word)>0: #word = word.strip() f.write(word + '\n') f.close()
class XNATScenePackager(object): """Class containing methods for packaging scenes pertinent to the Slicer-XNAT workflow. The major feature of this class is that it handles any shared nodes and 1) uploads them to a "shared" folder on XNAT 2) eliminates them from scene packages sent to XNAT.""" def __init__(self, browser = None): self.browser = browser self.viewer = self.browser.XNATView self.utils = XNATUtils() self.hostName = 'https://central.xnat.org' def determineSceneType(self): scene = slicer.app.mrmlScene() currURL = os.path.normpath(scene.GetURL()) if currURL == None or currURL == '': return None elif currURL.find(self.utils.projectPath) == 0: return "XNATSlicerScene" else: return "LocalScene" return None def bundleScene(self, args): # # STEP 1: Init variables. # XNATCommunicator = args['XNATCommunicator'] XNATDir = args['saveDir'] XNATSharedDir = args['sharedDir'] sceneName = args['fileName'] metadata = args['metadata'] packageName = os.path.basename(sceneName.split(".")[0]) # # STEP 2: Analyzes the scene type. # #print self.utils.lf() + "ANALYZING SCENE TYPE" sceneType = self.determineSceneType() #print self.utils.lf() + "SCENE TYPE: %s"%(sceneType) # # STEP 3: Create a directory for packaging. # tempDir = os.path.join(self.utils.tempUploadPath, packageName) #print self.utils.lf() + "CREATE PACKAGE DIRECTORY: %s"%(tempDir) try: #print self.utils.lf() + ("%s does not exist. Making it."%(tempDir)) self.utils.removeFilesInDir(tempDir) os.rmdir(tempDir) except Exception, e: pass try: os.mkdir(tempDir) except Exception, e: pass # # STEP 4: Write according to scene type and if there's matching metadata. # #print self.utils.lf() + "BEGINNING THE SCENE WRITE" self.browser.updateStatus(["", "Write all...", ""]) #print self.utils.lf() + "WRITING ALL!" self.writeScene_All(tempDir) slicer.app.processEvents() self.browser.updateStatus(["", "Finding mrml...", ""]) mrml = None for root, dirs, files in os.walk(tempDir): for relFileName in files: if relFileName.endswith("mrml"): mrml = os.path.join(root, relFileName) break slicer.app.processEvents() self.browser.updateStatus(["", "Bundling scene. Please wait...", ""]) return {'path':self.utils.adjustPathSlashes(tempDir), 'mrml': self.utils.adjustPathSlashes(mrml)} # SOURCE OF FOLLOWING CODE: # http://stackoverflow.com/questions/296499/how-do-i-zip-the-contents-of-a-folder-using-python-version-2-5 # # NOTE: To be deprecated after MRB methods are put in place. def zipdir(self, basedir=None, zipArchive=None): assert os.path.isdir(basedir) with closing(ZipFile(zipArchive, "w", ZIP_DEFLATED)) as z: for root, dirs, files in os.walk(basedir): #NOTE: ignore empty directories for fn in files: absfn = os.path.join(root, fn) zfn = absfn[len(basedir)+len(os.sep):] #XXX: relative path z.write(absfn, zfn) def packageDir(self, packageFilePath, sceneDir): #logic = slicer.app.applicationLogic() #logic.SaveSceneToSlicerDataBundleDirectory(sceneDir, None) slicer.app.applicationLogic().Zip(packageFilePath,sceneDir) def writeScene_All(self, saveDir): #slicer.app.processEvents() if os.path.exists(saveDir): self.utils.removeDirsAndFiles(saveDir) #slicer.app.processEvents() try: os.makedirs(saveDir + "/Data") except Exception, e: print self.utils.lf() + "Likely the dir already exists: " + str(e) slicer.app.applicationLogic().SaveSceneToSlicerDataBundleDirectory(saveDir, None) slicer.app.processEvents()
class XNATSaveWorkflow(object): def __init__(self, browser, XNATCommunicator, sessionArgs): """ Parent class of any load workflow """ self.browser = browser self.scenePackager = XNATScenePackager(self.browser) self.utils = XNATUtils() self.sessionArgs = sessionArgs self.XNATCommunicator = XNATCommunicator def saveScene(self): #======================================================================= # PACKAGE SCENE # # NOTE: The scene packager refers to the .metadata file. #======================================================================= package = self.scenePackager.bundleScene(self.sessionArgs) projectDir = package['path'] mrmlFile = package['mrml'] #======================================================================= # ZIP PACKAGE #======================================================================= self.browser.updateStatus(["Compressing package. Please wait...", "", ""]) packageFileName = projectDir + self.utils.defaultPackageExtension if os.path.exists(packageFileName): self.utils.removeFile(packageFileName) self.scenePackager.packageDir(packageFileName, projectDir) self.browser.updateStatus(["Deleting temporary package.", "", ""]) self.utils.removeDirsAndFiles(projectDir) #======================================================================= # UPLOAD PACKAGE #======================================================================= self.browser.updateStatus(["Sending '%s' to XNAT. Please wait..."% (os.path.basename(packageFileName)), "", ""]) #print ("UPLOADING HERE: " + self.sessionArgs['saveDir'] + "/" + os.path.basename(packageFileName)) self.XNATCommunicator.upload(packageFileName, self.sessionArgs['saveDir'] + "/" + os.path.basename(packageFileName)) slicer.app.processEvents() if self.sessionArgs['sharable']: self.browser.updateStatus(["", "Finished updating '%s' in XNAT."% (os.path.basename(packageFileName)), ""]) else: self.browser.updateStatus(["", "Finished writing '%s' to XNAT."% (os.path.basename(packageFileName)), ""]) #======================================================================= # UPDATE VIEWER #======================================================================= self.sessionArgs['sessionType'] = "scene upload" self.browser.XNATView.startNewSession(self.sessionArgs) self.browser.XNATView.loadButton.setEnabled(True) self.browser.XNATView.deleteButton.setEnabled(True) self.browser.XNATView.setCurrItemToChild(item = None, childFileName = os.path.basename(packageFileName)) def determineSaveLocation(self, itemType, selectedDir, saveLevel = None): """ Method goes through various steps to determine the optimal XNAT location to save the current scene. """ #======================================================================= # SET VARIABLES #======================================================================= print self.utils.lf() + "DETERMINE SAVE DIR" currDir = os.path.dirname(selectedDir) saveDir = "" #======================================================================= # NONE HANDLER #======================================================================= if not saveLevel: # This is where another analysis step could exist to determine where # the scene could be saved. saveLevel = self.utils.defaultXNATSaveLevel #======================================================================= # CHECK SAVE LEVEL VALIDITY #======================================================================= else: findCount = False for key, value in self.utils.xnatDepthDict.iteritems(): if value == saveLevel: findCount = True if not findCount: print (self.utils.lf() + "Couldn't find save level '%s'. Resorting to default: %s"%(saveLevel, self.utils.defaultXNATSaveLevel)) saveLevel = self.utils.defaultXNATSaveLevel # Look at the sessionManager, reconcile save dir based on that # and XNATSaveLevel if self.browser.XNATView.sessionManager.currSessionInfo: saveDir = self.utils.getSlicerDirAtLevel(self.browser.XNATView.sessionManager.currSessionInfo['RemoteURI'], saveLevel) else: return None print "SAVEDIR: " + saveDir otherRequiredDirs = [] baseDir = saveDir.split(self.utils.slicerDirName)[0] for folderName in self.utils.requiredSlicerFolders: otherRequiredDirs.append("%s%s/files/"%(baseDir, folderName)) return {'saveDir': saveDir, 'others': otherRequiredDirs}