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 local_python_base_dir = os.path.dirname ( 179 os.path.abspath(os.path.join(sys.argv[0], '..')) 180 ) 181 print "Running from local source tree (%s) ..." % local_python_base_dir 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 (%s) ..." % local_wxGladeWidgets_path 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 gnumed_DIR_README_TEXT = u"""GNUmed Electronic Medical Record 414 415 %s/ 416 417 This directory should only ever contain files which the 418 user will come into direct contact with while using the 419 application (say, by selecting a file from the file system, 420 as when selecting document parts from files). You can create 421 subdirectories here as you see fit for the purpose. 422 423 This directory will also serve as the default directory when 424 GNUmed asks the user to select a directory for storing a 425 file. 426 427 Any files which are NOT intended for direct user interaction 428 but must be configured to live at a known location (say, 429 inter-application data exchange files) should be put under 430 the hidden directory "%s/".""" % ( 431 os.path.expanduser(os.path.join('~', 'gnumed')), 432 os.path.expanduser(os.path.join('~', '.gnumed')) 433 ) 434 435 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts'))) 436 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck'))) 437 gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'error_logs'))) 438 gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed'))) 439 440 README = codecs.open(os.path.expanduser(os.path.join('~', 'gnumed', '00_README')), 'wb', 'utf8') 441 README.write(gnumed_DIR_README_TEXT) 442 README.close() 443 444 paths = gmTools.gmPaths(app_name = u'gnumed') 445 446 open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), 'a+').close()
447 #==========================================================
448 -def setup_date_time():
449 gmDateTime.init()
450 #==========================================================
451 -def setup_cfg():
452 """Detect and setup access to GNUmed config file. 453 454 Parts of this will have limited value due to 455 wxPython not yet being available. 456 """ 457 458 enc = gmI18N.get_encoding() 459 paths = gmTools.gmPaths(app_name = u'gnumed') 460 461 candidates = [ 462 # the current working dir 463 [u'workbase', os.path.join(paths.working_dir, 'gnumed.conf')], 464 # /etc/gnumed/ 465 [u'system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')], 466 # ~/.gnumed/ 467 [u'user', os.path.join(paths.user_config_dir, 'gnumed.conf')], 468 # CVS/tgz tree .../gnumed/client/ (IOW a local installation) 469 [u'local', os.path.join(paths.local_base_dir, 'gnumed.conf')] 470 ] 471 # --conf-file= 472 explicit_fname = _cfg.get(option = u'--conf-file', source_order = [(u'cli', u'return')]) 473 if explicit_fname is None: 474 candidates.append([u'explicit', None]) 475 else: 476 candidates.append([u'explicit', explicit_fname]) 477 478 for candidate in candidates: 479 _cfg.add_file_source ( 480 source = candidate[0], 481 file = candidate[1], 482 encoding = enc 483 ) 484 485 # --conf-file given but does not actually exist ? 486 if explicit_fname is not None: 487 if _cfg.source_files['explicit'] is None: 488 _log.error('--conf-file argument does not exist') 489 sys.exit(missing_cli_config_file % explicit_fname) 490 491 # any config file found at all ? 492 found_any_file = False 493 for f in _cfg.source_files.values(): 494 if f is not None: 495 found_any_file = True 496 break 497 if not found_any_file: 498 _log.error('no config file found at all') 499 sys.exit(no_config_files % '\n '.join(candidates)) 500 501 # mime type handling sources 502 fname = u'mime_type2file_extension.conf' 503 _cfg.add_file_source ( 504 source = u'user-mime', 505 file = os.path.join(paths.user_config_dir, fname), 506 encoding = enc 507 ) 508 _cfg.add_file_source ( 509 source = u'system-mime', 510 file = os.path.join(paths.system_config_dir, fname), 511 encoding = enc 512 )
513 #==========================================================
514 -def setup_ui_type():
515 global ui_type 516 517 ui_type = _cfg.get(option = u'--ui', source_order = [(u'cli', u'return')]) 518 519 if ui_type in [True, False, None]: 520 ui_type = 'wxp' 521 522 ui_type = ui_type.strip() 523 524 if ui_type not in _known_ui_types: 525 _log.error('unknown UI type: %s', ui_type) 526 _log.debug('known UI types: %s', str(_known_ui_types)) 527 print "GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type 528 ui_type = 'wxp' 529 530 _log.debug('UI type: %s', ui_type)
531 #==========================================================
532 -def setup_backend():
533 534 db_version = gmPG2.map_client_branch2required_db_version[current_client_branch] 535 _log.info('client expects database version [%s]', db_version) 536 _cfg.set_option ( 537 option = u'database_version', 538 value = db_version 539 ) 540 541 # set up database connection timezone 542 timezone = _cfg.get ( 543 group = u'backend', 544 option = 'client timezone', 545 source_order = [ 546 ('explicit', 'return'), 547 ('workbase', 'return'), 548 ('local', 'return'), 549 ('user', 'return'), 550 ('system', 'return') 551 ] 552 ) 553 if timezone is not None: 554 gmPG2.set_default_client_timezone(timezone)
555 #==========================================================
556 -def shutdown_backend():
557 gmPG2.shutdown()
558 #==========================================================
559 -def shutdown_logging():
560 561 # if _cfg.get(option = u'debug'): 562 # import types 563 564 # def get_refcounts(): 565 # refcount = {} 566 # # collect all classes 567 # for module in sys.modules.values(): 568 # for sym in dir(module): 569 # obj = getattr(module, sym) 570 # if type(obj) is types.ClassType: 571 # refcount[obj] = sys.getrefcount(obj) 572 # # sort by refcount 573 # pairs = map(lambda x: (x[1],x[0]), refcount.items()) 574 # pairs.sort() 575 # pairs.reverse() 576 # return pairs 577 578 # rcfile = open('./gm-refcount.lst', 'wb') 579 # for refcount, class_ in get_refcounts(): 580 # if not class_.__name__.startswith('wx'): 581 # rcfile.write('%10d %s\n' % (refcount, class_.__name__)) 582 # rcfile.close() 583 584 # do not choke on Windows 585 logging.raiseExceptions = False
586 #==========================================================
587 -def shutdown_tmp_dir():
588 589 tmp_dir = gmTools.gmPaths().tmp_dir 590 591 if _cfg.get(option = u'debug'): 592 _log.debug('not removing tmp dir (--debug mode): %s', tmp_dir) 593 return 594 595 _log.warning('removing tmp dir: %s', tmp_dir) 596 shutil.rmtree(tmp_dir, True)
597 #========================================================== 598 # main - launch the GNUmed wxPython GUI client 599 #---------------------------------------------------------- 600 setup_python_path() 601 setup_logging() 602 log_startup_info() 603 setup_console_exception_handler() 604 setup_cli() 605 setup_signal_handlers() 606 setup_local_repo_path() 607 608 from Gnumed.pycommon import gmI18N, gmTools, gmDateTime, gmHooks 609 setup_locale() 610 handle_help_request() 611 handle_version_request() 612 setup_paths_and_files() 613 setup_date_time() 614 setup_cfg() 615 setup_ui_type() 616 617 from Gnumed.pycommon import gmPG2 618 if ui_type in [u'web']: 619 gmPG2.auto_request_login_params = False 620 setup_backend() 621 622 623 gmHooks.run_hook_script(hook = u'startup-before-GUI') 624 625 if ui_type == u'wxp': 626 from Gnumed.wxpython import gmGuiMain 627 profile_file = _cfg.get(option = u'--profile', source_order = [(u'cli', u'return')]) 628 if profile_file is not None: 629 _log.info('writing profiling data into %s', profile_file) 630 import profile 631 profile.run('gmGuiMain.main()', profile_file) 632 else: 633 gmGuiMain.main() 634 elif ui_type == u'web': 635 from Gnumed.proxiedpyjamas import gmWebGuiServer 636 gmWebGuiServer.main() 637 638 elif ui_type == u'chweb': 639 from Gnumed.CherryPy import gmGuiWeb 640 gmGuiWeb.main() 641 642 gmHooks.run_hook_script(hook = u'shutdown-post-GUI') 643 644 shutdown_backend() 645 shutdown_tmp_dir() 646 _log.info('Normally shutting down as main module.') 647 shutdown_logging() 648 649 #========================================================== 650