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

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