Gnumed.gnumed

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  --tool=<TOOL>
     21   Run TOOL instead of the main GUI.
     22  --text-domain=<text domain>
     23   Set this to change the name of the language file to be loaded.
     24   Note, this does not change the directory the file is searched in,
     25   only the name of the file where messages are loaded from. The
     26   standard textdomain is, of course, "gnumed.mo".
     27  --log-file=<file>
     28   Use this to change the name of the log file.
     29   See gmLog2.py to find out where the standard log file would
     30   end up.
     31  --conf-file=<file>
     32   Use configuration file <file> instead of searching for it in
     33   standard locations.
     34  --lang-gettext=<language>
     35   Explicitly set the language to use in gettext translation. The very
     36   same effect can be achieved by setting the environment variable $LANG
     37   from a launcher script.
     38  --override-schema-check
     39   Continue loading the client even if the database schema version
     40   and the client software version cannot be verified to be compatible.
     41  --skip-update-check
     42   Skip checking for client updates. This is useful during development
     43   and when the update check URL is unavailable (down).
     44  --local-import
     45   Adjust the PYTHONPATH such that GNUmed can be run from a local source tree.
     46  --ui=<ui type>
     47   Start an alternative UI. Defaults to wxPython if not specified.
     48   Currently "wxp" (wxPython) only.
     49  --wxp=<version>
     50   Explicitely request a wxPython version. Can be set to either "2" or "3".
     51   Defaults to "try 3, then 2" if not set.
     52  --version, -V
     53   Show version information.
     54  --help, -h, or -?
     55   Show this help.
     56  """
     57  #==========================================================
     58  # SPDX-License-Identifier: GPL-2.0-or-later
     59  __author__ = "H. Herb <hherb@gnumed.net>, K. Hilbert <Karsten.Hilbert@gmx.net>, I. Haywood <i.haywood@ugrad.unimelb.edu.au>"
     60  __license__ = "GPL v2 or later (details at http://www.gnu.org)"
     61
     62
     63  # standard library
     64  import sys
     65  import os
     66  import platform
     67  import faulthandler
     68  import random
     69  import logging
     70  import signal
     71  import os.path
     72  import shutil
     73  import stat
     74  import io
     75
     76
     77  # do not run as module
     78  if __name__ != "__main__":
     79          print("GNUmed startup: This is not intended to be imported as a module !")
     80          print("-----------------------------------------------------------------")
     81          print(__doc__)
     82          sys.exit(1)
     83
     84
     85  # do not run as root
     86  if os.name in ['posix'] and os.geteuid() == 0:
     87          print("""
     88  GNUmed startup: GNUmed should not be run as root.
     89  -------------------------------------------------
     90
     91  Running GNUmed as <root> can potentially put all
     92  your medical data at risk. It is strongly advised
     93  against. Please run GNUmed as a non-root user.
     94  """)
     95          sys.exit(1)
     96
     97  #----------------------------------------------------------
     98  current_client_version = u'1.7.9'
     99  current_client_branch = u'1.7'
    100
    101  _log = None
    102  _pre_log_buffer = []
    103  _cfg = None
    104  _old_sig_term = None
    105  _known_short_options = 'h?V'
    106  _known_long_options = [
    107          'debug',
    108          'slave',
    109          'skip-update-check',
    110          'profile=',
    111          'text-domain=',
    112          'log-file=',
    113          'conf-file=',
    114          'lang-gettext=',
    115          'ui=',
    116          'override-schema-check',
    117          'local-import',
    118          'help',
    119          'version',
    120          'hipaa',
    121          'wxp=',
    122          'tool='
    123  ]
    124
    125  _known_ui_types = [
    126          'web',
    127          'wxp',
    128          'chweb'
    129  ]
    130
    131  _known_tools = [
    132          'check_enc_epi_xref',
    133          'export_pat_emr_structure',
    134          'check_mimetypes_in_archive'
    135  ]
    136
    137
    138  import_error_sermon = """
    139  GNUmed startup: Cannot load GNUmed Python modules !
    140  ---------------------------------------------------
    141  CRITICAL ERROR: Program halted.
    142
    143  Please make sure you have:
    144
    145   1) the required third-party Python modules installed
    146   2) the GNUmed Python modules linked or installed into site-packages/
    147      (if you do not run from a CVS tree the installer should have taken care of that)
    148   3) your PYTHONPATH environment variable set up correctly
    149
    150  <sys.path> is currently set to:
    151
    152   %s
    153
    154  If you are running from a copy of the CVS tree make sure you
    155  did run gnumed/check-prerequisites.sh with good results.
    156
    157  If you still encounter errors after checking the above
    158  requirements please ask on the mailing list.
    159  """
    160
    161
    162  missing_cli_config_file = """
    163  GNUmed startup: Missing configuration file.
    164  -------------------------------------------
    165
    166  You explicitly specified a configuration file
    167  on the command line:
    168
    169          --conf-file=%s
    170
    171  The file does not exist, however.
    172  """
    173
    174
    175  no_config_files = """
    176  GNUmed startup: Missing configuration files.
    177  --------------------------------------------
    178
    179  None of the below candidate configuration
    180  files could be found:
    181
    182   %s
    183
    184  Cannot run GNUmed without any of them.
    185  """
    186
    187  #==========================================================
    188  # convenience functions
    189  #----------------------------------------------------------
    190 -def _symlink_windows(source, link_name):
    191          import ctypes
    192          csl = ctypes.windll.kernel32.CreateSymbolicLinkW
    193          csl.argtypes = (ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_uint32)
    194          csl.restype = ctypes.c_ubyte
    195          if os.path.isdir(source):
    196                  flags = 1
    197          else:
    198                  flags = 0
    199          ret_code = csl(link_name, source.replace('/', '\\'), flags)
    200          if ret_code == 0:
    201                  raise ctypes.WinError()
    202          return ret_code
    203
    204  #==========================================================
    205  # startup helpers
    206  #----------------------------------------------------------
    207 -def setup_fault_handler(target=None):
    208          if target is None:
    209                  faulthandler.enable()
    210                  _pre_log_buffer.append('<faulthandler> enabled, target = [console]: %s' % faulthandler)
    211                  return
    212          _pre_log_buffer.append('<faulthandler> enabled, target = [%s]: %s' % (target, faulthandler))
    213          faulthandler.enable(file = target)
    214
    215  #==========================================================
    216 -def setup_console_encoding():
    217          print_lines = []
    218          try:
    219                  sys.stdout.reconfigure(errors = 'surrogateescape')
    220                  sys.stderr.reconfigure(errors = 'surrogateescape')
    221                  _pre_log_buffer.append('stdout/stderr reconfigured to use <surrogateescape> for encoding errors')
    222                  return
    223          except AttributeError:
    224                  line = 'cannot reconfigure sys.stdout/stderr to use <errors="surrogateescape"> (needs Python 3.7+)'
    225                  _pre_log_buffer.append(line)
    226                  print_lines.append(line)
    227          try:
    228                  _pre_log_buffer.append('sys.stdout/stderr default to "${PYTHONIOENCODING}=%s"' % os.environ['PYTHONIOENCODING'])
    229                  return
    230          except KeyError:
    231                  lines = [
    232                          '${PYTHONIOENCODING} is not set up, use <PYTHONIOENCODING=utf-8:surrogateescape> in the shell (for Python < 3.7)',
    233                          'console encoding errors may occur'
    234                  ]
    235                  for line in lines:
    236                          print_lines.append(line)
    237                          _pre_log_buffer.append(line)
    238                  for line in print_lines:
    239                          print('GNUmed startup:', line)
    240
    241  #==========================================================
    242 -def setup_python_path():
    243
    244          if not '--local-import' in sys.argv:
    245                  _pre_log_buffer.append('running against systemwide install')
    246                  return
    247
    248          local_python_import_dir = os.path.dirname (
    249                  os.path.abspath(os.path.join(sys.argv[0], '..'))
    250          )
    251          print("Running from local source tree (%s) ..." % local_python_import_dir)
    252          _pre_log_buffer.append("running from local source tree: %s" % local_python_import_dir)
    253
    254          # does the path exist at all, physically ?
    255          # (*broken* links are reported as False)
    256          link_name = os.path.join(local_python_import_dir, 'Gnumed')
    257          if os.path.exists(link_name):
    258                  _pre_log_buffer.append('local module import dir symlink exists: %s' % link_name)
    259          else:
    260                  real_dir = os.path.join(local_python_import_dir, 'client')
    261                  print('Creating local module import symlink ...')
    262                  print(' real dir:', real_dir)
    263                  print('     link:', link_name)
    264                  try:
    265                          os.symlink(real_dir, link_name)
    266                  except AttributeError:
    267                          _pre_log_buffer.append('Windows does not have os.symlink(), resorting to ctypes')
    268                          result = _symlink_windows(real_dir, link_name)
    269                          _pre_log_buffer.append('ctypes.windll.kernel32.CreateSymbolicLinkW() exit code: %s', result)
    270                  _pre_log_buffer.append('created local module import dir symlink: link [%s] => dir [%s]' % (link_name, real_dir))
    271
    272          sys.path.insert(0, local_python_import_dir)
    273          _pre_log_buffer.append('sys.path with local module import base dir prepended: %s' % sys.path)
    274
    275  #==========================================================
    276 -def setup_local_repo_path():
    277
    278          local_repo_path = os.path.expanduser(os.path.join (
    279                  '~',
    280                  '.gnumed',
    281                  'local_code',
    282                  str(current_client_branch)
    283          ))
    284          local_wxGladeWidgets_path = os.path.join(local_repo_path, 'Gnumed', 'wxGladeWidgets')
    285
    286          if not os.path.exists(local_wxGladeWidgets_path):
    287                  _log.debug('[%s] not found', local_wxGladeWidgets_path)
    288                  _log.info('local wxGlade widgets repository not available')
    289                  return
    290
    291          _log.info('local wxGlade widgets repository found:')
    292          _log.info(local_wxGladeWidgets_path)
    293
    294          if not os.access(local_wxGladeWidgets_path, os.R_OK):
    295                  _log.error('invalid repo: no read access')
    296                  return
    297
    298          all_entries = os.listdir(os.path.join(local_repo_path, 'Gnumed'))
    299          _log.debug('repo base contains: %s', all_entries)
    300          all_entries.remove('wxGladeWidgets')
    301          try:
    302                  all_entries.remove('__init__.py')
    303          except ValueError:
    304                  _log.error('invalid repo: lacking __init__.py')
    305                  return
    306          try:
    307                  all_entries.remove('__init__.pyc')
    308          except ValueError:
    309                  pass
    310
    311          if len(all_entries) > 0:
    312                  _log.error('insecure repo: additional files or directories found')
    313                  return
    314
    315          # repo must be 0700 (rwx------)
    316          stat_val = os.stat(local_wxGladeWidgets_path)
    317          _log.debug('repo stat(): %s', stat_val)
    318          perms = stat.S_IMODE(stat_val.st_mode)
    319          _log.debug('repo permissions: %s (octal: %s)', perms, oct(perms))
    320          if perms != 448:                                # octal 0700
    321                  if os.name in ['nt']:
    322                          _log.warning('this platform does not support os.stat() permission checking')
    323                  else:
    324                          _log.error('insecure repo: permissions not 0600')
    325                          return
    326
    327          print("Activating local wxGlade widgets repository (%s) ..." % local_wxGladeWidgets_path)
    328          sys.path.insert(0, local_repo_path)
    329          _log.debug('sys.path with repo:')
    330          _log.debug(sys.path)
    331
    332  #==========================================================
    333 -def setup_logging():
    334          try:
    335                  from Gnumed.pycommon import gmLog2 as _gmLog2
    336          except ImportError:
    337                  print(import_error_sermon % '\n '.join(sys.path))
    338                  sys.exit(1)
    339
    340          print("Log file:", _gmLog2._logfile.name)
    341          setup_fault_handler(target = _gmLog2._logfile)
    342
    343          global gmLog2
    344          gmLog2 = _gmLog2
    345
    346          global _log
    347          _log = logging.getLogger('gm.launcher')
    348
    349  #==========================================================
    350 -def log_startup_info():
    351          global _pre_log_buffer
    352          if len(_pre_log_buffer) > 0:
    353                  _log.info('early startup log buffer:')
    354          for line in _pre_log_buffer:
    355                  _log.info(' ' + line)
    356          del _pre_log_buffer
    357          _log.info('GNUmed client version [%s] on branch [%s]', current_client_version, current_client_branch)
    358          _log.info('Platform: %s', platform.uname())
    359          _log.info(('Python %s on %s (%s)' % (sys.version, sys.platform, os.name)).replace('\n', '<\\n>'))
    360          try:
    361                  import lsb_release
    362                  _log.info('lsb_release: %s', lsb_release.get_distro_information())
    363          except ImportError:
    364                  pass
    365          _log.info('threading: %s', sys.thread_info)
    366          _log.info('os.getcwd(): [%s]', os.getcwd())
    367          _log.info('process environment:')
    368          for key, val in os.environ.items():
    369                  _log.info(' %s: %s' % (('${%s}' % key).rjust(30),       val))
    370          import sysconfig
    371          _log.info('sysconfig - platform [%s] python version [%s]:', sysconfig.get_platform(), sysconfig.get_python_version())
    372          paths = sysconfig.get_paths()
    373          for path in paths:
    374                  _log.info(' %s: %s', path.rjust(30), paths[path])
    375          conf_vars = sysconfig.get_config_vars()
    376          for var in conf_vars:
    377                  _log.info(' %s: %s', var.rjust(40), conf_vars[var])
    378
    379  #==========================================================
    380 -def setup_console_exception_handler():
    381          from Gnumed.pycommon.gmTools import handle_uncaught_exception_console
    382
    383          sys.excepthook = handle_uncaught_exception_console
    384
    385  #==========================================================
    386 -def setup_cli():
    387          from Gnumed.pycommon import gmCfg2
    388
    389          global _cfg
    390          _cfg = gmCfg2.gmCfgData()
    391          _cfg.add_cli (
    392                  short_options = _known_short_options,
    393                  long_options = _known_long_options
    394          )
    395
    396          val = _cfg.get(option = '--debug', source_order = [('cli', 'return')])
    397          if val is None:
    398                  val = False
    399          _cfg.set_option (
    400                  option = 'debug',
    401                  value = val
    402          )
    403
    404          val = _cfg.get(option = '--slave', source_order = [('cli', 'return')])
    405          if val is None:
    406                  val = False
    407          _cfg.set_option (
    408                  option = 'slave',
    409                  value = val
    410          )
    411
    412          val = _cfg.get(option = '--skip-update-check', source_order = [('cli', 'return')])
    413          if val is None:
    414                  val = False
    415          _cfg.set_option (
    416                  option = 'skip-update-check',
    417                  value = val
    418          )
    419
    420          val = _cfg.get(option = '--hipaa', source_order = [('cli', 'return')])
    421          if val is None:
    422                  val = False
    423          _cfg.set_option (
    424                  option = 'hipaa',
    425                  value = val
    426          )
    427
    428          val = _cfg.get(option = '--local-import', source_order = [('cli', 'return')])
    429          if val is None:
    430                  val = False
    431          _cfg.set_option (
    432                  option = 'local-import',
    433                  value = val
    434          )
    435
    436          _cfg.set_option (
    437                  option = 'client_version',
    438                  value = current_client_version
    439          )
    440
    441          _cfg.set_option (
    442                  option = 'client_branch',
    443                  value = current_client_branch
    444          )
    445
    446  #==========================================================
    447 -def handle_sig_term(signum, frame):
    448          _log.critical('SIGTERM (SIG%s) received, shutting down ...' % signum)
    449          gmLog2.flush()
    450          print('GNUmed: SIGTERM (SIG%s) received, shutting down ...' % signum)
    451          if frame is not None:
    452                  print('%s::%s@%s' % (frame.f_code.co_filename, frame.f_code.co_name, frame.f_lineno))
    453
    454          # FIXME: need to do something useful here
    455
    456          if _old_sig_term in [None, signal.SIG_IGN]:
    457                  sys.exit(1)
    458          else:
    459                  _old_sig_term(signum, frame)
    460
    461  #----------------------------------------------------------
    462 -def setup_signal_handlers():
    463          global _old_sig_term
    464          old_sig_term = signal.signal(signal.SIGTERM, handle_sig_term)
    465
    466  #==========================================================
    467 -def setup_locale():
    468          gmI18N.activate_locale()
    469
    470          td = _cfg.get(option = '--text-domain', source_order = [('cli', 'return')])
    471          l =  _cfg.get(option = '--lang-gettext', source_order = [('cli', 'return')])
    472          gmI18N.install_domain(domain = td, language = l, prefer_local_catalog = _cfg.get(option = 'local-import'))
    473
    474  #       # make sure we re-get the default encoding
    475  #       # in case it changed
    476  #       gmLog2.set_string_encoding()
    477
    478  #==========================================================
    479 -def handle_help_request():
    480          src = [('cli', 'return')]
    481
    482          help_requested = (
    483                  _cfg.get(option = '--help', source_order = src) or
    484                  _cfg.get(option = '-h', source_order = src) or
    485                  _cfg.get(option = '-?', source_order = src)
    486          )
    487
    488          if help_requested:
    489                  print(_(
    490                          'Help requested\n'
    491                          '--------------'
    492                  ))
    493                  print(__doc__)
    494                  sys.exit(0)
    495
    496  #==========================================================
    497 -def handle_version_request():
    498          src = [('cli', 'return')]
    499
    500          version_requested = (
    501                  _cfg.get(option = '--version', source_order = src) or
    502                  _cfg.get(option = '-V', source_order = src)
    503          )
    504
    505          if version_requested:
    506
    507                  from Gnumed.pycommon.gmPG2 import map_client_branch2required_db_version, known_schema_hashes
    508
    509                  print('GNUmed version information')
    510                  print('--------------------------')
    511                  print('client     : %s on branch [%s]' % (current_client_version, current_client_branch))
    512                  print('database   : %s' % map_client_branch2required_db_version[current_client_branch])
    513                  print('schema hash: %s' % known_schema_hashes[map_client_branch2required_db_version[current_client_branch]])
    514                  sys.exit(0)
    515
    516  #==========================================================
    517 -def setup_paths_and_files():
    518          """Create needed paths in user home directory."""
    519
    520          gnumed_DIR_README_TEXT = """GNUmed Electronic Medical Record
    521
    522          %s/
    523
    524  This directory should only ever contain files which the
    525  user will come into direct contact with while using the
    526  application (say, by selecting a file from the file system,
    527  as when selecting document parts from files). You can create
    528  subdirectories here as you see fit for the purpose.
    529
    530  This directory will also serve as the default directory when
    531  GNUmed asks the user to select a directory for storing a
    532  file.
    533
    534  Any files which are NOT intended for direct user interaction
    535  but must be configured to live at a known location (say,
    536  inter-application data exchange files) should be put under
    537  the hidden directory "%s/".""" % (
    538                  os.path.expanduser(os.path.join('~', 'gnumed')),
    539                  os.path.expanduser(os.path.join('~', '.gnumed'))
    540          )
    541
    542          gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'scripts')))
    543          gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'spellcheck')))
    544          gmTools.mkdir(os.path.expanduser(os.path.join('~', '.gnumed', 'error_logs')))
    545          gmTools.mkdir(os.path.expanduser(os.path.join('~', 'gnumed')))
    546
    547          README = io.open(os.path.expanduser(os.path.join('~', 'gnumed', '00_README')), mode = 'wt', encoding = 'utf8')
    548          README.write(gnumed_DIR_README_TEXT)
    549          README.close()
    550
    551          # wxPython not available yet
    552          paths = gmTools.gmPaths(app_name = 'gnumed')
    553          print("Temp dir:", paths.tmp_dir)
    554
    555          # ensure there's a user-level config file
    556          io.open(os.path.expanduser(os.path.join('~', '.gnumed', 'gnumed.conf')), mode = 'a+t').close()
    557
    558          # symlink log file into temporary directory for easier debugging (everything in one place)
    559          logfile_link = os.path.join(paths.tmp_dir, 'zzz-gnumed.log')
    560          gmTools.mklink (gmLog2._logfile.name, logfile_link, overwrite = False)
    561
    562  #==========================================================
    563 -def setup_date_time():
    564          gmDateTime.init()
    565
    566  #==========================================================
    567 -def setup_cfg():
    568          """Detect and setup access to GNUmed config file.
    569
    570          Parts of this will have limited value due to
    571          wxPython not yet being available.
    572          """
    573
    574          enc = gmI18N.get_encoding()
    575          paths = gmTools.gmPaths(app_name = 'gnumed')
    576
    577          candidates = [
    578                  # the current working dir
    579                  ['workbase', os.path.join(paths.working_dir, 'gnumed.conf')],
    580                  # /etc/gnumed/
    581                  ['system', os.path.join(paths.system_config_dir, 'gnumed-client.conf')],
    582                  # ~/.gnumed/
    583                  ['user', os.path.join(paths.user_config_dir, 'gnumed.conf')],
    584                  # CVS/tgz tree .../gnumed/client/ (IOW a local installation)
    585                  ['local', os.path.join(paths.local_base_dir, 'gnumed.conf')]
    586          ]
    587          # --conf-file=
    588          explicit_fname = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')])
    589          if explicit_fname is None:
    590                  candidates.append(['explicit', None])
    591          else:
    592                  candidates.append(['explicit', explicit_fname])
    593
    594          for candidate in candidates:
    595                  _cfg.add_file_source (
    596                          source = candidate[0],
    597                          file = candidate[1],
    598                          encoding = enc
    599                  )
    600
    601          # --conf-file given but does not actually exist ?
    602          if explicit_fname is not None:
    603                  if _cfg.source_files['explicit'] is None:
    604                          _log.error('--conf-file argument does not exist')
    605                          print(missing_cli_config_file % explicit_fname)
    606                          sys.exit(1)
    607
    608          # any config file found at all ?
    609          found_any_file = False
    610          for f in _cfg.source_files.values():
    611                  if f is not None:
    612                          found_any_file = True
    613                          break
    614          if not found_any_file:
    615                  _log.error('no config file found at all')
    616                  print(no_config_files % '\n '.join(candidates))
    617                  sys.exit(1)
    618
    619          # mime type handling sources
    620          fname = 'mime_type2file_extension.conf'
    621          _cfg.add_file_source (
    622                  source = 'user-mime',
    623                  file = os.path.join(paths.user_config_dir, fname),
    624                  encoding = enc
    625          )
    626          _cfg.add_file_source (
    627                  source = 'system-mime',
    628                  file = os.path.join(paths.system_config_dir, fname),
    629                  encoding = enc
    630          )
    631
    632  #==========================================================
    633 -def setup_ui_type():
    634          global ui_type
    635          ui_type = _cfg.get(option = '--ui', source_order = [('cli', 'return')])
    636          if ui_type in [True, False, None]:
    637                  ui_type = 'wxp'
    638          ui_type = ui_type.strip()
    639          if ui_type not in _known_ui_types:
    640                  _log.error('unknown UI type requested: %s', ui_type)
    641                  _log.debug('known UI types are: %s', str(_known_ui_types))
    642                  print("GNUmed startup: Unknown UI type (%s). Defaulting to wxPython client." % ui_type)
    643                  ui_type = 'wxp'
    644          _log.debug('UI type: %s', ui_type)
    645
    646  #==========================================================
    647 -def setup_backend_environment():
    648
    649          db_version = gmPG2.map_client_branch2required_db_version[current_client_branch]
    650          _log.info('client expects database version [%s]', db_version)
    651          _cfg.set_option (
    652                  option = 'database_version',
    653                  value = db_version
    654          )
    655
    656          # set up database connection timezone
    657          timezone = _cfg.get (
    658                  group = 'backend',
    659                  option = 'client timezone',
    660                  source_order = [
    661                          ('explicit', 'return'),
    662                          ('workbase', 'return'),
    663                          ('local', 'return'),
    664                          ('user', 'return'),
    665                          ('system', 'return')
    666                  ]
    667          )
    668  #       if timezone is not None:
    669  #               gmPG2.set_default_client_timezone(timezone)
    670
    671  #==========================================================
    672 -def run_gui():
    673          gmHooks.run_hook_script(hook = 'startup-before-GUI')
    674
    675          if ui_type == 'wxp':
    676                  from Gnumed.wxpython import gmGuiMain
    677                  profile_file = _cfg.get(option = '--profile', source_order = [('cli', 'return')])
    678                  if profile_file is not None:
    679                          _log.info('writing profiling data into %s', profile_file)
    680                          import profile
    681                          profile.run('gmGuiMain.main()', profile_file)
    682                  else:
    683                          gmGuiMain.main()
    684          #elif ui_type == u'web':
    685          #       from Gnumed.proxiedpyjamas import gmWebGuiServer
    686          #       gmWebGuiServer.main()
    687          #elif ui_type == u'chweb':
    688          #       from Gnumed.CherryPy import gmGuiWeb
    689          #       gmGuiWeb.main()
    690
    691          gmHooks.run_hook_script(hook = 'shutdown-post-GUI')
    692
    693          return 0
    694
    695  #==========================================================
    696 -def run_tool():
    697          tool = _cfg.get(option = '--tool', source_order = [('cli', 'return')])
    698          if tool is None:
    699                  # not running a tool
    700                  return None
    701
    702          if tool not in _known_tools:
    703                  _log.error('unknown tool requested: %s', tool)
    704                  print('GNUmed startup: Unknown tool [%s] requested.' % tool)
    705                  print('GNUmed startup: Known tools: %s' % _known_tools)
    706                  return -1
    707
    708          print('')
    709          print('==============================================')
    710          print('Running tool: %s' % tool)
    711          print('----------------------------------------------')
    712          print('')
    713          login, creds = gmPG2.request_login_params()
    714          pool = gmConnectionPool.gmConnectionPool()
    715          pool.credentials = creds
    716          print('')
    717
    718          if tool == 'check_mimetypes_in_archive':
    719                  from Gnumed.business import gmDocuments
    720                  return gmDocuments.check_mimetypes_in_archive()
    721
    722          if tool == 'check_enc_epi_xref':
    723                  from Gnumed.business import gmEMRStructItems
    724                  return gmEMRStructItems.check_fk_encounter_fk_episode_x_ref()
    725
    726          if tool == 'export_pat_emr_structure':
    727                  # setup praxis
    728                  from Gnumed.business import gmPraxis
    729                  praxis = gmPraxis.gmCurrentPraxisBranch(branch = gmPraxis.get_praxis_branches()[0])
    730                  # get patient
    731                  from Gnumed.business import gmPersonSearch
    732                  pat = gmPersonSearch.ask_for_patient()
    733                  # setup exporters
    734                  from Gnumed.business import gmEMRStructItems
    735                  from Gnumed.exporters import gmTimelineExporter
    736                  from Gnumed.exporters import gmPatientExporter
    737                  while pat is not None:
    738                          print('patient:', pat['description_gender'])
    739                          # as EMR structure
    740                          fname = os.path.expanduser('~/gnumed/gm-emr_structure-%s.txt' % pat.subdir_name)
    741                          print('EMR structure:', gmEMRStructItems.export_emr_structure(patient = pat, filename = fname))
    742                          # as timeline
    743                          fname = os.path.expanduser('~/gnumed/gm-emr-%s.timeline' % pat.subdir_name)
    744                          try:
    745                                  print('EMR timeline:', gmTimelineExporter.create_timeline_file (
    746                                          patient = pat,
    747                                          filename = fname,
    748                                          include_documents = True,
    749                                          include_vaccinations = True,
    750                                          include_encounters = True
    751                                  ))
    752                          finally:
    753                                  pass
    754                          # as journal by encounter
    755                          exporter = gmPatientExporter.cEMRJournalExporter()
    756                          fname = os.path.expanduser('~/gnumed/gm-emr-journal_by_encounter-%s.txt' % pat.subdir_name)
    757                          print('EMR journal (by encounter):', exporter.save_to_file_by_encounter(patient = pat, filename = fname))
    758                          # as journal by mod time
    759                          fname = os.path.expanduser('~/gnumed/gm-emr-journal_by_mod_time-%s.txt' % pat.subdir_name)
    760                          print('EMR journal (by mod time):', exporter.save_to_file_by_mod_time(patient = pat, filename = fname))
    761                          # as statistical summary
    762                          fname = os.path.expanduser('~/gnumed/gm-emr-statistics-%s.txt' % pat.subdir_name)
    763                          output_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'replace')
    764                          emr = pat.emr
    765                          output_file.write(emr.format_statistics())
    766                          output_file.close()
    767                          print('EMR statistics:', fname)
    768                          # as text file
    769                          exporter = gmPatientExporter.cEmrExport(patient = pat)
    770                          fname = os.path.expanduser('~/gnumed/gm-emr-text_export-%s.txt' % pat.subdir_name)
    771                          output_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'replace')
    772                          exporter.set_output_file(output_file)
    773                          exporter.dump_constraints()
    774                          exporter.dump_demographic_record(True)
    775                          exporter.dump_clinical_record()
    776                          exporter.dump_med_docs()
    777                          output_file.close()
    778                          print('EMR text file:', fname)
    779                          # another patient ?
    780                          pat = gmPersonSearch.ask_for_patient()
    781
    782                  return 0
    783
    784          # tool export_patient_as (vcf, gdt, ...)
    785          #if tool == 'export_pat_demographics':
    786
    787          # should not happen (because checked against _known_tools)
    788          return -1
    789
    790  #==========================================================
    791  # shutdown helpers
    792  #----------------------------------------------------------
    793 -def shutdown_backend():
    794          gmPG2.shutdown()
    795
    796  #==========================================================
    797 -def shutdown_logging():
    798
    799  #       if _cfg.get(option = u'debug'):
    800  #               import types
    801
    802  #               def get_refcounts():
    803  #                       refcount = {}
    804  #                       # collect all classes
    805  #                       for module in sys.modules.values():
    806  #                               for sym in dir(module):
    807  #                                       obj = getattr(module, sym)
    808  #                                       if type(obj) is types.ClassType:
    809  #                                               refcount[obj] = sys.getrefcount(obj)
    810  #                       # sort by refcount
    811  #                       pairs = map(lambda x: (x[1],x[0]), refcount.items())
    812  #                       pairs.sort()
    813  #                       pairs.reverse()
    814  #                       return pairs
    815
    816  #               rcfile = io.open('./gm-refcount.lst', 'wt', encoding = 'utf8')
    817  #               for refcount, class_ in get_refcounts():
    818  #                       if not class_.__name__.startswith('wx'):
    819  #                               rcfile.write(u'%10d %s\n' % (refcount, class_.__name__))
    820  #               rcfile.close()
    821
    822          # do not choke on Windows
    823          logging.raiseExceptions = False
    824
    825  #==========================================================
    826 -def shutdown_tmp_dir():
    827
    828          tmp_dir = gmTools.gmPaths().tmp_dir
    829
    830          if _cfg.get(option = 'debug'):
    831                  _log.debug('not removing tmp dir (--debug mode): %s', tmp_dir)
    832                  return
    833
    834          _log.warning('removing tmp dir: %s', tmp_dir)
    835          shutil.rmtree(tmp_dir, True)
    836
    837  #==========================================================
    838  # main - launch the GNUmed wxPython GUI client
    839  #----------------------------------------------------------
    840
    841  random.seed()
    842
    843  # setup
    844  setup_fault_handler(target = None)
    845  setup_console_encoding()
    846  setup_python_path()
    847  setup_logging()
    848  log_startup_info()
    849  setup_console_exception_handler()
    850  setup_cli()
    851  setup_signal_handlers()
    852  setup_local_repo_path()
    853
    854  from Gnumed.pycommon import gmI18N
    855  from Gnumed.pycommon import gmTools
    856  from Gnumed.pycommon import gmDateTime
    857
    858  setup_locale()
    859  handle_help_request()
    860  handle_version_request()
    861  setup_paths_and_files()
    862  setup_date_time()
    863  setup_cfg()
    864  setup_ui_type()
    865
    866  from Gnumed.pycommon import gmPG2
    867  from Gnumed.pycommon import gmConnectionPool
    868  setup_backend_environment()
    869
    870  # main
    871  exit_code = run_tool()
    872  if exit_code is None:
    873          from Gnumed.pycommon import gmHooks
    874          exit_code = run_gui()
    875
    876  # shutdown
    877  shutdown_backend()
    878  shutdown_tmp_dir()
    879  _log.info('Normally shutting down as main module.')
    880  shutdown_logging()
    881
    882  sys.exit(exit_code)
    883
    884  #==========================================================
    885