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