""" CCP4ProjectViewer.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 license to address the requirements of UK law.sstac 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 June 2011 - Project document in a QtMainWindow """ ##@package CCP4ProjectViewer (QtGui) Project document in a QtMainWindow from PyQt4 import QtGui,QtCore,Qt from PyQt4.QtSvg import QSvgWidget from CCP4ErrorHandling import * from CCP4TaskManager import TASKMANAGER from CCP4Modules import QTAPPLICATION,PROJECTSMANAGER,WEBBROWSER,LAUNCHER,MIMETYPESHANDLER, \ PREFERENCES,JOBCONTROLLER,PIXMAPMANAGER,WORKFLOWMANAGER,CUSTOMTASKMANAGER,JOBCONTROLLERGUI from CCP4Config import DEVELOPER import CCP4ProjectWidget import CCP4WebBrowser import CCP4DbApi from CCP4MessageBox import CMessageBox import functools,os from CCP4DbUtils import COpenJob import CCP4Utils import CCP4GuiUtils import CCP4TaskWidget DEFAULT_WINDOW_SIZE = (1400,800) ALWAYS_SHOW_SERVER_BUTTON = False def isAlive(qobj): import sip try: sip.unwrapinstance(qobj) except RuntimeError: return False return True def PROJECTVIEWER(projectId=None,open=False): for pv in CProjectViewer.Instances: if pv.taskFrame.openJob.projectId == projectId: return pv if open: pv = CProjectViewer(projectId=projectId) return pv return None def getMenuIcon(parent,name,size): pixFile = TASKMANAGER().searchIconFile(name) fileName, fileExtension = os.path.splitext(pixFile) #print 'getMenuIcon',name,fileName, fileExtension icon=None #icon = QtGui.QLabel(parent) #pix = CCP4GuiUtils.loadSvgWithSize(pixFile,size,size) #icon.setPixmap(pix) if fileExtension.lower() == ".svg": try: #icon = QSvgWidget(pixFile,parent=parent) #icon.setFixedSize(size,size) icon = QtGui.QLabel(parent) pix = CCP4GuiUtils.loadSvgWithSize(pixFile,size,size) icon.setPixmap(pix) except: print 'Unable to load SVG task icon' icon=None if icon is None: icon = QtGui.QLabel(parent) icon.setPixmap(QtGui.QPixmap(pixFile).scaled(size,size)) return icon def currentlyOpenJobs(): jobIdList = [] for pv in CProjectViewer.Instances: jobIdList.append(pv.taskFrame.openJob.jobId) for tw in pv.findChildren(CTaskMainWindow): jobIdList.append(tw.objectName()[3:]) return jobIdList #------------------------------------------------------------------------------------------------------ class CProjectViewer(CCP4WebBrowser.CMainWindow): #------------------------------------------------------------------------------------------------------ ERROR_CODES = { 101 : { 'description' : 'Error creating task widget for' }, 102 : { 'description' : 'Wrong task name in imported params file' }, 103 : { 'description' : 'Error attempting to auto-generate task widget for' }, 104 : { 'description' : 'Error writing job parameters file' }, 120 : { 'description' : 'Unknown error attempting to kill remote job' } } INPUT_TAB = 0 OUTPUT_TAB = 1 COMMENT_TAB =2 MARGIN=2 Instances = [] def __init__(self,parent=None,projectId=None,jobId=None,graphical=True): CCP4WebBrowser.CMainWindow.__init__(self,parent) CProjectViewer.Instances.append(self) self.setObjectName('projectViewer') self.connect(self,QtCore.SIGNAL("destroyed(QObject*)"),CProjectViewer.updateInstances) self._dictionaryWidget = None if jobId is None: jobIdList = PROJECTSMANAGER().db().getProjectJobList(projectId=projectId,topLevelOnly=True,maxLength=1) if len(jobIdList)>0: jobId = jobIdList[0] openJob = COpenJob(projectId=projectId,jobId=jobId) PROJECTSMANAGER().db().updateProject(projectId=projectId,key='lastaccess') #print 'CProjectViewer openJob', openJob self.setWindowTitle(self.version+'Project Viewer: '+openJob.projectName) self.layout().setContentsMargins(CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN) self.layout().setSpacing(CProjectViewer.MARGIN) # left side project widget and buttons leftFrame = QtGui.QFrame(self) leftFrame.setLayout( QtGui.QVBoxLayout()) leftFrame.layout().setContentsMargins(CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN) leftFrame.layout().setSpacing(CProjectViewer.MARGIN) self._projectWidget = CCP4ProjectWidget.CProjectWidget(self,projectId) self._projectWidget.setMinimumSize(QtCore.QSize(370,300)) leftFrame.layout().addWidget(self._projectWidget) """ self._projectButtons = QtGui.QButtonGroup(self) self._projectButtons.addButton(QtGui.QPushButton('Project parameters',self),0) self._projectMenu = QtGui.QMenu(self) action = self._projectMenu.addAction('Ligand geometry') self.connect(action,QtCore.SIGNAL('triggered(bool)'),self.openDictionary) self._projectButtons.button(0).setMenu(self._projectMenu) self._projectButtons.addButton(QtGui.QPushButton('Task menu',self),1) self._projectButtons.button(1).setToolTip('Choose new job from menu') self.connect(self._projectButtons.button(1),QtCore.SIGNAL('released()'),self.showTaskChooser) layout0 = QtGui.QHBoxLayout() layout0.setContentsMargins(CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN) layout0.setSpacing(CProjectViewer.MARGIN) layout0.addStretch(1) for but in self._projectButtons.buttons(): layout0.addWidget(but) layout0.addStretch(1) leftFrame.layout().addLayout(layout0) """ # Right side is task frame or task chooser self.rightStack = QtGui.QStackedWidget(self) self.taskFrame = CTaskFrame(self,projectId=openJob.projectId) self.rightStack.addWidget(self.taskFrame) self.taskChooser = CChooseTaskFrame(self) self.rightStack.addWidget(self.taskChooser) self.rightStack.setCurrentIndex(0) # left/right splitter self.splitterSizes = [400,CCP4TaskWidget.WIDTH+CCP4TaskWidget.PADDING_ALLOWANCE] mainWidget = QtGui.QSplitter(self) mainWidget.setOrientation(QtCore.Qt.Horizontal) mainWidget.addWidget(leftFrame) mainWidget.addWidget(self.rightStack) self.rightStack.layout().setContentsMargins(CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN) self.rightStack.layout().setSpacing(CProjectViewer.MARGIN) # And set the splitter sizes after show ... self.setCentralWidget(mainWidget) self.connect(self.taskFrame.outputFrame.webView,QtCore.SIGNAL('csvDownloadRequest'),self.handleCsvDownloadRequest) # Signals to open a new task self.connect(self.taskFrame.buttons.button('clone'),QtCore.SIGNAL('released()'),self.cloneTask) self.connect(self.taskFrame.buttons.button('help').menu(),QtCore.SIGNAL('aboutToShow()'),self.showHelpMenu) self.connect(self.taskFrame.buttons.button('help').menu(),QtCore.SIGNAL('triggered(QAction*)'),self.handleHelpMenu) self.connect(self.taskFrame.outputFrame,QtCore.SIGNAL('nextTask'),self.handleNextTask) self.connect(self.taskFrame.outputFrame,QtCore.SIGNAL('interrupt'),functools.partial(self.stopJob,False,False)) self.connect(self.taskFrame,QtCore.SIGNAL('launchJobRequest'),self.launchJobRequest) #self.connect(self.taskFrame.buttons.button('next').menu(),QtCore.SIGNAL('triggered(QAction*)'),self.handleNextMenu) self.connect(self.taskChooser,QtCore.SIGNAL('taskClicked'),self.handleChooseTask) self.connect(self.taskChooser.taskTree,QtCore.SIGNAL('taskDropped'),self.handleChooseTaskAndFollowFrom) # Show chooser self.connect(self.taskChooser,QtCore.SIGNAL('closed'),functools.partial(self.showTaskChooser,False)) # Handle job/project deleted self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobDeleted'),self.handleJobDeleted) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobStatusUpdated'),self.taskFrame.titleBar.setStatusBar) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobStatusUpdated'),self.updateActionEnabled) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobFinished'),self.taskFrame.titleBar.setStatusBar) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('projectDeleted'),self.handleProjectDeleted) self.connect(PREFERENCES(),QtCore.SIGNAL('preferencesSaved'),self.toggleShowButtons) self.addToolBar(CCP4WebBrowser.CToolBar(self,'project','project')) toolbar = self.toolBar('project') if toolbar is not None: toolbar.extend(['task_menu','view_coot','view_ccp4mg','export_mtz','task_help','references','clone','run']) #if ALWAYS_SHOW_SERVER_BUTTON or JOBCONTROLLER().serversEnabled(): toolbar.extend(['run_remote']) toolbar.extend(['run_remote']) toolbar.setMovable(False) toolbar.show() ''' self.addToolBar(CCP4WebBrowser.CToolBar(self,'ccp4_update','ccp4_update')) toolbar = self.toolBar('ccp4_update') movie = QtGui.QMovie(os.path.normpath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','fire.gif'))) icon = QtGui.QLabel(self) movie.setScaledSize(QtCore.QSize(36,36)) icon.setMovie(movie) movie.start() icon.setObjectName('ccp4_update') toolbar.addWidget(icon) action = QtGui.QAction('Update available',self) toolbar.addAction(action) #toolbar.setMaximumWidth(100) ''' #print 'CProjectViewer.init jobId',jobId if jobId is not None: #if DEVELOPER(): # openJob = self.taskFrame.openTask(jobId=jobId) #else: try: openJob = self.taskFrame.openTask(jobId=jobId) self.setSelectedJob(jobId=openJob.jobId) except: print 'ERROR CProjectView.init opening jobId',jobId else: #Initialise task chooser open if not jobs in project self.showTaskChooser(True) projectDir = PROJECTSMANAGER().db().getProjectInfo(projectId=openJob.projectId,mode='projectdirectory') #PROJECTSMANAGER().makeReportFilesLink(projectDir) #print 'CProjectViewer.init projectDir',projectDir if graphical: self.show() #print 'CProjectViewer.__init__ splitterSizes',self.splitterSizes mainWidget.setSizes(self.splitterSizes) self.connect(self.taskFrame.tab,QtCore.SIGNAL('currentChanged(int)'),self.handleTaskFrameChanged) #self.lastTaskFrameMode = CProjectViewer.OUTPUT_TAB #self.handleTaskFrameChanged(CProjectViewer.INPUT_TAB) self.shortcuts = {} self.setShortcuts() self.updateActionEnabled() self.toggleShowButtons() # Handle projectWidget actions self.connect(self._projectWidget,QtCore.SIGNAL('purge'),self.purgeJobFiles) self.connect(self._projectWidget,QtCore.SIGNAL('cloneTask'),self.cloneTask) self.connect(self._projectWidget,QtCore.SIGNAL('export'),self.exportJobFile) self.connect(self._projectWidget,QtCore.SIGNAL('deleteJob'),self.deleteJob) self.connect(self._projectWidget,QtCore.SIGNAL('interruptJob'),functools.partial(self.stopJob,False,False)) self.connect(self._projectWidget,QtCore.SIGNAL('killJob'),functools.partial(self.stopJob,True,False)) self.connect(self._projectWidget,QtCore.SIGNAL('killDeleteJob'),functools.partial(self.stopJob,True,True)) self.connect(self._projectWidget,QtCore.SIGNAL('markFailedJob'),functools.partial(self.markFailedJob,False)) self.connect(self._projectWidget,QtCore.SIGNAL('markFinishedJob'),functools.partial(self.markFailedJob,True)) self.connect(self._projectWidget,QtCore.SIGNAL('remoteJobSignal'),self.handleRemoteJobSignal) self.connect(self._projectWidget,QtCore.SIGNAL('currentJobChanged'),self.handleJobListSelectionChange) self.connect(self._projectWidget,QtCore.SIGNAL('nextJob'),self.handleNext) self.connect(self._projectWidget,QtCore.SIGNAL('openFrame'),self.openTaskMainWindow) self.connect(self._projectWidget,QtCore.SIGNAL('showWhatNextPage'),self.showWhatNextPage) self.connect(self._projectWidget,QtCore.SIGNAL('showBibliography'),self.showBibliography) self.connect(self._projectWidget,QtCore.SIGNAL('openMergeMtz'),functools.partial(self.launchJobRequest,'mergeMtz')) #print 'projectName',openJob.projectName pInfo = PROJECTSMANAGER().db().getProjectInfo(projectId,['I1ProjectName','I1ProjectDirectory']) if pInfo['i1projectname'] is not None: self.addI1Widget( pInfo['i1projectname'], pInfo['i1projectdirectory']) self.checkForRemotePasswords() def close(self): # Update the last access info for all open projects openProjectIdList = [] for w in self.Instances: if w != self: openProjectIdList.append(w.taskFrame.openJob.projectId) #print 'CProjectViewer.close openProjectIdList',openProjectIdList PROJECTSMANAGER().db().updateProjectLastAcess(self.taskFrame.openJob.projectId,openProjectIdList) CCP4WebBrowser.CMainWindow.close(self) def addI1Widget(self,projectName,projectDir): try: import CCP4I1Projects frame = QtGui.QFrame(self) frame.setLayout(QtGui.QVBoxLayout()) frame.layout().setContentsMargins(0,0,0,0) frame.layout().setSpacing(0) self._projectWidget.tab.addTab(frame,'CCP4i project') self.i1Widget = CCP4I1Projects.CI1ProjectWidget(self) frame.layout().addWidget(self.i1Widget) #self._projectWidget.tab.addTab(self.i1Widget,'CCP4i project') infoList = [ projectName,projectName ] pObj = CCP4I1Projects.CI1TreeItemProject(self.i1Widget.model().rootItem,infoList=infoList,directory=projectDir) err=pObj.loadDatabase() if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage('CCP4i project','Error loading old CCP4 project data',parent=self) else: self.i1Widget.model().beginResetModel() self.i1Widget.model().rootItem = pObj self.i1Widget.model().endResetModel() except exception as e: print e return try: line = QtGui.QHBoxLayout() line.setContentsMargins(3,3,3,3) line.setSpacing(2) frame.layout().addLayout(line) line.addWidget(QtGui.QLabel('CCP4i project: '+projectName)) self.i1Reload = QtGui.QPushButton(self) self.i1Reload.setText('Reload') line.addWidget(self.i1Reload) self.connect(self.i1Reload,QtCore.SIGNAL('released()'),self.reloadI1Project) except: print e self.connect(self.i1Widget,QtCore.SIGNAL('showLogFile'),functools.partial(self.viewI1File,'log')) self.connect(self.i1Widget.projectView,QtCore.SIGNAL('fileClicked'),functools.partial(self.viewI1File,'clicked')) self.connect(self.i1Widget,QtCore.SIGNAL('viewInQtrview'),functools.partial(self.viewI1File,'qtrview')) self.connect(self.i1Widget,QtCore.SIGNAL('viewInCoot'),functools.partial(self.viewI1Job,'coot')) self.connect(self.i1Widget,QtCore.SIGNAL('viewInMg'),functools.partial(self.viewI1Job,'ccp4mg')) self.connect(self.i1Widget,QtCore.SIGNAL('viewDefFile'),functools.partial(self.viewI1Job,'def')) self.connect(self.i1Widget,QtCore.SIGNAL('reloadProject'),self.reloadI1Project) self.connect(self.i1Widget,QtCore.SIGNAL('viewFile'),functools.partial(self.viewI1File,'view')) self.connect(self.i1Widget,QtCore.SIGNAL('copyFile'),self.copyI1File) def viewI1Job(self,mode,projectId=None,jobId=None): #print 'viewI1Job',mode,projectId,jobId jItem = self.i1Widget.model().getJob1(jobId) if jItem is None: print 'ERROR in viewI1Job - could not find job:',jobId return if mode == 'def': defFile =os.path.join( self.i1Widget.model().rootItem.directory,'CCP4_DATABASE',str(jobId)+'_'+jItem.taskName+'.def') WEBBROWSER().openFile(defFile,format="text/plain") if mode in ['coot','ccp4mg']: fileList = [] for fItem in jItem.childOutFiles: if fItem.broken>0: path = fItem.filePath() if os.path.splitext(path)[1] in ['.mtz','.pdb']: fileList.append(fItem.filePath()) if mode == 'coot' : mode = 'coot0' LAUNCHER().openInViewer(viewer=mode,fileName=fileList) def viewI1File(self,mode,fileName,fileType=None): #print 'viewI1File',mode,fileName if fileName is None: return if mode == 'qtrview': if os.path.splitext(fileName)[1] == '.html': fileName = os.path.splitext(fileName)[0] LAUNCHER().launch('logview',[fileName]) elif mode in ['view','clicked']: if fileType is None: ext = os.path.splitext(fileName)[1] if ext == '.mtz': fileType = "application/CCP4-mtz" elif ext == '.pdb': fileType = "chemical/x-pdb" else: fileType = "text/plain" if fileType == "application/CCP4-mtz": LAUNCHER().launch('hklview',[fileName]) elif fileType == "chemical/x-pdb": WEBBROWSER().openFile(fileName) else: WEBBROWSER().openFile(fileName) def reloadI1Project(self): #print 'reloadI1Project' self.i1Widget.model().beginResetModel() err = self.i1Widget.model().rootItem.loadDatabase() if err.maxSeverity()>SEVERITY_WARNING: err.warningMessage('CCP4i project','Error loading old CCP4 project data',parent=self) self.i1Widget.model().endResetModel() def copyI1File(self,fileName): #print 'copyI1File',fileName if os.path.exists(fileName): urlList = [QtCore.QUrl()] urlList[0].setPath(fileName ) mimeData = QtCore.QMimeData() mimeData.setUrls(urlList) QTAPPLICATION().clipboard().setMimeData(mimeData) def toggleShowButtons(self,mode=None): if mode is None: mode = str(PREFERENCES().PROJECT_VIEWER_BUTTONS) if mode == 'toolbar': self.toolBar('project').show() self.taskFrame.findChild(QtGui.QFrame,'buttons').hide() else: self.toolBar('project').hide() self.taskFrame.findChild(QtGui.QFrame,'buttons').show() def updateActionEnabled(self,jobStatus=None): if jobStatus is None: jobStatus = self.taskFrame.openJob.status #print 'CProjectViewer.updateActionEnabled taskFrame',self.taskFrame.buttons.mode,self.taskFrame.openJob.jobNumber,self.taskFrame.openJob.status #import traceback #traceback.print_stack(limit=5) if jobStatus is None: #No job set - disable all buttons #for item in ['run','view','clone']: for item in ['run','clone','task_help']: self.findChild(QtGui.QAction,item).setEnabled(False) for item in ['run_remote']: obj = self.findChild(QtGui.QAction,item) if obj is not None: obj.setEnabled(False) else: if self.taskFrame.buttons.mode in ['task','input']: self.findChild(QtGui.QAction,'run').setEnabled(jobStatus in ['Pending','Interrupted']) self.findChild(QtGui.QAction,'view_coot').setEnabled(jobStatus in ['Finished','Interrupted']) self.findChild(QtGui.QAction,'view_ccp4mg').setEnabled(jobStatus in ['Finished','Interrupted']) obj = self.findChild(QtGui.QAction,'run_remote') if obj is not None: obj.setEnabled(jobStatus in ['Pending','Interrupted']) else: self.findChild(QtGui.QAction,'run').setEnabled(False) obj = self.findChild(QtGui.QAction,'run_remote') if obj is not None: obj.setEnabled(False) #self.findChild(QtGui.QAction,'view').setEnabled(jobStatus in ['Finished','Interrupted','To delete']) self.findChild(QtGui.QAction,'clone').setEnabled(True) refFileList = TASKMANAGER().searchReferenceFile(name=self.taskFrame.openJob.taskName,drillDown=True) if len(refFileList) == 0: # Is ther acustom biblio for this job import os,glob refFileList = glob.glob(os.path.join(self.taskFrame.openJob.jobDir,'*.medline.txt')) self.findChild(QtGui.QAction,'references').setEnabled((len(refFileList)>0)) enabled = False exportMenu = [] if jobStatus in ['Finished','Interrupted']: exportMenu = TASKMANAGER().exportJobFiles(taskName=self.taskFrame.openJob.taskName,jobId=self.taskFrame.openJob.jobId) for item in exportMenu: if item[0] == 'complete_mtz': enabled = True if not enabled: params = TASKMANAGER().getTaskAttribute(taskName=self.taskFrame.openJob.taskName,attribute='EXPORTMTZPARAMS',default=None) if params is not None: enabled = True #print 'updateActionEnabled',jobStatus,exportMenu,enabled self.findChild(QtGui.QAction,'export_mtz').setEnabled(enabled) helpFile = TASKMANAGER().searchHelpFile(name=self.taskFrame.openJob.taskName) self.findChild(QtGui.QAction,'task_help').setEnabled((helpFile is not None)) def runTask(self,mode='Now'): self.taskFrame.inputFrame.runTask(mode) def handleViewTask(self,mode): #print 'CProjectViewer.handleViewTask',mode self.taskFrame.handleViewTask(mode) def initialiseActionDefinitions(self): CCP4WebBrowser.CMainWindow.initialiseActionDefinitions(self) self.actionDefinitions['task_menu'] = dict ( text = "Task menu", tip = "Show/hide the task menu", slot = functools.partial(self.showTaskChooser,True), icon = 'taskmenu' ) self.actionDefinitions['clone'] = dict ( text = "Clone job", tip = "Make another job with same parameters", slot = self.cloneTask, icon = 'clone' ) self.actionDefinitions['run'] = dict ( text = "Run", tip = "Run this task", slot = self.runTask, icon = 'running' ) self.actionDefinitions['run_remote'] = dict ( text = "Run on server", tip = "Run this job on a server", slot = functools.partial(self.runTask,'run_remote'), icon = 'running' ) self.actionDefinitions['view_coot'] = dict ( text = "View in Coot", tip = "Show map & models related to the job in Coot", slot = functools.partial(self.handleViewTask,'view_coot'), icon = 'coot_rebuild' ) self.actionDefinitions['view_ccp4mg'] = dict ( text = "View in CCP4mg", tip = "Show map & models related to the job in CCP4mg", slot = functools.partial(self.handleViewTask,'view_ccp4mg'), icon = 'ccp4mg_edit_model' ) self.actionDefinitions['export_mtz'] = dict ( text = "Export MTZ", tip = "Export traditional MTZ file with all the data for the job", slot = self.exportMtz, icon = 'MiniMtzDataFile' ) self.actionDefinitions['task_help'] = dict ( text = "Help", tip = "Show documentation for this task", slot = functools.partial(self.handleHelpMenu,'task_help'), icon = 'help' ) self.actionDefinitions['references'] = dict ( text = "Bibliography", tip = "Show bibliographic references for the task", slot = functools.partial(self.handleHelpMenu,'references'), icon = 'biblio' ) def setShortcuts(self): for name,key in [ ['taskmenu' , QtCore.Qt.CTRL + QtCore.Qt.Key_M ], ['nextproject' , QtCore.Qt.CTRL + QtCore.Qt.Key_N ]]: self.shortcuts[name] = QtGui.QShortcut(QtGui.QKeySequence(key),self) self.connect(self.shortcuts[name],QtCore.SIGNAL('activated()'),functools.partial(self.handleShortcut,name)) def handleShortcut(self,mode): #print 'CProjectViewer.handleShortcut',mode if mode == 'taskmenu': self.showTaskChooser(True) elif mode == 'nextproject': idx = self.Instances.index(self) + 1 if idx >= len(self.Instances): idx = 0 #print 'CProjectViewer.handleShortcut nextproject',idx self.Instances[idx].show() self.Instances[idx].raise_() def projectWidget(self): return self._projectWidget def showHelpMenu(self): menu = self.taskFrame.buttons.button('help').menu() menu.clear() action = menu.addAction('Task description') action.setObjectName('task_help') action = menu.addAction('Bibliographic references') action.setObjectName('references') refFileList = TASKMANAGER().searchReferenceFile(name=self.taskFrame.openJob.taskName,drillDown=True) menu.findChild(QtGui.QAction,'references').setEnabled((len(refFileList)>0)) helpFile = TASKMANAGER().searchHelpFile(name=self.taskFrame.openJob.taskName) menu.findChild(QtGui.QAction,'task_help').setEnabled((helpFile is not None)) # Add task-specific help programHelpList = TASKMANAGER().getTaskAttribute(self.taskFrame.openJob.taskName,'PROGRAMHELP',default=self.taskFrame.openJob.taskName) #print 'CTaskFrame.getHelpFile programHelp',programHelpList if programHelpList is not None: if not isinstance(programHelpList,list): programHelpList = [ programHelpList ] for programHelp in programHelpList: if programHelp.count('$CCP4I2'): helpPath = CCP4Utils.getCCP4I2Dir()+programHelp[7:] elif programHelp.count('$CCP4'): helpPath = CCP4Utils.getCCP4Dir()+programHelp[5:] else: helpPath = os.path.join(CCP4Utils.getCCP4Dir(),'html',programHelp+'.html') #print 'CTaskFrame.getHelpFile helpPath',helpPath if os.path.exists(helpPath): action = menu.addAction(os.path.splitext(os.path.split(helpPath)[-1])[0]) action.setData(QtCore.QVariant(helpPath)) def handleHelpMenu(self,mode): if not isinstance(mode,str): mode = str(mode.objectName()) if mode == 'references': self.showBibliography(taskNameList=[self.taskFrame.openJob.taskName]) elif mode == 'task_help': fileName = TASKMANAGER().searchHelpFile(name=self.taskFrame.openJob.taskName) WEBBROWSER().openFile(fileName) else: fileName = action.data().toString().__str__() WEBBROWSER().openFile(fileName) def reportAvailable(self): status = self.taskFrame.openJob.status in CCP4DbApi.FINISHED_JOB_STATUS or self.taskFrame.openJob.status in [ 'Running', 'Running remotely', 'Failed' ] #print 'reportAvailable',self.taskFrame.openJob.status,status return status def testReportAvailable(self): import glob testReportList = glob.glob(os.path.join(PROJECTSMANAGER().db().getProjectInfo(projectId=self.taskFrame.openJob.projectId,mode='projectdirectory'),'CCP4_test*.log')) return (len(testReportList)>0) def redoReport(self): try: self.taskFrame.outputFrame.showOutput(redo=True,doReload=True) except CException as e: e.warningMessage('Creating report','Error creating report',parent=self) def openTask(self,taskName=None,jobId=None,followJobId=None): try: openJob = self.taskFrame.openTask(jobId=jobId,taskName=taskName,followJobId=followJobId) self.setSelectedJob(jobId=openJob.jobId) except: print 'ERROR CProjectView.init opening jobId,taskName',jobId,taskName def openDictionary(self,state): #print 'CProjectViewer.openDictionary' if self._dictionaryWidget is None or (not isAlive(self._dictionaryWidget)): import CCP4ModelWidgets self._dictionaryWidget = CCP4ModelWidgets.CDictDataDialog(parent=self,projectId=self.getProject()) self._dictionaryWidget.show() self._dictionaryWidget.raise_() def setSelectedJob(self,jobId): #print 'CProjectViewer.setSelectedJob',jobId self._projectWidget.selectJob(jobId) def Exit(self): PROJECTSMANAGER().db().updateProject(projectId=self.taskFrame.openJob.projectId,key='lastaccess') self.taskFrame.saveStatus() @staticmethod def updateInstances(qobj): l = [] for w in CProjectViewer.Instances: if isAlive(w): l.append(w) CProjectViewer.Instances = l #print 'projectviewer.updateInstances',CProjectViewer.Instances def getProject(self): return self.taskFrame.openJob.projectId def getOpenJobNumber(self): ''' Return the current job number ''' return self.taskFrame.openJob.jobnumber def showTaskChooser(self,state=None): if state is None: state = (self.rightStack.currentIndex()==0) if state: self.rightStack.setCurrentIndex(1) else: self.rightStack.setCurrentIndex(0) def handleChooseTask(self,taskName): #print 'handleChooseTask',taskName self.handleChooseTaskAndFollowFrom(taskName) def handleChooseTaskAndFollowFrom(self,taskName,data=None): #print 'handleChooseTaskAndData',taskName self.showTaskChooser(False) openJob = self.taskFrame.openTask(taskName=taskName) self.setSelectedJob(jobId=openJob.jobId) #self._projectWidget.projectView.setJobSelection(jobId=self.taskFrame.openJob.jobId) #print 'handleChooseTaskAndFollowFrom',self.taskFrame.openJob def handleJobListSelectionChange(self,fileId,jobId,pipelineJobId,detatch): ''' Keep the task display in step with the selection on the project job list ''' #print 'handleJobListSelectionChange',fileId,jobId,pipelineJobId,detatch,'current',self.taskFrame.openJob.jobId #if pipelineJobId is None: return if detatch: jobInfo = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode=['status','taskname']) if jobInfo['status'] in CCP4DbApi.FINISHED_JOB_STATUS: self.openTaskMainWindow('output',jobId) elif jobInfo['status'] in ['Running','Running remotely'] and TASKMANAGER().hasReportDefinition(name=jobInfo['taskname'],jobStatus=jobInfo['status']): self.openTaskMainWindow('output',jobId) else: self.openTaskMainWindow('input',pipelineJobId) else: if self.taskFrame.openJob.jobId is None or jobId != str(self.taskFrame.openJob.jobId): # Open task interface for the top level pipeline try: openJob = self.taskFrame.openTask(jobId=jobId) except CException as e: e.warningMessage('Opening new job','Failed opening job',parent=self) self.showTaskChooser(False) self.deleteSpawnedTaskWindows(jobId) #print 'handleJobListSelectionChange',openJob elif jobId == str(self.taskFrame.openJob.jobId) and self.rightStack.currentIndex() == 1: self.showTaskChooser(False) def handleTaskFrameChanged(self,mode): #print 'handleTaskFrameChanged',mode,self.taskFrame.openJob.jobId,self.taskFrame.openJob.status if mode == CProjectViewer.INPUT_TAB and self.taskFrame.inputFrame.taskWidget is None: #print 'Need to draw taskWidget' import CCP4Container oJ = self.taskFrame.openJob defFile = TASKMANAGER().lookupDefFile(oJ.taskname,oJ.taskversion) container = CCP4Container.CContainer(parent=self.taskFrame,definitionFile=defFile,guiAdmin=True) paramsFile = PROJECTSMANAGER().makeFileName(jobId = oJ.jobId,mode='JOB_INPUT') container.loadDataFromXml(paramsFile) taskWidget = self.taskFrame.inputFrame.createTaskWidget(oJ.taskname,projectId=oJ.projectid,jobId=oJ.jobId, container=container,taskEditable=False) self.taskFrame.connect(taskWidget,QtCore.SIGNAL('launchJobRequest'),self.taskFrame.launchJobRequest) if mode == CProjectViewer.INPUT_TAB: self.splitterSizes = self.centralWidget().sizes() w = CCP4TaskWidget.WIDTH + CCP4TaskWidget.PADDING_ALLOWANCE if self.splitterSizes[1]0: return jobIdList[0]['jobid'] else: return None def handleProjectDeleted(self,args): if args['projectId'] == self.taskFrame.openJob.projectId: self._projectWidget.setForceUpdate(False) self.close() def showHelp(self,mode='ccp4i2'): WEBBROWSER().showHelp(mode=mode) ''' def handleNextMenu1(self,jobId,taskName,action): #print 'handleNextMenu1',jobId # called from a spawned task frame nextTask = str(action.data().toString()) self.handleNext(nextTask,jobId) def handleNextMenu(self,action): #nextTitle = str(action.text()) taskName = str(action.data().toString()) if str(action.text()) == CTaskButtons.MOREINFO: self.showWhatNextPage(self.taskFrame.openJob.jobId,taskName) else: self.handleNext(taskName,self.taskFrame.openJob.jobId,self.taskFrame.openJob.projectId) ''' def handleNext(self,nextTask,jobId,patchParamsFile): #print 'handleNext',nextTask,jobId,patchParamsFile openJob = self.taskFrame.openTask(taskName=nextTask,followJobId=jobId,patchParamsFile=patchParamsFile) self.setSelectedJob(jobId=openJob.jobId) PROJECTSMANAGER().db().updateJob(jobId=openJob.jobId,key='preceedingjobid',value=jobId) def handleNextTask(self,taskName,patchParamsFile): #print 'CProjectViewer.handleNextTask',taskName,patchParamsFile if taskName == 'clone_rerun': self.cloneTask() else: openJob = self.taskFrame.openTask(taskName=taskName,followJobId=self.taskFrame.openJob.jobId,patchParamsFile=patchParamsFile) self.setSelectedJob(jobId=openJob.jobId) def showWhatNextPage(self,jobId,taskName=None,projectId=None): nextPage = TASKMANAGER().getWhatNextPage(taskName=taskName,jobId=jobId) if nextPage is None: taskTitle= TASKMANAGER().getTitle(taskName) QtGui.QMessageBox.warning(self,self.windowTitle(),'Sorry - no What Next? page for '+str(taskTitle)) else: view = WEBBROWSER().loadWebPage(helpFileName=nextPage) if view is not None: if projectId is None: projectId = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode='projectid') #print 'showWhatNextPage projectId',projectId view.report.setJobInfo(jobInfo= { 'jobId' : jobId, 'projectId' : projectId } ) def showBibliography(self,jobId=None,taskNameList=[]): print 'CProjectViewer.showBibliography',jobId,taskNameList if jobId is None: jobId = self.taskFrame.openJob.jobId import CCP4BibliographyViewer viewer = CCP4BibliographyViewer.CBibliographyViewer(self) viewer.setReferences(jobId=jobId,taskNameList=taskNameList) viewer.show() def openApplication(self,application): WEBBROWSER().openApplication(application) def launchJobRequest(self,taskName,args): #print 'CProjectViewer.launchJobRequest',taskName,args try: jobId,pName,jNumber = PROJECTSMANAGER().newJob(taskName=taskName,projectId=self.taskFrame.openJob.projectId) except: QtGui.QMessageBox.warning(self,'Error in creating new job','Unknown error creating new job'+str(taskName)+'\n'+str(e)) return # Create an input params file for job import CCP4Container container = CCP4Container.CContainer(definitionFile=TASKMANAGER().lookupDefFile(taskName),guiAdmin=True) # Load known data to the params file if taskName == 'mergeMtz' and args.get('fileName',None) is not None: container.inputData.MINIMTZINLIST[0].set({'fileName':args.get('fileName',None)}) container.saveDataToXml(fileName=PROJECTSMANAGER().makeFileName(jobId=jobId,mode='JOB_INPUT')) # If there is a jobId for a 'parent' job that launched this then it needs a call to CTaskWidget.handleLaunchedJobFinish # when the 'child' job finishes if args.get('launchJobId',None) is not None: # use non-existance of _launchedPopoutJobs attribute as flag to if getattr(self,'_launchedPopoutJobs',None) is None: self._launchedPopoutJobs = {jobId : args['launchJobId']} self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobFinished'),self.handleJobFinished) # Open a a popout window win = self.openTaskMainWindow('input',jobId) # Call 'parent' job to inform of progress and pass over reference to child widget #import CCP4TaskWidget childTaskWidget = None parentTaskWidget = None for taskWidget in self.findChildren(CCP4TaskWidget.CTaskWidget): if taskWidget.jobId() == jobId: childTaskWidget = taskWidget elif taskWidget.jobId() == args['launchJobId']: parentTaskWidget = taskWidget if parentTaskWidget is not None: parentTaskWidget.handleLaunchedJob(jobId=jobId,status=1,taskWidget=childTaskWidget) def handleJobFinished(self,args): #print 'CProjectViewer.handleJobFinished',args launchJobId = self._launchedPopoutJobs.get(args['jobId'],None) #import CCP4TaskWidget #print 'CProjectViewer.handleJobFinished taskWidgetList',self.findChildren(CCP4TaskWidget.CTaskWidget) if launchJobId is not None: for taskWidget in self.findChildren(CCP4TaskWidget.CTaskWidget): if taskWidget.jobId() == launchJobId: taskWidget.handleLaunchedJob(jobId=args['jobId'],status=args.get('status',None)) del self._launchedPopoutJobs[args['jobId']] def openTaskMainWindow(self,mode,jobId=None): #print 'CProjectViewer.openTaskMainWindow',mode,jobId,launchJobId openJob = COpenJob(jobId=jobId) if mode == 'input': paramsFile = PROJECTSMANAGER().makeFileName(jobId = jobId,mode='JOB_INPUT') #print 'CProjectViewer.openTaskMainWindow paramsFile',paramsFile if not os.path.exists(paramsFile): #print 'CProjectViewer.openTaskMainWindow: Failed to find input params file',paramsFile return None defFile = TASKMANAGER().lookupDefFile(openJob.taskname,openJob.taskversion) if defFile is None: #print 'CProjectViewer.openTaskMainWindow: Failed to find def file',openJob.taskname return None import CCP4Container container = CCP4Container.CContainer(parent=self,definitionFile=defFile,guiAdmin=True) container.loadDataFromXml(paramsFile) #print 'CProjectViewer.openTaskMainWindow',repr(container),paramsFile taskEditable = ( openJob.status in ['Unknown','Pending'] ) try: widget = CTaskInputFrame(self) widget.createTaskWidget(openJob.taskname,projectId=openJob.projectid,jobId=jobId,container=container,taskEditable=taskEditable) except CException as e: e.warningMessage(parent=self) return None except Exception as e: QtGui.QMessageBox.warning(self,'Error in creating task input','Unknown error creating task input widget for job number '+str(openJob.jobnumber)+str(e)) elif mode == 'output': #try: if 1: widget = CReportView(self) widget.showOutput(openJob=openJob) ''' except CException as e: e.warningMessage(parent=self) return None except Exception as e: CMessageBox(self,message='Unknown error creating report file for job number '+str(openJob.jobnumber),exception=e,openJob=openJob) return ''' elif mode == 'status': widget = CJobStatusWidget(self) widget.setJob(openJob) win = CTaskMainWindow(self,openJob.projectname,openJob.jobId) win.buttons.setMode(mode) import functools if win.buttons.button('clone') is not None: self.connect(win.buttons.button('clone'),QtCore.SIGNAL('released()'),functools.partial(self.cloneTask,jobId)) self.connect(win.buttons.button('view').menu(),QtCore.SIGNAL('triggered(QAction*)'),functools.partial(self.handleViewTask0,jobId)) if mode == 'input': self.connect(win.buttons.button('run'),QtCore.SIGNAL('released()'),win.runTask) win.titleBar.setOpenJob(openJob) #win.buttons.setNextMenu(jobId=jobId,taskName=openJob.taskname) win.buttons.setEnabled(openJob.status) win.centralWidget().layout().insertWidget(1,widget) win.show() win.raise_() return win #print 'CProjectViewer.handleOpenFrame widget',win.widget() def handleViewTask0(self,jobId,action=None): text = str(action.text()) #print 'CProjectViewer.handleViewTask0',jobId,text if text.count('4mg'): LAUNCHER().openInViewer(viewer='ccp4mg',jobId=jobId,projectId=self.taskFrame.openJob.projectId,guiParent=self) elif text.count('oot'): LAUNCHER().openInViewer(viewer='coot_job',jobId=jobId,projectId=self.taskFrame.openJob.projectId,guiParent=self) def openSendReport(self): import CCP4ErrorReportViewer widget = CCP4ErrorReportViewer.CSendJobError(self,projectId=self.taskFrame.openJob.projectId,projectName=self.taskFrame.openJob.projectName) widget.show() def openUpdate(self): import CCP4UpdateDialog widget = CCP4UpdateDialog.CUpdateDialog(self) widget.show() def deleteJob(self,jobIdList,deleteImportFiles=True): print 'CProjectsViewer.deleteJob',jobIdList,deleteImportFiles followOnJobs = [] allJobTree = [] if not isinstance(jobIdList,list): jobIdList = [jobIdList] try: jobTreeList = PROJECTSMANAGER().db().getMultiFollowOnJobs(jobIdList=jobIdList,traceImportFiles=deleteImportFiles) except CException as e: e.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error attempting to find jobs dependent on output files') return xtrJobIdList = [] for jobTree in jobTreeList: for followOnJob in jobTree[2]: if followOnJob[0] not in jobIdList: xtrJobIdList.append(followOnJob[0]) #Are there open pending jobs that might use files? jobsToDeleteWithSelectedFiles = [] #print 'deleteJob openJob.status',self.taskFrame.openJob.status if self.taskFrame.openJob.status == 'Pending': selectedFileDict = self.taskFrame.inputFrame.taskWidget.container.inputData.inputFilesFileIds() #print 'deleteJob openJob selectedFileDict',selectedFileDict if len(selectedFileDict)>0: jobsWithSelectedFiles = PROJECTSMANAGER().db().getJobSourceOfFiles(fileIdList = selectedFileDict.keys()) #print 'jobsWithSelectedFiles',jobsWithSelectedFiles jobsToDeleteWithSelectedFilesSet = set(jobIdList+xtrJobIdList) & set(jobsWithSelectedFiles) for i in range(len(jobsToDeleteWithSelectedFilesSet)): jobsToDeleteWithSelectedFiles.append(jobsToDeleteWithSelectedFilesSet.pop()) #print 'deleteJob jobsToDeleteWithSelectedFiles',jobsToDeleteWithSelectedFiles if len(xtrJobIdList) == 0 and len(jobsToDeleteWithSelectedFiles) == 0: # Delete in reverse order for iJob in range(len(jobTreeList)-1,-1,-1): delJobId,importFiles,followOnJobs = jobTreeList[iJob] try: PROJECTSMANAGER().deleteJob(jobId=delJobId,importFiles=importFiles,projectId=self.taskFrame.openJob.projectId, deleteImportFiles=deleteImportFiles) except CException as e: e.warningMessage(parent=self,windowTitle=self.windowTitle(),message='Error attempting to delete job') return except Exception as e: QtGui.QMessageBox.warning(self,self.windowTitle(),'Unrecognised error deleting job\n'+str(e)) return self.handleJobsDeleted() else: #QtGui.QMessageBox.warning(self,self.windowTitle(),"Deleting multiple jobs with 'knock on' jobs temporarily disabled\n") #return #print 'CProjectsViewer.deleteJob jobTree',jobTree deleteJobGui = CDeleteJobGui(self,self.taskFrame.openJob.projectId,jobIdList=jobIdList,jobTreeList=jobTreeList, jobsToDeleteWithSelectedFiles=jobsToDeleteWithSelectedFiles,deleteImportFiles=deleteImportFiles,ifXtrJobs=len(xtrJobIdList)>0) self.connect(deleteJobGui,QtCore.SIGNAL('jobsDeleted'),self.handleJobsDeleted) deleteJobGui.show() def handleJobsDeleted(self): #print 'CProjectsViewer.handleJobsDeleted' if self.taskFrame.inputFrame.taskWidget is not None: self.taskFrame.inputFrame.taskWidget.resetJobCombos() self.taskFrame.inputFrame.taskWidget.validate() def openManageImportFiles(self): import CCP4ImpExpWidgets widget = CCP4ImpExpWidgets.CManageImportFiles(self,projectId=self.taskFrame.openJob.projectId) widget.show() def handleCsvDownloadRequest(self,jobId,dataName): #print 'CProjectView.handleCsvDownloadRequest',jobId,dataName if jobId is None: jobId = self.taskFrame.openJob.jobId fileName = os.path.join(PROJECTSMANAGER().makeFileName(jobId= jobId,mode='TABLES_DIR'),dataName+'.csv') if not os.path.exists(fileName): return import CCP4FileBrowser self.fileBrowser = CCP4FileBrowser.CFileDialog(self, title='Save csv table file for '+dataName, filters= ['csv table file (*.csv)'], defaultSuffix='csv', fileMode=QtGui.QFileDialog.AnyFile ) self.connect(self.fileBrowser,QtCore.SIGNAL('selectFile'),functools.partial(self.downloadCsvFile,jobId,dataName)) self.fileBrowser.show() def downloadCsvFile(self,jobId,dataName,targetFile): #print 'CProjectView.downloadCsvFile',jobId,dataName,targetFile sourceFile = os.path.join(PROJECTSMANAGER().makeFileName(jobId=jobId,mode='TABLES_DIR'),dataName+'.csv') import shutil try: shutil.copyfile(sourceFile,targetFile) except: QtGui.QMessageBox.warning(self,self.windowTitle(),'Failed to copy csv table file to '+str(targetFile)) def grabWidget(self): import glob if not hasattr(self,'grabStatus'): self.grabStatus = { 'taskName' : None , 'saveDir' : None, 'mode' : None } if self.taskFrame.tab.currentIndex() == CTaskFrame.INPUT_TAB: wList = self.findChildren(CCP4TaskWidget.CTaskWidget) #print 'grabWidget wList',wList if len(wList)==0: QtGui.QMessageBox.warning(self,'Grab widget','Failed to find child task widget') return taskWidget = wList[0] elif self.taskFrame.tab.currentIndex() == CTaskFrame.OUTPUT_TAB: wList = self.findChildren(CReportView) #print 'grabWidget wList',wList if len(wList)==0: QtGui.QMessageBox.warning(self,'Grab widget','Failed to find child report view') return reportWidget = wList[0] else: return taskName = self.taskFrame.openJob.taskName if self.grabStatus['taskName'] is None or taskName != self.grabStatus['taskName']: rv = QtGui.QFileDialog.getExistingDirectory(caption='Select directory for saving screen grabs for task '+str(taskName)) if rv is None or len(rv)==0: return self.grabStatus['saveDir'] = str(rv) self.grabStatus['taskName'] = taskName if self.taskFrame.tab.currentIndex() == CTaskFrame.INPUT_TAB: self.grabStatus['mode'] = 'task' else: self.grabStatus['mode'] = 'report' fileList = glob.glob(os.path.join(self.grabStatus['saveDir'],self.grabStatus['taskName']+'_'+self.grabStatus['mode']+'*.png')) if len(fileList)==0: fileName = os.path.join(self.grabStatus['saveDir'],self.grabStatus['taskName']+'_'+self.grabStatus['mode']+'_1.png') else: fileList.sort() num = int(fileList[-1][-6:-4].strip('_')) fileName = os.path.join(self.grabStatus['saveDir'],self.grabStatus['taskName']+'_'+self.grabStatus['mode']+'_' + str(num+1) + '.png') pix0 = QtGui.QPixmap() if self.grabStatus['mode'] == 'task': pix1 = pix0.grabWidget(taskWidget.widget) else: pix1 = pix0.grabWidget(reportWidget) #print 'grabWidget size', pix1.width(),pix1.height() f = QtCore.QFile(fileName) f.open(QtCore.QIODevice.WriteOnly) pix1.save(f, "PNG") f.close() print 'Saved to ',fileName WEBBROWSER().openFile(fileName) def queryDeleteJob(self,jobId,jobInfo): #print 'into queryDeleteJob' self.queryDeleteWindow = QtGui.QDialog(self) self.queryDeleteWindow.setWindowTitle(self.windowTitle()) self.queryDeleteWindow.setModal(True) self.queryDeleteWindow.setLayout(QtGui.QVBoxLayout()) self.queryDeleteWindow.layout().addWidget(QtGui.QLabel('''By default interactive jobs (such as ''' + TASKMANAGER().getShortTitle(jobInfo['taskname'])+ \ ''') with no output files are deleted.\nYou can change this in the Preferences window.\nJob number '''+str(jobInfo.get('jobnumber',' ')) + \ '''will be deleted.''', self ) ) self.queryDelete = QtGui.QCheckBox('Delete interactive jobs with no output files',self.queryDeleteWindow) self.queryDelete.setChecked(bool(PREFERENCES().DELETE_INTERACTIVE_JOBS)) self.queryShow = QtGui.QCheckBox('Do not show this warning again',self.queryDeleteWindow) self.queryShow.setChecked(True) self.queryDeleteWindow.layout().addWidget(self.queryDelete) self.queryDeleteWindow.layout().addWidget(self.queryShow) butBox = QtGui.QDialogButtonBox(self.queryDeleteWindow) butBox.addButton(QtGui.QDialogButtonBox.Ok) self.queryDeleteWindow.layout().addWidget(butBox) self.connect(butBox.buttons()[0],QtCore.SIGNAL('clicked()'),functools.partial(self.handleQueryDeleteJob,jobId)) self.queryDeleteWindow.show() self.queryDeleteWindow.raise_() def handleQueryDeleteJob(self,jobId): ifShow = not(self.queryShow.isChecked()) ifDelete = self.queryDelete.isChecked() #print 'handleQueryDeleteJob',jobId,ifShow,ifDelete self.queryDeleteWindow.hide() self.queryDeleteWindow.deleteLater() PREFERENCES().DELETE_INTERACTIVE_JOBS.set(ifDelete) PREFERENCES().SHOW_DELETE_INTERACTIVE_JOBS.set(ifShow) PREFERENCES().save() if ifDelete: try: PROJECTSMANAGER().db().deleteJob(jobId=jobId,deleteChildren=True) except: print 'Projectviewer handleQueryDeleteJob failed to delete job' else: import CCP4DbApi PROJECTSMANAGER().db().updateJobStatus(jobId=jobId,status=CCP4DbApi.JOB_STATUS_FINISHED) # Function to return list of names of exportable MTZ(s) def handleLoadRemoteRun(self,jobId=None,projectId=None,jobNumber=None): import re import CCP4FileBrowser if jobId is None: jobId = self.taskFrame.openJob.jobId projectId = self.taskFrame.openJob.projectId jobNumber = self.taskFrame.openJob.jobNumber if jobNumber is None: jobNumber = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode=['jobnumber']) projectDir = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectdirectory') tarball = os.path.join(projectDir,'CCP4_TMP','job_'+jobNumber+'_finished.ccp4db.zip') if os.path.exists(tarball): self.loadRemoteRun(projectId,jobId,tarball) else: self.fileBrowser = CCP4FileBrowser.CFileDialog(self, title='Reload exported job from 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.fileBrowser,QtCore.SIGNAL('selectFile'),functools.partial(self.loadRemoteRun,projectId,jobId)) self.fileBrowser.show() def loadRemoteRun(self,projectId=None,jobId=None,compressedFile=None): if hasattr(self,'fileBrowser'): try: self.fileBrowser.hide() self.fileBrowser.deleteLater() del self.fileBrowser except: pass # Extract database xmlfile try: xmlFile = PROJECTSMANAGER().extractDatabaseXml(compressedFile) except CException as e: e.warningMessage('Reload exported job','Failed extracting database XML file from compressed file') return except Exception as e: QtGui.QMessageBox.warning(self,'Loading exported job','ERROR extracting database xml file') return # Read dbxml file into a CDbXml and check that it is for this project import CCP4DbApi self.dbImport = CCP4DbApi.CDbXml(db=PROJECTSMANAGER().db(),xmlFile=xmlFile) importProjectInfo = self.dbImport.loadProjectInfo() if projectId is not None and self.dbImport.projectId != projectId: QtGui.QMessageBox.warning(self,'Loading exported job','Job is not for this project') return self.dbImport.createTempTables() self.dbImport.loadTempTable() # If loading jobs to an existing project flag up jobs in temp tables that are already in db self.dbImport.setExclInTempTables() # Extract job files from the compressed file projectDir = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId,mode='projectdirectory') import CCP4Export self.importThread = CCP4Export.ImportProjectThread(self,projectDir=projectDir,compressedFile=compressedFile) self.importThread.extractJobs(self.importThread.compressedFile,self.importThread.projectDir,dbImport=self.dbImport) # Update the database self.dbImport.cleanupTempTables() stats = self.dbImport.importStats() #print 'loadRemoteRun stats',stats self.dbImport.importTempTables() self.dbImport.removeTempTables() self.dbImport.db.emitSignal('projectReset',{'projectId':self.dbImport.projectId}) if 0: self.dbImport.errReport.warningMessage('Load exported job','Error updating database',parent=self) jobInfo = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode=['status','finishtime']) self._projectWidget.model().updateFinishedJob({'jobId':jobId,'projectId':projectId, 'status':jobInfo['status'],'finishTime':jobInfo['finishtime']}) self._projectWidget.model().createChildJobs((jobId,projectId)) # Create report self.taskFrame.handleJobFinished({'jobId':jobId,'status':jobInfo['status']}) def checkForRemotePasswords(self): requirePass = JOBCONTROLLER().checkForRemotePasswords(self.taskFrame.openJob.projectId) #print 'checkForRemotePasswords',requirePass if len(requirePass)==0: return import CCP4JobControlGui self.passwordEntry = CCP4JobControlGui.CPasswordEntry(parent=self, label='Please enter password for '+requirePass[0]['username']+' on '+requirePass[0]['machine']+'\nTo enable recovering remote jobs') self.connect(self.passwordEntry,QtCore.SIGNAL('passwordEntered'),functools.partial(self.handlePasswordEntry,requirePass[0]['jobId'])) self.passwordEntry.show() def handlePasswordEntry(self,jobId,password): #print 'handlePasswordEntry',jobId,password JOBCONTROLLER().patchRemotePasswords(jobId,password) def widgetIsSaveable(self): return False def widgetIsPrintable(self): return False def widgetIsSearchable(self): return False def widgetIsRunable(self): return False def handleSave(self): pass def handlePrint(self): pass def handleRun(self): pass def openFind(self): pass def isFindFrameOpen(self): return False def deleteTab(self): pass def historyBack(self): pass def historyForward(self): pass def reloadPage(self): pass class CTaskButtonsLayout(QtGui.QHBoxLayout): def maximumSize(self): return QtCore.QSize(CCP4TaskWidget.WIDTH,50) def sizeHint(self): return self.maximumSize() class CTaskButtons(QtGui.QButtonGroup): #BUTTONS = ['run','view','clone','next'] #BUTTONTEXT = ['Run','View','Clone','Next'] BUTTONS = ['task_menu','help','view','clone','run'] BUTTONTEXT = ['Task menu','Help','View','Clone job','Run'] TOOLTIPS = ['Open another task from the task chooser','Open task and program documentation','View the output maps and model in molecular graphics', 'Create another job with the same parameters set','Check the input data then run the job'] MOREINFO = 'More info..' DEFAULTMODE = 0 RUNONLYMODE = 1 #buttons def __init__(self,parent,parentLayout=None,mode=DEFAULTMODE): QtGui.QButtonGroup.__init__(self,parent) self.mode = 'task' # task/input/output/status frame = QtGui.QFrame(parent) frame.setObjectName('buttons') layout = CTaskButtonsLayout() frame.setLayout(layout) layout.setContentsMargins(CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN,CProjectViewer.MARGIN) layout.setSpacing(0) layout.addStretch(1) if mode == CTaskButtons.RUNONLYMODE: butRange = [4] else: butRange = range(len(CTaskButtons.BUTTONTEXT)) for ii in butRange: self.addButton(QtGui.QPushButton(CTaskButtons.BUTTONTEXT[ii],parent),ii) but = self.button(CTaskButtons.BUTTONS[ii]) but.setToolTip(CTaskButtons.TOOLTIPS[ii]) but.setAutoDefault(False) layout.addWidget(but) layout.addStretch(1) #parentLayout.addLayout(layout) parentLayout.addWidget(frame) self.button('run').setDefault(True) if mode==CTaskButtons.DEFAULTMODE: helpMenu = QtGui.QMenu(parent) self.button('help').setMenu(helpMenu) viewMenu = QtGui.QMenu(parent) self.button('view').setMenu(viewMenu) #nextMenu = QtGui.QMenu(parent) #self.button('next').setMenu(nextMenu) action = viewMenu.addAction('In CCP4mg') action = viewMenu.addAction('In Coot') if ALWAYS_SHOW_SERVER_BUTTON: runMenu = QtGui.QMenu(parent) self.button('run').setMenu(runMenu) action = runMenu.addAction('Now') action = runMenu.addAction('Remotely - test output') action = runMenu.addAction('Remotely - import result') def setMode(self,mode): self.mode = mode def button(self,name): ii = CTaskButtons.BUTTONS.index(name) return QtGui.QButtonGroup.button(self,ii) def setRunMode(self,editor=None,status='Pending'): if editor: self.button('run').setText('Save') else: if status == 'Interrupted': self.button('run').setText('Restart') else: self.button('run').setText('Run') def setEnabled(self,jobStatus): #print 'CTaskButtons.setEnabled',self.mode,jobStatus if jobStatus is None: #No job set - disable all buttons #for item in ['run','view','clone','next']: for item in ['run','view','clone']: b = self.button(item) if b is not None: b.setEnabled(False) else: if self.mode in ['task','input']: self.button('run').setEnabled(jobStatus in ['Pending','Interrupted']) #self.button('run').setEnabled(jobStatus in ['Pending']) else: self.button('run').setEnabled(False) if self.button('view') is not None: self.button('view').setEnabled(jobStatus in ['Finished','Interrupted','To delete']) self.button('clone').setEnabled(True) ''' helpFileList = self.parent().getHelpFile() if len(helpFileList) == 0: #self.button('help').menu().setVisible(False) self.button('help').setEnabled( False ) else: self.button('help').setEnabled( True ) self.button('help').menu().clear() for text,filePath in helpFileList: #print 'CTaskButtons.setEnabled',text,'*',filePath action = QtGui.QAction(text,self) action.setData(QtCore.QVariant(filePath)) self.button('help').menu().addAction(action) ''' def setNextMenu(self,jobId=None,taskName=None): #print 'CTaskFrame.setNextMenu' nextList = TASKMANAGER().whatNext(taskName,jobId) menu = self.button('next').menu() menu.clear() for item in nextList: action = menu.addAction(item[1]) if item[2] is not None: action.setData(QtCore.QVariant(item[0]+' '+item[2])) else: action.setData(QtCore.QVariant(item[0])) menu.addSeparator() action=menu.addAction(CTaskButtons.MOREINFO) action.setData(QtCore.QVariant(taskName)) class CJobStatusWidget(QtGui.QFrame): MARGIN = 1 def __init__(self,parent=None): QtGui.QFrame.__init__(self,parent=None) # Save the parent viewer - the Qt widget is reparented to a StackedWidget when # it is put in a tab self.openJob = COpenJob() self.commentId = None self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(CJobStatusWidget.MARGIN,CJobStatusWidget.MARGIN,CJobStatusWidget.MARGIN,CJobStatusWidget.MARGIN) self.layout().setSpacing(CJobStatusWidget.MARGIN) splitter = QtGui.QSplitter(self) splitter.setOrientation(QtCore.Qt.Vertical) splitter.setSizes([2,1]) self.layout().addWidget(splitter) # Annotation frame frame = QtGui.QFrame(self) layout = QtGui.QVBoxLayout() frame.setLayout(layout) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Rate this job:',self)) self.thunk = QtGui.QComboBox(self) self.thunk.setEditable(False) for item in CCP4DbApi.JOB_EVALUATION_TEXT: self.thunk.addItem(CCP4ProjectWidget.jobIcon(item),item) line.addWidget(self.thunk) line.addWidget(QtGui.QLabel('or change job title..',self)) line.addStretch(1) layout.addLayout(line) self.title = QtGui.QLineEdit(self) self.title.setMaxLength(255) layout.addWidget(self.title) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Add comment..',self)) line.addStretch(1) layout.addLayout(line) self.annotation = CCommentTextEdit(self) layout.addWidget(self.annotation) line = QtGui.QHBoxLayout() line.addWidget(QtGui.QLabel('Previous comments..',self)) line.addStretch(1) layout.addLayout(line) self.history = QtGui.QTextEdit(self) self.history.setReadOnly(True) layout.addWidget(self.history) splitter.addWidget(frame) self.connect(self.thunk,QtCore.SIGNAL('currentIndexChanged(int)'),self.saveThunk) #self.connect(self.annotation,QtCore.SIGNAL('textChanged()'),self.saveAnnotation) self.connect(self.title,QtCore.SIGNAL('editingFinished()'),self.saveTitle) self.connect(self.annotation,QtCore.SIGNAL('newLine'),self.saveAnnotation) def setJob(self,openJob): #print 'CJobStatusWidget.setJob',jobId self.thunk.blockSignals(True) self.title.blockSignals(True) self.history.blockSignals(True) self.annotation.blockSignals(True) #print 'CJobStatusWidget.setJob',jobId,jobInfo self.saveAnnotation() self.openJob= openJob self.commentId = None if self.openJob.jobId is None: self.thunk.setCurrentIndex(0) self.annotation.setPlainText('') self.title.setText('') self.history.setReadOnly(False) self.history.clear() self.history.setReadOnly(True) else: self.thunk.setCurrentIndex(CCP4DbApi.JOB_EVALUATION_TEXT.index(self.openJob.evaluation)) if self.openJob.jobtitle is not None: self.title.setText(self.openJob.jobtitle) else: self.title.setText('') self.annotation.setPlainText('') self.history.setReadOnly(False) self.history.clear() db = PROJECTSMANAGER().db() commentList = db.getComments(jobId=self.openJob.jobId) text = '' for cid,user,time,comment in commentList: if db.timeIsToday(time): self.commentId = cid self.annotation.setPlainText(comment) else: strTime = db.timeString(time) #print 'setting history',user,comment text = text + '
'+user+' on '+strTime+'
\n

'+comment+'

\n' text = text + '' self.history.setHtml(text) self.history.setReadOnly(True) self.thunk.blockSignals(False) self.title.blockSignals(False) self.history.blockSignals(False) self.annotation.blockSignals(False) self.connect(self.openJob,QtCore.SIGNAL('jobUpdated'),self.handleJobUpdated) #print 'CJobStatusWidget.setJob DONE' def saveThunk(self,indx): #print 'saveThunk',indx if self.openJob.jobId is None: #print 'calling saveThunk jobId None' return try: PROJECTSMANAGER().db().updateJob(jobId=self.openJob.jobId,key='evaluation',value=indx) except: pass def saveTitle(self): if self.openJob.jobId is None: return try: text = str(self.title.text()) PROJECTSMANAGER().db().updateJob(jobId=self.openJob.jobId,key='jobTitle',value=text) except: pass def saveAnnotation(self): if self.openJob.jobId is None: return try: text = str(self.annotation.toPlainText()) if len(text) > 0: #print 'saveAnnotation',text,type(text) if self.commentId is None: self.commentId = PROJECTSMANAGER().db().createComment(jobId=self.openJob.jobId,comment=text) else: PROJECTSMANAGER().db().updateComment(commentId=self.commentId,comment=text) except: pass #print 'saveAnnotation',self.commentId def handleJobUpdated(self,key,value): #print 'CJobStatusWidget.handleJobUpdated',key,value if key == 'evaluation': self.thunk.blockSignals(True) self.thunk.setCurrentIndex(CCP4DbApi.JOB_EVALUATION_TEXT.index(self.openJob.evaluation)) self.thunk.blockSignals(False) class CCommentTextEdit(QtGui.QTextEdit): def keyReleaseEvent(self,event): #print 'CCommentTextEdit.keyReleaseEvent' if event.key() == QtCore.Qt.Key_Return: self.emit(QtCore.SIGNAL('newLine')) event.accept() else: event.ignore() class CChooseTaskFrame(QtGui.QFrame): def __init__(self,parent): QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QVBoxLayout()) self.taskTree = CTaskTree(self) #self.taskTree.setHeaderLabel('New task') self.taskTree.setHeaderHidden(True) self.layout().addWidget(self.taskTree) self.loadTaskTree() buttonGroup = QtGui.QButtonGroup(self) buttonGroup.addButton(QtGui.QPushButton('New job',self),0) buttonGroup.addButton(QtGui.QPushButton('Cancel',self),1) layout0 = QtGui.QHBoxLayout() layout0.addStretch(1) for but in buttonGroup.buttons(): layout0.addWidget(but) layout0.addStretch(1) self.layout().addLayout(layout0) self.connect(self.taskTree,QtCore.SIGNAL('itemDoubleClicked(QTreeWidgetItem *, int)'),self.handleChooseTask) self.connect(buttonGroup.button(0),QtCore.SIGNAL('released()'),self.handleChooseTask0) self.connect(buttonGroup.button(1),QtCore.SIGNAL('released()'),functools.partial(self.window().showTaskChooser,False)) self.connect(WORKFLOWMANAGER(),QtCore.SIGNAL('listChanged'),self.loadTaskTree) self.connect(CUSTOMTASKMANAGER(),QtCore.SIGNAL('listChanged'),self.loadTaskTree) self.connect(PREFERENCES(),QtCore.SIGNAL('preferencesSaved'),self.loadTaskTree) def loadTaskTree(self): import CCP4StyleSheet compact = PREFERENCES().COMPACT_TASK_MENU self.taskTree.clear() for module,title,taskList in TASKMANAGER().taskTree(): moduleItem = QtGui.QTreeWidgetItem() moduleItem.setFlags(QtCore.Qt.ItemIsEnabled) widget = CTaskTreeModuleWidget(title,iconName=module) #print 'loadTaskTree module',module self.taskTree.addTopLevelItem(moduleItem) self.taskTree.setItemWidget(moduleItem,0,widget) for taskName in taskList: #item = CTaskWidgetItem(moduleItem,taskName,self.taskTree) item = QtGui.QTreeWidgetItem() item.setData(0,101,QtCore.QVariant(taskName)) #widget = QtGui.QLabel(TASKMANAGER().getTitle(taskName)) widget = CTaskTreeItemWidget(taskName,compact=compact) moduleItem.addChild(item) self.taskTree.setItemWidget(item,0,widget) #self.taskTree.expandAll() def handleChooseTask(self,item,column=None): try: taskName = str(item.data(0,101).toString()) except: return if len(taskName)==0: return #print 'handleChooseTask',taskName,type(taskName) self.emit(QtCore.SIGNAL('taskClicked'),taskName) def handleChooseTask0(self): self.handleChooseTask(item=self.taskTree.currentItem()) class CTaskTreeModuleWidget(QtGui.QFrame): def __init__(self,moduleName,iconName=None): QtGui.QFrame.__init__(self) self.setObjectName('taskTreeModule') self.setLayout(QtGui.QHBoxLayout()) MARGIN = 2 #was 1 self.layout().setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) self.layout().setSpacing(MARGIN) icon = getMenuIcon(self,iconName,24) icon.setObjectName('icon') self.layout().addWidget(icon) label = QtGui.QLabel(moduleName,self) label.setObjectName('title') self.layout().insertSpacing ( 1, 4 ) self.layout().addWidget(label,1,QtCore.Qt.AlignVCenter) self.layout().addStretch(5) class CTaskTreeItemWidget(QtGui.QFrame): def __init__(self,taskName,compact=False): MARGIN = 4 # was 2 QtGui.QFrame.__init__(self) self.setObjectName('taskTreeItem') layout = QtGui.QHBoxLayout() layout.setSpacing(MARGIN) layout.setContentsMargins(MARGIN,MARGIN,MARGIN,MARGIN) self.setLayout(layout) rank = TASKMANAGER().getTaskAttribute(taskName,'RANK') import os qticonsDir = os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons') if compact: size = 24 else: self.setFrameStyle(QtGui.QFrame.NoFrame) # was QFrame.StyledPanel size = 48 if rank == 1 : self.setObjectName ( 'taskpipe') else : self.setObjectName ( 'tasktool') icon = getMenuIcon(self,taskName,size) icon.setObjectName('icon') self.layout().addWidget ( icon ) self.layout().insertSpacing ( 1, 6 ) # this is new, provides space between icon and text label = QtGui.QLabel(TASKMANAGER().getTitle(taskName),self) if rank == 2: label.setObjectName('title2') else: label.setObjectName('title') if compact: self.layout().addWidget(label) else: vBox = QtGui.QVBoxLayout() self.layout().addLayout(vBox) vBox.addWidget(label,0,QtCore.Qt.AlignBottom) desc = TASKMANAGER().getTaskAttribute(taskName,'DESCRIPTION') label = QtGui.QLabel(str(desc)) if rank == 2: label.setObjectName('description2') else: label.setObjectName('description') vBox.addWidget(label,0,QtCore.Qt.AlignTop) self.layout().addStretch(5) #if rank == 2: self.layout().insertSpacing ( 0, 48 ) class CTaskTree(QtGui.QTreeWidget): def __init__(self,parent): QtGui.QTreeWidget.__init__(self,parent) self.setObjectName('taskTree') self.setDragEnabled(True) self.setDragDropMode(QtGui.QAbstractItemView.DropOnly) self.setAcceptDrops(True) def supportedDropActions(self): #print 'CTaskTree.supportedDropActions' return QtCore.Qt.CopyAction def dropMimeData(self,parent,index,data,action): #print 'CTaskTree.dropMimeData',parent,index,data,action return True def mimeTypes(self): typesList = QtCore.QStringList() for item in MIMETYPESHANDLER().mimeTypes.keys(): typesList.append(item) typesList.append('jobid') #print 'CTaskTree.mimeTypes',typesList return typesList def dragEnterEvent(self,event): #print 'dragEnterEvent', event.mimeData().hasFormat('FollowFromJob') if event.mimeData().hasFormat('FollowFromJob'): 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('jobid'): event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() def dropEvent(self,event): #print 'dropEvent',event.mimeData().hasFormat('FollowFromJob') targetItem = self.itemAt(event.pos().x(),event.pos().y()) if event.mimeData().hasFormat('FollowFromJob'): #print 'dropEvent targetProject',targetItem,targetProjectId taskName = str(targetItem.data(0,101).toString()) data = str(event.mimeData().data('FollowFromJob').data()) #print 'CTaskTree.dropEvent',data self.emit(QtCore.SIGNAL('taskDropped'),taskName,data) event.setDropAction(QtCore.Qt.CopyAction) event.accept() else: event.ignore() class CTaskWidgetItem(QtGui.QTreeWidgetItem): def __init__(self,parent,taskName): self.taskName = taskName self.title = TASKMANAGER().getTitle(taskName) QtGui.QTreeWidgetItem.__init__(self,parent) def flags(self): return QtCore.Qt.ItemIsSelectable|QtCore.Qt.ItemIsEnabled|QtCore.Qt.ItemIsDropEnabled def data(self,ic,role): if ic == 0: if role == QtCore.Qt.FontRole: italicFont = QtGui.QFont() italicFont.setItalic(True) return italicFont if role == QtCore.Qt.DisplayRole: return QtCore.QVariant(self.title) if role == QtCore.Qt.ToolTipRole: desc = TASKMANAGER().getTaskAttribute(self.taskName,'DESCRIPTION') #print 'CTaskWidgetItem.data',self.taskName,desc if desc is not None: return QtCore.QVariant(desc) else: return QtCore.QVariant(self.taskName) if role == 101: return QtCore.QVariant(self.taskName) return QtCore.QVariant() def FILEWATCHER(): if CFileSystemWatcher.insts is None: CFileSystemWatcher.insts = CFileSystemWatcher() return CFileSystemWatcher.insts class CFileSystemWatcher(QtCore.QFileSystemWatcher): # Sub-class so we keep track of jobId as well as path insts = None def __init__(self,parent=None): if parent is None: parent = QTAPPLICATION() QtCore.QFileSystemWatcher.__init__(self,parent) # Fix for fail on NFS (and perhaps elsewhere) to convert to using internal poller mechanism # https://bugreports.qt.io/browse/QTBUG-8351 if PREFERENCES().FILESYSTEMWATCHERPOLLER: self.setObjectName(QtCore.QLatin1String("_qt_autotest_force_engine_poller")) self.jobPaths = {} self.connect(self,QtCore.SIGNAL('fileChanged(const QString &)'),self.handleFileChanged) self.connect(self,QtCore.SIGNAL('directoryChanged(const QString &)'),self.handleDirectoryChanged) self.fileSizes = {} self.jobsByUpdateInterval = {} def handleFileChanged(self,path): # Small changes introduced to handle a not-uncommon case. Specifically, if a watched file is overwritten by doing # a pseudo-atomic overwrite (write to temporary file, and then rename over the top of existing file), then a # sad things happens: the rename causes the original file to be unlinked andhence removed from the # observed path list. import sys, os #print 'CFileSystemWatcher.handleFileChanged',path path = str(path) for jobId,pathList in self.jobPaths.items(): if path in pathList: # Further processing appropriate only if the path represents a valid (existing) file object if os.path.isfile(path): # Ignore change in files that makes them > 100 characters (arbitrary cutoff)smaller (since probably implies they # are in the course of being overwritten) newFileSize = os.stat(path).st_size if path not in self.fileSizes or (path in self.fileSizes and (self.fileSizes[path] < (newFileSize+100))): self.emit(QtCore.SIGNAL('jobFileChanged'),jobId,path) self.fileSizes[path] = newFileSize return def clear(self): self.removePaths(self.files()) self.jobPaths = {} def addJobPath(self,jobId,path,updateInterval=None): import os, sys # Beware the file we want to watch might not exist # so put a watch on the (hopefully existing) parent directory print 'FILEWATCHER.addJobPath',jobId,path,updateInterval if updateInterval is not None: if not self.jobsByUpdateInterval.has_key(jobId): self.jobsByUpdateInterval[jobId] = { 'path' : path , 'size' : 0 } return if not self.jobPaths.has_key(jobId): self.jobPaths[jobId] = [] if not path in self.jobPaths[jobId]: self.jobPaths[jobId].append(path) directoryOfPath, fileName = os.path.split(path) currentlyWatchedDirectories = [str(directory) for directory in self.directories()] if not directoryOfPath in currentlyWatchedDirectories and os.path.exists(directoryOfPath): self.addPath(directoryOfPath) if os.path.isfile(path): self.addPath(path) print 'CFileSystemWatcher Currently watching files:',[str(file) for file in self.files()] print 'CFileSystemWatcher Currently watching directories:',[str(directory) for directory in self.directories()] def handleDirectoryChanged(self, path): #Here in case of a directory event: go through the #files we wish to be watching and add them to watch list *iff* they exist and are #not currently being watched import os currentlyWatchedPaths = [str(file) for file in self.files()] for jobId, pathsOfJob in self.jobPaths.items(): for pathOfJob in pathsOfJob: directoryOfPath, fileName = os.path.split(pathOfJob) if directoryOfPath == str(path) and pathOfJob not in currentlyWatchedPaths and os.path.isfile(pathOfJob): self.addPath(pathOfJob) def removeJob(self,jobId): if self.jobPaths.has_key(jobId): self.removePaths(self.jobPaths[jobId]) del self.jobPaths[jobId] print 'FILEWATCHER.removeJob',jobId,self.jobsByUpdateInterval.has_key(jobId) if self.jobsByUpdateInterval.has_key(jobId): del self.jobsByUpdateInterval[jobId] def removePath(self,path): for jobId,pathList in self.jobPaths.items(): if path in pathList: self.jobPaths[jobId].remove(path) QtCore.QFileSystemWatcher.removePath(self,path) return def listJobs(self): print self.jobPaths def triggerJobsByUpdateInterval(self): for jobId,value in self.jobsByUpdateInterval.items(): if os.path.isfile(value['path']): newSize = os.stat(value['path']).st_size print 'FILEWATCHER',jobId,value,newSize if newSize > value['size']: value['size'] = newSize self.emit(QtCore.SIGNAL('jobFileChanged'),jobId,value['path']) class CReportView(QtGui.QStackedWidget): def __init__(self,parent=None): QtGui.QStackedWidget.__init__(self,parent=parent) import functools import CCP4WebView #self.setLayout(QtGui.QVBoxLayout()) #self.layout().setSpacing(0) #self.layout().setContentsMargins(1,1,1,1) self.setObjectName('reportView') self.openJob = COpenJob() self.openChildJob = None self.generator = None self._reportFile = None self.webFrame = QtGui.QFrame(self) self.webFrame.setLayout(QtGui.QVBoxLayout()) self.webFrame.layout().setSpacing(0) self.webFrame.layout().setContentsMargins(1,1,1,1) self.webFrame.setObjectName('reportFrame') self.linksStack = QtGui.QStackedWidget(self) self.linksFrame = [] labelList = [ [], [['Results','results'],['Input data','inputData'],['Output data','outputData'],['Show details','Show details'],['Job details','jobDetails']], [['Error output','errors'],['Terminal output','terminal'],['Diagnostic','diagnostic']] ] for ii in range(3): self.linksFrame.append(QtGui.QFrame(self)) self.linksFrame[ii].setObjectName('reportLinkFrame'+str(ii)) self.linksFrame[ii].setFrameShape(QtGui.QFrame.StyledPanel) self.linksFrame[ii].setMaximumHeight(25) self.linksFrame[ii].setLayout(QtGui.QHBoxLayout()) self.linksFrame[ii].layout().setSpacing(0) self.linksFrame[ii].layout().setContentsMargins(1,1,1,1) for label,link in labelList[ii]: but = QtGui.QPushButton(label,self) but.setFlat(True) self.linksFrame[ii].layout().addWidget(but) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.handleLinkButton,link)) self.linksStack.addWidget(self.linksFrame[ii]) self.webFrame.layout().addWidget(self.linksStack) self.webView= CCP4WebView.CWebView(self,blockLoading=True) self.connect(self.webView,QtCore.SIGNAL('loadFinished(bool)'),self.handleLoadFinished) self.webFrame.layout().addWidget(self.webView) self.webFrame.layout().setStretch(1,10.0) self.addWidget(self.webFrame) self.nextFrame = QtGui.QFrame(self) self.nextFrame.setObjectName('highlight') self.nextFrame.setLayout(QtGui.QGridLayout()) self.nextFrame.layout().setSpacing(0) self.nextFrame.layout().setContentsMargins(0,0,0,0) self.webFrame.layout().addWidget(self.nextFrame) self.textView = None self.connect(self.webView.page(),QtCore.SIGNAL("NavigationRequest"),self.handleNavigationRequest) self.connect(self.webView.page(),QtCore.SIGNAL("CustomMimeTypeRequested"),self.handleCustomMimeTypeRequested) self.connect(FILEWATCHER(),QtCore.SIGNAL("jobFileChanged"),self.handleFileChanged) self.connect(JOBCONTROLLER(),QtCore.SIGNAL('remoteJobUpdated'),self.handleFileChanged) self.connect(self.openJob,QtCore.SIGNAL('jobStatusUpdated'),self.handleJobStatusUpdated) self.connect(self.openJob,QtCore.SIGNAL('workflowJobStatusUpdated'),self.handleWorkflowJobStatusUpdated) def setReportLinks(self,labelList=[]): # Set the self.linksFrame[1] which is the option for finished reports import functools for widget in self.linksFrame[1].findChildren(QtGui.QPushButton): self.linksFrame[1].layout().removeWidget(widget) widget.deleteLater() for label,brief in labelList: if brief is not None: but = QtGui.QPushButton(brief,self) but.setToolTip(label) else: but = QtGui.QPushButton(label,self) but.setFlat(True) self.linksFrame[1].layout().addWidget(but) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.handleLinkButton,label)) def clear(self): self.webView.clear() def setLinkButtons(self): try: linksObj= self.webView.report.getDataObject(id='links') except: pass #else: # print 'CReportView.setLinks',linksObj def setNextButtons(self,taskName=None,jobId=None,status=None,interruptLabel=None): #print 'CReportView.setNextButtons',taskName,jobId,status,interruptLabel buts = self.nextFrame.findChildren(QtGui.QPushButton) for but in buts: but.hide() but.deleteLater() if status is not None: if status == 'Finished' or status == 'Unsatisfactory': nextList = TASKMANAGER().whatNext(taskName,jobId) ii = 0 for link,label,defFile in nextList: but = QtGui.QPushButton(label,self) self.nextFrame.layout().addWidget(but,ii/3,ii%3) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('nextTask'),link,defFile)) ii = ii + 1 elif status == 'Failed': but = QtGui.QPushButton('Clone job to rerun',self) self.nextFrame.layout().addWidget(but,0,0) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.emit,QtCore.SIGNAL('nextTask'),'clone_rerun',None)) elif status in [ 'Running','Running remotely'] and taskName is not None: interruptLabel = TASKMANAGER().getTaskAttribute(taskName=taskName,attribute='INTERRUPTLABEL',default=None,script=True) #print 'setNextButtons interruptLabel',interruptLabel if interruptLabel is not None: but = QtGui.QPushButton(interruptLabel,self) self.nextFrame.layout().addWidget(but,0,0) self.connect(but,QtCore.SIGNAL('released()'),functools.partial(self.handleInterruptButton,jobId)) def handleInterruptButton(self,jobId): self.setNextButtons(jobId=jobId) self.emit(QtCore.SIGNAL('interrupt'),jobId) def handleLinkButton(self,link): #print 'handleLinkButton',link url = self.webView.url() url.setFragment(link) # This should not be necessary.. #self.webView.setTarget(link) self.webView.load(url) def handleNavigationRequest(self,url): # Intercept navigation requests and open links in another window #print 'CReportView.handleNavigationRequest',str(url.toLocalFile()),str(url.fragment()) path = str(url.path()) if str(url.toLocalFile()) == str(CCP4Utils.getCCP4I2Dir())+"/docs/": # probably an internal link so show in the report window newUrl = QtCore.QUrl.fromLocalFile(self._reportFile) newUrl.setFragment(url.fragment()) self.webView.load(newUrl) return if len(path)>11 and path[-11:] == 'report.html': jobId = self.webView.report.getLinkId(path) if jobId is not None: openJob = COpenJob(jobId=jobId) self.connect(self.openJob,QtCore.SIGNAL('jobStatusUpdated'),self.handleJobStatusUpdated) self.connect(self.openJob,QtCore.SIGNAL('workflowJobStatusUpdated'),self.handleWorkflowJobStatusUpdated) if not os.path.exists(path): # Requesting report file that is not yet created #print 'CReportView.handleNavigationRequest jobId',self.webView.report.linkIds,jobId if self.generator is None: import CCP4ReportGenerator self.generator = CCP4ReportGenerator.CReportGenerator(jobId=openJob.jobId,jobStatus=openJob.status,jobNumber=openJob.jobNumber) self.connect(self.generator,QtCore.SIGNAL('FinishedPictures'),self.handleMgFinished) #if DEVELOPER(): try: reportFile, newPageOrNewData = self.generator.makeReportFile() except CException as e: if reportErr and e.maxSeverity()>SEVERITY_WARNING: e.warningMessage(windowTitle=self.parent().windowTitle(),message='Failed creating job report',parent=self) except Exception as e: if reportErr: QtGui.QMessageBox.warning(self,self.parent().windowTitle(),'Unknown error creating report file for job number '+str(openJob.jobNumber)) #print 'CReportView.handleNavigationRequest reportFile',reportFile if os.path.exists(reportFile): err = self.generator.mergeIntoParent(parentFile=self._reportFile) if err.maxSeverity()<=SEVERITY_WARNING: self.webView.reload() return # See if the web browser can do something WEBBROWSER().loadPage(url=url,newTab=True) WEBBROWSER().raise_() def handleCustomMimeTypeRequested(self,url): #print 'CReportView.handleCustomMimeTypeRequest',url WEBBROWSER().CustomMimeTypeRequested(url) def showOutput(self,openJob=None,reportFile=None,reportErr=True,redo=False,doReload=False): import CCP4WebView,CCP4TextViewer,CCP4ReportGenerator #print 'showOutput',openJob,reportFile,redo #import traceback #traceback.print_stack(limit=8) # If not report file then try making it from an outputXmlFile # Otherwise use a log or diagnostic file if openJob is None: openJob = self.openJob linkList = None newPageOrNewData = "NEWPAGE" # Do we have a log file ot fall back to? logFile = PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='LOG') if not os.path.exists(logFile): logFile = logFile + '.html' if not os.path.exists(logFile): logFile = None #print 'showOutput logFile',logFile,'reportFile',reportFile if reportFile is None: hasReportDef = TASKMANAGER().hasReportDefinition(openJob.taskname, openJob.status) #print 'showOutput hasReportDefinition',openJob.taskname, openJob.status,hasReportDef if openJob.isWorkflow: childOpenJob = openJob.childOpenJob(-1) #print 'showOutput workflow child',childOpenJob if childOpenJob is not None: openJob = childOpenJob #print 'showOutput',openJob.status,CCP4DbApi.FINISHED_JOB_STATUS,openJob.status in CCP4DbApi.FINISHED_JOB_STATUS if openJob.status == 'Failed': self.generator = CCP4ReportGenerator.CReportGenerator(jobId=openJob.jobId,jobStatus=openJob.status,jobNumber=openJob.jobNumber) reportFile = self.generator.makeFailedReportFile(redo=redo) elif (openJob.status in CCP4DbApi.FINISHED_JOB_STATUS or (openJob.status in ['Running','Running remotely'] ) ) and hasReportDef: self.generator = CCP4ReportGenerator.CReportGenerator(jobId=openJob.jobId,jobStatus=openJob.status,jobNumber=openJob.jobNumber) self.connect(self.generator,QtCore.SIGNAL('FinishedPictures'),self.handleMgFinished) # Comment out to ensure errors are trapped if DEVELOPER(): reportFile, newPageOrNewData = self.generator.makeReportFile(redo=redo,doReload=doReload,useGeneric=(logFile is None)) else: try: #print 'showOutput makeReportFile' if openJob.status == 'Failed': reportFile = self.generator.makeFailedReportFile(redo=redo) else: reportFile, newPageOrNewData = self.generator.makeReportFile(redo=redo,doReload=doReload,useGeneric=(logFile is None)) except CException as e: # Dont report lack for report definition file if reportErr and e.maxSeverity()>SEVERITY_WARNING and e.code != 3: e.warningMessage(windowTitle=self.parent().windowTitle(),message='Failed creating job report',parent=self) reportFile = None except Exception as e: if reportErr: QtGui.QMessageBox.warning(self,self.parent().windowTitle(),'Unknown error creating report file for job number '+str(openJob.jobNumber)) reportFile = None if reportFile is None: reportFile = PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='REPORT') if not os.path.exists(reportFile): if logFile is not None: reportFile = logFile else: reportFile = PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='DIAGNOSTIC') if not os.path.exists(reportFile): reportFile = None if reportFile is None: reportFile = os.path.join(CCP4Utils.getCCP4I2Dir(),'docs','report_files','blank.html') #print 'CProjectViewer.showOutput reportFile',openJob.jobId,openJob.jobnumber,openJob.taskname,reportFile reloadedData = False if newPageOrNewData == 'NEWDATA' and reportFile is not None: #Here if we believe there is an existing report in the webview and we simply want new data #to be loaded into it loadedUrl=self.webView.url() reportUrl = QtCore.QUrl.fromLocalFile(reportFile) if reportUrl == loadedUrl: nElementsUpdated, validConversion = self.webView.reloadReportData() #print nElementsUpdated, validConversion if validConversion and nElementsUpdated>0: reloadedData = True if not reloadedData: if reportFile is None or os.path.splitext(reportFile)[1] in ['.html','.htm']: self.setCurrentIndex(0) if reportFile is None: self.webView.load( QtCore.QUrl()) self.linksStack.setCurrentIndex(0) else: url = QtCore.QUrl.fromLocalFile(reportFile) self.webView.load(url) if openJob.status == 'Failed': self.linksStack.setCurrentIndex(2) else: if linkList is not None: self.setReportLinks(linkList) self.linksStack.setCurrentIndex(1) else: # Xml file - treat as text self.linksStack.setCurrentIndex(0) if self.textView is None: self.textView = CCP4TextViewer.CTextViewer(self) self.addWidget(self.textView) self.setCurrentIndex(1) try: self.textView.open( reportFile) self.textView.setFont(style='fixed_width') except: pass #print 'CReportView.showOutput',reportFile self._reportFile = reportFile if openJob.jobId != self.openJob.jobId: FILEWATCHER().removeJob(self.openJob.jobId) self.addWatchFile(openJob) self.openJob = openJob self.connect(self.openJob,QtCore.SIGNAL('jobStatusUpdated'),self.handleJobStatusUpdated) self.connect(self.openJob,QtCore.SIGNAL('workflowJobStatusUpdated'),self.handleWorkflowJobStatusUpdated) self.emit(QtCore.SIGNAL('reportAvailable'), self.openJob.jobId, (reportFile is not None)) return reportFile #print 'CReportView.showOutput DONE' def handleLoadFinished(self,ok): #print 'CReportView.handleLoadFinished',ok #print self.webView.report.topFolds if not ok: return self.setReportLinks(self.webView.report.topFolds) def handleMgFinished(self,jobId): #print 'CProjectViewer.handleMgFinished',jobId,self.openJob.jobId #if jobId == self.openJob.jobId: self.webView.reload() if jobId == self.openJob.jobId: self.webView.attemptImageLoad() def handleFileChanged(self,jobId,outputXmlFile): #print 'CReportView.handleFileChanged',jobId,outputXmlFile,self.openJob.jobId,self.openJob.status if jobId == self.openJob.jobId: self.showOutput(redo=True) elif self.openJob.childjobs is not None and jobId in self.openJob.childjobs: self.showOutput(openJob= self.openChildJob,redo=True) def handleJobStatusUpdated(self,status): #print 'CReportView.handleJobStatusUpdated',status,'openJob',self.openJob.jobId,self.openJob.taskName if status in ['Running']: self.addWatchFile(self.openJob) elif status in ['Finished','Failed']: FILEWATCHER().removeJob(self.openJob.jobId) self.showOutput(redo=True) #print 'CReportView.handleJobStatusChanged watched files',FILEWATCHER().listJobs() def handleWorkflowJobStatusUpdated(self,argList): #argList = jobId,childJobId,taskName,status #print 'handleWorkflowJobStatusUpdated',argList if not argList[0] == self.openJob.jobId: return # NEED TO REMOVE FILEWATCH on subjobs too! FILEWATCHER().removeJob(self.openJob.jobId) if len(self.openJob.childjobs)>1: FILEWATCHER().removeJob(self.openJob.childjobs[-2]) self.openChildJob = COpenJob(jobId=argList[1]) self.addWatchFile(self.openChildJob) try: self.showOutput(openJob=self.openChildJob,redo=True) except: pass def addWatchFile(self,openJob): if not openJob.status in ['Running','Running remotely']: return #print 'addWatchFile',openJob.taskName # If there is specification for a 'running' report then watch the task program output runningXrt = TASKMANAGER().searchXrtFile(openJob.taskName,jobStatus='Running') if runningXrt is None: runningXrt = TASKMANAGER().getReportClass(openJob.taskName,jobStatus='Running') #print 'runTask runningXrt',openJob.taskName,runningXrt if runningXrt is not None: updateInterval = TASKMANAGER().getReportAttribute(openJob.taskName,'UPDATE_INTERVAL') watchFile = TASKMANAGER().getReportAttribute(openJob.taskName,'WATCHED_FILE') if watchFile is None: outputXmlFile = PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='PROGRAMXML') else: outputXmlFile = os.path.normpath(os.path.join(PROJECTSMANAGER().makeFileName(jobId=openJob.jobId,mode='ROOT'),watchFile)) #print 'addWatchFile',openJob.taskName,outputXmlFile FILEWATCHER().addJobPath(jobId=openJob.jobId,path=outputXmlFile,updateInterval=updateInterval) class CTaskInputFrame(QtGui.QFrame): lastOpenFolder = {} ERROR_CODES = { 101 : { 'description' : 'Error attempting to run internal plugin' } } def __init__(self,parent): QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(0,CProjectViewer.MARGIN,0,CProjectViewer.MARGIN) self.layout().setSpacing(CProjectViewer.MARGIN) self.taskWidget = None self.connect(PREFERENCES().TASK_WINDOW_LAYOUT,QtCore.SIGNAL('dataChanged'),self.redraw) self.connect(JOBCONTROLLER(),QtCore.SIGNAL('serverJobFailed'),self.handleServerJobFail) def createTaskWidget(self,taskName,projectId=None,jobId=None,container=None,taskEditable=True,followJobId=None,excludeInputData=False): # Create task widget taskWidgetClass = TASKMANAGER().getTaskWidgetClass(taskName) #print 'CTaskInput.createTaskWidget',taskName,taskWidgetClass,'followJobId',followJobId,'container',container,'taskEditable',taskEditable #if not taskEditable: # import traceback # traceback.print_stack(limit=5) if taskWidgetClass is not None: if DEVELOPER(): #if 1: taskWidget = taskWidgetClass(self) else: try: taskWidget = taskWidgetClass(self) except CException as e: e.warningMessage('Error opening task window: '+str(taskName),parent=self) raise e except Exception as e: mess = QtGui.QMessageBox.warning(self,'Error opening task window: '+str(taskName),str(e)) raise CException(self.__class__,101,taskName) taskWidget.folderAttributes.setAttribute(attribute='editable',folderFunction='all',value=taskEditable) taskWidget.setContainer(container) else: # Try for an auto-generated gui? import CCP4ContainerView taskWidget = CCP4ContainerView.CContainerView(self,container=container) taskWidget.folderAttributes.setAttribute(attribute='editable',folderFunction='all',value=taskEditable) # raise CException(self.__class__,103,taskName) taskWidget.setProjectId(projectId) taskWidget.setJobId(jobId) taskWidget.setDefaultParameters() taskWidget.excludeInputData = excludeInputData e = taskWidget.draw() #print 'createTaskWidget taskWidget HKLIN',repr(taskWidget.getContainer()),taskWidget.getContainer().inputData.HKLIN if len(e)>0: print 'drawTaskWidget errors',e.report() if followJobId is not None: taskWidget.setFollowJobId(followJobId) taskWidget.setDefaultFiles() if self.taskWidget is not None: CTaskInputFrame.lastOpenFolder[self.taskWidget.jobId()] = self.taskWidget.visibleFolder() self.closeTaskWidget() # Stick new widget in window self.taskWidget = taskWidget #print 'createTaskWidget',taskWidget if CTaskInputFrame.lastOpenFolder.get(jobId,None) is not None: self.taskWidget.setVisibleFolder(CTaskInputFrame.lastOpenFolder[jobId]) self.layout().insertWidget(1,self.taskWidget) self.taskWidget.show() invalidList = self.taskWidget.isValid() return self.taskWidget def closeTaskWidget(self): if self.taskWidget is not None: # Close any exisiting task widget # Probably should be saving a def file self.taskWidget.close() self.taskWidget.deleteLater() self.taskWidget = None def runTask(self,runMode): # Update control file path in db and set status to queue the job #print 'CTaskInputFrame.runTask',runMode if isinstance(runMode,QtGui.QAction): runMode = runMode.text().__str__() if self.taskWidget is None: return self.taskWidget.updateModelFromView() # The method to fix any issues in user input rv = self.taskWidget.fix() if len(rv)>0: rv.warningMessage(parent=self,windowTitle='Running task',message='Error in input data') return # Check validity invalidList = self.taskWidget.isValid() taskErrors = self.taskWidget.taskValidity() #print 'ProjectViewer.runtask invalidList',invalidList if len(invalidList)>0 or len(taskErrors)>0: text = '' if len(invalidList)>0: text = text + 'Can not run task - missing or invalid data\nInvalid items are highlighted on the interface\n' for item in invalidList: try: itemName = item.objectPath() except: itemName = 'Unknown' if isinstance(item,str): text = text + item + '\n' else: for xtra in ['label','toolTip']: label = item.qualifiers(xtra) if label is not None and label is not NotImplemented: itemName = itemName +': '+label text = text + itemName + '\n' print 'Invalid data value',itemName,item if len(taskErrors)>0: text = text = taskErrors.report(user=True,ifStack=False) if not hasattr(self,'messageBox'): self.messageBox = QtGui.QMessageBox(self) self.messageBox.setWindowTitle(str(self.taskWidget.title())) but = self.messageBox.addButton('Close',QtGui.QMessageBox.RejectRole) self.connect(but,QtCore.SIGNAL('clicked()'),self.messageBox.close) self.messageBox.setDefaultButton(but) self.messageDetailsButton = self.messageBox.addButton('Details',QtGui.QMessageBox.AcceptRole) self.connect(self.messageDetailsButton,QtCore.SIGNAL('clicked()'),functools.partial(self.showInvalidDetails,invalidList)) else: self.messageDetailsButton.show() self.messageBox.setText(text) self.messageBox.exec_() return jobId = self.taskWidget.jobId() projectId=self.taskWidget.projectId() taskName = self.taskWidget.taskName() container = self.taskWidget.getContainer() # Check if the task is blocked from local running print 'blockLocal',taskName, TASKMANAGER().getTaskAttribute(taskName,'blockLocal') if runMode.count('run_remote')==0 and TASKMANAGER().getTaskAttribute(taskName,'blockLocal'): title = TASKMANAGER().getTitle(taskName) QtGui.QMessageBox.warning(self,"Running "+title, "The task "+title+"\nand other large jobs should not be run on this computer.\nPlease use 'Run on server' to run on a bigger computer." ) return # Remove unset items from lists #print 'CTaskInputFrame.runTask calling removeUnsetListItems' container.removeUnsetListItems() ifImportFile,errors = PROJECTSMANAGER().importFiles(jobId=jobId,container=container) #print 'CTaskInputFrame.runTask',ifImportFile,errors rv = self.makeJobInputFile() #print 'CTaskInputFrame.runTask makeJobInputFile',rv if not rv: QtGui.QMessageBox.warning(self,str(self.taskWidget.title()),'Job failed writing input parameters file') return subJobWidgets = getattr( self.taskWidget,'subJobTaskWidgets',{}) for jobName,taskWidget in subJobWidgets.items(): taskWidget.saveToXml() preceedingjobId = self.taskWidget.getContainer().guiAdmin.followFrom #print 'CTaskInputFrame.runTask setting preceedingjobId',preceedingjobId,type(preceedingjobId) PROJECTSMANAGER().db().updateJob(jobId=jobId,key='preceedingjobid',value=preceedingjobId.pyType()) #Record input files in database import CCP4DbApi PROJECTSMANAGER().db().gleanJobFiles(jobId=jobId,container=container,projectId=projectId,roleList=[CCP4DbApi.FILE_ROLE_IN]) #print 'runTask isInternalPlugin',taskName,TASKMANAGER().isInternalPlugin(taskName) if self.taskWidget.isEditor(): PROJECTSMANAGER().updateJobStatus(jobId=jobId,status=CCP4DbApi.JOB_STATUS_FINISHED) else: # Delete a pre-existing report - assume we are restarting job try: os.remove(PROJECTSMANAGER().makeFileName(jobId=jobId,mode='REPORT')) except: pass if runMode.count('run_remote')>0: self.runRemotely(jobId,projectId) return elif TASKMANAGER().isInternalPlugin(taskName): try: self.runInternalTask() except: err = CException(self.__class__,101,taskName) err.warningMessage('Running internaltask','Failed running task',parent=self) else: PROJECTSMANAGER().updateJobStatus(jobId=jobId,status=CCP4DbApi.JOB_STATUS_QUEUED) self.redrawTaskWidget() def redrawTaskWidget(self,editable=False): # Redraw task gui as non-editable import CCP4TaskWidget container = self.taskWidget.container taskName = self.taskWidget.taskName() jobId = self.taskWidget.jobId() projectId=self.taskWidget.projectId() #print 'runTask redrawing task widget',jobId,taskName if isinstance(self.taskWidget,CCP4TaskWidget.CTaskWidget) and PREFERENCES().TASK_WINDOW_LAYOUT == 'FOLDER': scrollDisp = self.taskWidget.getScrollDisplacement() folderOpenStatus = self.taskWidget.widget.getFolderOpenStatus() #print 'runTask folderOpenStatus',folderOpenStatus taskWidget = self.createTaskWidget(taskName,container=container,taskEditable=editable,jobId=jobId,projectId=projectId) taskWidget.widget.setFolderOpenStatus(folderOpenStatus) taskWidget.setScrollDisplacement(scrollDisp) else: taskWidget = self.createTaskWidget(taskName,container=container,taskEditable=editable,jobId=jobId,projectId=projectId) def runInternalTask(self): jobId = self.taskWidget.jobId() projectId=self.taskWidget.projectId() taskName = self.taskWidget.taskName() cls = TASKMANAGER().getPluginScriptClass(taskName) if cls is not None: db = PROJECTSMANAGER().db() db.updateJobStatus(jobId=jobId,status='Running') jobInfo = db.getJobInfo(jobId=jobId,mode=['projectid','projectname','jobnumber']) workDirectory = os.path.join(db.getProjectDirectory(projectId=jobInfo['projectid']),'CCP4_JOBS','job_'+str(jobInfo['jobnumber']) ) if not os.path.exists(workDirectory): os.mkdir(workDirectory) name = str(jobInfo['projectname'])+'_'+str(jobInfo['jobnumber']) pluginObj = cls(parent=self,name=name,jobId=jobId,projectId=projectId) pluginObj.container.header.projectId = projectId pluginObj.container.header.jobId = jobId comFile = PROJECTSMANAGER().makeFileName(jobId=jobId,mode='JOB_INPUT') pluginObj.container.loadDataFromXml(comFile) import CCP4PluginScript dbHandler = CCP4PluginScript.CDatabaseHandler(projectId= jobInfo['projectid'],jobNumber=jobInfo['jobnumber'], projectName=jobInfo['projectname']) dbHandler.openDb() pluginObj.setDbData(handler=dbHandler,projectId= jobInfo['projectid'],jobNumber=jobInfo['jobnumber'], projectName=jobInfo['projectname'],jobId=jobId) pluginObj.process() return True else: return False def showInvalidDetails(self,invalidList): #print 'showInvalidDetails',invalidList text = 'Can not run task - missing or invalid data\nInvalid items are highlighted on the interface\n' for modelObj in invalidList: text = text + str(modelObj)+'\n' if modelObj is not None: text = text + modelObj.validity(modelObj.get()).report(ifStack=False) + '\n' self.messageDetailsButton.hide() self.messageBox.setText(text) self.messageBox.show() def makeJobInputFile(self): if self.taskWidget is None: return False #print 'CTaskInputFrame.makeJobInputFile',self.taskWidget,self.taskWidget._jobId #Beware -- this is trying to save status of previous task widget which may have #been deleted try: status = PROJECTSMANAGER().db().getJobInfo(jobId=self.taskWidget._jobId,mode=['status']) except: #print 'Error retrieving job info from db for job (CTaskInputFrame.makeJobInputFile)' print 'makeJobInputFile NOT saving input_params.xml file - db query fail' return False if status not in ['Pending','Interrupted']: # Ensure do not overwrite a job that is already started print 'makeJobInputFile NOT saving input_params.xml file' return False self.taskWidget.saveToXml() return True def makeJobBall(self,jobId,projectId,mechanism='ssh_shared'): import time,os,functools,re jobNumber = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode=['jobnumber']) projectInfo = PROJECTSMANAGER().db().getProjectInfo(projectId=projectId) #dbxml = JOBCONTROLLER().getServerParam(jobId,'dbXml') jobDir = PROJECTSMANAGER().db().jobDirectory(jobId=jobId) if mechanism not in ['ssh_shared','test','qsub_local','qsub_shared']: dbxml = os.path.join( projectInfo['projectdirectory'],'CCP4_TMP','DATABASE'+str(int(time.time()))+'.db.xml') else: dbxml = os.path.join( jobDir, 'DATABASE.db.xml' ) JOBCONTROLLER().setServerParam(jobId,'dbXml',dbxml) #print 'makeJobBall',mechanism,dbxml inputFilesList,inputFileIdList,fromJobIdList,errReport = PROJECTSMANAGER().getJobInputFiles(projectDir=projectInfo['projectdirectory'],jobIdList=[jobId],jobNumberList=[jobNumber]) print 'runRemotely inputFilesList',inputFilesList,'fromJobIdList',fromJobIdList print 'runRemotely errReport',errReport.report() jobNumberList,errReport = PROJECTSMANAGER().db().exportProjectXml(projectId,fileName=dbxml,jobList=[jobId],inputFileList=inputFileIdList,inputFileFromJobList=fromJobIdList) if errReport.maxSeverity()>SEVERITY_WARNING: errReport.warningMessage(title,'Error creating XML database file',parent=self) return False if mechanism in ['ssh_shared','qsub_local','qsub_shared']: self.runRemotely1(jobId,projectId) else: import CCP4Export tarball = os.path.join( projectInfo['projectdirectory'],'CCP4_TMP','job_'+jobNumber+'_setup.ccp4db.zip') if os.path.exists(tarball): os.remove(tarball) self.exportThread = CCP4Export.ExportProjectThread(self,projectDir=projectInfo['projectdirectory'],dbxml=dbxml,target=tarball,jobList=[jobNumber],inputFilesList=inputFilesList,directoriesList=[]) self.exportThread.jobsWithInputFiles = [jobNumber] self.connect(self.exportThread,QtCore.SIGNAL('finished()'),functools.partial(self.runRemotely1,jobId,projectId)) print 'makeJobBall starting',jobId,projectId,tarball self.exportThread.start() def runRemotely(self,jobId,projectId,message=None): dialog = JOBCONTROLLERGUI() jobInfo = PROJECTSMANAGER().db().getJobInfo(jobId,['jobnumber','taskname']) dialog.setWindowTitle('Run '+jobInfo['jobnumber']+ ' ' + TASKMANAGER().getTitle(jobInfo['taskname'])) dialog.setInfo(message) rv = dialog.exec_() print 'runRemotely',rv,dialog.valid(),dialog.get('mechanism') if rv == QtGui.QDialog.Accepted and dialog.valid(): JOBCONTROLLER().createServerParams(jobId,dialog.getParams()) print 'project viewer runRemotely runningReport',self.taskWidget.taskName(),TASKMANAGER().getReportClass(self.taskWidget.taskName(),jobStatus='Running') JOBCONTROLLER().setServerParam(jobId,'runningReport', (TASKMANAGER().getReportClass(self.taskWidget.taskName(),jobStatus='Running') is not None) ) self.makeJobBall(jobId,projectId,dialog.get('mechanism')) def runRemotely1(self,jobId,projectId): print 'runRemotely1',jobId,projectId PROJECTSMANAGER().updateJobStatus(jobId=jobId,status=CCP4DbApi.JOB_STATUS_QUEUED) self.redrawTaskWidget() def handleServerJobFail(self,jobId,projectId,exception): print 'handleServerJobFail',jobId,projectId,exception.report(user=True,ifStack=False) if projectId != self.taskWidget.projectId(): return if jobId == self.taskWidget.jobId(): self.redrawTaskWidget(editable=True) message = 'Failed to start remote job\n'+exception.report(mode=2,user=True,ifStack=False) if exception[0]['code'] == 331: message = message + '\nPlease check that work directory exists on remote machine' self.runRemotely(jobId,self.taskWidget.projectId(),message) def clear(self): if self.taskWidget is None: return self.taskWidget.close() self.taskWidget.deleteLater() self.taskWidget = None def redraw(self): if self.taskWidget is None: return taskName = self.taskWidget.taskName() jobId = self.taskWidget.jobId() projectId = self.taskWidget.projectId() container= self.taskWidget.getContainer() taskEditable = self.taskWidget.folderAttributes.attribute('editable') #print 'CProjectViewer.redrawTaskWidget',jobId # ? followJobId copied in container ? taskWidget = self.createTaskWidget(taskName,projectId=projectId,jobId=jobId,container=container,taskEditable=taskEditable) def parentProjectViewer(self): p = self.parent() while not isinstance(p,CProjectViewer): p = p.parent() return p class CTaskTitleBarLayout(QtGui.QHBoxLayout): def minimumSize(self): return QtCore.QSize(CCP4TaskWidget.WIDTH,25) def sizeHint(self): return self.minimumSize() class CTaskTitleBar(QtGui.QFrame): MARGIN = 2 def __init__(self,parent): QtGui.QFrame.__init__(self,parent) self.jobId= None self.setLayout(CTaskTitleBarLayout()) self.layout().setContentsMargins(0,0,0,0) self.layout().setSpacing(0) self.title = QtGui.QLabel(self) self.title.setObjectName('jobTitle') self.layout().addWidget(self.title) self.status = QtGui.QLabel(self) self.status.setObjectName('jobStatus') self.layout().addWidget(self.status) self.layout().setStretchFactor(self.title,3.0) self.layout().setStretchFactor(self.status,3.0) movie = QtGui.QMovie(os.path.normpath(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','running_1.gif'))) self.icon = QtGui.QLabel(self) movie.setScaledSize(QtCore.QSize(24,24)) self.icon.setMovie(movie) movie.start() self.icon.setObjectName('jobStatus') self.layout().addWidget(self.icon) def setOpenJob(self,openJob): #print 'CTaskTitleBar.setOpenJob',openJob.title self.title.setText(openJob.title) self.jobId = openJob.jobId self.setStatusBar({'jobId' : openJob.jobId, 'status' : openJob.info['status'] } ) def setStatusBar(self,info={}): #print 'CTaskTitleBar.setStatusBar',info if info.get('jobId') != self.jobId: return status = info.get('status',0) if isinstance(status,int): status = CCP4DbApi.JOB_STATUS_TEXT[status] self.status.setText('The job is '+status) if status in ['Running','Running remotely']: self.icon.show() else: #print 'CTaskTitleBar.setStatusBar hide' self.icon.hide() class CTaskFrame(QtGui.QFrame): INPUT_TAB = 0 OUTPUT_TAB = 1 COMMENT_TAB =2 MARGIN = 0 ERROR_CODES = { 100 : { 'description' : 'Unknown error drawing task widget' } } def __init__(self,parent,projectId=None): QtGui.QFrame.__init__(self,parent) self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(CTaskFrame.MARGIN,CTaskFrame.MARGIN,CTaskFrame.MARGIN,CTaskFrame.MARGIN) self.layout().setSpacing(CTaskFrame.MARGIN) self.openJob = COpenJob(projectId=projectId) self.titleBar = CTaskTitleBar(self) self.layout().addWidget(self.titleBar) self.tab = QtGui.QTabWidget(self) self.inputFrame = CTaskInputFrame(self) self.tab.addTab(self.inputFrame,'Input') self.outputFrame = CReportView(self) self.tab.addTab(self.outputFrame,'Results') self.statusFrame = CJobStatusWidget(self) self.tab.addTab(self.statusFrame,'Comments') self.setTaskTab('input') self.layout().addWidget(self.tab) bottomLayout = QtGui.QHBoxLayout() bottomLayout.setContentsMargins(0,0,0,0) bottomLayout.setSpacing(0) self.layout().addLayout(bottomLayout) self.buttons = CTaskButtons(self,parentLayout=bottomLayout) bottomLayout.addStretch() self.connect(self.buttons.button('run'),QtCore.SIGNAL('released()'),functools.partial(self.inputFrame.runTask,'Now')) self.connect(self.buttons.button('run'),QtCore.SIGNAL('released()'),functools.partial(self.buttons.button('run').setDefault,False)) if ALWAYS_SHOW_SERVER_BUTTON: self.connect(self.buttons.button('run').menu(),QtCore.SIGNAL('triggered(QAction*)'),self.inputFrame.runTask) self.connect(self.buttons.button('view').menu(),QtCore.SIGNAL('triggered(QAction*)'),self.handleViewTask) self.connect(self.buttons.button('task_menu'),QtCore.SIGNAL('released()'),functools.partial(self.window().showTaskChooser,True)) self.connect(self.outputFrame,QtCore.SIGNAL('reportAvailable'),self.handleReportAvailable) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobFinished'),self.handleJobFinished) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobStarted'),self.handleJobStarted) def saveStatus(self): self.inputFrame.makeJobInputFile() import CCP4DbUtils #print 'saveStatus',self.openJob if self.openJob.jobId is not None: try: CCP4DbUtils.makeJobBackup(jobId=self.openJob.jobId,projectName=self.openJob.projectName) except Exception as e: #print 'Failed to backup job details for job:'+str(self.openJob.jobId)+str(e) pass def setTaskTab(self,mode=None): if mode is None: return mode = mode.lower() if mode == 'status': self.tab.setCurrentIndex(self.COMMENT_TAB) elif mode == 'input': self.tab.setCurrentIndex(self.INPUT_TAB) elif mode == 'output': self.tab.setCurrentIndex(self.OUTPUT_TAB) def handleJobStarted(self,args): #print 'CTaskFrame.handleJobStarted',args,'openJob',self.openJob.jobId if args.get('jobId','') != self.openJob.jobId and args.get('parentJobId','') != self.openJob.jobId : return self.outputFrame.setNextButtons(args.get('taskName',None), args['jobId'],status='Running') def handleJobFinished(self,args): jobId = args.get('jobId','') status = args.get('status',None) #print 'CTaskFrame.handleJobFinished',jobId,status,'openJob',self.openJob.jobId,self.openJob.status,status == self.openJob.status if jobId == self.openJob.jobId: if isinstance(status,int): status = CCP4DbApi.JOB_STATUS_TEXT[status] if status != self.openJob.status: self.openJob.status = status self.buttons.setRunMode(status=self.openJob.status) self.buttons.setEnabled(self.openJob.status) self.window().updateActionEnabled() self.outputFrame.showOutput(self.openJob,redo=True) self.outputFrame.setNextButtons(self.openJob.taskname,self.openJob.jobId,status) self.outputFrame.setLinkButtons() elif args.get('parentJobId','') == self.openJob.jobId: # Have finished a sub-job that might have been interruptable so clear the 'next' buttons self.outputFrame.setNextButtons(jobId=jobId) def updateTaskFrame(self,openJob=None): #print 'CTaskFrame.update',openJob,self.openJob import CCP4TaskManager reportFile = None if openJob is not None: self.openJob = openJob self.statusFrame.setJob(self.openJob) if self.openJob.jobId is None: self.buttons.button('next').menu().clear() self.buttons.setEnabled(self.openJob.status) self.inputFrame.clear() self.outputFrame.clear() else: try: reportFile = self.outputFrame.showOutput(self.openJob,reportErr=False) except CException as e: print e e.warningMessage(self.window().windowTitle(),'Error creating job report',parent=self) except Exception as e: print e CMessageBox(self,message='Error creating report for job number '+str(self.openJob.jobnumber),exception=e,openJob=self.openJob) self.buttons.setEnabled(self.openJob.status) if self.openJob.status == 'Running': runningSubJob = PROJECTSMANAGER().db().getRunningSubJob(jobId=self.openJob.jobId) if runningSubJob is not None: self.outputFrame.setNextButtons(runningSubJob['taskName'],runningSubJob['jobId'],'Running') else: self.outputFrame.setNextButtons(self.openJob.taskname,self.openJob.jobId,self.openJob.status) else: self.outputFrame.setNextButtons(self.openJob.taskname,self.openJob.jobId,self.openJob.status) self.outputFrame.setLinkButtons() if self.inputFrame.taskWidget is not None: self.buttons.setRunMode(editor=self.inputFrame.taskWidget.isEditor(),status=self.openJob.status) if (self.openJob.status in CCP4DbApi.FINISHED_JOB_STATUS or self.openJob.status in ['Running','Failed'] ) and reportFile is not None: self.setTaskTab('output') self.tab.setTabEnabled(self.OUTPUT_TAB,True) else: self.setTaskTab('input') if reportFile is None: self.tab.setTabEnabled(self.OUTPUT_TAB,False) self.window().updateActionEnabled(self.openJob.status) def handleViewTask(self,mode): if not isinstance(mode,str): mode = str(mode.text()) #print 'CTaskFrame.handleViewTask',mode,self.openJob.jobId,self.openJob.jobNumber if mode.count('4mg'): LAUNCHER().openInViewer(viewer='ccp4mg',jobId=self.openJob.jobId,projectId=self.openJob.projectId,guiParent=self) elif mode.count('oot'): LAUNCHER().openInViewer(viewer='coot_job',jobId=self.openJob.jobId,projectId=self.openJob.projectId,guiParent=self) def handleReportAvailable(self,jobId,status): if jobId != self.openJob.jobId and jobId not in self.openJob.childjobs: return if status: self.tab.setTabEnabled(self.OUTPUT_TAB,True) self.setTaskTab('output') else: self.tab.setTabEnabled(self.OUTPUT_TAB,False) def openTask(self,taskName=None,jobId=None,cloneJobId=None,followJobId=None,patchParamsFile=None): import CCP4Container,CCP4File import time #print 'CTaskFrame.openTask',taskName,jobId,'followJobId',followJobId,'cloneJobId',cloneJobId # If there is jobid try to get the paramsFile and ensure consistent taskName # ??? Should we be concerned about version number ??? time1 = time.time() # Check we have a clone params file before creating new job cloneParamsFile = None if cloneJobId is not None: cloneParamsFile = PROJECTSMANAGER().makeFileName(jobId = cloneJobId,mode='JOB_INPUT') if not os.path.exists(cloneParamsFile): cloneParamsFile = PROJECTSMANAGER().makeFileName(jobId = cloneJobId,mode='PARAMS') if not os.path.exists(cloneParamsFile): QtGui.QMessageBox.warning(self,self.windowTitle(),'No parameter file found for task to clone') return None #print 'openTask',cloneJobId,cloneParamsFile paramsFile = None if jobId is not None: paramsFile = PROJECTSMANAGER().makeFileName(jobId = jobId,mode='JOB_INPUT') # We could be loading a sub-job without a input params file if not os.path.exists(paramsFile): paramsFile = PROJECTSMANAGER().makeFileName(jobId = jobId,mode='PARAMS') #print 'CProjectViewer.openTask paramsFile',paramsFile,os.path.exists(paramsFile) if os.path.exists(paramsFile): header = CCP4File.xmlFileHeader(paramsFile) if taskName is None: taskName = str(header.pluginName) elif taskName != str(header.pluginName): err = CException(self__class__,102,'Suggested: '+str(taskName)+' File: '+str(paramsFile)+' Contains: '+str(header.pluginName)) err.warningMessage('Error loading params file','Error loading params file for job id: '+jobId,parent=self) return None else: paramsFile = None ifNewJob= False else: jobId,pName,jNumber = PROJECTSMANAGER().newJob(taskName=taskName,projectId=self.openJob.projectId) ifNewJob= True openJob=COpenJob(jobId=jobId,projectId=self.openJob.projectId) taskEditable = ( openJob.status in ['Unknown','Pending'] ) if cloneJobId is not None: if paramsFile is None: paramsFile = PROJECTSMANAGER().makeFileName(jobId = openJob.jobId,mode='JOB_INPUT') try: import CCP4File CCP4File.cloneI2XmlFile(cloneParamsFile,paramsFile,{ 'jobId' : str(openJob.jobnumber) } ) openJob.clonedFromJobId = cloneJobId #print 'CTaskFrame.openTask cloneFromJobId',cloneJobId, openJob.clonedFromJobId except: print 'ERROR cloning params file',cloneParamsFile paramsFile = None else: import glob splitCloneParamsFile = os.path.split(cloneParamsFile) subJobParamsFiles = glob.glob(os.path.join(splitCloneParamsFile[0],'job_*_'+splitCloneParamsFile[1])) for subFile in subJobParamsFiles: #print 'CTaskFrame.openTask clone',subFile CCP4File.cloneI2XmlFile(subFile,os.path.join(os.path.split(paramsFile)[0],os.path.split(subFile)[1]),{ 'jobId' : str(openJob.jobnumber) } ) defFile = TASKMANAGER().lookupDefFile(openJob.taskname,openJob.taskversion) if defFile is None: print 'Failed to find def file in openTask' print 'CTaskFrame.openTask defFile',openJob.taskname,type(openJob.taskname),defFile,openJob.taskversion return self.openJob # Set up data container container = CCP4Container.CContainer(parent=self,definitionFile=defFile,guiAdmin=True) if patchParamsFile is not None: #print 'openTask patchParamsFile',patchParamsFile # expect patch params file to be passed thru from a whatNext command if patchParamsFile.startswith('$CCP4I2'): patchParamsFile = os.path.normpath(os.path.join(CCP4Utils.getCCP4I2Dir(),patchParamsFile[8:])) elif patchParamsFile.startswith('$PROJECT'): patchParamsFile = os.path.normpath(os.path.join(openJob.projectDir,patchParamsFile[9:])) #print 'openTask',patchParamsFile try: container.loadDataFromXml(patchParamsFile) except: print 'ERROR loading patch params file',patchParamsFile if paramsFile is not None: #print 'CProjectViewer.openTask loading',paramsFile try: container.loadDataFromXml(paramsFile) except: print 'ERROR loading params file',paramsFile if cloneParamsFile is not None: #print 'CProjectViewer.openTask loading cloned file',cloneParamsFile try: container.loadDataFromXml(cloneParamsFile) except: print 'ERROR loading cloned params file',cloneParamsFile try: if container.guiAdmin.jobTitle.isSet(): PROJECTSMANAGER().db().updateJob(jobId=jobId,key='jobTitle',value=container.guiAdmin.jobTitle.__str__()) except: print 'ERROR saving cloned jobTitle' PROJECTSMANAGER().setOutputFileNames(container=container,projectId=openJob.projectId, jobNumber=openJob.jobnumber,force=ifNewJob or (openJob.clonedFromJobId is not None )) self.saveStatus() # Should we make a guess here if there is no followJobId and it is a new job # and its not a clone with params already set! if openJob.clonedFromJobId is not None: followJobId = None elif ifNewJob and followJobId is None: followJobId = PROJECTSMANAGER().db().getProjectFollowFromJobId(projectId=openJob.projectId) #print 'openTask cloneId,ifNewJob,followJobId',openJob.clonedFromJobId,ifNewJob,followJobId try: t2 = time.time() if taskEditable or self.tab.currentIndex() == self.INPUT_TAB: taskWidget = self.inputFrame.createTaskWidget(openJob.taskname,projectId=openJob.projectid,jobId=openJob.jobId, container=container,taskEditable=taskEditable,followJobId=followJobId) self.connect(taskWidget,QtCore.SIGNAL('launchJobRequest'),self.launchJobRequest) else: self.inputFrame.closeTaskWidget() except CException as e: e.warningMessage(self.windowTitle(),'Error drawing task widget',parent=self) except Exception as e: import sys err = CErrorReport(self.__class__,999,details=str(e),exc_info=sys.exc_info()) err.warningMessage(self.windowTitle(),'Unknown error drawing task widget\n\n'+str(e),parent=self) else: t3 = time.time() self.openJob = openJob self.titleBar.setOpenJob(self.openJob) self.updateTaskFrame() print 'opentask times total',time.time()-time1 print 'opentask times drawing',t3-t2 return self.openJob try: taskWidget.deleteLater() except: pass return self.openJob def projectId(self): return self.openJob.projectId def launchJobRequest(self,taskName,args): self.emit(QtCore.SIGNAL('launchJobRequest'),taskName,args) class CTaskMainWindow(QtGui.QMainWindow): def __init__(self,parent,projectName,jobId,version=''): QtGui.QMainWindow.__init__(self,parent) self.version=version self.setWindowTitle(self.version+'job from project: '+projectName) self.setWindowIcon(CCP4WebBrowser.mainWindowIcon()) frame = QtGui.QFrame(self) frame.setLayout( QtGui.QVBoxLayout()) self.setObjectName('job'+str(jobId)) self.titleBar = CTaskTitleBar(self) frame.layout().addWidget(self.titleBar) self.buttons = CTaskButtons(self,frame.layout(),mode=CTaskButtons.RUNONLYMODE) self.setCentralWidget(frame) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobStatusUpdated'),self.titleBar.setStatusBar) self.connect(PROJECTSMANAGER().db(),QtCore.SIGNAL('jobFinished'),self.titleBar.setStatusBar) def widget(self): return self.centralWidget().layout().itemAt(1).widget() def closeEvent(self,event): #print 'CTaskMainWindow.closeEvent',repr(self.widget()),self.parent() self.deleteLater() self.emit(QtCore.SIGNAL('windowAboutToClose')) event.accept() def getTaskName(self): #print 'CTaskMainWindow.getTaskName NOT IMPLEMENTED' return None def runTask(self): self.widget().runTask('Local') #self.close() #self.deleteLater() class CDeleteJobGui(QtGui.QDialog): def __init__(self,parent=None,projectId=None,jobIdList=None,jobTreeList=[],deleteImportFiles=False,jobsToDeleteWithSelectedFiles=[],label=None,ifXtrJobs=False): QtGui.QDialog.__init__(self,parent) self.setWindowTitle('Delete jobs') self.projectId = projectId self.jobIdList = jobIdList self.jobTreeList = jobTreeList self.importFileList = [] self.deleteImportFiles = deleteImportFiles self.jobsToDeleteWithSelectedFiles = jobsToDeleteWithSelectedFiles #print 'CDeleteJobGui.followOnJobs',self.followOnJobs self.setModal(True) self.setLayout(QtGui.QVBoxLayout()) if len(jobsToDeleteWithSelectedFiles)>0: line = QtGui.QHBoxLayout() lab = QtGui.QLabel( self) lab.setPixmap( QtGui.QPixmap(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','list_delete.png') ).scaled(16,16) ) line.addWidget(lab) lab = QtGui.QLabel('marked jobs have files used in the current Job Input',self) lab.setObjectName('emphasise') line.addWidget(lab) line.addStretch(5) self.layout().addLayout(line) if ifXtrJobs: lab = QtGui.QLabel('Unselected jobs highlighted in pink',self) lab.setObjectName('emphasise') self.layout().addWidget(lab) nImportedFiles = 0 for jobTree in self.jobTreeList: nImportedFiles += len(jobTree[1]) if nImportedFiles >0: self.deleteImportWidget = QtGui.QCheckBox('Delete files imported by subsequent jobs - this may imply deleting additional jobs',self) if deleteImportFiles: self.deleteImportWidget.setCheckState(QtCore.Qt.Checked) self.layout().addWidget(self.deleteImportWidget) self.connect(self.deleteImportWidget,QtCore.SIGNAL('stateChanged(int)'),self.handleDeleteImportChanged) ''' if len(importFileList)>0: for importId,fileName in importFileList: self.layout().addWidget(QtGui.QLabel(fileName,self)) self.importFileList.append([importId,fileName]) ''' self.tree = CJobTree(self) self.layout().addWidget(self.tree) self.tree.clear() for jobTree in self.jobTreeList: self.tree.load(jobTree,jobsToDeleteWithSelectedFiles=self.jobsToDeleteWithSelectedFiles) buttonBox = QtGui.QDialogButtonBox(self) but = buttonBox.addButton('Delete all these jobs',QtGui.QDialogButtonBox.ApplyRole) but.setAutoDefault(0) self.connect(but,QtCore.SIGNAL('released()'),self.deleteJobs) but = buttonBox.addButton(QtGui.QDialogButtonBox.Cancel) but.setAutoDefault(0) self.connect(but,QtCore.SIGNAL('released()'),self.close) self.layout().addWidget(buttonBox) def handleDeleteImportChanged(self,deleteImportFiles): self.deleteImportFiles = deleteImportFiles self.followOnJobs = PROJECTSMANAGER().db().getFollowOnJobs(jobId=self.jobId,traceImportFiles=(deleteImportFiles>0)) #print 'CDeleteJobGui.handleDeleteImportChanged',deleteImportFiles,self.followOnJobs self.tree.load(self.followOnJobs,jobsToDeleteWithSelectedFiles=self.jobsToDeleteWithSelectedFiles) def deleteJobs(self): import os ''' # Beware importFilePath() returns a name for a new import so this will not work for importId,fileName in self.importFileList: filePath = PROJECTSMANAGER().importFilePath(projectId=self.projectId,baseName=fileName) try: os.remove(filePath) PROJECTSMANAGER().db().deleteImportFile(importId=importId) except: print 'ERROR deleting file',filePath ''' for jobTree in self.jobTreeList: self.deleteJobs0(jobTree,deleteImportFiles=self.deleteImportFiles) self.emit(QtCore.SIGNAL('jobsDeleted')) self.close() def deleteJobs0(self,jobTree=None,deleteImportFiles=False): #print 'CDeleteJobGui.deleteJobs0',jobTree,deleteImportFiles jobId,importFiles,descendents = jobTree if jobId is not None: PROJECTSMANAGER().deleteJob(jobId=jobId,importFiles=importFiles,projectId=self.projectId,deleteImportFiles=deleteImportFiles) for childJobTree in descendents: self.deleteJobs0(childJobTree,deleteImportFiles=False) class CJobTree(QtGui.QTreeWidget): def __init__(self,parent): QtGui.QTreeWidget.__init__(self,parent) self.setColumnCount(3) self.setHeaderLabels(['Job number','Task name','Status']) self.setColumnWidth(0,100) self.setColumnWidth(1,300) self.icon = QtGui.QIcon( QtGui.QPixmap(os.path.join(CCP4Utils.getCCP4I2Dir(),'qticons','list_delete.png') ).scaled(16,16) ) def load(self,jobTree,treeParentId=None,jobsToDeleteWithSelectedFiles=[]): #print 'CJobTree.load0',jobTree,treeParentId jobId,importFiles,childJobTree = jobTree jobInfo = PROJECTSMANAGER().db().getJobInfo(jobId=jobId,mode=['jobnumber','taskname','status']) taskTitle = TASKMANAGER().getTitle(jobInfo['taskname']) # Only add job once - beware a jobs could be child of multiple preceeding jobs # so appear in jobTree more than once if len(self.findItems(jobInfo['jobnumber'],QtCore.Qt.MatchExactly))==0: item = QtGui.QTreeWidgetItem([jobInfo['jobnumber'],taskTitle,str(jobInfo['status'])]) if treeParentId is None: self.addTopLevelItem(item) else: #treeParentId.addChild(item) self.addTopLevelItem(item) #print 'CJobTree.load0 Setting red',jobInfo['jobnumber'] for col in 0,1,2: item.setBackground(col,QtGui.QBrush(QtGui.QColor('pink'))) if jobId in jobsToDeleteWithSelectedFiles: item.setIcon(0,self.icon) for childJob in childJobTree: self.load(childJob,treeParentId=item,jobsToDeleteWithSelectedFiles=jobsToDeleteWithSelectedFiles)