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('Starting up as main module (%s).', __version__) 266 _log.info('GNUmed client version [%s] on branch [%s]', current_client_version, current_client_branch) 267 _log.info('Platform: %s', platform.uname()) 268 _log.info('Python %s on %s (%s)', sys.version, sys.platform, os.name) 269 try: 270 import lsb_release 271 _log.info('%s' % lsb_release.get_distro_information()) 272 except ImportError: 273 pass
274 #==========================================================
275 -def setup_console_exception_handler():
276 from Gnumed.pycommon.gmTools import handle_uncaught_exception_console 277 278 sys.excepthook = handle_uncaught_exception_console
279 #==========================================================
280 -def setup_cli():
281 from Gnumed.pycommon import gmCfg2 282 283 global _cfg 284 _cfg = gmCfg2.gmCfgData() 285 _cfg.add_cli ( 286 short_options = _known_short_options, 287 long_options = _known_long_options 288 ) 289 290 val = _cfg.get(option = '--debug', source_order = [('cli', 'return')]) 291 if val is None: 292 val = False 293 _cfg.set_option ( 294 option = u'debug', 295 value = val 296 ) 297 298 val = _cfg.get(option = '--slave', source_order = [('cli', 'return')]) 299 if val is None: 300 val = False 301 _cfg.set_option ( 302 option = u'slave', 303 value = val 304 ) 305 306 val = _cfg.get(option = '--skip-update-check', source_order = [('cli', 'return')]) 307 if val is None: 308 val = False 309 _cfg.set_option ( 310 option = u'skip-update-check', 311 value = val 312 ) 313 314 val = _cfg.get(option = '--hipaa', source_order = [('cli', 'return')]) 315 if val is None: 316 val = False 317 _cfg.set_option ( 318 option = u'hipaa', 319 value = val 320 ) 321 322 val = _cfg.get(option = '--local-import', source_order = [('cli', 'return')]) 323 if val is None: 324 val = False 325 _cfg.set_option ( 326 option = u'local-import', 327 value = val 328 ) 329 330 _cfg.set_option ( 331 option = u'client_version', 332 value = current_client_version 333 ) 334 335 _cfg.set_option ( 336 option = u'client_branch', 337 value = current_client_branch 338 )
339 340 #==========================================================
341 -def handle_sig_term(signum, frame):
342 _log.critical('SIGTERM (SIG%s) received, shutting down ...' % signum) 343 gmLog2.flush() 344 print 'GNUmed: SIGTERM (SIG%s) received, shutting down ...' % signum 345 if frame is not None: 346 print '%s::%s@%s' % (frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno) 347 348 # FIXME: need to do something useful here 349 350 if _old_sig_term in [None, signal.SIG_IGN]: 351 sys.exit(signal.SIGTERM) 352 else: 353 _old_sig_term(signum, frame)
354 #----------------------------------------------------------
355 -def setup_signal_handlers():
356 global _old_sig_term 357 old_sig_term = signal.signal(signal.SIGTERM, handle_sig_term)
358 #==========================================================
359 -def setup_locale():
360 gmI18N.activate_locale() 361 362 td = _cfg.get(option = '--text-domain', source_order = [('cli', 'return')]) 363 l = _cfg.get(option = '--lang-gettext', source_order = [('cli', 'return')]) 364 gmI18N.install_domain(domain = td, language = l, prefer_local_catalog = _cfg.get(option = u'local-import')) 365 366 # make sure we re-get the default encoding 367 # in case it changed 368 gmLog2.set_string_encoding()
369 #==========================================================
370 -def handle_help_request():
371 src = [(u'cli', u'return')] 372 373 help_requested = ( 374 _cfg.get(option = u'--help', source_order = src) or 375 _cfg.get(option = u'-h', source_order = src) or 376 _cfg.get(option = u'-?', source_order = src) 377 ) 378 379 if help_requested: 380 print _( 381 'Help requested\n' 382 '--------------' 383 ) 384 print __doc__ 385 sys.exit(0)
386 #==========================================================
387 -def handle_version_request():
388 src = [(u'cli', u'return')] 389 390 version_requested = ( 391 _cfg.get(option = u'--version', source_order = src) or 392 _cfg.get(option = u'-V', source_order = src) 393 ) 394 395 if version_requested: 396 397 from Gnumed.pycommon.gmPG2 import map_client_branch2required_db_version, known_schema_hashes 398 399 print 'GNUmed version information' 400 print '--------------------------' 401 print 'client : %s on branch [%s]' % (current_client_version, current_client_branch) 402 print 'database : %s' % map_client_branch2required_db_version[current_client_branch] 403 print 'schema hash: %s' % known_schema_hashes[map_client_branch2required_db_version[current_client_branch]] 404 sys.exit(0)
405 406 #==========================================================
407 -def setup_paths_and_files():
408 """Create needed paths in user home directory.""" 409 410 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts'))) 411 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck'))) 412 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'tmp'))) 413 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'docs'))) 414 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT'))) 415 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR'))) 416 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'xDT'))) 417 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed', 'logs'))) 418 419 paths = gmTools.gmPaths(app_name = u'gnumed') 420 421 open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), 'a+').close()
422 #==========================================================
423 -def setup_date_time():
424 gmDateTime.init()
425 #==========================================================
426 -def setup_cfg():
427 """Detect and setup access to GNUmed config file. 428 429 Parts of this will have limited value due to 430 wxPython not yet being available. 431 """ 432 433 enc = gmI18N.get_encoding() 434 paths = gmTools.gmPaths(app_name = u'gnumed') 435 436 candidates = [ 437 # the current working dir 438 [u'workbase', os.path.join(paths.working_dir, 'gnumed.conf')], 439 # /etc/gnumed/ 440 [u'system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')], 441 # ~/.gnumed/ 442 [u'user', os.path.join(paths.user_config_dir, 'gnumed.conf')], 443 # CVS/tgz tree .../gnumed/client/ (IOW a local installation) 444 [u'local', os.path.join(paths.local_base_dir, 'gnumed.conf')] 445 ] 446 # --conf-file= 447 explicit_fname = _cfg.get(option = u'--conf-file', source_order = [(u'cli', u'return')]) 448 if explicit_fname is None: 449 candidates.append([u'explicit', None]) 450 else: 451 candidates.append([u'explicit', explicit_fname]) 452 453 for candidate in candidates: 454 _cfg.add_file_source ( 455 source = candidate[0], 456 file = candidate[1], 457 encoding = enc 458 ) 459 460 # --conf-file given but does not actually exist ? 461 if explicit_fname is not None: 462 if _cfg.source_files['explicit'] is None: 463 _log.error('--conf-file argument does not exist') 464 sys.exit(missing_cli_config_file % explicit_fname) 465 466 # any config file found at all ? 467 found_any_file = False 468 for f in _cfg.source_files.values(): 469 if f is not None: 470 found_any_file = True 471 break 472 if not found_any_file: 473 _log.error('no config file found at all') 474 sys.exit(no_config_files % '\n '.join(candidates)) 475 476 # mime type handling sources 477 fname = u'mime_type2file_extension.conf' 478 _cfg.add_file_source ( 479 source = u'user-mime', 480 file = os.path.join(paths.user_config_dir, fname), 481 encoding = enc 482 ) 483 _cfg.add_file_source ( 484 source = u'system-mime', 485 file = os.path.join(paths.system_config_dir, fname), 486 encoding = enc 487 )
488 #==========================================================
489 -def setup_ui_type():
490 global ui_type 491 492 ui_type = _cfg.get(option = u'--ui', source_order = [(u'cli', u'return')]) 493 494 if ui_type in [True, False, None]: 495 ui_type = 'wxp' 496 497 ui_type = ui_type.strip() 498 499 if ui_type not in _known_ui_types: 500 _log.error('unknown UI type: %s', ui_type) 501 _log.debug('known UI types: %s', str(_known_ui_types)) 502 print "GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type 503 ui_type = 'wxp' 504 505 _log.debug('UI type: %s', ui_type)
506 #==========================================================
507 -def setup_backend():
508 509 db_version = gmPG2.map_client_branch2required_db_version[current_client_branch] 510 _log.info('client expects database version [%s]', db_version) 511 _cfg.set_option ( 512 option = u'database_version', 513 value = db_version 514 ) 515 516 # set up database connection timezone 517 timezone = _cfg.get ( 518 group = u'backend', 519 option = 'client timezone', 520 source_order = [ 521 ('explicit', 'return'), 522 ('workbase', 'return'), 523 ('local', 'return'), 524 ('user', 'return'), 525 ('system', 'return') 526 ] 527 ) 528 if timezone is not None: 529 gmPG2.set_default_client_timezone(timezone)
530 #==========================================================
531 -def shutdown_backend():
532 gmPG2.shutdown()
533 #==========================================================
534 -def shutdown_logging():
535 536 # if _cfg.get(option = u'debug'): 537 # import types 538 539 # def get_refcounts(): 540 # refcount = {} 541 # # collect all classes 542 # for module in sys.modules.values(): 543 # for sym in dir(module): 544 # obj = getattr(module, sym) 545 # if type(obj) is types.ClassType: 546 # refcount[obj] = sys.getrefcount(obj) 547 # # sort by refcount 548 # pairs = map(lambda x: (x[1],x[0]), refcount.items()) 549 # pairs.sort() 550 # pairs.reverse() 551 # return pairs 552 553 # rcfile = open('./gm-refcount.lst', 'wb') 554 # for refcount, class_ in get_refcounts(): 555 # if not class_.__name__.startswith('wx'): 556 # rcfile.write('%10d %s\n' % (refcount, class_.__name__)) 557 # rcfile.close() 558 559 # do not choke on Windows 560 logging.raiseExceptions = False
561 #==========================================================
562 -def shutdown_tmp_dir():
563 564 tmp_dir = gmTools.gmPaths().tmp_dir 565 566 if _cfg.get(option = u'debug'): 567 _log.debug('not removing tmp dir (--debug mode): %s', tmp_dir) 568 return 569 570 _log.warning('removing tmp dir: %s', tmp_dir) 571 shutil.rmtree(tmp_dir, True)
572 #========================================================== 573 # main - launch the GNUmed wxPython GUI client 574 #---------------------------------------------------------- 575 setup_python_path() 576 setup_logging() 577 log_startup_info() 578 setup_console_exception_handler() 579 setup_cli() 580 setup_signal_handlers() 581 setup_local_repo_path() 582 583 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks 584 setup_locale() 585 handle_help_request() 586 handle_version_request() 587 setup_paths_and_files() 588 setup_date_time() 589 setup_cfg() 590 setup_ui_type() 591 592 from Gnumed.pycommon import gmPG2 593 if ui_type in [u'web']: 594 gmPG2.auto_request_login_params = False 595 setup_backend() 596 597 598 gmHooks.run_hook_script(hook = u'startup-before-GUI') 599 600 if ui_type == u'wxp': 601 from Gnumed.wxpython import gmGuiMain 602 profile_file = _cfg.get(option = u'--profile', source_order = [(u'cli', u'return')]) 603 if profile_file is not None: 604 _log.info('writing profiling data into %s', profile_file) 605 import profile 606 profile.run('gmGuiMain.main()', profile_file) 607 else: 608 gmGuiMain.main() 609 elif ui_type == u'web': 610 from Gnumed.proxiedpyjamas import gmWebGuiServer 611 gmWebGuiServer.main() 612 613 elif ui_type == u'chweb': 614 from Gnumed.CherryPy import gmGuiWeb 615 gmGuiWeb.main() 616 617 gmHooks.run_hook_script(hook = u'shutdown-post-GUI') 618 619 shutdown_backend() 620 shutdown_tmp_dir() 621 _log.info('Normally shutting down as main module.') 622 shutdown_logging() 623 624 #========================================================== 625