Package Gnumed :: Module gnumed
[frames] | no frames]

Source Code for Module Gnumed.gnumed

  1  #!/usr/bin/env python 
  2   
  3  __doc__ = """GNUmed client launcher. 
  4   
  5  This is the launcher for the GNUmed GUI client. It takes 
  6  care of all the pre- and post-GUI runtime environment setup. 
  7   
  8  --quiet 
  9   Be extra quiet and show only _real_ errors in the log. 
 10  --debug 
 11   Pre-set the [debug mode] checkbox in the login dialog to 
 12   increase verbosity in the log file. Useful for, well, debugging :-) 
 13  --slave 
 14   Pre-set the [enable remote control] checkbox in the login 
 15   dialog to enable the XML-RPC remote control feature. 
 16  --hipaa 
 17   Enable HIPAA functionality which has user impact. 
 18  --profile=<file> 
 19   Activate profiling and write profile data to <file>. 
 20  --text-domain=<text domain> 
 21   Set this to change the name of the language file to be loaded. 
 22   Note, this does not change the directory the file is searched in, 
 23   only the name of the file where messages are loaded from. The 
 24   standard textdomain is, of course, "gnumed.mo". 
 25  --log-file=<file> 
 26   Use this to change the name of the log file. 
 27   See gmLog2.py to find out where the standard log file would 
 28   end up. 
 29  --conf-file=<file> 
 30   Use configuration file <file> instead of searching for it in 
 31   standard locations. 
 32  --lang-gettext=<language> 
 33   Explicitly set the language to use in gettext translation. The very 
 34   same effect can be achieved by setting the environment variable $LANG 
 35   from a launcher script. 
 36  --override-schema-check 
 37   Continue loading the client even if the database schema version 
 38   and the client software version cannot be verified to be compatible. 
 39  --skip-update-check 
 40   Skip checking for client updates. This is useful during development 
 41   and when the update check URL is unavailable (down). 
 42  --local-import 
 43   Adjust the PYTHONPATH such that GNUmed can be run from a local source tree. 
 44  --ui=<ui type> 
 45   Start an alternative UI. Defaults to wxPython if not specified. 
 46   Valid values: chweb (CherryPy), wxp (wxPython), web (ProxiedWeb) 
 47  --version, -V 
 48   Show version information. 
 49  --help, -h, or -? 
 50   Show this help. 
 51  """ 
 52  #========================================================== 
 53  __version__ = "$Revision: 1.169 $" 
 54  __author__  = "H. Herb <hherb@gnumed.net>, K. Hilbert <Karsten.Hilbert@gmx.net>, I. Haywood <i.haywood@ugrad.unimelb.edu.au>" 
 55  __license__ = "GPL v2 or later (details at http://www.gnu.org)" 
 56   
 57  # standard library 
 58  import sys 
 59  import os 
 60  import platform 
 61  import logging 
 62  import signal 
 63  import os.path 
 64  import shutil 
 65  import stat 
 66   
 67   
 68  # do not run as module 
 69  if __name__ != "__main__": 
 70          print "GNUmed startup: This is not intended to be imported as a module !" 
 71          print "-----------------------------------------------------------------" 
 72          print __doc__ 
 73          sys.exit(1) 
 74   
 75   
 76  # do not run as root 
 77  if os.name in ['posix'] and os.geteuid() == 0: 
 78          print """ 
 79  GNUmed startup: GNUmed should not be run as root. 
 80  ------------------------------------------------- 
 81   
 82  Running GNUmed as <root> can potentially put all 
 83  your medical data at risk. It is strongly advised 
 84  against. Please run GNUmed as a non-root user. 
 85  """ 
 86          sys.exit(1) 
 87   
 88  #---------------------------------------------------------- 
 89  #current_client_version = u'0.7.rc1' 
 90  current_client_version = u'GIT HEAD' 
 91  #current_client_branch = u'0.7' 
 92  current_client_branch = u'GIT tree' 
 93   
 94  _log = None 
 95  _cfg = None 
 96  _old_sig_term = None 
 97  _known_short_options = u'h?V' 
 98  _known_long_options = [ 
 99          u'debug', 
100          u'slave', 
101          u'skip-update-check', 
102          u'profile=', 
103          u'text-domain=', 
104          u'log-file=', 
105          u'conf-file=', 
106          u'lang-gettext=', 
107          u'ui=', 
108          u'override-schema-check', 
109          u'local-import', 
110          u'help', 
111          u'version', 
112          u'hipaa' 
113  ] 
114   
115  _known_ui_types = [ 
116          u'web', 
117          u'wxp', 
118          u'chweb' 
119  ] 
120   
121  import_error_sermon = """ 
122  GNUmed startup: Cannot load GNUmed Python modules ! 
123  --------------------------------------------------- 
124  CRITICAL ERROR: Program halted. 
125   
126  Please make sure you have: 
127   
128   1) the required third-party Python modules installed 
129   2) the GNUmed Python modules linked or installed into site-packages/ 
130      (if you do not run from a CVS tree the installer should have taken care of that) 
131   3) your PYTHONPATH environment variable set up correctly 
132   
133  sys.path is currently set to: 
134   
135   %s 
136   
137  If you are running from a copy of the CVS tree make sure you 
138  did run gnumed/check-prerequisites.sh with good results. 
139   
140  If you still encounter errors after checking the above 
141  requirements please ask on the mailing list. 
142  """ 
143   
144   
145  missing_cli_config_file = u""" 
146  GNUmed startup: Missing configuration file. 
147  ------------------------------------------- 
148   
149  You explicitly specified a configuration file 
150  on the command line: 
151   
152          --conf-file=%s 
153   
154  The file does not exist, however. 
155  """ 
156   
157   
158  no_config_files = u""" 
159  GNUmed startup: Missing configuration files. 
160  -------------------------------------------- 
161   
162  None of the below candidate configuration 
163  files could be found: 
164   
165   %s 
166   
167  Cannot run GNUmed without any of them. 
168  """ 
169  #========================================================== 
170  # convenience functions 
171  #========================================================== 
172 -def setup_python_path():
173 174 if not u'--local-import' in sys.argv: 175 return 176 177 print "Running from local source tree ..." 178 179 local_python_base_dir = os.path.dirname ( 180 os.path.abspath(os.path.join(sys.argv[0], '..')) 181 ) 182 183 # does the path exist at all, physically ? 184 # (*broken* links are reported as False) 185 link_name = os.path.join(local_python_base_dir, 'Gnumed') 186 if not os.path.exists(link_name): 187 real_dir = os.path.join(local_python_base_dir, 'client') 188 print "Creating module import symlink ..." 189 print ' real dir:', real_dir 190 print ' link:', link_name 191 os.symlink(real_dir, link_name) 192 193 print "Adjusting PYTHONPATH ..." 194 sys.path.insert(0, local_python_base_dir)
195 #==========================================================
196 -def setup_local_repo_path():
197 198 local_repo_path = os.path.expanduser(os.path.join ( 199 '~', 200 '.gnumed', 201 'local_code', 202 str(current_client_branch) 203 )) 204 local_wxGladeWidgets_path = os.path.join(local_repo_path, 'Gnumed', 'wxGladeWidgets') 205 206 if not os.path.exists(local_wxGladeWidgets_path): 207 _log.debug('[%s] not found', local_wxGladeWidgets_path) 208 _log.info('local wxGlade widgets repository not available') 209 return 210 211 _log.info('local wxGlade widgets repository found:') 212 _log.info(local_wxGladeWidgets_path) 213 214 if not os.access(local_wxGladeWidgets_path, os.R_OK): 215 _log.error('invalid repo: no read access') 216 return 217 218 all_entries = os.listdir(os.path.join(local_repo_path, 'Gnumed')) 219 _log.debug('repo base contains: %s', all_entries) 220 all_entries.remove('wxGladeWidgets') 221 try: 222 all_entries.remove('__init__.py') 223 except ValueError: 224 _log.error('invalid repo: lacking __init__.py') 225 return 226 try: 227 all_entries.remove('__init__.pyc') 228 except ValueError: 229 pass 230 231 if len(all_entries) > 0: 232 _log.error('insecure repo: additional files or directories found') 233 return 234 235 # repo must be 0700 (rwx------) 236 stat_val = os.stat(local_wxGladeWidgets_path) 237 _log.debug('repo stat(): %s', stat_val) 238 perms = stat.S_IMODE(stat_val.st_mode) 239 _log.debug('repo permissions: %s (octal: %s)', perms, oct(perms)) 240 if perms != 448: # octal 0700 241 if os.name in ['nt']: 242 _log.warning('this platform does not support os.stat() permission checking') 243 else: 244 _log.error('insecure repo: permissions not 0600') 245 return 246 247 print "Activating local wxGlade widgets repository ..." 248 sys.path.insert(0, local_repo_path) 249 _log.debug('sys.path with repo:') 250 _log.debug(sys.path)
251 #==========================================================
252 -def setup_logging():
253 try: 254 from Gnumed.pycommon import gmLog2 as _gmLog2 255 except ImportError: 256 sys.exit(import_error_sermon % '\n '.join(sys.path)) 257 258 global gmLog2 259 gmLog2 = _gmLog2 260 261 global _log 262 _log = logging.getLogger('gm.launcher')
263 #==========================================================
264 -def log_startup_info():
265 _log.info(u'Starting up as main module (%s).', __version__) 266 _log.info(u'GNUmed client version [%s] on branch [%s]', current_client_version, current_client_branch) 267 _log.info(u'Platform: %s', platform.uname()) 268 _log.info(u'Python %s on %s (%s)', sys.version, sys.platform, os.name) 269 try: 270 import lsb_release 271 _log.info(u'%s' % lsb_release.get_distro_information()) 272 except ImportError: 273 pass 274 _log.info('process environment:') 275 for key, val in os.environ.items(): 276 _log.info(u' %s: %s', (u'${%s}' % key).rjust(30), val)
277 #==========================================================
278 -def setup_console_exception_handler():
279 from Gnumed.pycommon.gmTools import handle_uncaught_exception_console 280 281 sys.excepthook = handle_uncaught_exception_console
282 #==========================================================
283 -def setup_cli():
284 from Gnumed.pycommon import gmCfg2 285 286 global _cfg 287 _cfg = gmCfg2.gmCfgData() 288 _cfg.add_cli ( 289 short_options = _known_short_options, 290 long_options = _known_long_options 291 ) 292 293 val = _cfg.get(option = '--debug', source_order = [('cli', 'return')]) 294 if val is None: 295 val = False 296 _cfg.set_option ( 297 option = u'debug', 298 value = val 299 ) 300 301 val = _cfg.get(option = '--slave', source_order = [('cli', 'return')]) 302 if val is None: 303 val = False 304 _cfg.set_option ( 305 option = u'slave', 306 value = val 307 ) 308 309 val = _cfg.get(option = '--skip-update-check', source_order = [('cli', 'return')]) 310 if val is None: 311 val = False 312 _cfg.set_option ( 313 option = u'skip-update-check', 314 value = val 315 ) 316 317 val = _cfg.get(option = '--hipaa', source_order = [('cli', 'return')]) 318 if val is None: 319 val = False 320 _cfg.set_option ( 321 option = u'hipaa', 322 value = val 323 ) 324 325 val = _cfg.get(option = '--local-import', source_order = [('cli', 'return')]) 326 if val is None: 327 val = False 328 _cfg.set_option ( 329 option = u'local-import', 330 value = val 331 ) 332 333 _cfg.set_option ( 334 option = u'client_version', 335 value = current_client_version 336 ) 337 338 _cfg.set_option ( 339 option = u'client_branch', 340 value = current_client_branch 341 )
342 343 #==========================================================
344 -def handle_sig_term(signum, frame):
345 _log.critical('SIGTERM (SIG%s) received, shutting down ...' % signum) 346 gmLog2.flush() 347 print 'GNUmed: SIGTERM (SIG%s) received, shutting down ...' % signum 348 if frame is not None: 349 print '%s::%s@%s' % (frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno) 350 351 # FIXME: need to do something useful here 352 353 if _old_sig_term in [None, signal.SIG_IGN]: 354 sys.exit(signal.SIGTERM) 355 else: 356 _old_sig_term(signum, frame)
357 #----------------------------------------------------------
358 -def setup_signal_handlers():
359 global _old_sig_term 360 old_sig_term = signal.signal(signal.SIGTERM, handle_sig_term)
361 #==========================================================
362 -def setup_locale():
363 gmI18N.activate_locale() 364 365 td = _cfg.get(option = '--text-domain', source_order = [('cli', 'return')]) 366 l = _cfg.get(option = '--lang-gettext', source_order = [('cli', 'return')]) 367 gmI18N.install_domain(domain = td, language = l, prefer_local_catalog = _cfg.get(option = u'local-import')) 368 369 # make sure we re-get the default encoding 370 # in case it changed 371 gmLog2.set_string_encoding()
372 #==========================================================
373 -def handle_help_request():
374 src = [(u'cli', u'return')] 375 376 help_requested = ( 377 _cfg.get(option = u'--help', source_order = src) or 378 _cfg.get(option = u'-h', source_order = src) or 379 _cfg.get(option = u'-?', source_order = src) 380 ) 381 382 if help_requested: 383 print _( 384 'Help requested\n' 385 '--------------' 386 ) 387 print __doc__ 388 sys.exit(0)
389 #==========================================================
390 -def handle_version_request():
391 src = [(u'cli', u'return')] 392 393 version_requested = ( 394 _cfg.get(option = u'--version', source_order = src) or 395 _cfg.get(option = u'-V', source_order = src) 396 ) 397 398 if version_requested: 399 400 from Gnumed.pycommon.gmPG2 import map_client_branch2required_db_version, known_schema_hashes 401 402 print 'GNUmed version information' 403 print '--------------------------' 404 print 'client : %s on branch [%s]' % (current_client_version, current_client_branch) 405 print 'database : %s' % map_client_branch2required_db_version[current_client_branch] 406 print 'schema hash: %s' % known_schema_hashes[map_client_branch2required_db_version[current_client_branch]] 407 sys.exit(0)
408 409 #==========================================================
410 -def setup_paths_and_files():
411 """Create needed paths in user home directory.""" 412 413 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts'))) 414 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck'))) 415 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'docs'))) 416 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT'))) 417 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR'))) 418 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'xDT'))) 419 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'logs'))) 420 421 paths = gmTools.gmPaths(app_name = u'gnumed') 422 423 open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), 'a+').close()
424 #==========================================================
425 -def setup_date_time():
426 gmDateTime.init()
427 #==========================================================
428 -def setup_cfg():
429 """Detect and setup access to GNUmed config file. 430 431 Parts of this will have limited value due to 432 wxPython not yet being available. 433 """ 434 435 enc = gmI18N.get_encoding() 436 paths = gmTools.gmPaths(app_name = u'gnumed') 437 438 candidates = [ 439 # the current working dir 440 [u'workbase', os.path.join(paths.working_dir, 'gnumed.conf')], 441 # /etc/gnumed/ 442 [u'system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')], 443 # ~/.gnumed/ 444 [u'user', os.path.join(paths.user_config_dir, 'gnumed.conf')], 445 # CVS/tgz tree .../gnumed/client/ (IOW a local installation) 446 [u'local', os.path.join(paths.local_base_dir, 'gnumed.conf')] 447 ] 448 # --conf-file= 449 explicit_fname = _cfg.get(option = u'--conf-file', source_order = [(u'cli', u'return')]) 450 if explicit_fname is None: 451 candidates.append([u'explicit', None]) 452 else: 453 candidates.append([u'explicit', explicit_fname]) 454 455 for candidate in candidates: 456 _cfg.add_file_source ( 457 source = candidate[0], 458 file = candidate[1], 459 encoding = enc 460 ) 461 462 # --conf-file given but does not actually exist ? 463 if explicit_fname is not None: 464 if _cfg.source_files['explicit'] is None: 465 _log.error('--conf-file argument does not exist') 466 sys.exit(missing_cli_config_file % explicit_fname) 467 468 # any config file found at all ? 469 found_any_file = False 470 for f in _cfg.source_files.values(): 471 if f is not None: 472 found_any_file = True 473 break 474 if not found_any_file: 475 _log.error('no config file found at all') 476 sys.exit(no_config_files % '\n '.join(candidates)) 477 478 # mime type handling sources 479 fname = u'mime_type2file_extension.conf' 480 _cfg.add_file_source ( 481 source = u'user-mime', 482 file = os.path.join(paths.user_config_dir, fname), 483 encoding = enc 484 ) 485 _cfg.add_file_source ( 486 source = u'system-mime', 487 file = os.path.join(paths.system_config_dir, fname), 488 encoding = enc 489 )
490 #==========================================================
491 -def setup_ui_type():
492 global ui_type 493 494 ui_type = _cfg.get(option = u'--ui', source_order = [(u'cli', u'return')]) 495 496 if ui_type in [True, False, None]: 497 ui_type = 'wxp' 498 499 ui_type = ui_type.strip() 500 501 if ui_type not in _known_ui_types: 502 _log.error('unknown UI type: %s', ui_type) 503 _log.debug('known UI types: %s', str(_known_ui_types)) 504 print "GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type 505 ui_type = 'wxp' 506 507 _log.debug('UI type: %s', ui_type)
508 #==========================================================
509 -def setup_backend():
510 511 db_version = gmPG2.map_client_branch2required_db_version[current_client_branch] 512 _log.info('client expects database version [%s]', db_version) 513 _cfg.set_option ( 514 option = u'database_version', 515 value = db_version 516 ) 517 518 # set up database connection timezone 519 timezone = _cfg.get ( 520 group = u'backend', 521 option = 'client timezone', 522 source_order = [ 523 ('explicit', 'return'), 524 ('workbase', 'return'), 525 ('local', 'return'), 526 ('user', 'return'), 527 ('system', 'return') 528 ] 529 ) 530 if timezone is not None: 531 gmPG2.set_default_client_timezone(timezone)
532 #==========================================================
533 -def shutdown_backend():
534 gmPG2.shutdown()
535 #==========================================================
536 -def shutdown_logging():
537 538 # if _cfg.get(option = u'debug'): 539 # import types 540 541 # def get_refcounts(): 542 # refcount = {} 543 # # collect all classes 544 # for module in sys.modules.values(): 545 # for sym in dir(module): 546 # obj = getattr(module, sym) 547 # if type(obj) is types.ClassType: 548 # refcount[obj] = sys.getrefcount(obj) 549 # # sort by refcount 550 # pairs = map(lambda x: (x[1],x[0]), refcount.items()) 551 # pairs.sort() 552 # pairs.reverse() 553 # return pairs 554 555 # rcfile = open('./gm-refcount.lst', 'wb') 556 # for refcount, class_ in get_refcounts(): 557 # if not class_.__name__.startswith('wx'): 558 # rcfile.write('%10d %s\n' % (refcount, class_.__name__)) 559 # rcfile.close() 560 561 # do not choke on Windows 562 logging.raiseExceptions = False
563 #==========================================================
564 -def shutdown_tmp_dir():
565 566 tmp_dir = gmTools.gmPaths().tmp_dir 567 568 if _cfg.get(option = u'debug'): 569 _log.debug('not removing tmp dir (--debug mode): %s', tmp_dir) 570 return 571 572 _log.warning('removing tmp dir: %s', tmp_dir) 573 shutil.rmtree(tmp_dir, True)
574 #========================================================== 575 # main - launch the GNUmed wxPython GUI client 576 #---------------------------------------------------------- 577 setup_python_path() 578 setup_logging() 579 log_startup_info() 580 setup_console_exception_handler() 581 setup_cli() 582 setup_signal_handlers() 583 setup_local_repo_path() 584 585 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks 586 setup_locale() 587 handle_help_request() 588 handle_version_request() 589 setup_paths_and_files() 590 setup_date_time() 591 setup_cfg() 592 setup_ui_type() 593 594 from Gnumed.pycommon import gmPG2 595 if ui_type in [u'web']: 596 gmPG2.auto_request_login_params = False 597 setup_backend() 598 599 600 gmHooks.run_hook_script(hook = u'startup-before-GUI') 601 602 if ui_type == u'wxp': 603 from Gnumed.wxpython import gmGuiMain 604 profile_file = _cfg.get(option = u'--profile', source_order = [(u'cli', u'return')]) 605 if profile_file is not None: 606 _log.info('writing profiling data into %s', profile_file) 607 import profile 608 profile.run('gmGuiMain.main()', profile_file) 609 else: 610 gmGuiMain.main() 611 elif ui_type == u'web': 612 from Gnumed.proxiedpyjamas import gmWebGuiServer 613 gmWebGuiServer.main() 614 615 elif ui_type == u'chweb': 616 from Gnumed.CherryPy import gmGuiWeb 617 gmGuiWeb.main() 618 619 gmHooks.run_hook_script(hook = u'shutdown-post-GUI') 620 621 shutdown_backend() 622 shutdown_tmp_dir() 623 _log.info('Normally shutting down as main module.') 624 shutdown_logging() 625 626 #========================================================== 627