#!/usr/bin/env python
__doc__ = """GNUmed web client launcher.
"""
#==========================================================
# $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gnumed.py,v $
# $Id: gnumed.py,v 1.169 2010-01-31 18:20:41 ncq Exp $
__version__ = "$Revision: 1 $"
__author__ = "S. Hilbert
"
__license__ = "GPL (details at http://www.gnu.org)"
import cherrypy
# stdlib
import sys, time, os, cPickle, zlib, locale, os.path, datetime as pyDT, webbrowser, shutil, logging, urllib2
# GNUmed libs
from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks
from Gnumed.pycommon import gmLoginInfo, gmPG2, gmBackendListener, gmTools, gmCfg2, gmI18N, gmDispatcher
from Gnumed.business import gmMedDoc
#try:
# _('dummy-no-need-to-translate-but-make-epydoc-happy')
#except NameError:
# _ = lambda x:x
_cfg = gmCfg2.gmCfgData()
_provider = None
_scripting_listener = None
_log = logging.getLogger('gm.main')
_log.info(__version__)
_log.info('web GUI framework')
#================================================================
# convenience functions
#----------------------------------------------------------------
def connect_to_database(login_info=None, max_attempts=3, expected_version=None, require_version=True):
"""Display the login dialog and try to log into the backend.
- up to max_attempts times
- returns True/False
"""
# force programmer to set a valid expected_version
expected_hash = gmPG2.known_schema_hashes[expected_version]
client_version = _cfg.get(option = u'client_version')
global current_db_name
current_db_name = u'gnumed_%s' % expected_version
attempt = 0
while attempt < max_attempts:
_log.debug('login attempt %s of %s', (attempt+1), max_attempts)
connected = False
login = login_info
if login is None:
_log.info("did not provide a login information")
# try getting a connection to verify the DSN works
dsn = gmPG2.make_psycopg2_dsn (
database = login.database,
host = login.host,
port = login.port,
user = login.user,
password = login.password
)
try:
conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True)
connected = True
except gmPG2.cAuthenticationError, e:
attempt += 1
_log.error(u"login attempt failed: %s", e)
if attempt < max_attempts:
if (u'host=127.0.0.1' in (u'%s' % e)) or (u'host=' not in (u'%s' % e)):
msg = _(
'Unable to connect to database:\n\n'
'%s\n\n'
"Are you sure you have got a local database installed ?\n"
'\n'
"Please retry with proper credentials or cancel.\n"
'\n'
'You may also need to check the PostgreSQL client\n'
'authentication configuration in pg_hba.conf. For\n'
'details see:\n'
'\n'
'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
)
else:
msg = _(
"Unable to connect to database:\n\n"
"%s\n\n"
"Please retry with proper credentials or cancel.\n"
"\n"
'You may also need to check the PostgreSQL client\n'
'authentication configuration in pg_hba.conf. For\n'
'details see:\n'
'\n'
'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL'
)
msg = msg % e
msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
gmGuiHelpers.gm_show_error (
msg,
_('Connecting to backend')
)
del e
continue
except gmPG2.dbapi.OperationalError, e:
_log.error(u"login attempt failed: %s", e)
msg = _(
"Unable to connect to database:\n\n"
"%s\n\n"
"Please retry another backend / user / password combination !\n"
) % gmPG2.extract_msg_from_pg_exception(e)
msg = regex.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg)
gmGuiHelpers.gm_show_error (
msg,
_('Connecting to backend')
)
del e
continue
# connect was successful
gmPG2.set_default_login(login = login)
#gmPG2.set_default_client_encoding(encoding = dlg.panel.backend_profile.encoding)
# compatible = gmPG2.database_schema_compatible(version = expected_version)
# if compatible or not require_version:
#dlg.panel.save_state()
# continue
# if not compatible:
# connected_db_version = gmPG2.get_schema_version()
# msg = msg_generic % (
# client_version,
# connected_db_version,
# expected_version,
# gmTools.coalesce(login.host, ''),
# login.database,
# login.user
# )
# if require_version:
# gmGuiHelpers.gm_show_error(msg + msg_fail, _('Verifying database version'))
# pass
#gmGuiHelpers.gm_show_info(msg + msg_override, _('Verifying database version'))
# # FIXME: make configurable
# max_skew = 1 # minutes
# if _cfg.get(option = 'debug'):
# max_skew = 10
# if not gmPG2.sanity_check_time_skew(tolerance = (max_skew * 60)):
# if _cfg.get(option = 'debug'):
# gmGuiHelpers.gm_show_warning(msg_time_skew_warn % max_skew, _('Verifying database settings'))
# else:
# gmGuiHelpers.gm_show_error(msg_time_skew_fail % max_skew, _('Verifying database settings'))
# continue
# sanity_level, message = gmPG2.sanity_check_database_settings()
# if sanity_level != 0:
# gmGuiHelpers.gm_show_error((msg_insanity % message), _('Verifying database settings'))
# if sanity_level == 2:
# continue
# gmExceptionHandlingWidgets.set_is_public_database(login.public_db)
# gmExceptionHandlingWidgets.set_helpdesk(login.helpdesk)
listener = gmBackendListener.gmBackendListener(conn = conn)
break
#dlg.Destroy()
return connected
#----------------------------------------------------------------------------
#internal helper functions
#----------------------------------------------------
def __get_backend_profiles():
"""Get server profiles from the configuration files.
1) from system-wide file
2) from user file
Profiles in the user file which have the same name
as a profile in the system file will override the
system file.
"""
# find active profiles
src_order = [
(u'explicit', u'extend'),
(u'system', u'extend'),
(u'user', u'extend'),
(u'workbase', u'extend')
]
profile_names = gmTools.coalesce (
_cfg.get(group = u'backend', option = u'profiles', source_order = src_order),
[]
)
# find data for active profiles
src_order = [
(u'explicit', u'return'),
(u'workbase', u'return'),
(u'user', u'return'),
(u'system', u'return')
]
profiles = {}
for profile_name in profile_names:
# FIXME: once the profile has been found always use the corresponding source !
# FIXME: maybe not or else we cannot override parts of the profile
profile = cBackendProfile()
profile_section = 'profile %s' % profile_name
profile.name = profile_name
profile.host = gmTools.coalesce(_cfg.get(profile_section, u'host', src_order), u'').strip()
port = gmTools.coalesce(_cfg.get(profile_section, u'port', src_order), 5432)
try:
profile.port = int(port)
if profile.port < 1024:
raise ValueError('refusing to use priviledged port (< 1024)')
except ValueError:
_log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name)
continue
profile.database = gmTools.coalesce(_cfg.get(profile_section, u'database', src_order), u'').strip()
if profile.database == u'':
_log.warning('database name not specified, skipping profile [%s]', profile_name)
continue
profile.encoding = gmTools.coalesce(_cfg.get(profile_section, u'encoding', src_order), u'UTF8')
profile.public_db = bool(_cfg.get(profile_section, u'public/open access', src_order))
profile.helpdesk = _cfg.get(profile_section, u'help desk', src_order)
label = u'%s (address@hidden)' % (profile_name, profile.database, profile.host)
profiles[label] = profile
# sort out profiles with incompatible database versions if not --debug
# NOTE: this essentially hardcodes the database name in production ...
if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')):
profiles2remove = []
for label in profiles:
if profiles[label].database != current_db_name:
profiles2remove.append(label)
for label in profiles2remove:
del profiles[label]
if len(profiles) == 0:
host = u'salaam.homeunix.com'
label = u'public GNUmed database (address@hidden)' % (current_db_name, host)
profiles[label] = cBackendProfile()
profiles[label].name = label
profiles[label].host = host
profiles[label].port = 5432
profiles[label].database = current_db_name
profiles[label].encoding = u'UTF8'
profiles[label].public_db = True
profiles[label].helpdesk = u'http://wiki.gnumed.de'
return profiles
# ------------------------------------------------------------
def GetLoginInfo(username=None, password=None, backend=None ):
# username is provided through the web interface
# password is provided
# we need the profile
"""convenience function for compatibility with gmLoginInfo.LoginInfo"""
#if not self.cancelled:
# FIXME: do not assume conf file is latin1 !
#profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()]
#self.__backend_profiles = self.__get_backend_profiles()
__backend_profiles = __get_backend_profiles()
profile = __backend_profiles[backend.encode('utf8').strip()]
_log.debug(u'backend profile "%s" selected', profile.name)
_log.debug(u' details: <%s> on address@hidden:%s (%s, %s)',
username,
profile.database,
profile.host,
profile.port,
profile.encoding,
gmTools.bool2subst(profile.public_db, u'public', u'private')
)
#_log.debug(u' helpdesk: "%s"', profile.helpdesk)
login = gmLoginInfo.LoginInfo (
user = username,
password = password,
host = profile.host,
database = profile.database,
port = profile.port
)
#login.public_db = profile.public_db
#login.helpdesk = profile.helpdesk
return login
#----------------------------------------------
def _signal_debugging_monitor(*args, **kwargs):
try:
kwargs['originated_in_database']
print '==> got notification from database "%s":' % kwargs['signal']
except KeyError:
print '==> received signal from client: "%s"' % kwargs['signal']
del kwargs['signal']
for key in kwargs.keys():
print ' [%s]: %s' % (key, kwargs[key])
#================================================================
class cBackendProfile:
pass
#================================================================
class gmApp:
def doSomething(self):
msg = 'shema version is:' + gmPG2.get_schema_version() +'\n\n'
msg2 =''
for item in gmMedDoc.get_document_types():
msg2 = msg2 +'\n' + str(item)
msg = msg + msg2
return msg
def doLogin(self, username=None, password=None, backend=None):
login_info = GetLoginInfo(username, password, backend)
override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')])
connected = connect_to_database (
login_info,
expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')],
require_version = not override
)
msg = self.doSomething()
return msg
doLogin.exposed = True
# ------------------------------------------------------------
def index(self):
# backend is hardcoded for now, make it use drop down list later
return """
"""
index.exposed = True
#==========================================================
# main - launch the GNUmed web client
#----------------------------------------------------------
def main():
if _cfg.get(option = 'debug'):
gmDispatcher.connect(receiver = _signal_debugging_monitor)
_log.debug('gmDispatcher signal monitor activated')
cherrypy.quickstart(gmApp())