""" CCP4ProjectManagerGui.py: CCP4 GUI Project Copyright (C) 2010 University of York This library is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3, modified in accordance with the provisions of the sstatusW license to address the requirements of UK law. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ """ Liz Potterton Feb 2010 - Create CCP4ProjectManager mostly as placeholder Liz Potterton May 2010 - Rename CCP4ProjectManagerGui """ ##@package CCP4ProjectManagerGui (QtGui) Project handling tools for web browser (currently just a Project menu) import functools from PyQt4 import QtGui,QtCore from CCP4Modules import PROJECTSMANAGER,PROGRAMLOG,WEBBROWSER,PREFERENCES,MIMETYPESHANDLER from CCP4ErrorHandling import * import CCP4Widgets from CCP4Config import DEVELOPER DIAGNOSTIC = True def openProject(projectId=None,projectName=None): # 'openProject',projectId,projectName if projectId is None: projectId = PROJECTSMANAGER().db().getProjectId(projectName=projectName) if projectId is None: return None import CCP4ProjectViewer for window in CCP4ProjectViewer.CProjectViewer.Instances: if hasattr(window,'openJob') and window.openJob.projectId == projectId: window.show() window.raise_() return window else: p = CCP4ProjectViewer.CProjectViewer(projectId=projectId) p.show() p.raise_() return p def setNewProjectMode(): if PREFERENCES().NEW_PROJECT_STYLE == 'New': CNewProjectGui.MODE = 2 else: CNewProjectGui.MODE = 1 class CNewProjectGui(QtCore.QObject): MODE = 2 def __init__(self,parent=None): QtCore.QObject.__init__(self,parent=parent) self.createDir = False self.directory = None self.name = None self.page1 = None self.page2 = None self.page3 = None self.connect(self,QtCore.SIGNAL('toPage2'),self.toPage2) def start(self): self.createDir = False self.directory = None self.name = None if CNewProjectGui.MODE ==1: if self.page1 is None: self.page1 = QtGui.QDialog(self.parent()) self.page1.setModal(True) self.page1.setWindowTitle('Create New Project') self.page1.setLayout(QtGui.QVBoxLayout()) textWidget = QtGui.QLabel(self.page1) textWidget.setText( \ '''A CCP4i2 project has a project directory where all files are saved.To create a new project you must select a directory and provide a unique name for the project. Do you want to create a new directory or use an existing directory?''') self.page1.layout().addWidget(textWidget) buttonBox = QtGui.QDialogButtonBox(self.page1) self.page1.layout().addWidget(buttonBox) but = buttonBox.addButton('Create new directory',QtGui.QDialogButtonBox.YesRole) but = buttonBox.addButton('Use existing directory',QtGui.QDialogButtonBox.NoRole) but = buttonBox.addButton('Cancel',QtGui.QDialogButtonBox.RejectRole) self.connect(buttonBox,QtCore.SIGNAL('clicked(QAbstractButton*)'),self.toPage2) self.page1.show() elif CNewProjectGui.MODE == 2: self.toPage2() def toPage2(self,abstractButton=None): import CCP4FileBrowser if CNewProjectGui.MODE == 1: if abstractButton is None: caption = 'Select directory for project' fileMode = QtGui.QFileDialog.Directory else: self.page1.close() if abstractButton.text().__str__() == 'Cancel': self.finish() return elif abstractButton.text().__str__().count('Create'): self.createDir = True caption = 'Create new directory for project' fileMode = CCP4FileBrowser.CFileDialog.NewDirectory else: caption = 'Select existing directory for project' fileMode = QtGui.QFileDialog.Directory #print 'CNewProjectGui.toPage2',self.createDir # Need to redo page to be sure have correct fileMode if self.page2 is not None: self.page2.deleteLater() self.page2 = None self.page2 = CCP4FileBrowser.CFileDialog(self.parent(),title=caption, fileMode=fileMode) self.connect(self.page2,QtCore.SIGNAL('selectFile'),self.toPage3) self.connect(self.page2,QtCore.SIGNAL('rejected()'),self.finish) self.page2.show() elif CNewProjectGui.MODE == 2: rv = QtGui.QFileDialog.getExistingDirectory(caption='Select directory for saving project files') self.toPage3(str(rv)) ''' self.page2 = QtGui.QFileDialog(self.parent(),caption) self.page2.setFileMode(fileMode) #if self.createDir: self.page2.setAcceptMode(QtGui.QFileDialog.AcceptSave) self.page2.setOption(QtGui.QFileDialog.ShowDirsOnly,True) self.page2.fileLineEdit.setCharWidth(60,mode='minimum') self.connect(self.page2,QtCore.SIGNAL('fileSelected(const QString&)'),self.toPage3) self.connect(self.page2,QtCore.SIGNAL('rejected()'),self.page2Rejected) ''' def finish(self): self.emit(QtCore.SIGNAL('finished')) def toPage3(self,fileName): import os print 'CNewProjectGui.toPage3',fileName if len(fileName)==0: return self.directory = fileName # Test directory appropriate altName = PROJECTSMANAGER().aliasForDirectory(self.directory) if altName is not None: QtGui.QMessageBox.warning(self.parent(),'Directory already a project','This directory is already used by the project: '+altName) self.emit(QtCore.SIGNAL('toPage2')) return parentProject,relpath,parentProjectId = PROJECTSMANAGER().interpretDirectory(self.directory) if parentProject is not None: QtGui.QMessageBox.warning(self.parent(),'This is a sub-directory of a project','This is in the directory of project: '+parentProject+'\nPlease put it in a separate direectory') self.emit(QtCore.SIGNAL('toPage2')) return # Make directory if not os.path.exists(self.directory) and self.createDir: parentDir = os.path.split(self.directory)[0] if not os.path.exists(parentDir): QtGui.QMessageBox.warning(self.parent(),'Directory does not exist','The parent directory for the project directory does not exist: ' +parentDir ) return try: os.mkdir(self.directory) except: QtGui.QMessageBox.warning(self.parent(),'Error making directory','Error making directory: '+self.directory) self.finish() return if self.page3 is None: self.page3 = QtGui.QDialog(self.parent()) self.page3.setModal(True) self.page3.setWindowTitle('Create New Project') self.page3.setLayout(QtGui.QVBoxLayout()) self.pag3Label = QtGui.QLabel(self.page1) self.page3.layout().addWidget(self.pag3Label) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Project name',self.page1)) self.projectNameWidget = QtGui.QLineEdit(self.page3) line.addWidget(self.projectNameWidget ) self.page3.layout().addLayout(line) buttonBox = QtGui.QDialogButtonBox(self.page3) but = buttonBox.addButton('Cancel',QtGui.QDialogButtonBox.RejectRole) but = buttonBox.addButton('Create project',QtGui.QDialogButtonBox.ApplyRole) but.setDefault(true); self.page3.layout().addWidget(buttonBox) self.connect(buttonBox,QtCore.SIGNAL('clicked(QAbstractButton*)'),self.makeProject) self.pag3Label.setText('Project directory: '+str(self.directory)) self.projectNameWidget.setText(os.path.splitext(os.path.split(self.directory)[-1])[0]) self.page3.show() def makeProject(self,abstractButton): import os self.page3.close() if abstractButton.text().__str__() == 'Cancel': self.finish() return self.name = self.projectNameWidget.text().__str__() if not os.path.exists(self.directory): try: os.mkdir(self.directory) except: QtGui.QMessageBox.warning(self.parent(),'Error making directory','Error making directory: '+self.directory) self.finish() return try: projectId = PROJECTSMANAGER().createProject(projectName=self.name,projectPath=self.directory) #print 'CNewProjectGui.addProject created',p except CException as e: e.warningMessage(parent=self.parent()) return except: QtGui.QMessageBox.warning(self.parent(),'Error creating project','Unknown error creating project') return else: openProject(projectId=projectId) self.finish() class CNewProjectGui0(QtGui.QDialog): insts = None def __init__(self,parent=None): QtGui.QDialog.__init__(self,parent) # This makes this window stay on top even when we want the file browser to be on top #self.setModal(True) self.setWindowTitle('Create a New Project') self.setLayout(QtGui.QVBoxLayout()) import CCP4Data import CCP4File import CCP4DataManager self.name = CCP4Data.CString(parent=self) self.directory = CCP4File.CDataFile(parent=self,qualifiers={'isDirectory' : True, 'mustExist': False, 'fromPreviousJob' : False }) MARGIN = 1 line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Name of project/folder',self)) self.nameWidget = CCP4DataManager.DATAMANAGER().widget(model=self.name,parentWidget=self) self.nameWidget.setToolTip('Project name - may be used by programs that only allow\none word of alphanumeric characters, dash or underscore.') line.addWidget(self.nameWidget) self.layout().addLayout(line) for textLine in [ "By default all projects go in the 'CCP4I2_PROJECTS' directory in your", "home area - click 'Select directory' to choose an alternative.", "Hint to organise your projects: in the 'Manage projects' window", "you can use a project as a folder and drag other projects into it" ]: line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel(textLine,self)) self.layout().addLayout(line) line = QtGui.QHBoxLayout() ''' self.directoryWidget = CCP4DataManager.DATAMANAGER().widget(model=self.directory,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False} ) self.directoryWidget.fileLineEdit.setCharWidth(60,mode='minimum') line.addWidget(self.directoryWidget) self.layout().addLayout(line) ''' line = QtGui.QHBoxLayout() buttonBox = QtGui.QDialogButtonBox(self) but = buttonBox.addButton('Create project',QtGui.QDialogButtonBox.AcceptRole) but.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(but,QtCore.SIGNAL('released()'),self.addProject) but = buttonBox.addButton('Select directory',QtGui.QDialogButtonBox.ActionRole) but.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(but,QtCore.SIGNAL('released()'),self.selectDir) but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) but.setFocusPolicy(QtCore.Qt.NoFocus) but.setDefault(True) self.connect(but,QtCore.SIGNAL('released()'),self.close) but = buttonBox.addButton(QtGui.QDialogButtonBox.Help) but.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(but,QtCore.SIGNAL('released()'),self.help) line.addStretch(0.5) line.addWidget(buttonBox) line.addStretch(0.5) self.layout().addLayout(line) self.connect(self.directory,QtCore.SIGNAL('dataChanged'),self.setProjectName) self.connect(self,QtCore.SIGNAL('rejected()'),self.close) def setProjectName(self): import os if not self.directory.isSet() or self.name.isSet(): return tail = os.path.split(self.directory.__str__())[1] self.name.set(tail) self.nameWidget.updateViewFromModel() def help(self): import CCP4Modules CCP4Modules.HELPBROWSER().loadWebPage(helpFileName='general/tutorial' , target='projects') def clear(self): self.name.unSet() self.directory.unSet() #self.directoryWidget.updateViewFromModel() self.nameWidget.updateViewFromModel() def selectDir(self): self.nameWidget.updateModelFromView() name = self.name.get() if name is None: QtGui.QMessageBox.warning(self,'No project name','You must provide a project name') return elif PROJECTSMANAGER().projectNameStatus(name) is not 0: QtGui.QMessageBox.warning(self,'Project exists','A project of that name already exists') return rv = QtGui.QFileDialog.getExistingDirectory(caption='Select directory for saving project '+str(name)) #print 'selectDir getExistingDirectory',rv,len(rv) if rv is not None and len(rv)>0: self.addProject(str(rv)) def addProject(self,directory = None): #print 'CNewProjectGui.addProject',directory,'*' import os import CCP4Utils self.nameWidget.updateModelFromView() name = self.name.get() if name is None: QtGui.QMessageBox.warning(self,'No project name','You must provide a project name') return elif PROJECTSMANAGER().projectNameStatus(name) is not 0: QtGui.QMessageBox.warning(self,'Project exists','A project of that name already exists') return name0 = CCP4Utils.safeOneWord(name) if name0 != name: print 'Fixing name to ',name0 if directory is None: directory = CCP4Utils.getProjectDirectory() if directory is None: QtGui.QMessageBox.warning(self,'CCP4I2_PROJECTS directory','Failed creating a CCP4I2_PROJECTS directory in your home directory') return directory = os.path.join(directory,name0) else: directory = os.path.normpath(directory) if os.path.isfile(directory): QtGui.QMessageBox.warning(self,'Create project','Selected directory is a file') return elif CCP4Utils.samefile(directory,CCP4Utils.getHOME()): QtGui.QMessageBox.warning(self,'Create project','Selected directory is users home area') return elif CCP4Utils.samefile(directory,CCP4Utils.getProjectDirectory()): QtGui.QMessageBox.warning(self,'Create project','Selected directory is the CCP4 master project directory') return altName = PROJECTSMANAGER().aliasForDirectory(directory) if altName is not None: QtGui.QMessageBox.warning(self,'Directory already a project','This directory is already used by the project: '+altName) return parentProject,relpath,parentProjectId = PROJECTSMANAGER().interpretDirectory(directory) if parentProject is not None: rv = QtGui.QMessageBox.question(self,'Make sub-project', 'This is sub-directory of another project \nMake this project a sub-project of '+parentProject, QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel) if rv == QtGui.QMessageBox.Cancel: return if not os.path.exists(directory): parentDir = os.path.split(directory)[0] if not os.path.exists(parentDir): QtGui.QMessageBox.warning(self,'Directory does not exist','The parent directory for the project directory does not exist: ' +parentDir ) return ''' rv = QtGui.QMessageBox.question(self,'Make new directory','Make a new directory for the project', QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel) if rv == QtGui.QMessageBox.Cancel: return ''' try: os.mkdir(directory) except: QtGui.QMessageBox.warning(self,'Error making directory','Error making directory: '+directory) return if DEVELOPER(): projectId = PROJECTSMANAGER().createProject(projectName=name0,projectPath=directory) else: try: projectId = PROJECTSMANAGER().createProject(projectName=name0,projectPath=directory) #print 'CNewProjectGui.addProject created',p except CException as e: e.warningMessage(parent=self) return except: QtGui.QMessageBox.warning(self,'Error creating project','Unknown error creating project') return ''' if self.xtalContents.isChecked() and pView is not None: pView.openTask(taskName='newProject') ''' self.emit(QtCore.SIGNAL('projectCreated'),projectId) pView = openProject(projectId) #print 'CNewProjectGui.addProject to close' self.close() def close(self): for child in self.findChildren(QtGui.QDialog): child.close() QtGui.QDialog.close(self) class CExportOptionsDialog(QtGui.QDialog): def __init__(self,parent=None,projectId=None,projectName=None): QtGui.QDialog.__init__(self,parent) #self.setModal(True) self.setWindowTitle('Options for saving project: '+str(projectName)) self.setLayout(QtGui.QVBoxLayout()) self.modeGroup = QtGui.QButtonGroup(self) self.modeGroup.setExclusive(True) line = QtGui.QHBoxLayout() but = QtGui.QRadioButton('Export entire project',self) self.modeGroup.addButton(but,1) but.setChecked(True) line.addWidget(but) self.layout().addLayout(line) line = QtGui.QHBoxLayout() but = QtGui.QRadioButton('Export every job after previous import/export',self) self.modeGroup.addButton(but,2) line.addWidget(but) self.layout().addLayout(line) line = QtGui.QHBoxLayout() self.impExpWidget = CImportExportListWidget(self,projectId=projectId) line.addSpacing(20) line.addWidget(self.impExpWidget) self.layout().addLayout(line) line = QtGui.QHBoxLayout() but = QtGui.QRadioButton('Select jobs to export',self) self.modeGroup.addButton(but,3) line.addWidget(but) self.layout().addLayout(line) line = QtGui.QHBoxLayout() self.selectionWidget = CCP4Widgets.CJobSelectionLineEdit(self,projectId) line.addSpacing(20) line.addWidget(self.selectionWidget) self.layout().addLayout(line) #if DEVELOPER(): if False: line = QtGui.QHBoxLayout() self.excludeI2files = QtGui.QCheckBox(self) self.excludeI2files.setChecked(True) line.addWidget(self.excludeI2files) line.addWidget(QtGui.QLabel('Exclude demo data files from CCP4I2 distribution',self)) self.layout().addLayout(line) line = QtGui.QHBoxLayout() buttonBox = QtGui.QDialogButtonBox(self) but = buttonBox.addButton('Export',QtGui.QDialogButtonBox.AcceptRole) but.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(but,QtCore.SIGNAL('released()'),self.handleExport) but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.close) but = buttonBox.addButton(QtGui.QDialogButtonBox.Help) but.setFocusPolicy(QtCore.Qt.NoFocus) self.connect(but,QtCore.SIGNAL('released()'),self.help) line.addStretch(0.5) line.addWidget(buttonBox) line.addStretch(0.5) self.layout().addLayout(line) def handleExport(self): self.emit(QtCore.SIGNAL('export'),self.modeGroup.checkedId()) def help(self): pass class CExportJobSelection(QtGui.QLineEdit): def __init__(self,parent,projectId=None): QtGui.QLineEdit.__init__(self,parent) self.projectId = projectId self.setToolTip("Enter list of jobs e.g. '27-29,31'") def getSelection(self): errList = [] seleList = [] text = str(self.text()) splitList = text.split(',') #print 'CExportJobSelection.getSelection',text,type(text) for item in splitList: #print 'CExportJobSelection.getSelection split',item rSplit = item.split('-') if len(rSplit)==1: try: jobId = PROJECTSMANAGER().db().getJobId(projectId=self.projectId,jobNumber=item.strip()) except: errList.append(item) else: seleList.append(jobId) elif len(rSplit)==2: try: jobList = PROJECTSMANAGER().db().getJobsInRange(projectId=self.projectId,jobNumberRange=[rSplit[0].strip(),rSplit[1].strip()]) except: errList.append(item) else: if len(jobList)==0: errList.append(item) else: seleList.extend(jobList) else: errList.append(item) #print 'CExportJobSelection.getSelection', seleList,errList return seleList,errList class CImportExportListWidget(QtGui.QComboBox): def __init__(self,parent,projectId=None): QtGui.QComboBox.__init__(self,parent) self.projectId = projectId self.setEditable(False) self.load() def load(self): import time exportList = PROJECTSMANAGER().db().getProjectExportInfo(projectId=self.projectId) importList = PROJECTSMANAGER().db().getProjectImportInfo(projectId=self.projectId) #print 'CImportExportListWidget.load',len(exportList),len(importList) def formatImport(data): imDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[1])) exDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[2])) text = 'Import '+imDate+' from '+importList[iIm][3]+ ' on '+exDate return text,QtCore.QVariant(data[0]) def formatExport(data): exDate = time.strftime( '%a %d %b %H:%M' , time.localtime(data[1])) text = 'Export '+exDate return text,QtCore.QVariant(data[0]) iEx = 0 iIm = 0 while ( iEx=len(exportList): text,qVar = formatImport(importList[iIm]) iIm += 1 elif iIm>=len(importList): text,qVar = formatExport(exportList[iEx]) iEx += 1 elif importList[iIm][1]>exportList[iEx][1]: text,qVar = formatImport(importList[iIm]) iIm += 1 else: text,qVar = formatExport(exportList[iEx]) iEx += 1 self.addItem(text,qVar) def getCurrentItem(self): qVar = self.itemData(self.currentIndex()) #print 'CImportExportListWidget.getCurrentItem',qVar return qVar.toString().__str__() def getTime(self): tid = self.getCurrentItem() try: info = PROJECTSMANAGER().db().getProjectExportInfo(projectExportId=tid) except: info = {} if info.get('projectexporttime',None) is not None: return info['projectexporttime'] else: info = PROJECTSMANAGER().db().getProjectImportInfo(projectImportId=tid) return info['projectimporttime'] class CImportNewProjectDialog(QtGui.QDialog): def __init__(self,parent=None): MARGIN = 2 QtGui.QDialog.__init__(self,parent) #self.setModal(True) self.setWindowTitle('Import a New Project?') self.setLayout(QtGui.QVBoxLayout()) import CCP4Data import CCP4File import CCP4DataManager self.projectInfoDisplay = CProjectInfoDisplay(self) self.layout().addWidget(self.projectInfoDisplay) line = QtGui.QHBoxLayout() lab = QtGui.QLabel('The project id in the file does not match any existing project.') lab.setObjectName('emphasise') line.addWidget(lab) self.layout().addLayout(line) self.modeButs = QtGui.QButtonGroup(self) self.modeButs.setExclusive(True) but = QtGui.QRadioButton('Create a new project directory',self) but.setChecked(True) self.modeButs.addButton(but,1) self.layout().addWidget(but) line = QtGui.QFrame() line.setLayout(QtGui.QHBoxLayout()) line.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) line.layout().setSpacing(MARGIN) line.layout().addSpacing(30) self.directory0 = CCP4File.CDataFile(parent=self,qualifiers={ 'isDirectory' : True,'mustExist': False,'fromPreviousJob' : False }) self.directoryWidget0 = CCP4DataManager.DATAMANAGER().widget(model=self.directory0,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False} ) self.directoryWidget0.fileLineEdit.setCharWidth(60,mode='minimum') line.layout().addWidget(self.directoryWidget0) self.layout().addWidget(line) line = QtGui.QHBoxLayout() line.addSpacing(30) line.addWidget(QtGui.QLabel('Project name',self)) self.projectNameWidget = QtGui.QLineEdit(self) line.addWidget(self.projectNameWidget) self.layout().addLayout(line) but = QtGui.QRadioButton('Add to exisiting project',self) self.modeButs.addButton(but,2) self.layout().addWidget(but) line = QtGui.QHBoxLayout() line.layout().addSpacing(30) self.dbTreeWidget = CProjectsTreeWidget(self) self.dbTreeWidget.populate() line.addWidget(self.dbTreeWidget) self.layout().addLayout(line) line = QtGui.QHBoxLayout() buttonBox = QtGui.QDialogButtonBox(self) but = buttonBox.addButton('Import',QtGui.QDialogButtonBox.AcceptRole) self.connect(but,QtCore.SIGNAL('released()'),self.handleAccept) but.setFocusPolicy(QtCore.Qt.NoFocus) but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) but.setFocusPolicy(QtCore.Qt.NoFocus) but.setDefault(True) self.connect(but,QtCore.SIGNAL('released()'),self.close) line.addStretch(0.1) line.addWidget(buttonBox) line.addStretch(0.1) self.layout().addLayout(line) def reset(self,importProjectInfo): self.directory0.unSet() self.projectInfoDisplay.load(importProjectInfo) self.projectNameWidget.setText(importProjectInfo['projectName']) def handleAccept(self): if self.modeButs.checkedId() == 1: if not self.directory0.isSet(): QtGui.QMessageBox.warning(self,'Import a New Project?','Please enter a new project directory') return projectName = self.projectName() if len(projectName)==0: QtGui.QMessageBox.warning(self,'Import a New Project?','Please enter a name for the new project') return try: projectId = PROJECTSMANAGER().db().getProjectId(projectName = projectName) except: pass else: QtGui.QMessageBox.warning(self,'Import a New Project?','There is already a project in the database called '+projectName) return else: pid = self.dbTreeWidget.selectedProjectId() if pid is None: QtGui.QMessageBox.warning(self,'Import a New Project?','Please select a project to merge with') return self.emit(QtCore.SIGNAL('import')) def mode(self): return self.modeButs.checkedId() def newDirectory(self): return self.directory0.__str__() def projectName(self): return str(self.projectNameWidget.text()).strip() def mergeProject(self): return self.dbTreeWidget.selectedProjectId() class CImportExistingProjectDialog(QtGui.QDialog): def __init__(self,parent=None,projectName=None): MARGIN = 2 QtGui.QDialog.__init__(self,parent) #self.setModal(True) self.setWindowTitle('Import to an Existing Project?') self.setLayout(QtGui.QVBoxLayout()) import CCP4Data import CCP4File import CCP4DataManager self.projectInfoDisplay = CProjectInfoDisplay(self) self.layout().addWidget(self.projectInfoDisplay) line = QtGui.QHBoxLayout() lab = QtGui.QLabel('The project id in the file matches project '+projectName) lab.setObjectName('emphasise') line.addWidget(lab) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget( QtGui.QLabel('Import jobs to that existing project')) self.layout().addLayout(line) line = QtGui.QHBoxLayout() buttonBox = QtGui.QDialogButtonBox(self) but = buttonBox.addButton('Import',QtGui.QDialogButtonBox.AcceptRole) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('import'))) but.setFocusPolicy(QtCore.Qt.NoFocus) but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) but.setFocusPolicy(QtCore.Qt.NoFocus) but.setDefault(True) self.connect(but,QtCore.SIGNAL('released()'),self.close) line.addStretch(0.1) line.addWidget(buttonBox) line.addStretch(0.1) self.layout().addLayout(line) def reset(self,importProjectInfo): self.projectInfoDisplay.load(importProjectInfo) class CProjectInfoDisplay(QtGui.QFrame): def __init__(self,parent,projectInfo={}): QtGui.QFrame.__init__(self,parent) self.setObjectName('highlight') self.setLayout(QtGui.QVBoxLayout()) self.widgets = {} for item in ['projectName','hostName','userId','creationTime']: self.widgets[item] = QtGui.QLabel(self) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('The compressed file contains data for project: ',self)) line.addWidget(self.widgets['projectName']) line.addStretch(0.5) self.layout().addLayout(line) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Created on',self)) line.addWidget(self.widgets['hostName']) line.addWidget(QtGui.QLabel('by:',self)) line.addWidget(self.widgets['userId']) line.addWidget(QtGui.QLabel('on:',self)) line.addWidget(self.widgets['creationTime']) line.addStretch(0.5) self.layout().addLayout(line) self.load(projectInfo) def load(self,projectInfo): #print 'CProjectInfoDisplay.load',projectInfo for item in ['projectName','hostName','userId']: self.widgets[item].setText(str(projectInfo.get(item,''))) import time t = projectInfo.get('creationTime',None) if t is None: text = '' else: text = time.strftime(PROJECTSMANAGER().db().TIMEFORMAT,time.localtime(float(t))) self.widgets['creationTime'].setText(text) class CProjectsTreeWidget(QtGui.QTreeWidget): PROJECTICON = None def __init__(self,parent): QtGui.QTreeWidget.__init__(self,parent) self.projInfo = {} self.setColumnCount(2) self.expandAll() self.setHeaderLabels(['Name','Directory']) self.setItemsExpandable(True) self.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) self.setDragEnabled(True) self.setAcceptDrops(True) self.header().resizeSection(0,200) #self.setDragDropMode(QtGui.QAbstractItemView.InternalMove) def projectIcon(self): if CProjectsTreeWidget.PROJECTICON is None: import CCP4Utils,os fileName = os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','project.png') CProjectsTreeWidget.PROJECTICON = QtGui.QIcon(QtGui.QPixmap(fileName)) return CProjectsTreeWidget.PROJECTICON def populate(self,args={}): self.clear() proj_dir_list0=PROJECTSMANAGER().getProjectDirectoryList() #print 'CProjectsView.populate',proj_dir_list proj_dir_list = [] self.projInfo = {} for pid,pName,pDir,parent in proj_dir_list0: if parent is None: proj_dir_list.append(pid) self.projInfo[pid] = { 'name' : pName, 'dir' : pDir, 'children' : [] } for pid,pName,pDir,parent in proj_dir_list0: if parent is not None: self.projInfo[parent]['children'].append(pid) self.drawTree(projectList=proj_dir_list,parent=None) def makeTreeWidgetItem(self,pid): item = QtGui.QTreeWidgetItem([self.projInfo[pid]['name'],self.projInfo[pid]['dir']],1001) item.setFlags(QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsDragEnabled) item.setData(0,QtCore.Qt.UserRole,QtCore.QVariant(pid)) item.setData(0,QtCore.Qt.DecorationRole,self.projectIcon()) return item def drawTree(self,projectList=None,parent=None): for pid in projectList: item = self.makeTreeWidgetItem(pid) if parent is None: self.addTopLevelItem(item) else: parent.addChild(item) if len(self.projInfo[pid]['children'])>0: self.drawTree(self.projInfo[pid]['children'],item) def sizeHint(self): return QtCore.QSize(600,200) def mousePressEvent(self,event): if event.button() == QtCore.Qt.RightButton: self.emit(QtCore.SIGNAL('rightMousePress'),event) QtGui.QTreeWidget.mousePressEvent(self,event) def selectedItem(self): seleList = self.selectedItems() #print 'contentsTree.selectedItem',seleList if len(seleList) == 0: return None elif len(seleList) == 1: return seleList[0] else: #print 'Error in CContentsTree - more than one selected item' return seleList[0] def mimeData(self,widgetItemList): import CCP4Data #print 'CProjectsTreeWidget.mimeData',widgetItemList encodedData = QtCore.QByteArray() for widgetItem in widgetItemList: pid = CCP4Data.varToUUID(widgetItem.data(0,QtCore.Qt.UserRole)) #print 'CProjectsTreeWidget.mimeData',pid encodedData.append(str(pid)) # With mime type as text the data can be dropped on desktop # but the type of the data is lost mimeData = QtCore.QMimeData() mimeData.setData('project',encodedData) #mimeData.setText('project '+str(pid)) return mimeData def dragEnterEvent(self,event): if event.mimeData().hasFormat('project'): event.accept() else: event.ignore() def dragMoveEvent(self,event): dropItem = self.itemAt(event.pos().x(),event.pos().y()) #print 'dragMoveEvent',event.mimeData().hasFormat('project') #if event.mimeData().hasFormat('project') and dropItem is not None: if event.mimeData().hasFormat('project'): event.setDropAction(QtCore.Qt.MoveAction) event.accept() else: event.ignore() def dropEvent(self,event): targetItem = self.itemAt(event.pos().x(),event.pos().y()) #print 'CProjectsTreeWidget.dropEvent',event,targetItem if event.mimeData().hasFormat('project'): import CCP4DbApi movedProject = CCP4DbApi.UUIDTYPE(event.mimeData().data('project').data()) targetProjectId = self.item2ProjectId(targetItem) #print 'dropEvent targetProject',targetItem,targetProjectId try: PROJECTSMANAGER().db().updateProject(movedProject,key='parentProjectId',value=targetProjectId) except CException as e: print 'Failed to move project',e.report() event.setDropAction(QtCore.Qt.IgnoreAction) event.ignore() return except Exception as e: print 'Failed to move project',e event.setDropAction(QtCore.Qt.IgnoreAction) event.ignore() return #self.drawTree([movedProject],targetItem) #if targetProjectId is not None: # self.projInfo[targetProjectId]['children'].append(movedProject) self.populate() event.setDropAction(QtCore.Qt.MoveAction) event.accept() else: event.ignore() def startDrag(self,dropActions): item = self.currentItem() projectId = self.item2ProjectId(item) mimeData = self.mimeData([item]) drag = QtGui.QDrag(self) drag.setMimeData(mimeData) pixmap = item.icon(0).pixmap(18,18) drag.setHotSpot(QtCore.QPoint(9,9)) drag.setPixmap(pixmap) if drag.exec_(QtCore.Qt.MoveAction) == QtCore.Qt.MoveAction: #self.takeItem(self.row(item)) try: if item.parent() is None: indx = self.indexOfTopLevelItem(item) self.takeTopLevelItem(indx) else: parent = item.parent() parent.removeChild(item) parentId = self.item2ProjectId(parent) self.projInfo[parentId]['children'].remove(projectId) except: pass #print 'WARNING from CProjectsTreeWidget.startDrag' def selectedProjectId(self): selList = self.selectedItems() #print 'CProjectsTreeWidget.selectedProjectId',selList if len(selList)==0: return None return self.item2ProjectId(selList[0]) def item2ProjectId(self,item): if item is None: return None import CCP4Data projectId = CCP4Data.varToUUID(item.data(0,QtCore.Qt.UserRole)) return projectId ''' def mimeTypes(self): return ['project'] def supportedDropActions(self): print 'supportedDropActions' return QtCore.Qt.MoveAction def dropMimeData(self,parent,index,data,action): print 'CProjectsTreeWidget.dropMimeData',parent,index,data,action return True ''' class CProjectsStatusBar(QtGui.QStatusBar): def __init__(self,parent): QtGui.QStatusBar.__init__(self,parent) self.setObjectName('statusWidget') self.setSizeGripEnabled(False) self.progressWidget =QtGui.QProgressBar(self) self.progressWidget.hide() def showMessage(self,text=None,timeout=0): #print 'CProjectsStatusBar.showMessage',text QtGui.QStatusBar.showMessage(self,text,timeout*1000) self.show() def clear(self): self.clearMessage() self.hideProgress() def showProgress(self,max=None,min=None): self.addPermanentWidget(self.progressWidget) self.progressWidget.show() self.progressWidget.setMaximum(max) def setProgress(self,value): self.progressWidget.setValue(value) def hideProgress(self): self.removeWidget(self.progressWidget) class CProjectManagerDialog(QtGui.QDialog): def __init__(self,parent=None): QtGui.QDialog.__init__(self,parent) self.newProjectGui = None self.importProjectManager = None layout = QtGui.QGridLayout() self.setLayout(layout) self.projectsView = CProjectsTreeWidget(self) self.connect(self.projectsView,QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem*,int)'),self.handleDoubleClick) self.projectsView.populate() self.layout().addWidget(self.projectsView,1,0) buttonLayout = QtGui.QVBoxLayout() buttonDef = [ ['Open',self.openProject,True,True], ['Add project or folder',self.addProject,True,False], ['Rename project',self.handleRenameProject,True,False], ['Move directory',self.handleMoveProject,True,False], ['Delete',self.handleDeleteProject,True,False], ['Cleanup files',self.handleCleanup,True,False], ['Export',self.handleExport1,True,False], ['Import',self.handleImportProject,True,False] ] # ['Associate CCP4i project',self.handleI1Project,True,False], # ['Import project XML',self.handleImportXmlDatabase,True,False] # ['Recover project',self.handleRecover,True,False], # ['Archive',self.archive,False,False], #['Test&Repair',self.repairProject,False,False] ]: if DEVELOPER(): buttonDef.append( ['Rerun test project',self.handleRerun,True,False] ) for label,slot,enabled,default in buttonDef: button =QtGui.QPushButton(self,text=label) button.setEnabled(enabled) if default: button.setDefault(True) buttonLayout.addWidget(button) self.connect(button,QtCore.SIGNAL('clicked()'),slot) self.layout().addLayout(buttonLayout,1,1) self.statusWidget = CProjectsStatusBar(self) self.layout().addWidget(self.statusWidget,2,0,1,2) self.exportThread= None self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectsListChanged'),self.projectsView.populate) # Beware this projectUpdated signal has arguments that should be handled by the target method self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectUpdated'),self.projectsView.populate) def addProject(self): if self.newProjectGui is None: self.newProjectGui = CNewProjectGui0(parent=self) #self.newProjectGui.start() self.newProjectGui.show() def handleRenameProject(self): projectId=self.projectsView.selectedProjectId() if projectId is None: return pName = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectname') self.renameDialog = QtGui.QDialog(self) self.renameDialog.setModal(True) self.renameDialog.setWindowTitle('Rename project?') self.renameDialog.setLayout(QtGui.QVBoxLayout()) self.renameDialog.layout().addWidget(QtGui.QLabel('Rename project: '+pName,self)) self.renameWidget = QtGui.QLineEdit(self) self.renameWidget.setText(pName) self.renameDialog.layout().addWidget(self.renameWidget) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton('Rename',QtGui.QDialogButtonBox.ApplyRole) but.setDefault(False) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.renameProject,projectId)) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.renameDialog.close) self.renameDialog.layout().addWidget(butBox) self.renameDialog.show() self.renameDialog.raise_() def renameProject(self,projectId): self.renameDialog.hide() import CCP4Utils #print 'renameProject',projectId,self.renameWidget.text() name0 = self.renameWidget.text().__str__() if len(name0)==0: return name = CCP4Utils.safeOneWord(name0) if name0 != name: print 'Fixing name to ',name try: PROJECTSMANAGER().db().getProjectId(name) except: # Failed finding project of this name - good! pass else: QtGui.QMessageBox.warning(self,'Rename project','A project called '+name+' already exists') return try: PROJECTSMANAGER().db().updateProject(projectId,'projectname',name) except CException as e: e.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error changing project name') def handleI1Project(self): projectId=self.projectsView.selectedProjectId() if projectId is None: return import CCP4File,CCP4DataManager pInfo = PROJECTSMANAGER().db().getProjectInfo(projectId,mode=['projectname','i1projectname,i1projectirectory']) print 'handleI1Project pInfo',pInfo # Setup dataobj and widgets self.i1NameWidget = QtGui.QLineEdit(self) if pInfo.get('i1projectname',None) is not None: self.i1NameWidget.setText(pInfo['i1projectname']) self.i1Dir = CCP4File.CDataFile(parent=self,qualifiers= { 'isDirectory' : True, 'mustExist' : True } ) if pInfo.get('i1projectdirectory',None) is not None: self.i1Dir.set(pInfo['i1projectdirectory']) self.i1DirWidget = CCP4DataManager.DATAMANAGER().widget(model=self.i1Dir,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False } ) self.i1DirWidget.updateViewFromModel() self.i1DirWidget.fileLineEdit.setCharWidth(60,mode='minimum') # layout dialog box self.I1Dialog = QtGui.QDialog(self) self.I1Dialog.setModal(True) self.I1Dialog.setWindowTitle('Associate with CCP4i project') self.I1Dialog.setLayout(QtGui.QVBoxLayout()) self.I1Dialog.layout().addWidget(QtGui.QLabel("Associate "+pInfo['projectname']+ " with 'old' CCP4i project:",self)) self.I1Dialog.layout().addWidget(self.i1NameWidget) self.I1Dialog.layout().addWidget(QtGui.QLabel("CCP4i project directory:",self)) self.I1Dialog.layout().addWidget(self.i1DirWidget) self.connect(self.i1NameWidget,QtCore.SIGNAL('editingFinished()'),self.autoFillDir) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton('Apply',QtGui.QDialogButtonBox.ApplyRole) but.setDefault(False) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.associateI1Project,projectId)) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.I1Dialog.close) self.I1Dialog.layout().addWidget(butBox) self.I1Dialog.show() self.I1Dialog.raise_() def autoFillDir(self): pass def associateI1Project(self,projectId): pName = str(self.i1NameWidget.text()) self.i1DirWidget.updateModelFromView() pDir = self.i1Dir.fullPath() def openProject(self): projectId=self.projectsView.selectedProjectId() if projectId is not None: openProject(projectId) def archive(self): pass def handleDoubleClick(self,item,col=None): print 'handleDoubleCLick',item,col nameVar = item.data(0,QtCore.Qt.DisplayRole) name = nameVar.toString().__str__() openProject(projectName=name) def handleExport1(self): if getattr(self,'exportThread',None) is not None: QtGui.QMessageBox.warning(self,'Export project','Please wait - another project export currently in progress') return projectId=self.projectsView.selectedProjectId() if projectId is None: #print 'handleExport calling statusBar' self.statusWidget.showMessage("Select a project before selecting 'Export'",5) return projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) if not hasattr(self,'exportOptions'): self.exportOptionsWidget = CExportOptionsDialog(self,projectId,projectInfo['projectname']) self.connect(self.exportOptionsWidget,QtCore.SIGNAL('export'),functools.partial(self.handleExport2,projectId)) self.exportOptionsWidget.show() def handleExport2(self,projectId,mode): #print 'handleExport2',projectId,mode if mode == 0: return if mode == 1: after = None jobList = None if mode == 2: after = self.exportOptionsWidget.impExpWidget.getTime() jobList = None elif mode == 3: after = None jobList,errList = self.exportOptionsWidget.selectionWidget.getSelection() if len(errList)>0: errText = 'Unable to interpret selection: ' for err in errList: errText = errText +"'"+err+"' " mess = QtGui.QMessageBox.warning(self,'Export project',errText) return #if DEVELOPER(): if False: excludeI2files = self.exportOptionsWidget.excludeI2files.isChecked() else: excludeI2files = False #print 'handleExport2',after,jobList self.exportOptionsWidget.close() projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) import CCP4FileBrowser,CCP4Export self.browser = CCP4FileBrowser.CFileDialog(self, title='Save project data to compressed file', filters= ['CCP4 Project Database (*.'+CCP4Export.COMPRESSED_SUFFIX+')'], defaultSuffix=CCP4Export.COMPRESSED_SUFFIX, fileMode=QtGui.QFileDialog.AnyFile ) self.connect(self.browser,QtCore.SIGNAL('selectFile'),functools.partial(self.compressProject,projectId,after,jobList,excludeI2files)) self.browser.show() def export(self,projectId,fileName): jobNumberList,errReport = PROJECTSMANAGER().db().exportProjectXml(projectId,fileName) if len(errReport)>0: errReport.warningMessage(parent=self,windowTitle='Errors exporting project',message='Some errors in exporting project to file:\n'+fileName) def compressProject(self,projectId,after=None,jobList=None,excludeI2files=False,fileName=None): print 'CProjectManagerDialog.compressProject',after,jobList,excludeI2files,fileName self.browser.hide() self.browser.deleteLater() projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) # If there is a limited set of jobs then find the input jobs that are not output by jobs on that list inputFilesList,inputFileIdList,fromJobIdList,errReport = PROJECTSMANAGER().getJobInputFiles(projectDir=projectInfo['projectdirectory'],jobIdList=jobList,useDb=True,excludeI2files=excludeI2files) #print 'CProjectManagerDialog.compressProject inputFilesList,fromJobIdList',inputFilesList,fromJobIdList import time,os dbxml = os.path.join( projectInfo['projectdirectory'],'CCP4_TMP','DATABASE'+str(int(time.time()))+'.db.xml') self.statusWidget.showMessage('Creating XML database:'+dbxml) #print 'Creating temporary database xml file in:',dbxml # exportProjectXml returns list of TOP-LEVEL jobNumbers for the export jobNumberList,errReport = PROJECTSMANAGER().db().exportProjectXml(projectId,fileName=dbxml,recordExport=True,status='exportable',after=after,jobList=jobList,inputFileList=inputFileIdList,inputFileFromJobList=fromJobIdList) #print 'CProjectManagerDialog.compressProject jobNumberList',jobNumberList if errReport.maxSeverity()>SEVERITY_WARNING: self.statusWidget.clearMessage() errReport.warningMessage('Export project','Error creating XML database file',parent=self) return #except: # jobList = [] # err = CErrorReport(self.__class__,171) # err.warningMessage(title,'Error getting project jobs from database',parent=self) #print 'compressProject',jobList if jobList is not None: directoriesList = [] else: directoriesList = ['CCP4_IMPORTED_FILES','CCP4_PROJECT_FILES'] import CCP4Export self.exportThread = CCP4Export.ExportProjectThread(self,projectDir=projectInfo['projectdirectory'],dbxml=dbxml,target=fileName,jobList=jobNumberList,inputFilesList=inputFilesList,directoriesList=directoriesList,) self.connect(self.exportThread,QtCore.SIGNAL('savingJobData'),self.updateSavingJobData) self.connect(self.exportThread,QtCore.SIGNAL('startSavingJobData'),self.progressSavingJobData) self.connect(self.exportThread,QtCore.SIGNAL('finished()'),functools.partial(self.doneSavingJobData,projectInfo['projectname'],fileName)) self.exportThread.start() ''' compressedFile,errReport = PROJECTSMANAGER().compressProject(projectId,fileName,after=after) if compressedFile is None: errReport.warningMessage('Exporting database','Error creating export compressed file') else: QtGui.QMessageBox.information(self,'Exporting project','Project saved as'+compressedFile) ''' def progressSavingJobData(self,numberOfJobs): self.statusWidget.showMessage('Exporting project: Saving '+str(numberOfJobs)+' jobs to compressed file') self.statusWidget.showProgress(numberOfJobs) def updateSavingJobData(self,ret): jobNumber,jobsDone = ret #print 'updateSavingJobData',jobNumber,jobsDone if jobNumber in ['IMPORT','DATABASE','PROJECT']: label = ['imported files','database file','project data'][ ['IMPORT','DATABASE','PROJECT'].index(jobNumber)] self.statusWidget.clear() if not jobsDone: self.statusWidget.showMessage('Exporting project: Saving '+label) else: self.statusWidget.showMessage('Exporting project: Finished saving '+label) else: self.statusWidget.setProgress(jobsDone) def doneSavingJobData(self,projectName,filename): if self.exportThread.errorReport.maxSeverity()>SEVERITY_WARNING: self.exportThread.errorReport.warningMessage('Saving job data','Error saving data files for export',parent=self) else: self.statusWidget.hideProgress() self.statusWidget.showMessage('Project '+str(projectName)+' saved to: '+str(filename)) self.exportThread.deleteLater() self.exportThread = None def handleImportProject(self): import CCP4DbApi CCP4DbApi.CDbXml.updateInstances() if len(CCP4DbApi.CDbXml.Instances)>0: QtGui.QMessageBox.warning(self,'Import project','Another import is already in progress - please wait') return import CCP4FileBrowser,CCP4Export self.browser = CCP4FileBrowser.CFileDialog(self, title='Import project compressed file', filters = [ MIMETYPESHANDLER().getMimeTypeInfo("application/CCP4-compressed-db",'filter') ], defaultSuffix= MIMETYPESHANDLER().getMimeTypeInfo("application/CCP4-compressed-db",'fileExtensions')[0], fileMode=QtGui.QFileDialog.ExistingFile ) self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.handleImportProject1) self.browser.show() def handleImportProject1(self,compressedFile): import CCP4FileBrowser if hasattr(self,'browser'): try: self.browser.hide() self.browser.deleteLater() del self.browser except: pass self.statusWidget.showMessage('Checking contents of compressed file') try: xmlFile = PROJECTSMANAGER().extractDatabaseXml(compressedFile) except CException as e: e.warningMessage('Import project','Failed extracting database XML file from compressed file',parent=self) self.statusWidget.clear() except Exception as e: QtGui.QMessageBox.warning(self,'Import project','Error extracting database xml file from '+str(compressedFile)) self.statusWidget.clear() return try: import CCP4DbApi self.dbImport = CCP4DbApi.CDbXml(db=PROJECTSMANAGER().db(),xmlFile=xmlFile) #self.dbImport.setDiagnostic(True) importProjectInfo = self.dbImport.loadProjectInfo() except: QtGui.QMessageBox.warning(self,'Import project','Error attempting to read database file in\n'+str(compressedFile)) return try: projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=self.dbImport.projectId) except: projectInfo = None #print 'handleImportProject1 importProjectInfo',importProjectInfo if projectInfo is None: # There is no matching projectId in db- query user to create new project if not hasattr(self,'importNewDialog'): self.importNewDialog = CImportNewProjectDialog(self) self.importNewDialog.reset(importProjectInfo=self.dbImport.headerInfo()) self.connect(self.importNewDialog,QtCore.SIGNAL('import'),functools.partial(self.importProject0,compressedFile)) self.importNewDialog.show() else: self.importExistingDialog=CImportExistingProjectDialog(self,projectName=projectInfo['projectname']) self.importExistingDialog.reset(importProjectInfo=self.dbImport.headerInfo()) self.connect(self.importExistingDialog,QtCore.SIGNAL('import'),functools.partial(self.importProject1,compressedFile,projectInfo['projectdirectory'],self.dbImport.projectId)) self.importExistingDialog.show() def importProject0(self,compressedFile): # Close importNewDialog if not hasattr(self,'importNewDialog'): return self.importNewDialog.close() if self.importNewDialog.mode() == 1: # Create a new project dirName = self.importNewDialog.newDirectory() self.importNewDialog.deleteLater() del self.importNewDialog self.importProject(compressedFile,dirName) else: # Merge into existing project despite incompatible projectId projectId = self.importNewDialog.mergeProject() self.importNewDialog.deleteLater() del self.importNewDialog projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) self.dbImport.projectId = projectId self.dbImport.projectDirectory = projectInfo['projectdirectory'] #self.dbImport.projectName = self.importNewDialog.projectName() self.importProject(compressedFile=compressedFile,dirName=self.dbImport.projectDirectory,existingProject=projectId,forceProjectId=True) def importProject1(self,compressedFile,dirName,existingProject=None): self.importExistingDialog.close() self.importExistingDialog.deleteLater() del self.importExistingDialog # Append to existing project self.importProject(compressedFile=compressedFile,dirName=dirName,existingProject=existingProject) def importProject(self,compressedFile,dirName,existingProject=None,forceProjectId=False): import os if DIAGNOSTIC: print 'CProjectManagerDialog.importProject',compressedFile,dirName,existingProject # Load the database.xml into temporary tables in db self.dbImport.projectDirectory = dirName if existingProject is None: ret = self.dbImport.createProject() if ret.maxSeverity()>SEVERITY_WARNING: print ret.report() ret.warningMessage(parent=self,windowTitle='Error creating project in database',ifStack=False) return self.dbImport.createTempTables() if forceProjectId: self.dbImport.loadTempTable(resetProjectId=existingProject) else: self.dbImport.loadTempTable() # If loading jobs to an existing project flag up jobs in temp tables that # are already in db if existingProject is not None: self.dbImport.setExclInTempTables() # Flag imported files to be imported (there is no checking yet that they exist) self.dbImport.setExclImportedFiles() if DIAGNOSTIC: print 'CProjectManagerDialog.importProject setting Temp Tables',self.dbImport.errReport.report() if self.dbImport.errReport.maxSeverity()>SEVERITY_WARNING: if DIAGNOSTIC: print 'Error report from the import process..' if DIAGNOSTIC: print self.dbImport.errReport.report() self.dbImport.errReport.warningMessage(parent=self,windowTitle='Error loading data from project export file',ifStack=False) # Make project directory if necessary if not os.path.exists(dirName): try: os.mkdir(dirName) except: QtGui.QMessageBox.warning(self,'Import project','Failed to create directory:'+dirName) return self.statusWidget.showMessage('Unpacking project files to '+dirName) import CCP4Export # Unpack project files from the tar file (possibly in separate thread) # Pass import thread dbImport to enable query database and flagging loaded jobs/files if DIAGNOSTIC: print 'CProjectManagerDialog.importProject creating import thread' self.importThread = CCP4Export.ImportProjectThread(self,projectDir=dirName,compressedFile=compressedFile, dbImport=self.dbImport,diagnostic=DIAGNOSTIC) #self.connect(self.importThread,QtCore.SIGNAL('extractingJobData'),self.handleImportProgress) self.connect(self.importThread,QtCore.SIGNAL('finished()'),self.doneImportProgress) errReport = self.importThread.run() if DIAGNOSTIC: print 'CProjectManagerDialog.importProject import thread running',errReport.report() self.dbImport.cleanupTempTables() #self.dbImport.listTempJobs('TempJobs after cleanup') #self.dbImport.listTempFiles('TempFiles after cleanup') #self.dbImport.listTempFileUses('TempFileUses after cleanup') stats = self.dbImport.importStats() if DIAGNOSTIC: for key,value in stats.items(): if key == 'failedFiles': if len(value)>0: print 'Failed to import files..(probably already present)' for item in value: print 'Job_'+str(item[4]),item[2] else: print 'CProjectManagerDialog.importProject stats', key,value if errReport.maxSeverity()>SEVERITY_WARNING: self.dbImport.removeTempTables() text = 'ERRORS UNPACKING DATA FILES\n' for err in errReport: text = text + err['details'] + '\n' QtGui.QMessageBox.warning(self,'Import failed',text) self.statusWidget.showMessage('Error unpacking project files to '+dirName) return self.statusWidget.showMessage('Loading project data to database') self.dbImport.importTempTables() self.dbImport.removeTempTables() self.statusWidget.showMessage('Finished importing project') self.dbImport.db.emitSignal('projectReset',{'projectId':self.dbImport.projectId}) if stats['jobsTotal']>stats['jobsImported'] or stats['filesTotal']>stats['filesImported']: text = 'Some of the jobs/files are already in the database in project '+str(self.dbImport.projectName)+'\n' + \ 'Imported '+str(stats['jobsImported'])+' new jobs from '+str(stats['jobsTotal'])+' in file \n' + \ 'and '+str(stats['filesImported'])+' new data files from '+str(stats['filesTotal'])+' in file\n' else: text = 'Successfully imported '+str(stats['jobsImported'])+' jobs and '+str(stats['filesImported'])+' data files' if stats.get('incrJobNumber',0) > 0: text = text +'\nImporting jobs '+str(stats['importMin'])+' to '+str(stats['importMax'])+' have been renumbered\n'+str(int(stats['importMin'])+int(stats['incrJobNumber']))+' to '+str(int(stats['importMax'])+int(stats['incrJobNumber'])) +' to avoid clash with existing jobs' if len(text)>0: QtGui.QMessageBox.information(self,'Import complete',text) def createImportProgress(self): pass def handleImportProgress(self,ret): #print 'handleImportProgress',ret jobNo,done = ret def doneImportProgress(self): pass def repairProject(self): pass def handleDeleteProject(self): projectId=self.projectsView.selectedProjectId() if projectId is None: return projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) self.deleteDialog = QtGui.QDialog(self) #self.deleteDialog.setModal(True) self.deleteDialog.setWindowTitle('Delete project?') self.deleteDialog.setLayout(QtGui.QVBoxLayout()) self.deleteDialog.layout().addWidget(QtGui.QLabel('Delete project from database: '+projectInfo['projectname'],self)) self.deleteDirectory = QtGui.QCheckBox('Delete directory: '+projectInfo['projectdirectory'],self) self.deleteDialog.layout().addWidget(self.deleteDirectory) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton('Delete project',QtGui.QDialogButtonBox.ApplyRole) but.setDefault(False) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.deleteProject,projectId)) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.deleteDialog.close) self.deleteDialog.layout().addWidget(butBox) self.deleteDialog.show() self.deleteDialog.raise_() def deleteProject(self,projectId): deleteDirectory = self.deleteDirectory.isChecked() self.deleteDialog.close() if projectId is None: return e = PROJECTSMANAGER().deleteProject(projectId=projectId,deleteDirectory=deleteDirectory) if e.maxSeverity()>SEVERITY_WARNING: e.warningMessage('Deleting project',parent=self) def handleMoveProject(self): projectId=self.projectsView.selectedProjectId() #print 'handleMoveProject projectId',projectId,type(projectId) if projectId is None: return projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) self.moveProjectId = projectId self.moveDialog = QtGui.QDialog(self) self.moveDialog.setModal(True) self.moveDialog.setWindowTitle('Move project directory?') self.moveDialog.setLayout(QtGui.QVBoxLayout()) self.moveDialog.layout().addWidget(QtGui.QLabel('Move the project directory for project: '+projectInfo['projectname'],self)) self.moveDialog.layout().addWidget(QtGui.QLabel('Current directory: '+projectInfo['projectdirectory'],self)) # Mode button group self.moveMode = -1 self.moveModeGroup = QtGui.QButtonGroup(self) self.moveModeGroup.setExclusive(True) but = QtGui.QRadioButton('Move the directory and register with database',self) self.moveModeGroup.addButton(but,1) self.moveDialog.layout().addWidget(but) but = QtGui.QRadioButton('Directory is already moved - just register with database',self) self.moveModeGroup.addButton(but,0) self.moveDialog.layout().addWidget(but) self.connect(self.moveModeGroup,QtCore.SIGNAL('buttonReleased(int)'),self.handleMoveModeChange) import CCP4File,CCP4DataManager self.moveStack = QtGui.QStackedLayout() self.moveDialog.layout().addLayout(self.moveStack) self.moveStack.addWidget(QtGui.QLabel('Choose mode above',self)) self.moveDirectory0 = CCP4File.CDataFile(parent=self,qualifiers= { 'isDirectory' : True, 'mustExist' : True } ) import CCP4FileBrowser self.moveDirectory0Widget = CCP4DataManager.DATAMANAGER().widget(model=self.moveDirectory0,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False } ) self.moveDirectory0Widget.updateViewFromModel() self.moveDirectory0Widget.fileLineEdit.setCharWidth(60,mode='minimum') self.moveStack.addWidget(self.moveDirectory0Widget) self.moveDirectory1 = CCP4File.CDataFile(parent=self,qualifiers= { 'isDirectory' : True,'mustExist' : False } ) self.moveDirectory1Widget = CCP4DataManager.DATAMANAGER().widget(model=self.moveDirectory1,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False} ) self.moveDirectory1Widget.updateViewFromModel() self.moveDirectory1Widget.fileLineEdit.setCharWidth(60,mode='minimum') self.moveStack.addWidget(self.moveDirectory1Widget) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton(QtGui.QDialogButtonBox.Apply) but.setDefault(False) self.connect(but,QtCore.SIGNAL('released()'),self.moveProject) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.moveDialog.close) self.moveDialog.layout().addWidget(butBox) self.moveStack.setCurrentIndex(self.moveMode -1 ) self.moveDialog.show() self.moveDialog.raise_() def handleMoveModeChange(self,mode): mode = int(mode) if mode != self.moveMode: self.moveMode = mode self.moveDirectory0Widget.closeBrowser() self.moveDirectory1Widget.closeBrowser() self.moveStack.setCurrentIndex(self.moveMode + 1 ) #print 'handleMoveModeChange',self.moveMode,self.moveStack.currentIndex() def moveProject(self): import os errMess = None if self.moveMode < 0: errMess = 'Choose if moving the directory or just updating database' if self.moveMode == 0: if not self.moveDirectory0.isSet(): errMess = 'Choose directory' else: newDir = str(self.moveDirectory0) if not os.path.exists(newDir) or not os.path.isdir(newDir): errMess = 'Directory does not exist or is not directory' else: if not self.moveDirectory1.isSet(): errMess = 'Choose new directory path' else: newDir = str(self.moveDirectory1) if os.path.exists(newDir): errMess = 'New directory should not exist already' #else: # if not os.path.exists(os.path.split(newDir)[0]): # errMess = 'Parent directory for new directory must exist' if errMess is not None: QtGui.QMessageBox.warning(self,'Can not move directory',errMess) return #print 'moveProject',newDir,self.moveMode projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=self.moveProjectId) if self.moveMode == 1: import shutil try: shutil.copytree(projectInfo['projectdirectory'],newDir) except Exception as e: QtGui.QMessageBox.warning(self,'Error moving directory',str(e)) return try: PROJECTSMANAGER().db().updateProject(self.moveProjectId,key='projectdirectory',value=newDir) except CException as e: e.warningMessage(windowTitle='Error updating database',parent=self) return except Exception as e: QtGui.QMessageBox.warning(self,'Error updating database',str(e)) return self.moveDialog.close() def handleCleanup(self): projectId=self.projectsView.selectedProjectId() #print 'handleCleanup projectId',projectId if projectId is None: ret = QtGui.QMessageBox.question(self,self.windowTitle(),'Remove temporary files from all projects?',QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel) if ret != QtGui.QMessageBox.Ok: return PROJECTSMANAGER().cleanupAllProjects(context='temporary') else: msgBox = QtGui.QMessageBox() msgBox.setWindowTitle('Cleanup files') msgBox.setText('Do you want to delete only temporary files or temporary files and intermediate data files from sub-jobs') b = msgBox.addButton(QtGui.QMessageBox.Cancel) b = msgBox.addButton('Temporary files only',QtGui.QMessageBox.ApplyRole) self.connect(b,QtCore.SIGNAL('released()'),functools.partial(self.handleCleanup2,projectId,'temporary')) b = msgBox.addButton('Temporary and intermediate files',QtGui.QMessageBox.ApplyRole) self.connect(b,QtCore.SIGNAL('released()'),functools.partial(self.handleCleanup2,projectId,'intermediate')) msgBox.exec_() def handleCleanup2(self,projectId,context): print 'handleCleanup2',projectId,context import CCP4ProjectsManager cleanup = CCP4ProjectsManager.CPurgeProject(projectId = projectId) cleanup.purgeProject(context=context) def handleRerun(self): import CCP4FileBrowser projectId=self.projectsView.selectedProjectId() if projectId is None: return self.rerunProjectId = projectId filter_list = [] self.rerunDialog = CCP4FileBrowser.CFileDialog(self,fileMode=QtGui.QFileDialog.Directory, title='Directory for rerun output') self.connect(self.rerunDialog, QtCore.SIGNAL('selectFile'), self.rerunProject) self.rerunDialog.show() def rerunProject(self,outputDirectory): import os import CCP4ProjectBasedTesting,CCP4TextViewer self.rerunDialog.close() if not os.path.exists(outputDirectory) or not os.path.isdir(outputDirectory): QtGui.QMessageBox.warning(self,'No suitable directory for project rerun','You must provide a directory in which the rerun project directory will be created') return self.projectBasedTest = CCP4ProjectBasedTesting.CProjectBasedTesting(sourceProjectList=[self.rerunProjectId],outputDirectory=outputDirectory,useCurrentDb=True) #self.projectBasedTest.start() self.projectBasedTest.runTests() logFile = self.projectBasedTest.logFiles[-1] print 'Re-running project log file:',logFile widget = WEBBROWSER().openFile(logFile) self.connect(self.projectBasedTest,QtCore.SIGNAL('reportUpdated'),WEBBROWSER().reloadFile) def handleRecover(self): ret = QtGui.QMessageBox.question(self,'Recover project',"This will attempt to restore a project to the database\n from the information in the project directory.\nAn intermediary file called my_project_dir.ccp4db.xml will be created and read automatically.\nThis file could also be edited and read with the 'Import project XML' tool.",QtGui.QMessageBox.Ok|QtGui.QMessageBox.Cancel) if ret == QtGui.QMessageBox.Cancel: return import CCP4FileBrowser self.browser = CCP4FileBrowser.CFileDialog(self, title='Recover database from the project directory', fileMode=QtGui.QFileDialog.Directory ) self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.recoverProject) self.browser.show() ''' import CCP4File,CCP4DataManager self.recoverDialog = QtGui.QDialog(self) self.recoverDialog.setModal(True) self.recoverDialog.setWindowTitle('Recover project database') self.recoverDialog.setLayout(QtGui.QVBoxLayout()) self.recoverDialog.layout().addWidget(QtGui.QLabel('Recover database information from the project directory..')) self.recoverDirectory = CCP4File.CDataFile(parent=self,qualifiers= { 'isDirectory' : True, 'mustExist' : True } ) import CCP4FileBrowser self.recoverDirectoryWidget = CCP4DataManager.DATAMANAGER().widget(model=self.recoverDirectory,parentWidget=self,qualifiers={'jobCombo': False, 'projectBrowser' : False } ) self.recoverDirectoryWidget.updateViewFromModel() self.recoverDirectoryWidget.fileLineEdit.setCharWidth(60,mode='minimum') self.recoverDialog.layout().addWidget(self.recoverDirectoryWidget) butBox = QtGui.QDialogButtonBox(self) but = butBox.addButton(QtGui.QDialogButtonBox.Apply) but.setDefault(False) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.recoverProject,****)) but = butBox.addButton(QtGui.QDialogButtonBox.Cancel) self.connect(but,QtCore.SIGNAL('released()'),self.recoverDialog.close) self.recoverDialog.layout().addWidget(butBox) self.recoverDialog.show() self.recoverDialog.raise_() ''' def recoverProject(self,projectDir): #print 'recoverProject',projectDir import os if not os.path.exists(projectDir) or not os.path.isdir(projectDir): QtGui.QMessageBox.warning(self,'No project directory selected','You must select a project directory') return if not os.path.exists(os.path.join(projectDir,'CCP4_JOBS')): QtGui.QMessageBox.warning(self,'Not a project directory?','This does not appear to be a CCP4i2 project directory which should contain a CCP4_JOBS sub-directory') return import CCP4DbUtils self.makeDbXml = CCP4DbUtils.CMakeProjectDbXml(self,projectDir=projectDir,projectName=os.path.split(projectDir)[-1]) self.connect(self.makeDbXml,QtCore.SIGNAL('jobLoaded'),self.statusWidget.setProgress) self.statusWidget.showMessage('Making database file from project directory') self.statusWidget.showProgress(self.makeDbXml.numberOfJobs()) errReport = self.makeDbXml.loadProject() if errReport.maxSeverity()>SEVERITY_WARNING: errReport.warningMessage(parent=self,windowTitle='Error recovering project directory', message='Some errors are reported',ifStack=False,minSeverity=SEVERITY_ERROR) return xmlFile = self.makeDbXml.saveXmlFile() if len(errReport)>0: errReport.warningMessage(parent=self,windowTitle='Project database information recovered', message='The project database information has been recovered and saved to '+xmlFile+ \ '\nThere are some warnings which can probably be ignored.\nTo seee click Show Details' ,ifStack=False) self.statusWidget.showMessage('Loading database file to database') self.importXmlDatabase(xmlFile,projectDir=projectDir) self.statusWidget.clear() self.statusWidget.removeProgress() def handleImportXmlDatabase(self): import CCP4FileBrowser self.browser = CCP4FileBrowser.CFileDialog(self, title='Recover database from the project directory', filters= ['CCP4 Database XML (*.ccp4db.xml)'], defaultSuffix= 'xml', fileMode=QtGui.QFileDialog.ExistingFile ) self.connect(self.browser,QtCore.SIGNAL('selectFile'),self.handleRecoverDirSelected) self.browser.show() def handleRecoverDirSelected(self,xmlFile): self.browser.hide() self.importXmlDatabase(xmlFile) def importXmlDatabase(self,xmlFile,projectDir=None): import CCP4DbApi self.dbImport = CCP4DbApi.CDbXml(db=PROJECTSMANAGER().db(),xmlFile=xmlFile) projectInfo = self.dbImport.loadProjectInfo() # Check that we have a projectId and it is not already in DB print 'importXmlDatabase projectInfo',projectInfo if self.dbImport.projectId is None: print 'Imported project XML file does not appear to have a projectId' return try: dbProjectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=self.dbImport.projectId,mode=['projectname','projectdirectory']) except: pass else: QtGui.QMessageBox.warning(self,'Error importing database XML','There is aready a project with the same projectID and project name '+str(dbProjectInfo.get('projectname','unknown'))+' and it has project directory '+str(dbProjectInfo.get('projectdirectory','unknown'))) return if projectDir is not None: self.dbImport.projectDirectory = projectDir ret = self.dbImport.createProject() # Expect an error from createProject if the project is already in db #print 'CProjectManagerDialog.importProject from createProject',ret.report() self.dbImport.createTempTables() self.dbImport.loadTempTable() if self.dbImport.errReport.maxSeverity()>SEVERITY_WARNING: print 'Error report from the import process..' self.dbImport.errReport.warningMessage(windowTitle='Project manager- importing XML', message='Failed importing database from XML file',ifStack=False,parent=self) else: self.dbImport.setAllToImport() #self.dbImport.listTempJobs('Temp jobs') #self.dbImport.listTempFiles('Temp files') self.dbImport.importTempTables() self.dbImport.removeTempTables() openProject(projectId=self.dbImport.projectId)