""" utils/startup.py.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. You should have received a copy of the modified GNU Lesser General Public License along with this library. If not, copies may be downloaded from http://www.ccp4.ac.uk/ccp4license.php This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. """ ''' Liz Potterton Feb 2010 - Some minimal functionality to bootstrap program startup ''' import os import sys import glob import shutil import atexit from PyQt4 import QtCore, QtGui def getCCP4I2Dir(up=1): target = os.environ.get('CCP4I2_TOP', None) if target is not None: return os.path.abspath(target) else: target = os.path.join(os.path.abspath(sys.argv[0]), "..") abstarget = os.path.abspath(target) splittarget = abstarget.split('/') if splittarget.count('ccp4i2'): splittarget.reverse() up = splittarget.index('ccp4i2') while up > 0: abstarget = os.path.dirname(abstarget) up = up - 1 return abstarget def setupEnvironment(path=''): if not path: path = getCCP4I2Dir() os.environ["CCP4I2_TOP"] = path def setupPythonpath(top='', mode='qtgui'): top = getCCP4I2Dir() sys.path.insert(0, top) return def setupGuiPluginsPath(top=''): # Add all ccp4i2/tasks/* directories to search path if not top: top = getCCP4I2Dir() pluginsSearchPath = [os.path.join(top, "tasks")] pluginDirs = [] for searchPath in pluginsSearchPath: pluginDirs.extend(glob.glob(os.path.join(searchPath, '*'))) for pD in pluginDirs: sys.path.append(pD) def setupPluginsPath(top=''): # Add all ccp4i2/wrappers/* and ccp4i2/plugins/* directories to search path if not top: top = getCCP4I2Dir() pluginsSearchPath = [os.path.join(top, 'wrappers'), os.path.join(top, 'pipelines')] plineWraps = glob.glob(os.path.join(top, 'pipelines', '*', 'wrappers')) pluginsSearchPath.extend(plineWraps) pluginDirs = [] for searchPath in pluginsSearchPath: pluginDirs.extend(glob.glob(os.path.join(searchPath, '*'))) for pD in pluginDirs: sys.path.append(os.path.join(pD, 'script')) def testForCCP4Environment(): ccp4_env = os.environ.get('CCP4', 'NOT SET') print 'Using CCP4 from: ', ccp4_env if ccp4_env == 'NOT SET': return False else: return True def startGraphics(): from core import CCP4Config from QApp import CGuiApplication # KJS : does this need to go in here (is fn used ?) app = CGuiApplication(sys.argv) CCP4Config.CONFIG().set('graphical', True) CCP4Config.CONFIG().set('qt', True) return app def setQtWidgetStyle(): from core import CCP4Modules try: factory = QtGui.QStyleFactory() preferences = CCP4Modules.PREFERENCES() stylePref = CCP4Modules.PREFERENCES().WINDOWS_STYLE.__str__() style = factory.create(stylePref) CCP4Modules.QTAPPLICATION().setStyle(style) except Exception as e: print 'ERROR setting window style..' print e def startBrowser(args, app=None, splash=None): from core import CCP4Modules from qtgui import CCP4WebBrowser from qtgui import CCP4StyleSheet, CCP4WebView # KJS: Removed the linux check. Unclear why it's in here. # if sys.platform == "linux2": win = QtGui.QWidget(); splash.finish(win); splash.show() config = loadConfig() config.set('graphical', True) config.set('qt', True) kw = {'graphical' : True} ii = 0 while ii < len(args): if args[ii] in ['-db']: if ii+1 < len(args): ii += 1 kw['dbFileName'] = os.path.abspath(args[ii]) elif args[ii] in ['-dbLocal']: import tempfile tFile = tempfile.NamedTemporaryFile() tFileName = tFile.name tFile.close() kw['dbFileName'] = tFileName kw['useLocalDBFile'] = tFileName print "Using local database file",tFileName print print "*********************************************************************" print "To avoid possible database corruption issues, you are running CCP4i2 " print "in 'Local Database Mode'." print print "Please be aware that to avoid data loss:" print print "You must only have one instance of CCP4i2 running at a time." print "You must wait for jobs to finish before closing CCP4i2." print " If you do quit CCP4i2 whilst jobs are still running, then any" print " information (output files/reports) about these jobs will be lost" print "*********************************************************************" print QtGui.QMessageBox.information(None,"Using local copy of database","To avoid possible database corruption issues, you are running CCP4i2 in 'Local Database Mode'.

Please be aware that to avoid data loss:

You must only have one instance of CCP4i2 running at a time.
You must wait for jobs to finish before closing CCP4i2.

It is intended that these will be temporary restrictions.") elif args[ii] in ['-u', '-user', '-username']: if ii + 1 < len(args): ii += 1 kw['userName'] = args[ii] ii += 1 if app is None: app = CCP4Modules.QTAPPLICATION() startPrintHandler(app) CCP4WebBrowser.setupWebkit() proj_man = startProjectsManager(**kw) proj_man.startCheckForFinishedJobs() job_cont = startJobController() if kw.get('dbFileName',None) is not None: job_cont.setDbFile(kw['dbFileName']) launcher = CCP4Modules.LAUNCHER() #print 'startup.startBrowser app',app setQtWidgetStyle() def pushLocalDB(): from core import CCP4Config, CCP4Utils import shutil import tempfile if kw.has_key('dbFileName') and kw.has_key('useLocalDBFile') and kw['dbFileName'] is not None and kw['useLocalDBFile'] is not None and kw['dbFileName'] == kw['useLocalDBFile']: print "Need to push back temporary database file ......" origFileName = CCP4Config.DBFILE() if origFileName is None: origFileName = os.path.join(CCP4Utils.getDotDirectory(), 'db', 'database.sqlite') pm = CCP4Modules.PROJECTSMANAGER() tFile = tempfile.NamedTemporaryFile(delete=False) tFileName = tFile.name tFile.close() pm.db().backupDB(tFileName) try: os.remove(origFileName+"-wal") except: pass try: os.remove(origFileName+"-shm") except: pass print "Copying",kw['dbFileName'],"back to",origFileName,"original DB" try: shutil.copy(tFileName,origFileName) except: time.sleep(1) try: shutil.copy(tFileName,origFileName) except: print "Second attempt to copy database file",kw['dbFileName'],"back to",origFileName,"failed." print "This is a serious error." QtGui.QMessageBox.critical(None,"Copying back database failed","Second attempt to copy database file "+kw['dbFileName']+" back to "+origFileName+" failed.
This is a serious error. You need to type:

cp "+kw['dbFileName']+" "+origFileName+"

on the command line (terminal) to fix this.") else: print "No need to push back local db file." #---------------------------------------------------------------------- def closeHTTPServer(parent=None): # Called on exit to find and close all CHTTPServerThread threads from qtcore import CCP4HTTPServerThread if CCP4HTTPServerThread.CHTTPServerThread.insts is not None: CCP4HTTPServerThread.CHTTPServerThread.insts.quitServer() # KJS : Should be ok. #---------------------------------------------------------------------- app.connect(proj_man, QtCore.SIGNAL("doCheckForFinishedJobs"), proj_man.checkForFinishedJobs) # the aboutToQuit() signal does not work atexit is too late to save status - for func in (proj_man.Exit, job_cont.Exit, launcher.Exit, closeHTTPServer, sys.__stdout__.flush, sys.__stderr__.flush, CCP4Modules.PRINTHANDLER().exit, pushLocalDB): atexit.register(func) startHTTPServer(parent=app) CCP4WebBrowser.restoreStatus() CCP4WebBrowser.applyCommandLine(args) CCP4StyleSheet.setStyleSheet() CCP4WebView.setGlobalSettings() if hasattr(splash, "close"): splash.close() def startDefEd(): from core import CCP4Config from qtgui import CCP4DefEd pars = CCP4Config.CConfig(qt=True, graphical=True, developer=True) defEd = CCP4DefEd.CDefEd() defEd.raise_() return defEd def startJobController(): from core import CCP4Modules print 'Starting Job Controller' jc = CCP4Modules.JOBCONTROLLER() jc.startTimer() print 'Starting Job Controller - DONE' jc.restoreRunningJobs() return jc def copyDB(origFileName,f2): import sqlite3,re print origFileName,f2 con = sqlite3.connect(origFileName,5.0,1, check_same_thread = False) sql = "".join([s+"\n" for s in con.iterdump()]) f2trunc = open(f2,"w") f2trunc.close() conbak = sqlite3.connect(f2) cur = conbak.cursor() #I am doing a simpler thing. Splitting based on semi-colon + newline is not safe. Strings may contain #this sequence. e.g. in comments. The simpler way may be (theoretically?) slower, but should be safer. try: cur.executescript(sql) except: print "Fail",com conbak.close() raise """ p = re.compile(";[\r\n]+") sql_commands = p.split(sql) for com in sql_commands: if com == "COMMIT": continue try: cur.execute(com) except: print "Fail",com conbak.close() raise """ conbak.commit() print "Saved backup database to",f2 conbak.close() con.close() def startProjectsManager(dbFileName=None, checkForFinishedJobs=False, useLocalDBFile=None, **kw): from core import CCP4Config from core import CCP4Utils from core import CCP4Modules,CCP4ProjectsManager print dbFileName print useLocalDBFile if CCP4ProjectsManager.CProjectsManager.insts is not None: print 'Attempting to start Project Manager for second time - should not do this!' return CCP4ProjectsManager.CProjectsManager.insts else: print 'Starting Project Manager' if dbFileName is not None: print 'Using database file: ', dbFileName pm = CCP4Modules.PROJECTSMANAGER() try: if dbFileName is not None and useLocalDBFile is not None and dbFileName == useLocalDBFile: origFileName = CCP4Config.DBFILE() if origFileName is None: origFileName = os.path.join(CCP4Utils.getDotDirectory(), 'db', 'database.sqlite') if os.path.exists(origFileName): import time print "Copying",origFileName,"to",dbFileName,"and using this as DB" t1 = time.time() copyDB(origFileName,dbFileName) t2 = time.time() print "Copying database to local file took",t2-t1,"seconds" else: print "Using local file",dbFileName,"as DB" db = startDb(pm, mode='sqlite', fileName=dbFileName,**kw) pm.setDatabase(db) except: print "Failed to open database file!!!!!!!!" if dbFileName is None and CCP4Config.DBFILE() is None: #This is manual backup from qtgui import CCP4BackupDBBrowser dbOrigName = os.path.join(CCP4Utils.getDotDirectory(), 'db', 'database.sqlite') win = CCP4BackupDBBrowser.CBackupDBBrowser() win.setDBDirectory(os.path.join(CCP4Utils.getDotDirectory(), 'db')) if win.exec_(): f = win.selectedFile print "Recovering from file",f #The journal and shm files are not going to be valid with backup, so we remove them. The backups should be 'complete' try: os.remove(dbOrigName + "-wal") except: pass try: os.remove(dbOrigName + "-shm") except: pass shutil.copyfile(f, dbOrigName) #FIXME - Should auto restart here. QtGui.QMessageBox.information(None,"Restart CCP4i2","CCP4i2 will now quit. You will need to restart CCP4i2 to continue.") sys.exit() else: sys.exit() elif dbFileName is not None: res = QtGui.QMessageBox.warning(None,"Database file corrupted?","The specified databse file "+dbFileName+" is corrupted or not a valid CCP4i2 database. As specifying the db parameter on the command line is non-standard behaviour, automatic fixing of the problem is not possible. You must specify a valid CCP4i2 database file or seek expert assistance.") print dbFileName,"corrupted" sys.exit() else: res = QtGui.QMessageBox.warning(None,"Database file corrupted?","The specified databse file "+CCP4Config.DBFILE()+" is corrupted or not a valid CCP4i2 database. As specifying the dbFile parameter in the config file is non-standard behaviour, automatic fixing of the problem is not possible. You must specify a valid CCP4i2 database file in this parameter or seek expert assistance.") print CCP4Config.DBFILE(),"corrupted" sys.exit() if checkForFinishedJobs: pm.startCheckForFinishedJobs() print 'Starting Project Manager - DONE' return pm def startDb(parent=None, fileName=None, mode='sqlite', userName=None, userPassword=None,**kw): from core import CCP4Utils from core import CCP4ErrorHandling from dbapi import CCP4DbApi from qtgui import CCP4DbManagerGui try: db = CCP4DbApi.CDbApi(parent=parent, fileName=fileName, mode=mode, createDb=True, userName=userName, userPassword=userPassword, loadDiagnostic=kw.get('loadDiagnostic',True)) except CCP4ErrorHandling.CException as e: print 'startDb', e[0]['code'], kw.get('graphical', False) if e[0]['code'] == 121 and kw.get('graphical', False): try: currentUserName = CCP4Utils.getUserId() dialog = CCP4DbManagerGui.CChallengeUnknownUser(currentUserName=currentUserName) rv = dialog.exec_() except: rv = False if rv: ownerName = dialog.getOwner() reset = dialog.getReset() try: db = CCP4DbApi.CDbApi(parent=parent, fileName=fileName, mode=mode, createDb=True, userName=ownerName, userPassword=userPassword) except: pass else: try: db.createUser(currentUserName) except: print 'Failed adding current user to database user list' if reset: try: db.updateUser(ownerName, newUserName=currentUserName) except CCP4ErrorHandling.CException as e: e.warningMessage(parent=parent) return db if kw.get('graphical', True): try: e.warningMessage(parent=parent) except: pass print e.report() print 'Please try removing $HOME/.CCP4I2/db directory so new database is created' if e[0]['code'] == 222: print 'In future updates to database schema will be handled better!' else: print 'Please report to ccp4@ccp4.ac.uk' sys.exit() except Exception as e: print e print 'Please try removing $HOME/.CCP4I2/db directory so new database is created' print 'Please report to ccp4@ccp4.ac.uk' sys.exit() return db def loadConfig(): from core import CCP4Config, CCP4Utils config = CCP4Config.CONFIG(os.path.join(CCP4Utils.getDotDirectory(), 'configs', 'ccp4i2_config.params.xml')) return config def startHTTPServer(parent=None, port=None): from qtcore import CCP4HTTPServerThread from core import CCP4Utils if port is None: port = CCP4HTTPServerThread.DEFAULT_PORT diry = os.path.join(CCP4Utils.getCCP4I2Dir(), 'docs') t = CCP4HTTPServerThread.CHTTPServerThread(parent=parent, parentDir=diry, port=port) t.start() return def startPrintHandler(app): from core import CCP4PrintHandler printHandler = CCP4PrintHandler.CPrintHandler(parent=app) try: printHandler.cleanupLogs() except Exception as e: print 'ERROR cleaning up print logs' print e # Do a getFileObject() here to ensure the filename is main_thread f = printHandler.getFileObject(thread=None, name='main_thread') sys.stdout = printHandler sys.stderr = printHandler app.connect(app, QtCore.SIGNAL('aboutToQuit()'), printHandler.exit) # Output version info to the print log