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