def loadTopObject(repositoryPath, topObject): """ Load given topObject """ filePath = xmlUtil.findTopObjectPath(repositoryPath, topObject) topObjId = xmlUtil.getTopObjIdFromFileName(filePath, mustBeMultipart=True) return loadFromStream(open(filePath), topObjId=topObjId, topObject=topObject)
def save(repositoryPath, topObject, mapping=None, comment=None, simplified=True, compact=True, expanded=False): fileLocation = xmlUtil.findTopObjectPath(repositoryPath, topObject) if os.path.exists(fileLocation): # File that fits guid already exists - overwrite it pass else: # file does not exist - make sure the directories are there. dirName = os.path.dirname(fileLocation) if not os.path.exists(dirName): os.makedirs(dirName) elif not os.path.isdir(dirName): raise ApiError("%s exists and is not a directory" % dirName) stream = open(fileLocation, 'w') try: saveToStream(stream, topObject, mapping, comment, simplified, compact) finally: stream.close()
def createTopObjectFallback(topObject): """ Create backup of topObject in same directory as original file but with '.bak' appended. This function is not intended to be used outside this module but could be. """ location = xmlUtil.getTopObjectPath(topObject) if not os.path.exists(location): return backupLocation = location + '.bak' # copy rather than move because will need that much disk space in any case # and sometimes move fails at OS level if file with that name already exists directory = os.path.dirname(backupLocation) if not os.path.exists(directory): os.makedirs(directory) shutil.copy(location, backupLocation)
def loadFromFile(memopsRoot, filePath, partialLoad=False): """ Load topObject from filePath and MemopsRoot """ # open file stream = open(filePath) # load file try: topObjId = xmlUtil.getTopObjIdFromFileName(filePath, mustBeMultipart=True) topObject = loadFromStream(stream, topObject=memopsRoot, topObjId=topObjId, partialLoad=partialLoad) finally: stream.close() return topObject
def fullLoadValidationStore(topObj): """hard load ValidationStore from old location """ from memops.format.xml import Util as xmlUtil from memops.universal import Io as uniIo from memops.format.xml import XmlIO root = topObj.memopsRoot locator = ( root.findFirstPackageLocator(targetName='ccp.molecule.Validation') or root.findFirstPackageLocator(targetName='any')) repository = locator.findFirstRepository() #repository = topObj.activeRepositories[0] fileLocation = repository.getFileLocation( 'ccp.molecule.StructureValidation') filePath = uniIo.joinPath(fileLocation, xmlUtil.getTopObjectFile(topObj)) XmlIO.loadFromStream(open(filePath), topObject=topObj, topObjId=topObj.guid, partialLoad=False)
def loadProjectFile(filePath, partialLoad=False): """ Load project by file name """ topObjId = xmlUtil.getTopObjIdFromFileName(filePath, mustBeMultipart=False) filePath = os.path.abspath(filePath) lowestDir = os.path.dirname(filePath) project = loadFromStream(open(filePath), topObjId=topObjId, partialLoad=partialLoad) activeRepositories = project.__dict__['activeRepositories'] if not activeRepositories: for repository in project.packageLocator.repositories: path = repository.getFileLocation('memops.Implementation') if path == lowestDir: activeRepositories.append(repository) break # return project
def createTopObjectFallback(topObject): """ Create backup of topObject in same directory as original file but with '.bak' appended. This function is not intended to be used outside this module but could be. """ location = xmlUtil.getTopObjectPath(topObject) if not os.path.exists(location): return backupLocation = location + '.bak' if not checkFileAtPath(location) and checkFileAtPath(backupLocation): # current file no good and current backup good so do not do backup print 'File at location "%s" not complete so not backing up' % location return # copy rather than move because will need that much disk space in any case # and sometimes move fails at OS level if file with that name already exists directory = os.path.dirname(backupLocation) if not os.path.exists(directory): os.makedirs(directory) shutil.copy(location, backupLocation)
def backupProject(project, dataLocationStores=None, skipRefData=True, clearOutDir=False): def modificationTime(path): return os.stat(path)[8] backupRepository = project.findFirstRepository(name="backup") if not backupRepository: print('Warning: no backup path set, so no backup done') return backupUrl = backupRepository.url backupPath = backupUrl.path if not dataLocationStores: dataLocationStores = set() if clearOutDir: removePath(backupPath) topObjects = tuple(project.topObjects) + (project, ) for topObject in topObjects: if skipRefData: repository = topObject.findFirstActiveRepository(name='refData') if repository: continue if topObject.isModified: topObject.backup() else: repository = topObject.findFirstActiveRepository() if repository: origFile = xmlUtil.findTopObjectPath(repository.url.path, topObject) if os.path.exists(origFile): # problem with appending repository.name is that topObject.backup() # above does not do it this way, so end up with inconsistent backup ###backupDir = joinPath(backupPath, repository.name) # so use same backup pah as topObject.backup() backupDir = backupPath backupFile = xmlUtil.findTopObjectPath( backupDir, topObject) if not os.path.exists(backupFile) or \ (modificationTime(backupFile) < modificationTime(origFile)): directory = os.path.dirname(backupFile) if not os.path.exists(directory): os.makedirs(directory) shutil.copy(origFile, backupFile) else: # one is stuffed print( 'Warning: could not backup %s since could not find original file "%s"' % (topObject, origFile)) else: # one is stuffed print( 'Warning: could not backup %s since could not find repository' % topObject) dataBackupPath = joinPath(backupPath, 'data') for dataLocationStore in dataLocationStores: dataBackupDir = joinPath(dataBackupPath, dataLocationStore.name) for dataStore in dataLocationStore.dataStores: origFile = dataStore.dataUrl.url.path backupFile = joinPath(dataBackupDir, dataStore.path) if os.path.exists(origFile): if not os.path.exists(backupFile) or \ (modificationTime(backupFile) < modificationTime(origFile)): directory = os.path.dirname(backupFile) if not os.path.exists(directory): os.makedirs(directory) shutil.copy(origFile, backupFile) else: print( 'Warning: could not backup dataStore "%s" because could not find original file "%s"' % (dataStore.name, origFile))
def loadProject(path, projectName=None, showWarning=None, askFile=None, askDir=None, suppressGeneralDataDir=False): """ Loads a project file and checks and deletes unwanted project repositories and changes the project repository path if the project has moved. Returns the project. (The project repository path is effectively the userData repository.) showWarning (if not None) has signature showWarning(title, message) askFile (if not None) has signature askFile(title, message, initial_value = '') askDir (if not None) has signature askDir(title, message, initial_value = '') Throws an IOError if there is an I/O error. Throws an ApiError if there is an API exception. """ from memops.format.xml import XmlIO if not showWarning: showWarning = printWarning path = normalisePath(path, makeAbsolute=True) # check if path exists and is directory if not os.path.exists(path): raise IOError('path "%s" does not exist' % path) if not os.path.isdir(path): raise IOError('path "%s" is not a directory' % path) projectFile = xmlUtil.getProjectFile(path, projectName) if projectName: # projectName was specified so projectFile better exist if not os.path.exists(projectFile): raise IOError('project file "%s" does not exist' % projectFile) else: # projectName was not specified so default projectFile might not exist if not os.path.exists(projectFile): projectFiles = xmlUtil.getPossibleProjectFiles(path) if len(projectFiles) == 0: raise IOError('"%s" contains no project file' % path) elif len(projectFiles) == 1: projectFile = projectFiles[0] elif askFile: projectFile = askFile('Select project file', 'Select project file', initial_value=projectFiles[0]) if not projectFile: # cancelled raise IOError('Cancelled') else: raise IOError( '"%s" contains %d project files, not sure which to use' % (path, len(projectFiles))) # TBD: should projectName be based on projectFile or on path??? # the way it is set here do not need to change project.name # but if you used the path then you would need to change it projectName = xmlUtil.getTopObjIdFromFileName(projectFile) # doing the loadProject twice has a bit of an overhead, but not much try: # below assumes TopObjects exist where stated, so might fail project = XmlIO.loadProject(path, projectName, partialLoad=False) except: print("\nFirst loading attempt failed - compatibility problem?.") project = None def isGeneralDataWriteable(generalDataRepository): oldPath = generalDataRepository.url.path return (isWindowsOS() or os.access(oldPath, os.W_OK | os.X_OK)) def isGeneralDataOk(project): if suppressGeneralDataDir: return True generalDataRepository = project.findFirstRepository(name='generalData') if generalDataRepository: return isGeneralDataWriteable(generalDataRepository) return True if project is not None and (not xmlUtil.areAllTopObjectsPresent(project) or not isGeneralDataOk(project)): # if not all loaded (shell) TopObjects can be found, try again project = None ###print "\nSome files unfindable - has project moved?." if project is None: ###print "Re-trying, skipping cached TopObjects:" project = XmlIO.loadProject(path, projectName, partialLoad=True) warningMessages = [] # try and fix project repository path, if needed packageLocator = project.packageLocator repositories = packageLocator.repositories for repository in repositories: if repository.url.path == path: oldPath = path break else: # change first repository path to point to this one, and also change backup repository = repositories[0] oldPath = repository.url.path warningMessages.append('Project file has moved from\n"%s"\nto\n"%s"' % (oldPath, path)) repository.url = Implementation.Url(path=path) # Necessary because activeRepositories are not set right # if file names do not match: project.__dict__['activeRepositories'].append(repository) projectRepository = repository # check backup path backupRepository = project.findFirstRepository(name="backup") if backupRepository: backupUrl = backupRepository.url oldBackupPath = backupUrl.path newBackupPath = path + '_backup' if oldBackupPath.startswith(oldPath): # hopefully true if path != oldPath: warningMessages.append( 'Backup is being changed from\n"%s"\nto\n"%s"' % (oldBackupPath, newBackupPath)) backupRepository.url = Implementation.Url(path=newBackupPath) elif not os.path.exists(oldBackupPath): warningMessages.append( 'Backup is being changed from\n"%s"\nto\n"%s"' % (oldBackupPath, newBackupPath)) backupRepository.url = Implementation.Url(path=newBackupPath) # check if project repository is called 'userData' if projectRepository.name != 'userData': warningMessages.append( 'Project has non-standard repository name "%s"' % projectRepository.name) # repoint dataStores that are in same directory as project # (but only if old path does not exist and new one does) if path != oldPath: oldDirectory = os.path.dirname(oldPath) newDirectory = os.path.dirname(path) for dataLocationStore in project.dataLocationStores: for dataStore in dataLocationStore.dataStores: fullPath = dataStore.fullPath if not os.path.exists(fullPath): dataUrl = dataStore.dataUrl dataLocation = dataUrl.url.dataLocation if dataLocation == oldDirectory or \ (dataLocation.startswith(oldDirectory) and dataLocation[len(oldDirectory)] == dirsep): newDataUrlPath = newDirectory + dataLocation[ len(oldDirectory):] newPath = joinPath(newDataUrlPath, dataStore.path) if os.path.exists(newPath): warningMessages.append( 'DataStore %s:%s path has been changed from\n"%s"\nto\n"%s"' % (dataStore.dataLocationStore.name, dataStore.serial, oldDirectory, newDataUrlPath)) dataUrl.url = dataUrl.url.clone( path=newDataUrlPath) # change refData to current one if need be refDataRepository = project.findFirstRepository(name='refData') if refDataRepository: oldPath = refDataRepository.url.path newPath = normalisePath(getDataDirectory()) if newPath != oldPath: warningMessages.append( 'refData has been changed from\n"%s"\nto\n"%s"' % (oldPath, newPath)) refDataRepository.url = Implementation.Url(path=newPath) else: warningMessages.append('Project has no repository with name "refData"') # change generalData to current one if need be generalDataRepository = project.findFirstRepository(name='generalData') if generalDataRepository and not suppressGeneralDataDir: oldPath = generalDataRepository.url.path if not os.path.exists(oldPath) or not isGeneralDataWriteable( generalDataRepository): newPath = normalisePath(os.path.expanduser('~/.ccpn/data')) if not os.path.exists(newPath): os.makedirs(newPath) warningMessages.append( 'generalData has been changed from\n"%s"\nto\n"%s"' % (oldPath, newPath)) generalDataRepository.url = Implementation.Url(path=newPath) # check other repository paths for repository in project.repositories: if repository not in (projectRepository, refDataRepository, backupRepository, generalDataRepository): oldPath = repository.url.path if not repository.stored: title = 'Repository being deleted' msg = 'Repository "%s" with path "%s" has no packageLocators, deleting' % ( repository.name, oldPath) repository.delete() elif not os.path.exists(oldPath): title = 'Repository path does not exist' if showWarning is printWarning: tt = '' else: tt = ' (see console for packages)' msg = 'Repository "%s" path "%s" does not exist%s' % ( repository.name, oldPath, tt) print('List of packageLocators for repository "%s":' % repository.name) for packageLocator in repository.stored: print(' %s' % packageLocator.targetName) if askDir: newPath = askDir(title, msg + ': enter new path', initial_value=oldPath) while newPath and not os.path.exists(newPath): msg = 'Path "%s" does not exist' % newPath newPath = askDir(title, msg + ': enter new path', initial_value=newPath) if newPath: repository.url = Implementation.Url( path=normalisePath(newPath)) else: warningMessages.append(msg) if warningMessages: showWarning('Warnings', '\n\n'.join(warningMessages)) return project
def saveProject(project, newPath=None, newProjectName=None, changeBackup=True, createFallback=False, removeExisting=False, showYesNo=None, checkValid=False, changeDataLocations=False, showWarning=None, revertToOriginal=False): """ Save the userData for a project to a location given by newPath (the url.path of the userData repository) if set, or the existing location if not. If userData does not exist then throws IOError. If newPath is not specified then it is set to oldPath. If newProjectName is not specified then it is set to oldProjectName if newPath==oldPath, otherwise it is set to basename(newPath). If changeBackup, then also changes backup URL path for project. If createFallback, then makes copy of existing modified topObjects files (in newPath, not oldPath) before doing save. If newPath != oldPath and newPath exists (either as file or as directory): If removeExisting: Delete the newPath. Else if showYesNo: Ask the user if it is ok to delete the newPath If yes, delete. If no, return without saving. Else: Raise an IOError Elif newProjectName != oldProjectName and there exists corresponding path (file/directory): If removeExisting: Delete the path. Else if showYesNo: Ask the user if it is ok to delete the path. If yes, delete. If no, return without saving. Else: Raise an IOError If checkValid then does checkAllValid on project If changeDataLocations then copy to project directory If revertToOriginal then after the save changes paths back to as they were originally If there is no exception or early return then at end userData is pointing to newPath. Return True if save done, False if not (unless there is an exception) """ # check project valid (so don't save an obviously invalid project) if checkValid: project.checkAllValid() # only want to change path for userData userData = project.findFirstRepository(name='userData') if not userData: raise IOError('Problem: userData not found') oldPath = userData.url.path oldProjectName = project.name if newPath: # normalise newPath newPath = normalisePath(newPath, makeAbsolute=True) else: newPath = oldPath # if newProjectName isn't specified then use default if not newProjectName: if newPath == oldPath: newProjectName = oldProjectName else: newProjectName = os.path.basename(newPath) # below is because of data model limit newProjectName = newProjectName[:32] renameProject(project, newProjectName) # if newPath same as oldPath, check if newProjectName already exists if it's not same as oldProjectName if newPath == oldPath: if newProjectName != oldProjectName: location = xmlUtil.getTopObjectPath(project) if os.path.exists(location): answer = checkRemoveIfExists(location, removeExisting, showYesNo) if answer is None: project.__dict__[ 'name'] = oldProjectName # TBD: for now name is frozen so change this way return False else: # check instead if newPath already exists if os.path.exists(newPath): answer = checkRemoveIfExists(newPath, removeExisting, showYesNo) if answer is None: if newProjectName != oldProjectName: project.__dict__[ 'name'] = oldProjectName # TBD: for now name is frozen so change this way return False # TBD: should we be removing it? removePath(newPath) else: # NBNB 2008/04/03 Rasmus Fogh. Added because it otherwise fell over when # target path did not exist upDir = os.path.dirname(newPath) if not os.path.exists(upDir): os.makedirs(upDir) # check if any topObject activeRepository is not either of above refData = project.findFirstRepository(name='refData') genData = project.findFirstRepository(name='generalData') topObjects = [] repositories = set() for topObject in project.topObjects: repository = topObject.findFirstActiveRepository() if repository and repository not in (userData, refData, genData): topObjects.append(topObject) repositories.add(repository) if topObjects: print( 'Warning, topObjects %s, in repositories %s, being left in original locations' % (topObjects, repositories)) try: oldUrl = userData.url if changeBackup: # change project backup url to point to new path backupRepository = project.findFirstRepository(name="backup") if backupRepository: oldBackupUrl = backupRepository.url else: changeBackup = False # copy userData files over if newPath != oldPath: # if os.path.exists(oldPath): # only copy if this is a directory if os.path.isdir(oldPath): # just copy everything from oldPath to newPath print( 'Copying directory %s to %s (this might take some time if there are big files)' % (oldPath, newPath)) shutil.copytree(oldPath, newPath) # but need toz remove all implementation files implPath = joinPath(newPath, ImpConstants.modellingPackageName, ImpConstants.implementationPackageName) #implPath = pathImplDirectory(newPath) removePath(implPath) # and need to repoint dataUrl's that were copied over oldPathP = oldPath + '/' for dataLocationStore in project.dataLocationStores: for dataStore in dataLocationStore.dataStores: oldDataPath = dataStore.fullPath if oldDataPath.startswith(oldPathP): dataUrl = dataStore.dataUrl oldDataUrlPath = dataUrl.url.dataLocation if oldDataUrlPath.startswith( oldPathP): # normally true newDataUrlPath = newPath + oldDataUrlPath[ len(oldPath):] dataUrl.url = Implementation.Url( path=newDataUrlPath) else: # path split awkwardly between absolute and relative newDataUrlPath = newPath dataUrl.url = Implementation.Url( path=newDataUrlPath) dataStore.path = oldDataPath[len(oldPath):] # change userData url to point to new path userData.url = Implementation.Url(path=newPath) # above will set project.isModified = True if changeBackup: # change project backup repository url to point to new path backupRepository.url = Implementation.Url(path=newPath + '_backup') # change project name if newProjectName != oldProjectName: if not project.isModified: # if it isModified it will be saved below if createFallback: createTopObjectFallback(project) project.save() # create fallbacks and keep track of modified topObjects modifiedTopObjects = [] if createFallback: for topObject in (project, ) + tuple(project.topObjects): if not topObject.isDeleted and topObject.isModified: createTopObjectFallback(topObject) modifiedTopObjects.append(topObject) if changeDataLocations: dataLocationStores = project.sortedDataLocationStores() userRepository = project.findFirstRepository(name='userData') userPath = userRepository.url.dataLocation # 2010 Aug 11: remove data directory from path #dataPath = joinPath(userPath, 'data') dataPath = userPath # 2010 Aug 11: change name #dataStorePrefix = 'dataStore' dataStorePrefix = 'spectra' if os.path.exists(dataPath): files = [ xx for xx in os.listdir(dataPath) if xx.startswith(dataStorePrefix) ] offset = len(files) else: offset = 0 copyingList = [] dataUrlDict = {} for dataLocationStore in dataLocationStores: for dataStore in dataLocationStore.sortedDataStores(): # wb104: 24 Mar 2010: below check is a complete kludge # we should check whether dataStore is instance of # NumericMatrix, etc., but those are in ccp so should # not be imported here # in any case, there is no proper way to find out if # a dataStore is used without explicit knowledge of class knt = 0 # hicard = 1 for attr in ('nmrDataSourceImage', ): if hasattr(dataStore, attr): if getattr(dataStore, attr): knt += 1 # hicard > 1 for attr in ('externalDatas', 'nmrDataSources'): if hasattr(dataStore, attr): knt += len(getattr(dataStore, attr)) if knt == 0: continue oldFullPath = dataStore.fullPath if not oldFullPath.startswith(userPath + '/'): # first figure out new dataUrl path dataUrl = dataStore.dataUrl oldPath = dataUrl.url.dataLocation if dataUrl in dataUrlDict: newUrl = dataUrlDict[dataUrl] else: offset += 1 newUrlPath = '%s%d' % (dataStorePrefix, offset) newUrlPath = joinPath(dataPath, newUrlPath) newUrl = dataUrlDict[dataUrl] = Implementation.Url( path=newUrlPath) # then add to list to copy over if original data exists if os.path.exists(oldFullPath): newFullPath = joinPath(newUrl.dataLocation, dataStore.path) copyingList.append((oldFullPath, newFullPath)) # now copy data files over nfilesToCopy = len(copyingList) for n, (oldFullPath, newFullPath) in enumerate(copyingList): dirName = os.path.dirname(newFullPath) if not os.path.exists(dirName): os.makedirs(dirName) print('Copying file %s to %s (%d of %d)' % (oldFullPath, newFullPath, n + 1, nfilesToCopy)) shutil.copy(oldFullPath, newFullPath) # finally change dataUrl paths for dataUrl in dataUrlDict: dataUrl.url = dataUrlDict[dataUrl] # save modifications # change way doing save in case exception is thrown if createFallback: for topObject in modifiedTopObjects: try: topObject.save() except: location = xmlUtil.getTopObjectPath(topObject) print('Exception working on topObject %s, file %s' % (topObject, location)) raise # be safe and do below in case new modifications after # modifiedTopObjects has been created project.saveModified() else: project.saveModified() if not isWindowsOS(): os.system('touch "%s"' % newPath) # so that user can see which are most recent badTopObjects = [] for topObject in modifiedTopObjects: if not checkFileIntegrity(topObject): badTopObjects.append(topObject) if badTopObjects: if showWarning: showWarning( 'Incomplete save', 'It looks like one or more files did not save completely, see console for list' ) print( 'It looks like one or more files did not save completely, you should check them:' ) for topObject in badTopObjects: print print('%s, path:' % topObject) print(xmlUtil.getTopObjectPath(topObject)) return False if revertToOriginal: # TBD: the below does not change back dataUrl paths if newProjectName != oldProjectName: project.__dict__[ 'name'] = oldProjectName # TBD: for now name is frozen so change this way if newPath != oldPath: userData.url = oldUrl if changeBackup: backupRepository.url = oldBackupUrl return True except: # saveModified failed so revert to old values if newProjectName != oldProjectName: project.__dict__[ 'name'] = oldProjectName # TBD: for now name is frozen so change this way if newPath != oldPath: userData.url = oldUrl if changeBackup: backupRepository.url = oldBackupUrl try: removePath(newPath) except: pass raise
def checkFileIntegrity(topObject): """Check that topObject file on disk ends correctly """ path = xmlUtil.getTopObjectPath(topObject) return checkFileAtPath(path)
def loadProject(repositoryPath, projectName, partialLoad=False): """ Load project by projectName """ filePath = xmlUtil.getProjectFile(repositoryPath, projectName) return loadProjectFile(filePath, partialLoad=partialLoad)
if __name__ == '__main__': from memops.format.xml import Util as xmlUtil from memops.format.xml import XmlIO from memops.universal import Io as uniIo from memops.api import Implementation import Tkinter # standard project name projectName = 'refIsotopeEditor' # get or make project repDir = uniIo.joinPath(uniIo.normalisePath(uniIo.os.getcwd()), projectName) projectFile = xmlUtil.getProjectFile(repDir, projectName=projectName) if os.path.isfile(projectFile): project = XmlIO.loadProjectFile(projectFile) else: project = Implementation.MemopsRoot(name=projectName) # reorder to make sure data are saved in refData labelingSchemeLocator = project.findFirstPackageLocator( targetName='ccp.molecule.ChemCompLabel') repositories = list(labelingSchemeLocator.repositories) for rr in repositories: if rr.name == 'refData': refRepository = rr break else: refRepository = None
def loadProject(path, projectName=None, showWarning=None, askFile=None, askDir=None, suppressGeneralDataDir = False): """ Loads a project file and checks and deletes unwanted project repositories and changes the project repository path if the project has moved. Returns the project. (The project repository path is effectively the userData repository.) showWarning (if not None) has signature showWarning(title, message) askFile (if not None) has signature askFile(title, message, initial_value = '') askDir (if not None) has signature askDir(title, message, initial_value = '') Throws an IOError if there is an I/O error. Throws an ApiError if there is an API exception. """ from memops.format.xml import XmlIO if not showWarning: showWarning = printWarning path = normalisePath(path, makeAbsolute=True) # check if path exists and is directory if not os.path.exists(path): raise IOError('path "%s" does not exist' % path) if not os.path.isdir(path): raise IOError('path "%s" is not a directory' % path) projectFile = xmlUtil.getProjectFile(path, projectName) if projectName: # projectName was specified so projectFile better exist if not os.path.exists(projectFile): raise IOError('project file "%s" does not exist' % projectFile) else: # projectName was not specified so default projectFile might not exist if not os.path.exists(projectFile): projectFiles = xmlUtil.getPossibleProjectFiles(path) if len(projectFiles) == 0: raise IOError('"%s" contains no project file' % path) elif len(projectFiles) == 1: projectFile = projectFiles[0] elif askFile: projectFile = askFile('Select project file', 'Select project file', initial_value=projectFiles[0]) if not projectFile: # cancelled raise IOError('Cancelled') else: raise IOError('"%s" contains %d project files, not sure which to use' % (path, len(projectFiles))) # TBD: should projectName be based on projectFile or on path??? # the way it is set here do not need to change project.name # but if you used the path then you would need to change it projectName = xmlUtil.getTopObjIdFromFileName(projectFile) # doing the loadProject twice has a bit of an overhead, but not much try: # below assumes TopObjects exist where stated, so might fail project = XmlIO.loadProject(path, projectName, partialLoad=False) # check whether all topObject directories exist where they are said to exist # if not then reload project with partialLoad = True for repository in project.repositories: if not os.path.exists(repository.getFileLocation()): project = XmlIO.loadProject(path, projectName, partialLoad=True) break except: # this can happen if Implementation file is inconsistent with data model project = XmlIO.loadProject(path, projectName, partialLoad=True) # try and fix project repository path, if needed packageLocator = project.packageLocator repositories = packageLocator.repositories for repository in repositories: if repository.url.path == path: oldPath = path break else: # change first repository path to point to this one, and also change backup repository = repositories[0] oldPath = repository.url.path showWarning('Project moved', 'Project file has moved from "%s" to "%s", updating path' \ % (oldPath, path)) repository.url = Implementation.Url(path=path) projectRepository = repository # check backup path backupRepository = project.findFirstRepository(name="backup") if backupRepository: backupUrl = backupRepository.url oldBackupPath = backupUrl.path newBackupPath = path + '_backup' if oldBackupPath.startswith(oldPath): # hopefully true if path != oldPath: showWarning('Backup changed', 'Backup is being changed from "%s" to "%s", updating path' \ % (oldBackupPath, newBackupPath)) backupRepository.url = Implementation.Url(path=newBackupPath) elif not os.path.exists(oldBackupPath): showWarning('Backup changed', 'Backup is being changed from "%s" to "%s", updating path' \ % (oldBackupPath, newBackupPath)) backupRepository.url = Implementation.Url(path=newBackupPath) # check if project repository is called 'userData' if projectRepository.name != 'userData': showWarning('Project non-standard', 'Project has non-standard repository name "%s"' % projectRepository.name) # change refData to current one if need be refDataRepository = project.findFirstRepository(name='refData') if refDataRepository: oldPath = refDataRepository.url.path newPath = normalisePath(getDataDirectory()) if newPath != oldPath: showWarning('refData changed', 'refData has been changed from "%s" to "%s", updating paths' % (oldPath, newPath)) refDataRepository.url = Implementation.Url(path=newPath) else: showWarning('No refData repository', 'Project has no repository with name "refData"') # change generalData to current one if need be generalDataRepository = project.findFirstRepository(name='generalData') if generalDataRepository and not suppressGeneralDataDir: oldPath = generalDataRepository.url.path if not os.path.exists(oldPath): newPath = normalisePath(os.path.expanduser('~/.ccpn/data')) if not os.path.exists(newPath): os.makedirs(newPath) showWarning('generalData changed', 'generalData has been changed from "%s" to "%s", updating paths' % (oldPath, newPath)) generalDataRepository.url = Implementation.Url(path=newPath) # check other repository paths for repository in project.repositories: if repository not in (projectRepository, refDataRepository, backupRepository, generalDataRepository): oldPath = repository.url.path if not repository.stored: title = 'Repository being deleted' msg = 'Repository "%s" with path "%s" has no packageLocators, deleting' % (repository.name, oldPath) repository.delete() elif not os.path.exists(oldPath): title = 'Repository path does not exist' if showWarning is printWarning: tt = '' else: tt = ' (see console for packages)' msg = 'Repository "%s" path "%s" does not exist%s' % (repository.name, oldPath, tt) print 'List of packageLocators for repository "%s":' % repository.name for packageLocator in repository.stored: print ' %s' % packageLocator.targetName if askDir: newPath = askDir(title, msg + ': enter new path', initial_value=oldPath) while newPath and not os.path.exists(newPath): msg = 'Path "%s" does not exist' % newPath newPath = askDir(title, msg + ': enter new path', initial_value=newPath) if newPath: repository.url = Implementation.Url(path=normalisePath(newPath)) else: showWarning(title, msg) return project
def saveProject(project, newPath = None, newProjectName = None, changeBackup = True, createFallback = False, removeExisting = False, showYesNo = None, checkValid = False): """ Save the userData for a project to a location given by newPath (the url.path of the userData repository) if set, or the existing location if not. If userData does not exist then throws IOError. If newPath is not specified then it is set to oldPath. If newProjectName is not specified then it is set to oldProjectName if newPath==oldPath, otherwise it is set to basename(newPath). If changeBackup, then also changes backup URL path for project. If createFallback, then makes copy of existing modified topObjects files (in newPath, not oldPath) before doing save. If newPath != oldPath and newPath exists (either as file or as directory): If removeExisting: Delete the newPath. Else if showYesNo: Ask the user if it is ok to delete the newPath If yes, delete. If no, return without saving. Else: Raise an IOError Elif newProjectName != oldProjectName and there exists corresponding path (file/directory): If removeExisting: Delete the path. Else if showYesNo: Ask the user if it is ok to delete the path. If yes, delete. If no, return without saving. Else: Raise an IOError if checkValid then does checkAllValid on project If there is no exception or early return then at end userData is pointing to newPath. Return True if save done, False if not (unless there is an exception) """ # check project valid (so don't save an obviously invalid project) if checkValid: project.checkAllValid() # only want to change path for userData userData = project.findFirstRepository(name='userData') if not userData: raise IOError('Problem: userData not found') oldPath = userData.url.path oldProjectName = project.name if newPath: # normalise newPath newPath = normalisePath(newPath, makeAbsolute=True) else: newPath = oldPath # if newProjectName isn't specified then use default if not newProjectName: if newPath == oldPath: newProjectName = oldProjectName else: newProjectName = os.path.basename(newPath) # change project name if newProjectName != oldProjectName: project.override = True # TBD: for now name is frozen so change this way try: # below constraint is not checked in setName() if override is True so repeat here isValid = newProjectName.isalnum() # superfluous but faster in most cases if not isValid: for cc in newProjectName: if cc != '_' and not cc.isalnum(): isValid = False break else: isValid = True if (not (isValid)): raise ApiError('project name must only have characters that are alphanumeric or underscore') # below checks for length of name as well project.name = newProjectName finally: project.override = False # if newPath same as oldPath, check if newProjectName already exists if it's not same as oldProjectName if newPath == oldPath: if newProjectName != oldProjectName: location = xmlUtil.getTopObjectPath(project) if os.path.exists(location): answer = checkRemoveIfExists(location, removeExisting, showYesNo) if answer is None: project.__dict__['name'] = oldProjectName # TBD: for now name is frozen so change this way return False else: # check instead if newPath already exists if os.path.exists(newPath): answer = checkRemoveIfExists(newPath, removeExisting, showYesNo) if answer is None: if newProjectName != oldProjectName: project.__dict__['name'] = oldProjectName # TBD: for now name is frozen so change this way return False # TBD: should we be removing it? removePath(newPath) else: # NBNB 2008/04/03 Rasmus Fogh. Added because it otherwise fell over when # target path did not exist upDir = os.path.dirname(newPath) if not os.path.exists(upDir): os.makedirs(upDir) # check if any topObject activeRepository is not either of above refData = project.findFirstRepository(name='refData') genData = project.findFirstRepository(name='generalData') topObjects = [] repositories = set() for topObject in project.topObjects: repository = topObject.findFirstActiveRepository() if repository and repository not in (userData, refData, genData): topObjects.append(topObject) repositories.add(repository) if topObjects: print 'Warning, topObjects %s, in repositories %s, being left in original locations' % (topObjects, repositories) try: oldUrl = userData.url if changeBackup: # change project backup url to point to new path backupRepository = project.findFirstRepository(name="backup") if backupRepository: oldBackupUrl = backupRepository.url else: changeBackup = False # copy userData files over if newPath != oldPath: # if os.path.exists(oldPath): # only copy if this is a directory if os.path.isdir(oldPath): # just copy everything from oldPath to newPath shutil.copytree(oldPath, newPath) # but need to remove all implementation files implPath = joinPath(newPath, ImpConstants.modellingPackageName, ImpConstants.implementationPackageName) #implPath = pathImplDirectory(newPath) removePath(implPath) # change userData url to point to new path userData.url = Implementation.Url(path=newPath) # above will set project.isModified = True if changeBackup: # change project backup repository url to point to new path backupRepository.url = Implementation.Url(path=newPath+'_backup') # change project name if newProjectName != oldProjectName: if not project.isModified: # if it isModified it will be saved below if createFallback: createTopObjectFallback(project) project.save() # create fallbacks if createFallback: for topObject in (project,)+tuple(project.topObjects): if not topObject.isDeleted and topObject.isModified: createTopObjectFallback(topObject) # save modifications project.saveModified() if not isWindowsOS(): os.system('touch %s' % newPath) # so that user can see which are most recent return True except: # saveModified failed so revert to old values if newProjectName != oldProjectName: project.__dict__['name'] = oldProjectName # TBD: for now name is frozen so change this way if newPath != oldPath: userData.url = oldUrl if changeBackup: backupRepository.url = oldBackupUrl try: removePath(newPath) except: pass raise