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