"""
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