Source code for schrodinger.application.livedesign.login_gui
import sys
import requests
from schrodinger import get_maestro
from schrodinger.Qt import QtCore
from schrodinger.ui.qt import basewidgets
from schrodinger.ui.qt.appframework2 import settings
from schrodinger.utils import fileutils
from . import login
from . import login_ui
maestro = get_maestro()
[docs]class LiveDesignLoginDialog(settings.SettingsPanelMixin,
basewidgets.BaseWidget):
ui_module = login_ui
[docs] def initSetOptions(self):
super(LiveDesignLoginDialog, self).initSetOptions()
self.setWindowModality(QtCore.Qt.WindowModal)
self.ld_client = None
self.ld_models = None
self.import_paths = []
self.setWindowTitle("Connect to LiveDesign")
if maestro:
# Add a direct connection as we need the global vars to be reset as
# soon as the project is closed, not when the login panel is
# reopened as it would be if the callback method decorator is used.
maestro.project_close_callback_add(self.onProjectClosed)
[docs] def initSetUp(self):
super(LiveDesignLoginDialog, self).initSetUp()
self.ui.login_btn.clicked.connect(self.connectToLD)
# Press the login btn when user presses Enter from any of the lineedits
line_edits = (self.ui.host_le, self.ui.username_le, self.ui.password_le)
for le in line_edits:
le.returnPressed.connect(self.ui.login_btn.click)
self.setDefaults()
[docs] def setDefaults(self):
self._configurePanelSettings()
self.setPersistent(login.HOST)
self.setPersistent(login.USERNAME)
self.ui.login_btn.setDefault(True)
self.ui.login_btn.setAutoDefault(True)
[docs] def definePanelSettings(self):
"""
See parent class for docstring
"""
return [(login.HOST, self.ui.host_le),
(login.USERNAME, self.ui.username_le),
(login.CLIENT, 'ld_client'), (login.MODELS, 'ld_models')]
[docs] def connectToLD(self):
"""
Return False if an error occurred.
:return: Whether to continue to the next panel.
:rtype: bool
"""
msg = ""
self.ui.error_label.setText(msg)
if not self[login.HOST]:
msg = login.ENTER_HOST_MSG
elif not (self[login.USERNAME] and self.ui.password_le.text()):
msg = login.ENTER_CREDENTIALS_MSG
else:
self.checkHost()
try:
msg = self.setupLDClient()
except requests.exceptions.HTTPError:
msg = login.INVALID_CREDENTIALS_MSG
except requests.exceptions.ConnectionError as err:
if 'Operation timed out' in str(err):
# Show special message when the request timed out - which
# is the most common exception raised.
msg = login.TIMEOUT_MSG
else:
# Invalid host name, etc. Message will be shown on the 2nd
# line in the status label.
msg = login.NO_LDCLIENT_MSG + ":\n%s" % err
except Exception as err:
# Print the traceback to the terminal, so that we know what type
# of exception to catch. Still show something to the user as
# well (without crashing the GUI):
msg = login.NO_LDCLIENT_MSG
import traceback
traceback.print_exc()
if msg:
self.ui.error_label.setText(msg)
return
else:
try:
self.ld_client.ping()
self.ui.error_label.setText("")
except requests.HTTPError as error:
# somehow when running in Maestro, the HTTPError was not caught.
msg = login.INVALID_CREDENTIALS_MSG if "Unauthorized" in str(
error) else str(error)
self.ui.error_label.setText(msg)
return
except RuntimeError as error:
# The client version does not match the server version
msg = login.VERSION_MISMATCH_MSG if 'not supported' in str(
error) else str(error)
self.ui.error_label.setText(msg)
return
self._setGlobalVars(self[login.USERNAME], self.ui.password_le.text(),
self[login.HOST], self.import_paths)
self.savePersistentOptions()
self.close()
[docs] def checkHost(self):
"""
Check if the host is entered correctly. If not, updates the host string
with the correct format.
"""
self[login.HOST] = login.format_host(self[login.HOST])
def _getFetchFiles(self):
"""
Return list containing tuples used to fetch files from server.
:return: a list of 3-tuples, each of which contain
1. server path
2. tar filename base (no extension)
3. glob path (which can contain wildcards such as "*")
:rtype: list(tuple(str, str, str))
"""
return [(login.LDCLIENT_PATH, 'ldclient', 'ldclient-*')]
[docs] def setupLDClient(self):
"""
Fetch client.py from the given host.
"""
tmp_dir = fileutils.get_directory_path(fileutils.TEMP)
self.import_paths = []
for server_path, tar_filename, glob_path in self._getFetchFiles():
url = self[login.HOST] + server_path
try:
client_path = login.download_ld_client(url, tmp_dir,
tar_filename, glob_path)
except RuntimeError as error:
return str(error) + login.CONTACT_SUPPORT_MSG
self.import_paths.append(client_path)
sys.path.insert(0, client_path)
try:
import ldclient.client
import ldclient.models
except ImportError as e:
return login.IMPORT_ERROR_MSG + login.CONTACT_SUPPORT_MSG
# Check if LDClient can be instantiated with given login credentials
ld_client_host = self[login.HOST] + login.API_PATH
self.ld_client = ldclient.client.LDClient(
ld_client_host,
self[login.USERNAME],
self.ui.password_le.text(),
compatibility_mode=login.LD_VERSION_COMPATIBILITY_MODE)
self.ld_models = ldclient.models
[docs] def onProjectClosed(self):
"""
If current project is closed, then all saved credentials will be reset.
"""
self._setGlobalVars(None, None, None, [])
self.ui.password_le.setText('')
def _setGlobalVars(self, uname, pwd, host, paths):
"""
Helper method to set the global variables.
:param uname: username
:type uname: str
:param pwd: password
:type pwd: str
:param host: LiveDesign server host
:type host: str
:param paths: the paths used to import necessary modules
:type paths: List of str
"""
login._SESSION_USERNAME = uname
login._SESSION_PWD = pwd
login._SESSION_HOST = host
login._SESSION_IMPORT_PATHS = paths
# =============================================================================
# For testing outside of Maestro
# =============================================================================
[docs]def panel():
return LiveDesignLoginDialog.panel()
if __name__ == "__main__":
panel()