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}
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 XNATCommunicator(object): def __init__(self, browser, server, user, password, cachedir): self.browser = browser self.server = server self.user = user self.password = password self.cachedir = cachedir self.utils = XNATUtils() self.XNAT = None self.progDialog = None self.setup() self.totalDLSize = 0 self.downloadedBytes = 0 def setup(self): pass def getFiles_URL(self, srcDstMap, withProgressBar = True, fileOrFolder = None): #======================================================================= # GET TOTAL SIZE OF DOWNLOADS FOR ALL FILES #======================================================================= self.totalDLSize = 0 self.downloadedBytes = 0 downloadFolders = [] #======================================================================= # REMOVE EXISTING DST FILES #======================================================================= for src, dst in srcDstMap.iteritems(): if os.path.exists(dst): self.utils.removeFile(dst) timeStart = time.time() #======================================================================= # DOWNLOAD THE FILES #======================================================================= if fileOrFolder == "file": for src, dst in srcDstMap.iteritems(): #print("FILE DOWNLOAD src:%s\tdst:%s"%(src, dst)) self.totalDLSize = int(self.XNAT.select(self.cleanSelectString(src)).size()) if withProgressBar: self.urllib2GetWithProgress(self.cleanSelectString(src), dst) else: f.get(dst) elif fileOrFolder == "folder": import tempfile xnatFileFolders = [] #======================================================================= # DETERMINE SOURCE FOLDERS, CREATE NEW DICT BASED ON BASENAME #======================================================================= for src, dst in srcDstMap.iteritems(): #print("FOLDER D/L src:%s\tdst:%s"%(src, dst)) srcFolder = os.path.dirname(src) if not srcFolder in xnatFileFolders: xnatFileFolders.append(srcFolder) #======================================================================= # GET THE 'FILEOBJECTS' #======================================================================= fObjs = [] for f in xnatFileFolders: #print("FOLDER DOWNLOAD %s"%(f)) fObjs = self.XNAT.select(self.cleanSelectString(os.path.dirname(f))).files().get('~/tmp/files') for fO in fObjs: self.totalDLSize += int(fO.size()) for f in xnatFileFolders: if withProgressBar: src = self.cleanSelectString(f + "?format=zip") dst = tempfile.mktemp('', 'XNATDownload', self.utils.tempPath) + ".zip" downloadFolders.append(self.utils.adjustPathSlashes(dst)) if os.path.exists(dst): self.utils.removeFile(dst) #print("DOWNLOADING %s to %s"%(src, dst)) self.urllib2GetWithProgress(src, dst) timeEnd = time.time() totalTime = (timeEnd-timeStart) bps = self.totalDLSize/totalTime #print "DOWNLOAD FOLDERS: " + str(downloadFolders) return downloadFolders #qt.QMessageBox.warning(None, "Time", "Total time: %s. Bps: %s"%(totalTime, str(bps))) def getFolderContents(self): pass def getItemValue(self): pass def makeDir(self): pass def upload(self): pass def delete(self): pass def getSize(self): pass def downloadFailed(self, windowTitle, msg): qt.QMessageBox.warning(None, windowTitle, msg) def buffer_read(self, response, fileToWrite, dialog=None, buffer_size=8192): try: itemSize = response.info().getheader('Content-Length').strip() itemSize = int(itemSize) except Exception, e: #print ("ITEM SIZE ERROR %s"%(e)) pass while 1: buffer = response.read(buffer_size) self.downloadedBytes += len(buffer) fileToWrite.write(buffer) if not buffer: break percent = (float(self.downloadedBytes) / self.totalDLSize) percent = round(percent*100, 2) if percent == 100: self.browser.updateStatus(["", "Loading, please wait...", ""]) self.browser.generalProgressBar.setVisible(False) dialog.setValue(percent) return self.downloadedBytes