Package Gnumed :: Package ProxiedWeb :: Module gmWebGuiServer
[frames] | no frames]

Source Code for Module Gnumed.ProxiedWeb.gmWebGuiServer

  1  #!/usr/bin/env python 
  2   
  3  __doc__ = """GNUmed web user interface server launcher. 
  4  """ 
  5  #========================================================== 
  6  __version__ = "$Revision: 0.1 $" 
  7  __author__  = "S. Hilbert <Sebastian.Hilbert@gmx.net>" 
  8  __license__ = "GPL (details at http://www.gnu.org)" 
  9   
 10  # stdlib 
 11  import re, sys, time, os, cPickle, zlib, locale, os.path, datetime as pyDT, webbrowser, shutil, logging, urllib2 
 12   
 13  # json-rpc 
 14  from jsonserver import SimpleForkingJSONRPCServer 
 15   
 16  # GNUmed libs 
 17  from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks 
 18  from Gnumed.pycommon import gmLoginInfo, gmBackendListener, gmTools, gmCfg2, gmI18N, gmDispatcher, gmBusinessDBObject 
 19  from Gnumed.pycommon.gmBusinessDBObject import jsonclasshintify 
 20  from Gnumed.pycommon import gmPG2 
 21  from Gnumed.business import gmDocuments 
 22   
 23  #try: 
 24  #       _('dummy-no-need-to-translate-but-make-epydoc-happy') 
 25  #except NameError: 
 26  #       _ = lambda x:x 
 27   
 28  _cfg = gmCfg2.gmCfgData() 
 29  _provider = None 
 30  _scripting_listener = None 
 31   
 32  _log = logging.getLogger('gm.main') 
 33  _log.info(__version__) 
 34  _log.info('web GUI framework') 
 35   
 36  #================================================================ 
 37  # convenience functions 
 38  #---------------------------------------------------------------- 
39 -def connect_to_database(login_info=None, max_attempts=3, expected_version=None, require_version=True):
40 """Display the login dialog and try to log into the backend. 41 42 - up to max_attempts times 43 - returns True/False 44 """ 45 from Gnumed.pycommon import gmPG2 46 # force programmer to set a valid expected_version 47 expected_hash = gmPG2.known_schema_hashes[expected_version] 48 client_version = _cfg.get(option = u'client_version') 49 global current_db_name 50 current_db_name = u'gnumed_%s' % expected_version 51 52 attempt = 0 53 54 while attempt < max_attempts: 55 56 _log.debug('login attempt %s of %s', (attempt+1), max_attempts) 57 58 connected = False 59 60 login = login_info 61 if login is None: 62 _log.info("did not provide a login information") 63 64 # try getting a connection to verify the DSN works 65 dsn = gmPG2.make_psycopg2_dsn ( 66 database = login.database, 67 host = login.host, 68 port = login.port, 69 user = login.user, 70 password = login.password 71 ) 72 try: 73 #conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True) 74 conn = gmPG2.get_raw_connection(dsn = dsn, verbose = True, readonly = True) 75 connected = True 76 77 except gmPG2.cAuthenticationError, e: 78 attempt += 1 79 _log.error(u"login attempt failed: %s", e) 80 if attempt < max_attempts: 81 if (u'host=127.0.0.1' in (u'%s' % e)) or (u'host=' not in (u'%s' % e)): 82 msg = _( 83 'Unable to connect to database:\n\n' 84 '%s\n\n' 85 "Are you sure you have got a local database installed ?\n" 86 '\n' 87 "Please retry with proper credentials or cancel.\n" 88 '\n' 89 'You may also need to check the PostgreSQL client\n' 90 'authentication configuration in pg_hba.conf. For\n' 91 'details see:\n' 92 '\n' 93 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL' 94 ) 95 else: 96 msg = _( 97 "Unable to connect to database:\n\n" 98 "%s\n\n" 99 "Please retry with proper credentials or cancel.\n" 100 "\n" 101 'You may also need to check the PostgreSQL client\n' 102 'authentication configuration in pg_hba.conf. For\n' 103 'details see:\n' 104 '\n' 105 'wiki.gnumed.de/bin/view/Gnumed/ConfigurePostgreSQL' 106 ) 107 msg = msg % e 108 msg = re.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg) 109 gmGuiHelpers.gm_show_error ( 110 msg, 111 _('Connecting to backend') 112 ) 113 del e 114 continue 115 116 except gmPG2.dbapi.OperationalError, e: 117 _log.error(u"login attempt failed: %s", e) 118 msg = _( 119 "Unable to connect to database:\n\n" 120 "%s\n\n" 121 "Please retry another backend / user / password combination !\n" 122 ) % gmPG2.extract_msg_from_pg_exception(e) 123 msg = re.sub(r'password=[^\s]+', u'password=%s' % gmTools.u_replacement_character, msg) 124 gmGuiHelpers.gm_show_error ( 125 msg, 126 _('Connecting to backend') 127 ) 128 del e 129 continue 130 131 # connect was successful 132 gmPG2.set_default_login(login = login) 133 #gmPG2.set_default_client_encoding(encoding = dlg.panel.backend_profile.encoding) 134 135 # compatible = gmPG2.database_schema_compatible(version = expected_version) 136 # if compatible or not require_version: 137 #dlg.panel.save_state() 138 # continue 139 140 # if not compatible: 141 # connected_db_version = gmPG2.get_schema_version() 142 # msg = msg_generic % ( 143 # client_version, 144 # connected_db_version, 145 # expected_version, 146 # gmTools.coalesce(login.host, '<localhost>'), 147 # login.database, 148 # login.user 149 # ) 150 151 # if require_version: 152 # gmGuiHelpers.gm_show_error(msg + msg_fail, _('Verifying database version')) 153 # pass 154 #gmGuiHelpers.gm_show_info(msg + msg_override, _('Verifying database version')) 155 156 # # FIXME: make configurable 157 # max_skew = 1 # minutes 158 # if _cfg.get(option = 'debug'): 159 # max_skew = 10 160 # if not gmPG2.sanity_check_time_skew(tolerance = (max_skew * 60)): 161 # if _cfg.get(option = 'debug'): 162 # gmGuiHelpers.gm_show_warning(msg_time_skew_warn % max_skew, _('Verifying database settings')) 163 # else: 164 # gmGuiHelpers.gm_show_error(msg_time_skew_fail % max_skew, _('Verifying database settings')) 165 # continue 166 167 # sanity_level, message = gmPG2.sanity_check_database_settings() 168 # if sanity_level != 0: 169 # gmGuiHelpers.gm_show_error((msg_insanity % message), _('Verifying database settings')) 170 # if sanity_level == 2: 171 # continue 172 173 # gmExceptionHandlingWidgets.set_is_public_database(login.public_db) 174 # gmExceptionHandlingWidgets.set_helpdesk(login.helpdesk) 175 176 listener = gmBackendListener.gmBackendListener(conn = conn) 177 break 178 179 #dlg.Destroy() 180 181 return connected
182 183 #---------------------------------------------------------------------------- 184 #internal helper functions 185 #----------------------------------------------------
186 -def __get_backend_profiles():
187 """Get server profiles from the configuration files. 188 189 1) from system-wide file 190 2) from user file 191 192 Profiles in the user file which have the same name 193 as a profile in the system file will override the 194 system file. 195 """ 196 # find active profiles 197 src_order = [ 198 (u'explicit', u'extend'), 199 (u'system', u'extend'), 200 (u'user', u'extend'), 201 (u'workbase', u'extend') 202 ] 203 204 profile_names = gmTools.coalesce ( 205 _cfg.get(group = u'backend', option = u'profiles', source_order = src_order), 206 [] 207 ) 208 209 # find data for active profiles 210 src_order = [ 211 (u'explicit', u'return'), 212 (u'workbase', u'return'), 213 (u'user', u'return'), 214 (u'system', u'return') 215 ] 216 217 profiles = {} 218 219 for profile_name in profile_names: 220 # FIXME: once the profile has been found always use the corresponding source ! 221 # FIXME: maybe not or else we cannot override parts of the profile 222 profile = cBackendProfile() 223 profile_section = 'profile %s' % profile_name 224 225 profile.name = profile_name 226 profile.host = gmTools.coalesce(_cfg.get(profile_section, u'host', src_order), u'').strip() 227 port = gmTools.coalesce(_cfg.get(profile_section, u'port', src_order), 5432) 228 try: 229 profile.port = int(port) 230 if profile.port < 1024: 231 raise ValueError('refusing to use priviledged port (< 1024)') 232 except ValueError: 233 _log.warning('invalid port definition: [%s], skipping profile [%s]', port, profile_name) 234 continue 235 profile.database = gmTools.coalesce(_cfg.get(profile_section, u'database', src_order), u'').strip() 236 if profile.database == u'': 237 _log.warning('database name not specified, skipping profile [%s]', profile_name) 238 continue 239 profile.encoding = gmTools.coalesce(_cfg.get(profile_section, u'encoding', src_order), u'UTF8') 240 profile.public_db = bool(_cfg.get(profile_section, u'public/open access', src_order)) 241 profile.helpdesk = _cfg.get(profile_section, u'help desk', src_order) 242 243 label = u'%s (%s@%s)' % (profile_name, profile.database, profile.host) 244 profiles[label] = profile 245 246 # sort out profiles with incompatible database versions if not --debug 247 # NOTE: this essentially hardcodes the database name in production ... 248 if not (_cfg.get(option = 'debug') or current_db_name.endswith('_devel')): 249 profiles2remove = [] 250 for label in profiles: 251 if profiles[label].database != current_db_name: 252 profiles2remove.append(label) 253 for label in profiles2remove: 254 del profiles[label] 255 256 if len(profiles) == 0: 257 host = u'salaam.homeunix.com' 258 label = u'public GNUmed database (%s@%s)' % (current_db_name, host) 259 profiles[label] = cBackendProfile() 260 profiles[label].name = label 261 profiles[label].host = host 262 profiles[label].port = 5432 263 profiles[label].database = current_db_name 264 profiles[label].encoding = u'UTF8' 265 profiles[label].public_db = True 266 profiles[label].helpdesk = u'http://wiki.gnumed.de' 267 268 return profiles
269 270 # ------------------------------------------------------------
271 -def GetLoginInfo(username=None, password=None, backend=None ):
272 273 # username is provided through the web interface 274 # password is provided 275 # we need the profile 276 277 """convenience function for compatibility with gmLoginInfo.LoginInfo""" 278 from Gnumed.pycommon import gmLoginInfo 279 #if not self.cancelled: 280 # FIXME: do not assume conf file is latin1 ! 281 #profile = self.__backend_profiles[self._CBOX_profile.GetValue().encode('latin1').strip()] 282 #self.__backend_profiles = self.__get_backend_profiles() 283 __backend_profiles = __get_backend_profiles() 284 profile = __backend_profiles[backend.encode('utf8').strip()] 285 286 _log.debug(u'backend profile "%s" selected', profile.name) 287 _log.debug(u' details: <%s> on %s@%s:%s (%s, %s)', 288 username, 289 profile.database, 290 profile.host, 291 profile.port, 292 profile.encoding, 293 gmTools.bool2subst(profile.public_db, u'public', u'private') 294 ) 295 #_log.debug(u' helpdesk: "%s"', profile.helpdesk) 296 login = gmLoginInfo.LoginInfo ( 297 user = username, 298 password = password, 299 host = profile.host, 300 database = profile.database, 301 port = profile.port 302 ) 303 #login.public_db = profile.public_db 304 #login.helpdesk = profile.helpdesk 305 return login
306 307 #----------------------------------------------
308 -def _signal_debugging_monitor(*args, **kwargs):
309 try: 310 kwargs['originated_in_database'] 311 print '==> got notification from database "%s":' % kwargs['signal'] 312 except KeyError: 313 print '==> received signal from client: "%s"' % kwargs['signal'] 314 315 del kwargs['signal'] 316 for key in kwargs.keys(): 317 print ' [%s]: %s' % (key, kwargs[key])
318 319 #================================================================
320 -class cBackendProfile:
321 pass
322 323 #================================================================ 324 325 326 PYJSDIR = sys._getframe().f_code.co_filename 327 PYJSDIR = os.path.split(os.path.dirname(PYJSDIR))[0] 328 PYJSDIR = os.path.join(PYJSDIR, 'pyjamas') 329 330 DEFAULT_BACKEND = "GNUmed database on this machine (Linux/Mac) (gnumed_v15@)" 331
332 -class HTTPServer(SimpleForkingJSONRPCServer):
333 '''An application instance containing any number of streams. Except for constructor all methods are generators.''' 334 count = 0
335 - def __init__(self):
336 SimpleForkingJSONRPCServer.__init__(self, ("localhost", 60001)) 337 338 self.register_function(self.echo) 339 self.register_function(self.login) 340 self.register_function(self.get_doc_types) 341 self.register_function(self.get_documents) 342 self.register_function(self.get_schema_version) 343 self.register_function(self.doSomething)
344
345 - def echo(self, text):
346 return text
347 - def reverse(self, text):
348 return text[::-1]
349 - def uppercase(self, text):
350 return text.upper()
351 - def lowercase(self,text):
352 return text.lower()
353
354 - def login(self, username=None, password=None, backend=None):
355 from Gnumed.pycommon import gmPG2 356 if backend is None: 357 backend = DEFAULT_BACKEND 358 login_info = GetLoginInfo(username, password, backend) 359 override = _cfg.get(option = '--override-schema-check', 360 source_order = [('cli', 'return')]) 361 cb = _cfg.get(option = 'client_branch') 362 expected_version = gmPG2.map_client_branch2required_db_version[cb] 363 connected = connect_to_database ( 364 login_info, 365 expected_version = expected_version, 366 require_version = not override 367 ) 368 return connected
369
370 - def get_schema_version(self):
372
373 - def get_documents(self, key):
374 doc_folder = gmDocuments.cDocumentFolder(aPKey=key) 375 return jsonclasshintify(doc_folder.get_documents())
376
377 - def get_doc_types(self):
379
380 - def doSomething(self):
381 msg = 'schema version is:' + gmPG2.get_schema_version() +'\n\n' 382 msg2 ='' 383 for item in gmDocuments.get_document_types(): 384 msg2 = msg2 +'\n' + str(item) 385 msg = msg + msg2 386 return "<pre>%s</pre>" % msg
387 388 389 #========================================================== 390 # main - launch the GNUmed web client 391 #---------------------------------------------------------- 392
393 -def main():
394 395 if _cfg.get(option = 'debug'): 396 gmDispatcher.connect(receiver = _signal_debugging_monitor) 397 _log.debug('gmDispatcher signal monitor activated') 398 399 server = HTTPServer() 400 server.serve_forever()
401