Package Gnumed :: Package wxpython :: Module gmGuiMain
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmGuiMain

   1  # -*- coding: utf-8 -*- 
   2   
   3  __doc__ = """GNUmed GUI client. 
   4   
   5  This contains the GUI application framework and main window 
   6  of the all signing all dancing GNUmed Python Reference 
   7  client. It relies on the <gnumed.py> launcher having set up 
   8  the non-GUI-related runtime environment. 
   9   
  10  copyright: authors 
  11  """ 
  12  #============================================================================== 
  13  __author__  = "H. Herb <hherb@gnumed.net>,\ 
  14                             K. Hilbert <Karsten.Hilbert@gmx.net>,\ 
  15                             I. Haywood <i.haywood@ugrad.unimelb.edu.au>" 
  16  __license__ = 'GPL v2 or later (details at http://www.gnu.org)' 
  17   
  18  # stdlib 
  19  import sys 
  20  import time 
  21  import os 
  22  import os.path 
  23  import datetime as pyDT 
  24  import shutil 
  25  import logging 
  26  import urllib.request 
  27  import subprocess 
  28  import glob 
  29  import io 
  30   
  31  _log = logging.getLogger('gm.main') 
  32   
  33   
  34  # GNUmed libs 
  35  from Gnumed.pycommon import gmCfg2 
  36  _cfg = gmCfg2.gmCfgData() 
  37   
  38   
  39  # 3rd party libs: wxPython 
  40  try: 
  41          import wx 
  42          _log.info('wxPython version loaded: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo)) 
  43  except ImportError: 
  44          _log.exception('cannot import wxPython') 
  45          print('GNUmed startup: Cannot import wxPython library.') 
  46          print('GNUmed startup: Make sure wxPython is installed.') 
  47          print('CRITICAL ERROR: Error importing wxPython. Halted.') 
  48          raise 
  49   
  50  # do this check just in case, so we can make sure 
  51  # py2exe and friends include the proper version, too 
  52  version = int('%s%s' % (wx.MAJOR_VERSION, wx.MINOR_VERSION)) 
  53  if (version < 28) or ('unicode' not in wx.PlatformInfo): 
  54          print('GNUmed startup: Unsupported wxPython version (%s: %s).' % (wx.VERSION_STRING, wx.PlatformInfo)) 
  55          print('GNUmed startup: wxPython 2.8+ with unicode support is required.') 
  56          print('CRITICAL ERROR: Proper wxPython version not found. Halted.') 
  57          raise ValueError('wxPython 2.8+ with unicode support not found') 
  58   
  59   
  60  # more GNUmed libs 
  61  from Gnumed.pycommon import gmCfg 
  62  from Gnumed.pycommon import gmPG2 
  63  from Gnumed.pycommon import gmDispatcher 
  64  from Gnumed.pycommon import gmGuiBroker 
  65  from Gnumed.pycommon import gmI18N 
  66  from Gnumed.pycommon import gmExceptions 
  67  from Gnumed.pycommon import gmShellAPI 
  68  from Gnumed.pycommon import gmTools 
  69  from Gnumed.pycommon import gmDateTime 
  70  from Gnumed.pycommon import gmHooks 
  71  from Gnumed.pycommon import gmBackendListener 
  72  from Gnumed.pycommon import gmLog2 
  73  from Gnumed.pycommon import gmNetworkTools 
  74  from Gnumed.pycommon import gmMimeLib 
  75   
  76  from Gnumed.business import gmPerson 
  77  from Gnumed.business import gmClinicalRecord 
  78  from Gnumed.business import gmPraxis 
  79  from Gnumed.business import gmEMRStructItems 
  80  from Gnumed.business import gmArriba 
  81  from Gnumed.business import gmStaff 
  82   
  83  from Gnumed.exporters import gmPatientExporter 
  84   
  85  from Gnumed.wxpython import gmGuiHelpers 
  86  from Gnumed.wxpython import gmHorstSpace 
  87  from Gnumed.wxpython import gmDemographicsWidgets 
  88  from Gnumed.wxpython import gmPersonCreationWidgets 
  89  from Gnumed.wxpython import gmEMRStructWidgets 
  90  from Gnumed.wxpython import gmPatSearchWidgets 
  91  from Gnumed.wxpython import gmAllergyWidgets 
  92  from Gnumed.wxpython import gmListWidgets 
  93  from Gnumed.wxpython import gmProviderInboxWidgets 
  94  from Gnumed.wxpython import gmCfgWidgets 
  95  from Gnumed.wxpython import gmExceptionHandlingWidgets 
  96  from Gnumed.wxpython import gmNarrativeWorkflows 
  97  from Gnumed.wxpython import gmPhraseWheel 
  98  from Gnumed.wxpython import gmMedicationWidgets 
  99  from Gnumed.wxpython import gmStaffWidgets 
 100  from Gnumed.wxpython import gmDocumentWidgets 
 101  from Gnumed.wxpython import gmTimer 
 102  from Gnumed.wxpython import gmMeasurementWidgets 
 103  from Gnumed.wxpython import gmFormWidgets 
 104  from Gnumed.wxpython import gmSnellen 
 105  from Gnumed.wxpython import gmVaccWidgets 
 106  from Gnumed.wxpython import gmPersonContactWidgets 
 107  from Gnumed.wxpython import gmI18nWidgets 
 108  from Gnumed.wxpython import gmCodingWidgets 
 109  from Gnumed.wxpython import gmOrganizationWidgets 
 110  from Gnumed.wxpython import gmAuthWidgets 
 111  from Gnumed.wxpython import gmFamilyHistoryWidgets 
 112  from Gnumed.wxpython import gmDataPackWidgets 
 113  from Gnumed.wxpython import gmContactWidgets 
 114  from Gnumed.wxpython import gmAddressWidgets 
 115  from Gnumed.wxpython import gmBillingWidgets 
 116  from Gnumed.wxpython import gmKeywordExpansionWidgets 
 117  from Gnumed.wxpython import gmAccessPermissionWidgets 
 118  from Gnumed.wxpython import gmPraxisWidgets 
 119  from Gnumed.wxpython import gmEncounterWidgets 
 120  from Gnumed.wxpython import gmAutoHintWidgets 
 121  from Gnumed.wxpython import gmPregWidgets 
 122  from Gnumed.wxpython import gmExternalCareWidgets 
 123  from Gnumed.wxpython import gmHabitWidgets 
 124  from Gnumed.wxpython import gmSubstanceMgmtWidgets 
 125  from Gnumed.wxpython import gmATCWidgets 
 126  from Gnumed.wxpython import gmLOINCWidgets 
 127  from Gnumed.wxpython import gmVisualProgressNoteWidgets 
 128  from Gnumed.wxpython import gmHospitalStayWidgets 
 129  from Gnumed.wxpython import gmProcedureWidgets 
 130   
 131   
 132  try: 
 133          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
 134  except NameError: 
 135          _ = lambda x:x 
 136   
 137  _provider = None 
 138  _scripting_listener = None 
 139  _original_wxEndBusyCursor = None 
140 141 #============================================================================== 142 -class cLog_wx2gm(wx.Log):
143 # redirect wx.LogXXX() calls to python logging log
144 - def DoLogTextAtLevel(self, level, msg):
145 _log.log(level, msg)
146 147 __wxlog = cLog_wx2gm() 148 _log.info('redirecting wx.Log to [%s]', __wxlog) 149 wx.Log.SetActiveTarget(__wxlog)
150 #wx.LogDebug('test message') 151 152 #============================================================================== 153 -class gmTopLevelFrame(wx.Frame):
154 """GNUmed client's main windows frame. 155 156 This is where it all happens. Avoid popping up any other windows. 157 Most user interaction should happen to and from widgets within this frame 158 """ 159 #----------------------------------------------
160 - def __init__(self, parent, id, title, size=wx.DefaultSize):
161 """You'll have to browse the source to understand what the constructor does 162 """ 163 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE) 164 165 self.__setup_font() 166 167 self.__gb = gmGuiBroker.GuiBroker() 168 self.__pre_exit_callbacks = [] 169 self.bar_width = -1 170 self.menu_id2plugin = {} 171 172 _log.info('workplace is >>>%s<<<', gmPraxis.gmCurrentPraxisBranch().active_workplace) 173 174 self.__setup_main_menu() 175 self.setup_statusbar() 176 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % ( 177 gmTools.coalesce(_provider['title'], ''), 178 _provider['firstnames'][:1], 179 _provider['lastnames'], 180 _provider['short_alias'], 181 _provider['db_user'] 182 )) 183 184 self.__set_window_title_template() 185 self.__update_window_title() 186 187 #icon_bundle = wx.IconBundle() 188 #icon_bundle.AddIcon(wx.Icon("my_icon_16_16.ico", wx.BITMAP_TYPE_ICO)) 189 #icon_bundle.AddIcon(wx.Icon("my_icon_32_32.ico", wx.BITMAP_TYPE_ICO)) 190 #self.SetIcons(icon_bundle) 191 self.SetIcon(gmTools.get_icon(wx = wx)) 192 193 self.__register_events() 194 195 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1) 196 self.vbox = wx.BoxSizer(wx.VERTICAL) 197 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1) 198 199 self.SetAutoLayout(True) 200 self.SetSizerAndFit(self.vbox) 201 202 # don't allow the window to get too small 203 # setsizehints only allows minimum size, therefore window can't become small enough 204 # effectively we need the font size to be configurable according to screen size 205 #self.vbox.SetSizeHints(self) 206 self.__set_GUI_size()
207 208 #----------------------------------------------
209 - def __setup_font(self):
210 211 font = self.GetFont() 212 _log.debug('system default font is [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc()) 213 214 desired_font_face = _cfg.get ( 215 group = 'workplace', 216 option = 'client font', 217 source_order = [ 218 ('explicit', 'return'), 219 ('workbase', 'return'), 220 ('local', 'return'), 221 ('user', 'return'), 222 ('system', 'return') 223 ] 224 ) 225 226 fonts2try = [] 227 if desired_font_face is not None: 228 _log.info('client is configured to use font [%s]', desired_font_face) 229 fonts2try.append(desired_font_face) 230 231 if wx.Platform == '__WXMSW__': 232 sane_font_face = 'Noto Sans' 233 _log.info('MS Windows: appending fallback font candidate [%s]', sane_font_face) 234 fonts2try.append(sane_font_face) 235 sane_font_face = 'DejaVu Sans' 236 _log.info('MS Windows: appending fallback font candidate [%s]', sane_font_face) 237 fonts2try.append(sane_font_face) 238 239 if len(fonts2try) == 0: 240 return 241 242 for font_face in fonts2try: 243 success = font.SetFaceName(font_face) 244 if success: 245 self.SetFont(font) 246 _log.debug('switched font to [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc()) 247 return 248 font = self.GetFont() 249 _log.error('cannot switch font from [%s] (%s) to [%s]', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc(), font_face) 250 251 return
252 253 #----------------------------------------------
254 - def __set_GUI_size(self):
255 """Try to get previous window size from backend.""" 256 257 cfg = gmCfg.cCfgSQL() 258 259 # width 260 width = int(cfg.get2 ( 261 option = 'main.window.width', 262 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 263 bias = 'workplace', 264 default = 800 265 )) 266 267 # height 268 height = int(cfg.get2 ( 269 option = 'main.window.height', 270 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 271 bias = 'workplace', 272 default = 600 273 )) 274 275 dw = wx.DisplaySize()[0] 276 dh = wx.DisplaySize()[1] 277 278 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 279 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM())) 280 _log.debug('previous GUI size [%s:%s]', width, height) 281 282 # max size 283 if width > dw: 284 _log.debug('adjusting GUI width from %s to %s', width, dw) 285 width = dw 286 287 if height > dh: 288 _log.debug('adjusting GUI height from %s to %s', height, dh) 289 height = dh 290 291 # min size 292 if width < 100: 293 _log.debug('adjusting GUI width to minimum of 100 pixel') 294 width = 100 295 if height < 100: 296 _log.debug('adjusting GUI height to minimum of 100 pixel') 297 height = 100 298 299 _log.info('setting GUI to size [%s:%s]', width, height) 300 301 self.SetClientSize(wx.Size(width, height))
302 303 #----------------------------------------------
304 - def __setup_main_menu(self):
305 """Create the main menu entries. 306 307 Individual entries are farmed out to the modules. 308 309 menu item template: 310 311 item = menu_*.Append(-1) 312 self.Bind(wx.EVT_MENU, self.__on_*, item) 313 """ 314 global wx 315 self.mainmenu = wx.MenuBar() 316 self.__gb['main.mainmenu'] = self.mainmenu 317 318 # -- menu "GNUmed" ----------------- 319 menu_gnumed = wx.Menu() 320 self.menu_plugins = wx.Menu() 321 menu_gnumed.Append(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins) 322 item = menu_gnumed.Append(-1, _('Check for updates'), _('Check for new releases of the GNUmed client.')) 323 self.Bind(wx.EVT_MENU, self.__on_check_for_updates, item) 324 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.')) 325 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item) 326 menu_gnumed.AppendSeparator() 327 328 # GNUmed / Preferences 329 menu_config = wx.Menu() 330 331 item = menu_config.Append(-1, _('All options'), _('List all options as configured in the database.')) 332 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item) 333 334 # GNUmed / Preferences / Database 335 menu_cfg_db = wx.Menu() 336 item = menu_cfg_db.Append(-1, _('Language'), _('Configure the database language')) 337 self.Bind(wx.EVT_MENU, self.__on_configure_db_lang, item) 338 item = menu_cfg_db.Append(-1, _('Welcome message'), _('Configure the database welcome message (all users).')) 339 self.Bind(wx.EVT_MENU, self.__on_configure_db_welcome, item) 340 menu_config.Append(wx.NewId(), _('Database ...'), menu_cfg_db) 341 342 # GNUmed / Preferences / Client 343 menu_cfg_client = wx.Menu() 344 item = menu_cfg_client.Append(-1, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.')) 345 self.Bind(wx.EVT_MENU, self.__on_configure_export_chunk_size, item) 346 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.')) 347 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item) 348 menu_config.Append(wx.NewId(), _('Client parameters ...'), menu_cfg_client) 349 350 # GNUmed / Preferences / UI 351 menu_cfg_ui = wx.Menu() 352 item = menu_cfg_ui.Append(-1, _('Medication measurements'), _('Select the measurements panel to show in the medications plugin.')) 353 self.Bind(wx.EVT_MENU, self.__on_cfg_meds_lab_pnl, item) 354 item = menu_cfg_ui.Append(-1, _('General measurements'), _('Select the measurements panel to show in the top pane.')) 355 self.Bind(wx.EVT_MENU, self.__on_cfg_top_lab_pnl, item) 356 357 # gnumed / config / ui / docs 358 menu_cfg_doc = wx.Menu() 359 item = menu_cfg_doc.Append(-1, _('Review dialog'), _('Configure review dialog after document display.')) 360 self.Bind(wx.EVT_MENU, self.__on_configure_doc_review_dialog, item) 361 item = menu_cfg_doc.Append(-1, _('UUID display'), _('Configure unique ID dialog on document import.')) 362 self.Bind(wx.EVT_MENU, self.__on_configure_doc_uuid_dialog, item) 363 item = menu_cfg_doc.Append(-1, _('Empty documents'), _('Whether to allow saving documents without parts.')) 364 self.Bind(wx.EVT_MENU, self.__on_configure_partless_docs, item) 365 item = menu_cfg_doc.Append(-1, _('Generate UUID'), _('Whether to generate UUIDs for new documents.')) 366 self.Bind(wx.EVT_MENU, self.__on_configure_generate_doc_uuid, item) 367 menu_cfg_ui.Append(wx.NewId(), _('Document handling ...'), menu_cfg_doc) 368 369 # gnumed / config / ui / updates 370 menu_cfg_update = wx.Menu() 371 item = menu_cfg_update.Append(-1, _('Auto-check'), _('Whether to auto-check for updates at startup.')) 372 self.Bind(wx.EVT_MENU, self.__on_configure_update_check, item) 373 item = menu_cfg_update.Append(-1, _('Check scope'), _('When checking for updates, consider latest branch, too ?')) 374 self.Bind(wx.EVT_MENU, self.__on_configure_update_check_scope, item) 375 item = menu_cfg_update.Append(-1, _('URL'), _('The URL to retrieve version information from.')) 376 self.Bind(wx.EVT_MENU, self.__on_configure_update_url, item) 377 menu_cfg_ui.Append(wx.NewId(), _('Update handling ...'), menu_cfg_update) 378 379 # gnumed / config / ui / patient 380 menu_cfg_pat_search = wx.Menu() 381 item = menu_cfg_pat_search.Append(-1, _('Birthday reminder'), _('Configure birthday reminder proximity interval.')) 382 self.Bind(wx.EVT_MENU, self.__on_configure_dob_reminder_proximity, item) 383 item = menu_cfg_pat_search.Append(-1, _('Immediate source activation'), _('Configure immediate activation of single external person.')) 384 self.Bind(wx.EVT_MENU, self.__on_configure_quick_pat_search, item) 385 item = menu_cfg_pat_search.Append(-1, _('Initial plugin'), _('Configure which plugin to show right after person activation.')) 386 self.Bind(wx.EVT_MENU, self.__on_configure_initial_pat_plugin, item) 387 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default region for person creation.')) 388 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item) 389 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.')) 390 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item) 391 menu_cfg_ui.Append(wx.NewId(), _('Person ...'), menu_cfg_pat_search) 392 393 # gnumed / config / ui / soap handling 394 menu_cfg_soap_editing = wx.Menu() 395 item = menu_cfg_soap_editing.Append(-1, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.')) 396 self.Bind(wx.EVT_MENU, self.__on_allow_multiple_new_episodes, item) 397 item = menu_cfg_soap_editing.Append(-1, _('Auto-open editors'), _('Configure auto-opening editors for recent problems.')) 398 self.Bind(wx.EVT_MENU, self.__on_allow_auto_open_episodes, item) 399 item = menu_cfg_soap_editing.Append(-1, _('SOAP fields'), _('Configure SOAP editor - individual SOAP fields vs text editor like')) 400 self.Bind(wx.EVT_MENU, self.__on_use_fields_in_soap_editor, item) 401 menu_cfg_ui.Append(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing) 402 403 # gnumed / config / External tools 404 menu_cfg_ext_tools = wx.Menu() 405 # item = menu_cfg_ext_tools.Append(-1, _('IFAP command'), _('Set the command to start IFAP.')) 406 # self.Bind(wx.EVT_MENU, self.__on_configure_ifap_cmd, item) 407 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.')) 408 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item) 409 item = menu_cfg_ext_tools.Append(-1, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.')) 410 self.Bind(wx.EVT_MENU, self.__on_configure_ooo_settle_time, item) 411 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.')) 412 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item) 413 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.')) 414 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item) 415 # item = menu_cfg_ext_tools.Append(-1, _('FreeDiams path'), _('Set the path for the FreeDiams binary.')) 416 # self.Bind(wx.EVT_MENU, self.__on_configure_freediams_cmd, item) 417 item = menu_cfg_ext_tools.Append(-1, _('ADR URL'), _('URL for reporting Adverse Drug Reactions.')) 418 self.Bind(wx.EVT_MENU, self.__on_configure_adr_url, item) 419 item = menu_cfg_ext_tools.Append(-1, _('vaccADR URL'), _('URL for reporting Adverse Drug Reactions to *vaccines*.')) 420 self.Bind(wx.EVT_MENU, self.__on_configure_vaccine_adr_url, item) 421 item = menu_cfg_ext_tools.Append(-1, _('Vacc plans URL'), _('URL for vaccination plans.')) 422 self.Bind(wx.EVT_MENU, self.__on_configure_vaccination_plans_url, item) 423 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.')) 424 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item) 425 menu_config.Append(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools) 426 427 # gnumed / config / billing 428 menu_cfg_bill = wx.Menu() 429 item = menu_cfg_bill.Append(-1, _('Invoice template (no VAT)'), _('Select the template for printing an invoice without VAT.')) 430 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_no_vat, item) 431 item = menu_cfg_bill.Append(-1, _('Invoice template (with VAT)'), _('Select the template for printing an invoice with VAT.')) 432 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_with_vat, item) 433 item = menu_cfg_bill.Append(-1, _('Catalogs URL'), _('URL for billing catalogs (schedules of fees).')) 434 self.Bind(wx.EVT_MENU, self.__on_configure_billing_catalogs_url, item) 435 436 # gnumed / config / emr 437 menu_cfg_emr = wx.Menu() 438 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.')) 439 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item) 440 item = menu_cfg_emr.Append(-1, _('Prescription mode'), _('Select the default mode for creating a prescription.')) 441 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_mode, item) 442 item = menu_cfg_emr.Append(-1, _('Prescription template'), _('Select the template for printing a prescription.')) 443 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_template, item) 444 item = menu_cfg_emr.Append(-1, _('Default Gnuplot template'), _('Select the default template for plotting test results.')) 445 self.Bind(wx.EVT_MENU, self.__on_cfg_default_gnuplot_template, item) 446 item = menu_cfg_emr.Append(-1, _('Fallback provider'), _('Select the doctor to fall back to for patients without a primary provider.')) 447 self.Bind(wx.EVT_MENU, self.__on_cfg_fallback_primary_provider, item) 448 449 # gnumed / config / emr / encounter 450 menu_cfg_encounter = wx.Menu() 451 item = menu_cfg_encounter.Append(-1, _('Edit before patient change'), _('Edit encounter details before change of patient.')) 452 self.Bind(wx.EVT_MENU, self.__on_cfg_enc_pat_change, item) 453 item = menu_cfg_encounter.Append(-1, _('Minimum duration'), _('Minimum duration of an encounter.')) 454 self.Bind(wx.EVT_MENU, self.__on_cfg_enc_min_ttl, item) 455 item = menu_cfg_encounter.Append(-1, _('Maximum duration'), _('Maximum duration of an encounter.')) 456 self.Bind(wx.EVT_MENU, self.__on_cfg_enc_max_ttl, item) 457 item = menu_cfg_encounter.Append(-1, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.')) 458 self.Bind(wx.EVT_MENU, self.__on_cfg_enc_empty_ttl, item) 459 item = menu_cfg_encounter.Append(-1, _('Default type'), _('Default type for new encounters.')) 460 self.Bind(wx.EVT_MENU, self.__on_cfg_enc_default_type, item) 461 menu_cfg_emr.Append(wx.NewId(), _('Encounter ...'), menu_cfg_encounter) 462 463 # gnumed / config / emr / episode 464 menu_cfg_episode = wx.Menu() 465 item = menu_cfg_episode.Append(-1, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.')) 466 self.Bind(wx.EVT_MENU, self.__on_cfg_epi_ttl, item) 467 menu_cfg_emr.Append(wx.NewId(), _('Episode ...'), menu_cfg_episode) 468 469 menu_config.Append(wx.NewId(), _('User interface ...'), menu_cfg_ui) 470 menu_config.Append(wx.NewId(), _('EMR ...'), menu_cfg_emr) 471 menu_config.Append(wx.NewId(), _('Billing ...'), menu_cfg_bill) 472 menu_gnumed.Append(wx.NewId(), _('Preferences ...'), menu_config) 473 474 # gnumed / master data 475 menu_master_data = wx.Menu() 476 item = menu_master_data.Append(-1, _('Manage lists'), _('Manage various lists of master data.')) 477 self.Bind(wx.EVT_MENU, self.__on_manage_master_data, item) 478 item = menu_master_data.Append(-1, _('Manage praxis'), _('Manage your praxis branches.')) 479 self.Bind(wx.EVT_MENU, self.__on_manage_praxis, item) 480 item = menu_master_data.Append(-1, _('Install data packs'), _('Install reference data from data packs.')) 481 self.Bind(wx.EVT_MENU, self.__on_install_data_packs, item) 482 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.')) 483 self.Bind(wx.EVT_MENU, self.__on_update_atc, item) 484 item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.')) 485 self.Bind(wx.EVT_MENU, self.__on_update_loinc, item) 486 item = menu_master_data.Append(-1, _('Create fake vaccines'), _('Re-create fake generic vaccines.')) 487 self.Bind(wx.EVT_MENU, self.__on_generate_vaccines, item) 488 menu_gnumed.Append(wx.NewId(), _('&Master data ...'), menu_master_data) 489 490 # gnumed / users 491 menu_users = wx.Menu() 492 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user')) 493 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item) 494 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users')) 495 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item) 496 item = menu_users.Append(-1, _('&Change DB owner PWD'), _('Change the password of the GNUmed database owner')) 497 self.Bind(wx.EVT_MENU, self.__on_edit_gmdbowner_password, item) 498 menu_gnumed.Append(wx.NewId(), _('&Users ...'), menu_users) 499 500 menu_gnumed.AppendSeparator() 501 502 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.')) 503 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item) 504 505 self.mainmenu.Append(menu_gnumed, '&GNUmed') 506 507 # -- menu "Person" --------------------------- 508 menu_person = wx.Menu() 509 510 item = menu_person.Append(-1, _('Search'), _('Search for a person.')) 511 self.Bind(wx.EVT_MENU, self.__on_search_person, item) 512 acc_tab = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, item.GetId())]) 513 self.SetAcceleratorTable(acc_tab) 514 item = menu_person.Append(-1, _('&Register person'), _("Register a new person with GNUmed")) 515 self.Bind(wx.EVT_MENU, self.__on_create_new_patient, item) 516 517 menu_person_import = wx.Menu() 518 item = menu_person_import.Append(-1, _('From &External sources'), _('Load and possibly create person from available external sources.')) 519 self.Bind(wx.EVT_MENU, self.__on_load_external_patient, item) 520 item = menu_person_import.Append(-1, _('&vCard file \u2192 patient'), _('Import demographics from .vcf vCard file as patient')) 521 self.Bind(wx.EVT_MENU, self.__on_import_vcard_from_file, item) 522 item = menu_person_import.Append(-1, _('Clipboard (&XML) \u2192 patient'), _('Import demographics from clipboard (LinuxMedNews XML) as patient')) 523 self.Bind(wx.EVT_MENU, self.__on_import_xml_linuxmednews, item) 524 item = menu_person_import.Append(-1, _('Clipboard (&vCard) \u2192 patient'), _('Import demographics from clipboard (vCard) as patient')) 525 self.Bind(wx.EVT_MENU, self.__on_import_vcard_from_clipboard, item) 526 menu_person.Append(wx.NewId(), '&Import\u2026', menu_person_import) 527 528 menu_person_export = wx.Menu() 529 menu_person_export_clipboard = wx.Menu() 530 item = menu_person_export_clipboard.Append(-1, '&GDT', _('Export demographics of currently active person as GDT into clipboard.')) 531 self.Bind(wx.EVT_MENU, self.__on_export_gdt2clipboard, item) 532 item = menu_person_export_clipboard.Append(-1, '&XML (LinuxMedNews)', _('Export demographics of currently active person as XML (LinuxMedNews) into clipboard')) 533 self.Bind(wx.EVT_MENU, self.__on_export_linuxmednews_xml2clipboard, item) 534 item = menu_person_export_clipboard.Append(-1, '&vCard', _('Export demographics of currently active person as vCard into clipboard')) 535 self.Bind(wx.EVT_MENU, self.__on_export_vcard2clipboard, item) 536 menu_person_export.Append(wx.NewId(), _('\u2192 &Clipboard as\u2026'), menu_person_export_clipboard) 537 538 menu_person_export_file = wx.Menu() 539 item = menu_person_export_file.Append(-1, '&GDT', _('Export demographics of currently active person into GDT file.')) 540 self.Bind(wx.EVT_MENU, self.__on_export_as_gdt, item) 541 item = menu_person_export_file.Append(-1, '&vCard', _('Export demographics of currently active person into vCard file.')) 542 self.Bind(wx.EVT_MENU, self.__on_export_as_vcard, item) 543 menu_person_export.Append(wx.NewId(), _('\u2192 &File as\u2026'), menu_person_export_file) 544 545 menu_person.Append(wx.NewId(), 'E&xport\u2026', menu_person_export) 546 547 item = menu_person.Append(-1, _('&Merge persons'), _('Merge two persons into one.')) 548 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item) 549 item = menu_person.Append(-1, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.')) 550 self.Bind(wx.EVT_MENU, self.__on_delete_patient, item) 551 menu_person.AppendSeparator() 552 item = menu_person.Append(-1, _('Add &tag'), _('Add a text/image tag to this person.')) 553 self.Bind(wx.EVT_MENU, self.__on_add_tag2person, item) 554 item = menu_person.Append(-1, _('Enlist as user'), _('Enlist current person as GNUmed user')) 555 self.Bind(wx.EVT_MENU, self.__on_enlist_patient_as_staff, item) 556 menu_person.AppendSeparator() 557 558 self.mainmenu.Append(menu_person, '&Person') 559 self.__gb['main.patientmenu'] = menu_person 560 561 # -- menu "EMR" --------------------------- 562 menu_emr = wx.Menu() 563 564 # -- EMR / Manage 565 menu_emr_manage = wx.Menu() 566 item = menu_emr_manage.Append(-1, _('&Past history (health issue / PMH)'), _('Add a past/previous medical history item (health issue) to the EMR of the active patient')) 567 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item) 568 item = menu_emr_manage.Append(-1, _('&Episode'), _('Add an episode of illness to the EMR of the active patient')) 569 self.Bind(wx.EVT_MENU, self.__on_add_episode, item) 570 item = menu_emr_manage.Append(-1, _('&Medication'), _('Add medication / substance use entry.')) 571 self.Bind(wx.EVT_MENU, self.__on_add_medication, item) 572 item = menu_emr_manage.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.')) 573 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item) 574 item = menu_emr_manage.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.')) 575 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item) 576 item = menu_emr_manage.Append(-1, _('&Hospitalizations'), _('Manage hospitalizations.')) 577 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item) 578 item = menu_emr_manage.Append(-1, _('&External care'), _('Manage external care.')) 579 self.Bind(wx.EVT_MENU, self.__on_manage_external_care, item) 580 item = menu_emr_manage.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.')) 581 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item) 582 item = menu_emr_manage.Append(-1, _('&Measurements'), _('Manage measurement results for the current patient.')) 583 self.Bind(wx.EVT_MENU, self.__on_manage_measurements, item) 584 item = menu_emr_manage.Append(-1, _('&Vaccinations: by shot'), _('Manage vaccinations for the current patient (by shots given).')) 585 self.Bind(wx.EVT_MENU, self.__on_manage_vaccination, item) 586 item = menu_emr_manage.Append(-1, _('&Vaccinations: by indication'), _('Manage vaccinations for the current patient (by indication).')) 587 self.Bind(wx.EVT_MENU, self.__on_show_all_vaccinations_by_indication, item) 588 item = menu_emr_manage.Append(-1, _('&Vaccinations: latest'), _('List latest vaccinations for the current patient.')) 589 self.Bind(wx.EVT_MENU, self.__on_show_latest_vaccinations, item) 590 item = menu_emr_manage.Append(-1, _('&Family history (FHx)'), _('Manage family history.')) 591 self.Bind(wx.EVT_MENU, self.__on_manage_fhx, item) 592 item = menu_emr_manage.Append(-1, _('&Encounters'), _('List all encounters including empty ones.')) 593 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item) 594 item = menu_emr_manage.Append(-1, _('&Pregnancy'), _('Calculate EDC.')) 595 self.Bind(wx.EVT_MENU, self.__on_calc_edc, item) 596 item = menu_emr_manage.Append(-1, _('Suppressed hints'), _('Manage dynamic hints suppressed in this patient.')) 597 self.Bind(wx.EVT_MENU, self.__on_manage_suppressed_hints, item) 598 item = menu_emr_manage.Append(-1, _('Substance abuse'), _('Manage substance abuse documentation of this patient.')) 599 self.Bind(wx.EVT_MENU, self.__on_manage_substance_abuse, item) 600 menu_emr.Append(wx.NewId(), _('&Manage ...'), menu_emr_manage) 601 602 # - EMR / 603 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient')) 604 self.Bind(wx.EVT_MENU, self.__on_search_emr, item) 605 606 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.')) 607 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item) 608 609 # # - EMR / Show as / 610 # menu_emr_show = wx.Menu() 611 612 item = menu_emr.Append(-1, _('Statistics'), _('Show a high-level statistic summary of the EMR.')) 613 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item) 614 615 # menu_emr.Append(wx.NewId(), _('Show as ...'), menu_emr_show) 616 # self.__gb['main.emr_showmenu'] = menu_emr_show 617 618 menu_emr.AppendSeparator() 619 620 # -- EMR / Export as 621 menu_emr_export = wx.Menu() 622 item = menu_emr_export.Append(-1, _('Journal (encounters)'), _("Copy EMR of the active patient as a chronological journal into export area")) 623 self.Bind(wx.EVT_MENU, self.__on_export_emr_as_journal, item) 624 item = menu_emr_export.Append(-1, _('Journal (mod time)'), _("Copy EMR of active patient as journal (by last modification time) into export area")) 625 self.Bind(wx.EVT_MENU, self.__on_export_emr_by_last_mod, item) 626 item = menu_emr_export.Append(-1, _('Text document'), _("Copy EMR of active patient as text document into export area")) 627 self.Bind(wx.EVT_MENU, self.__export_emr_as_textfile, item) 628 item = menu_emr_export.Append(-1, _('Timeline file'), _("Copy EMR of active patient as timeline file (XML) into export area")) 629 self.Bind(wx.EVT_MENU, self.__export_emr_as_timeline_xml, item) 630 item = menu_emr_export.Append(-1, _('Care structure'), _("Copy EMR of active patient as care structure text file into export area")) 631 self.Bind(wx.EVT_MENU, self.__export_emr_as_care_structure, item) 632 # structure file 633 item = menu_emr_export.Append(-1, _('MEDISTAR import format (as file)'), _("GNUmed -> MEDISTAR. Save progress notes of active patient's active encounter into a text file.")) 634 self.Bind(wx.EVT_MENU, self.__on_export_for_medistar, item) 635 menu_emr.Append(wx.NewId(), _('Put into export area as ...'), menu_emr_export) 636 637 menu_emr.AppendSeparator() 638 639 self.mainmenu.Append(menu_emr, _("&EMR")) 640 self.__gb['main.emrmenu'] = menu_emr 641 642 # -- menu "Paperwork" --------------------- 643 menu_paperwork = wx.Menu() 644 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.')) 645 self.Bind(wx.EVT_MENU, self.__on_new_letter, item) 646 item = menu_paperwork.Append(-1, _('Screenshot -> export area'), _('Put a screenshot into the patient export area.')) 647 self.Bind(wx.EVT_MENU, self.__on_save_screenshot_into_export_area, item) 648 menu_paperwork.AppendSeparator() 649 item = menu_paperwork.Append(-1, _('List Placeholders'), _('Show a list of all placeholders.')) 650 self.Bind(wx.EVT_MENU, self.__on_show_placeholders, item) 651 # item = menu_paperwork.Append(-1, _('Select receiver'), _('Select a letter receiver for testing.')) 652 # self.Bind(wx.EVT_MENU, self.__on_test_receiver_selection, item) 653 self.mainmenu.Append(menu_paperwork, _('&Correspondence')) 654 self.__gb['main.paperworkmenu'] = menu_paperwork 655 656 # -- menu "Tools" ------------------------- 657 self.menu_tools = wx.Menu() 658 item = self.menu_tools.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients')) 659 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item) 660 viewer = _('no viewer installed') 661 if gmShellAPI.detect_external_binary(binary = 'ginkgocadx')[0]: 662 viewer = 'Ginkgo CADx' 663 elif os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 664 viewer = 'OsiriX' 665 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]: 666 viewer = 'Aeskulap' 667 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]: 668 viewer = 'AMIDE' 669 elif gmShellAPI.detect_external_binary(binary = 'dicomscope')[0]: 670 viewer = 'DicomScope' 671 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]: 672 viewer = '(x)medcon' 673 item = self.menu_tools.Append(-1, _('DICOM viewer'), _('Start DICOM viewer (%s) for CD-ROM (X-Ray, CT, MR, etc). On Windows just insert CD.') % viewer) 674 self.Bind(wx.EVT_MENU, self.__on_dicom_viewer, item) 675 if viewer == _('no viewer installed'): 676 _log.info('neither of Ginkgo CADx / OsiriX / Aeskulap / AMIDE / DicomScope / xmedcon found, disabling "DICOM viewer" menu item') 677 self.menu_tools.Enable(id = item.Id, enable=False) 678 # self.menu_tools.Append(-1, _("Dermatology"), _("A tool to aid dermatology diagnosis")) 679 # self.Bind(wx.EVT_MENU, self.__dermtool, item) 680 item = self.menu_tools.Append(-1, _('Snellen chart'), _('Display fullscreen snellen chart.')) 681 self.Bind(wx.EVT_MENU, self.__on_snellen, item) 682 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.')) 683 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item) 684 item = self.menu_tools.Append(-1, 'arriba', _('arriba: cardiovascular risk assessment (%s).') % 'www.arriba-hausarzt.de') 685 self.Bind(wx.EVT_MENU, self.__on_arriba, item) 686 if not gmShellAPI.detect_external_binary(binary = 'arriba')[0]: 687 _log.info('<arriba> not found, disabling "arriba" menu item') 688 self.menu_tools.Enable(id = item.Id, enable = False) 689 690 menu_lab = wx.Menu() 691 item = menu_lab.Append(-1, _('Show HL7'), _('Show formatted data from HL7 file')) 692 self.Bind(wx.EVT_MENU, self.__on_show_hl7, item) 693 item = menu_lab.Append(-1, _('Unwrap XML'), _('Unwrap HL7 data from XML file (Excelleris, ...)')) 694 self.Bind(wx.EVT_MENU, self.__on_unwrap_hl7_from_xml, item) 695 item = menu_lab.Append(-1, _('Stage HL7'), _('Stage HL7 data from file')) 696 self.Bind(wx.EVT_MENU, self.__on_stage_hl7, item) 697 item = menu_lab.Append(-1, _('Browse pending'), _('Browse pending (staged) incoming data')) 698 self.Bind(wx.EVT_MENU, self.__on_incoming, item) 699 700 self.menu_tools.Append(wx.NewId(), _('Lab results ...'), menu_lab) 701 702 self.menu_tools.AppendSeparator() 703 704 self.mainmenu.Append(self.menu_tools, _("&Tools")) 705 self.__gb['main.toolsmenu'] = self.menu_tools 706 707 # -- menu "Knowledge" --------------------- 708 menu_knowledge = wx.Menu() 709 710 # -- Knowledge / Drugs 711 menu_drug_dbs = wx.Menu() 712 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.')) 713 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item) 714 # # - IFAP drug DB 715 # item = menu_drug_dbs.Append(-1, u'ifap', _('Start "ifap index PRAXIS" %s drug browser (Windows/Wine, Germany)') % gmTools.u_registered_trademark) 716 # self.Bind(wx.EVT_MENU, self.__on_ifap, item) 717 item = menu_drug_dbs.Append(-1, 'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)')) 718 self.Bind(wx.EVT_MENU, self.__on_kompendium_ch, item) 719 menu_knowledge.Append(wx.NewId(), _('&Drug Resources'), menu_drug_dbs) 720 721 # menu_knowledge.AppendSeparator() 722 723 item = menu_knowledge.Append(-1, _('Medical links (www)'), _('Show a page of links to useful medical content.')) 724 self.Bind(wx.EVT_MENU, self.__on_medical_links, item) 725 726 self.mainmenu.Append(menu_knowledge, _('&Knowledge')) 727 self.__gb['main.knowledgemenu'] = menu_knowledge 728 729 # -- menu "Office" -------------------- 730 self.menu_office = wx.Menu() 731 732 item = self.menu_office.Append(-1, _('&Audit trail'), _('Display database audit trail.')) 733 self.Bind(wx.EVT_MENU, self.__on_display_audit_trail, item) 734 735 self.menu_office.AppendSeparator() 736 737 item = self.menu_office.Append(-1, _('&Bills'), _('List all bills across all patients.')) 738 self.Bind(wx.EVT_MENU, self.__on_show_all_bills, item) 739 740 item = self.menu_office.Append(-1, _('&Organizations'), _('Manage organizations.')) 741 self.Bind(wx.EVT_MENU, self.__on_manage_orgs, item) 742 743 self.mainmenu.Append(self.menu_office, _('&Office')) 744 self.__gb['main.officemenu'] = self.menu_office 745 746 # -- menu "Help" -------------- 747 help_menu = wx.Menu() 748 help_menu.Append(-1, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.')) 749 self.Bind(wx.EVT_MENU, self.__on_display_wiki, item) 750 help_menu.Append(-1, _('User manual (www)'), _('Go to the User Manual on the web.')) 751 self.Bind(wx.EVT_MENU, self.__on_display_user_manual_online, item) 752 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.')) 753 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item) 754 item = help_menu.Append(-1, _('&Clear status line'), _('Clear out the status line.')) 755 self.Bind(wx.EVT_MENU, self.__on_clear_status_line, item) 756 item = help_menu.Append(-1, _('Browse work dir'), _('Browse user working directory [%s].') % os.path.join(gmTools.gmPaths().home_dir, 'gnumed')) 757 self.Bind(wx.EVT_MENU, self.__on_browse_work_dir, item) 758 759 menu_debugging = wx.Menu() 760 item = menu_debugging.Append(-1, _('Screenshot'), _('Save a screenshot of this GNUmed client.')) 761 self.Bind(wx.EVT_MENU, self.__on_save_screenshot, item) 762 item = menu_debugging.Append(-1, _('Show log file'), _('Show log file in text viewer.')) 763 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item) 764 item = menu_debugging.Append(-1, _('Backup log file'), _('Backup content of the log to another file.')) 765 self.Bind(wx.EVT_MENU, self.__on_backup_log_file, item) 766 item = menu_debugging.Append(-1, _('Email log file'), _('Send log file to the authors for help.')) 767 self.Bind(wx.EVT_MENU, self.__on_email_log_file, item) 768 item = menu_debugging.Append(-1, _('Browse tmp dir'), _('Browse temporary directory [%s].') % gmTools.gmPaths().tmp_dir) 769 self.Bind(wx.EVT_MENU, self.__on_browse_tmp_dir, item) 770 item = menu_debugging.Append(-1, _('Browse internal work dir'), _('Browse internal working directory [%s].') % os.path.join(gmTools.gmPaths().home_dir, '.gnumed')) 771 self.Bind(wx.EVT_MENU, self.__on_browse_internal_work_dir, item) 772 item = menu_debugging.Append(-1, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.')) 773 self.Bind(wx.EVT_MENU, self.__on_display_bugtracker, item) 774 item = menu_debugging.Append(-1, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.')) 775 self.Bind(wx.EVT_MENU, self.__on_unblock_cursor, item) 776 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.')) 777 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item) 778 # item = menu_debugging.Append(-1, _('Reload hook script'), _('Reload hook script from hard drive.')) 779 # self.Bind(wx.EVT_MENU, self.__on_reload_hook_script, item) 780 if _cfg.get(option = 'debug'): 781 item = menu_debugging.Append(-1, _('Lock/unlock patient search'), _('Lock/unlock patient search - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !')) 782 self.Bind(wx.EVT_MENU, self.__on_toggle_patient_lock, item) 783 item = menu_debugging.Append(-1, _('Test error handling'), _('Throw an exception to test error handling.')) 784 self.Bind(wx.EVT_MENU, self.__on_test_exception, item) 785 item = menu_debugging.Append(-1, _('Test access violation exception'), _('Simulate an access violation exception.')) 786 self.Bind(wx.EVT_MENU, self.__on_test_access_violation, item) 787 item = menu_debugging.Append(-1, _('Test access checking'), _('Simulate a failing access check.')) 788 self.Bind(wx.EVT_MENU, self.__on_test_access_checking, item) 789 item = menu_debugging.Append(-1, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).')) 790 self.Bind(wx.EVT_MENU, self.__on_invoke_inspector, item) 791 try: 792 import wx.lib.inspection 793 except ImportError: 794 menu_debugging.Enable(id = ID, enable = False) 795 try: 796 import faulthandler 797 item = menu_debugging.Append(-1, _('Test fault handler'), _('Simulate a catastrophic fault (SIGSEGV).')) 798 self.Bind(wx.EVT_MENU, self.__on_test_segfault, item) 799 except ImportError: 800 pass 801 item = menu_debugging.Append(-1, _('Test placeholder'), _('Manually test placeholders')) 802 self.Bind(wx.EVT_MENU, self.__on_test_placeholders, item) 803 804 help_menu.Append(wx.NewId(), _('Debugging ...'), menu_debugging) 805 help_menu.AppendSeparator() 806 807 item = help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), '') 808 self.Bind(wx.EVT_MENU, self.OnAbout, item) 809 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.')) 810 self.Bind(wx.EVT_MENU, self.__on_about_database, item) 811 item = help_menu.Append(-1, _('About contributors'), _('Show GNUmed contributors')) 812 self.Bind(wx.EVT_MENU, self.__on_show_contributors, item) 813 help_menu.AppendSeparator() 814 815 self.mainmenu.Append(help_menu, _("&Help")) 816 # among other things the Manual is added from a plugin 817 self.__gb['main.helpmenu'] = help_menu 818 819 # and activate menu structure 820 self.SetMenuBar(self.mainmenu)
821 822 #----------------------------------------------
823 - def __load_plugins(self):
824 pass
825 #---------------------------------------------- 826 # event handling 827 #----------------------------------------------
828 - def __register_events(self):
829 """register events we want to react to""" 830 831 self.Bind(wx.EVT_CLOSE, self.OnClose) 832 self.Bind(wx.EVT_QUERY_END_SESSION, self._on_query_end_session) 833 self.Bind(wx.EVT_END_SESSION, self._on_end_session) 834 835 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection) 836 gmDispatcher.connect(signal = 'statustext', receiver = self._on_set_statustext) 837 gmDispatcher.connect(signal = 'request_user_attention', receiver = self._on_request_user_attention) 838 gmDispatcher.connect(signal = 'register_pre_exit_callback', receiver = self._register_pre_exit_callback) 839 gmDispatcher.connect(signal = 'plugin_loaded', receiver = self._on_plugin_loaded) 840 841 gmDispatcher.connect(signal = 'db_maintenance_warning', receiver = self._on_db_maintenance_warning) 842 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal) 843 844 # FIXME: xxxxxxx signal 845 846 gmPerson.gmCurrentPatient().register_before_switching_from_patient_callback(callback = self._before_switching_from_patient_callback)
847 848 #----------------------------------------------
849 - def _on_database_signal(self, **kwds):
850 851 if kwds['table'] == 'dem.praxis_branch': 852 if kwds['operation'] != 'UPDATE': 853 return True 854 branch = gmPraxis.gmCurrentPraxisBranch() 855 if branch['pk_praxis_branch'] != kwds['pk_row']: 856 return True 857 self.__update_window_title() 858 return True 859 860 if kwds['table'] == 'dem.names': 861 pat = gmPerson.gmCurrentPatient() 862 if pat.connected: 863 if pat.ID != kwds['pk_identity']: 864 return True 865 self.__update_window_title() 866 return True 867 868 if kwds['table'] == 'dem.identity': 869 if kwds['operation'] != 'UPDATE': 870 return True 871 pat = gmPerson.gmCurrentPatient() 872 if pat.connected: 873 if pat.ID != kwds['pk_identity']: 874 return True 875 self.__update_window_title() 876 return True 877 878 return True
879 880 #-----------------------------------------------
881 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
882 883 _log.debug('registering plugin with menu system') 884 _log.debug(' generic name: %s', plugin_name) 885 _log.debug(' class name: %s', class_name) 886 _log.debug(' specific menu: %s', menu_name) 887 _log.debug(' menu item: %s', menu_item_name) 888 889 # add to generic "go to plugin" menu 890 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name) 891 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 892 self.menu_id2plugin[item.Id] = class_name 893 894 # add to specific menu if so requested 895 if menu_name is not None: 896 menu = self.__gb['main.%smenu' % menu_name] 897 item = menu.Append(-1, menu_item_name, menu_help_string) 898 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item) 899 self.menu_id2plugin[item.Id] = class_name 900 901 return True
902 #----------------------------------------------
903 - def __on_raise_a_plugin(self, evt):
904 gmDispatcher.send ( 905 signal = 'display_widget', 906 name = self.menu_id2plugin[evt.Id] 907 )
908 #----------------------------------------------
909 - def _on_query_end_session(self, *args, **kwargs):
910 wx.Bell() 911 wx.Bell() 912 wx.Bell() 913 _log.warning('unhandled event detected: QUERY_END_SESSION') 914 _log.info('we should be saving ourselves from here') 915 gmLog2.flush() 916 print('unhandled event detected: QUERY_END_SESSION')
917 #----------------------------------------------
918 - def _on_end_session(self, *args, **kwargs):
919 wx.Bell() 920 wx.Bell() 921 wx.Bell() 922 _log.warning('unhandled event detected: END_SESSION') 923 gmLog2.flush() 924 print('unhandled event detected: END_SESSION')
925 #-----------------------------------------------
926 - def _register_pre_exit_callback(self, callback=None):
927 if not callable(callback): 928 raise TypeError('callback [%s] not callable' % callback) 929 930 self.__pre_exit_callbacks.append(callback)
931 #-----------------------------------------------
932 - def _on_set_statustext_pubsub(self, context=None):
933 msg = '%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg']) 934 wx.CallAfter(self.SetStatusText, msg) 935 936 try: 937 if context.data['beep']: 938 wx.Bell() 939 except KeyError: 940 pass
941 #-----------------------------------------------
942 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
943 944 if msg is None: 945 msg = _('programmer forgot to specify status message') 946 947 if loglevel is not None: 948 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' ')) 949 950 msg = '%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg) 951 wx.CallAfter(self.SetStatusText, msg) 952 953 if beep: 954 wx.Bell()
955 #-----------------------------------------------
956 - def _on_db_maintenance_warning(self):
957 958 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.')) 959 wx.Bell() 960 if not wx.GetApp().IsActive(): 961 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 962 963 gmHooks.run_hook_script(hook = 'db_maintenance_warning') 964 965 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 966 None, 967 -1, 968 caption = _('Database shutdown warning'), 969 question = _( 970 'The database will be shut down for maintenance\n' 971 'in a few minutes.\n' 972 '\n' 973 'In order to not suffer any loss of data you\n' 974 'will need to save your current work and log\n' 975 'out of this GNUmed client.\n' 976 ), 977 button_defs = [ 978 { 979 'label': _('Close now'), 980 'tooltip': _('Close this GNUmed client immediately.'), 981 'default': False 982 }, 983 { 984 'label': _('Finish work'), 985 'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'), 986 'default': True 987 } 988 ] 989 ) 990 decision = dlg.ShowModal() 991 if decision == wx.ID_YES: 992 top_win = wx.GetApp().GetTopWindow() 993 wx.CallAfter(top_win.Close)
994 #-----------------------------------------------
995 - def _on_request_user_attention(self, msg=None, urgent=False):
996 # already in the foreground ? 997 if not wx.GetApp().IsActive(): 998 if urgent: 999 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR) 1000 else: 1001 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO) 1002 1003 if msg is not None: 1004 self.SetStatusText(msg) 1005 1006 if urgent: 1007 wx.Bell() 1008 1009 gmHooks.run_hook_script(hook = 'request_user_attention')
1010 #-----------------------------------------------
1011 - def _on_post_patient_selection(self, **kwargs):
1012 self.__update_window_title() 1013 gmDispatcher.send(signal = 'statustext', msg = '') 1014 try: 1015 gmHooks.run_hook_script(hook = 'post_patient_activation') 1016 except: 1017 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.')) 1018 raise
1019 #----------------------------------------------
1021 msg = _( 1022 'Before activation of another patient review the\n' 1023 'encounter details of the patient you just worked on:\n' 1024 ) 1025 gmEncounterWidgets.sanity_check_encounter_of_active_patient(parent = self, msg = msg) 1026 return True
1027 #---------------------------------------------- 1028 # menu "paperwork" 1029 #----------------------------------------------
1030 - def __on_show_docs(self, evt):
1031 gmDispatcher.send(signal='show_document_viewer')
1032 #----------------------------------------------
1033 - def __on_new_letter(self, evt):
1034 pat = gmPerson.gmCurrentPatient() 1035 if not pat.connected: 1036 gmDispatcher.send(signal = 'statustext', msg = _('Cannot write letter. No active patient.'), beep = True) 1037 return True 1038 gmFormWidgets.print_doc_from_template(parent = self)#, keep_a_copy = True)
1039 1040 #----------------------------------------------
1041 - def __on_show_placeholders(self, evt):
1044 1045 #----------------------------------------------
1047 evt.Skip() 1048 pat = gmPerson.gmCurrentPatient() 1049 if not pat.connected: 1050 gmDispatcher.send(signal = 'statustext', msg = _('Cannot put screenshot into export area. No active patient.'), beep = True) 1051 return True 1052 screenshot_file = self.__save_screenshot_to_file() 1053 pat.export_area.add_file(filename = screenshot_file, hint = _('GMd screenshot'))
1054 1055 #----------------------------------------------
1056 - def __on_test_receiver_selection(self, evt):
1057 dlg = gmFormWidgets.cReceiverSelectionDlg(None, -1) 1058 dlg.patient = gmPerson.gmCurrentPatient() 1059 choice = dlg.ShowModal() 1060 name = dlg.name 1061 adr = dlg.address 1062 dlg.Destroy() 1063 if choice == wx.ID_CANCEL: 1064 print('receiver selection cancelled') 1065 return 1066 1067 print(name) 1068 print(adr.format())
1069 1070 #---------------------------------------------- 1071 # help menu 1072 #----------------------------------------------
1073 - def OnAbout(self, event):
1074 1075 return 1076 1077 # segfaults on wxPhoenix 1078 from Gnumed.wxpython import gmAbout 1079 frame_about = gmAbout.AboutFrame ( 1080 self, 1081 -1, 1082 _("About GNUmed"), 1083 size=wx.Size(350, 300), 1084 style = wx.MAXIMIZE_BOX, 1085 version = _cfg.get(option = 'client_version'), 1086 debug = _cfg.get(option = 'debug') 1087 ) 1088 frame_about.Centre(wx.BOTH) 1089 gmTopLevelFrame.otherWin = frame_about 1090 frame_about.Show(True) 1091 frame_about.Destroy()
1092 1093 #----------------------------------------------
1094 - def __on_about_database(self, evt):
1095 praxis = gmPraxis.gmCurrentPraxisBranch() 1096 msg = praxis.db_logon_banner 1097 1098 login = gmPG2.get_default_login() 1099 1100 auth = _( 1101 '\n\n' 1102 ' praxis: %s\n' 1103 ' branch: %s\n' 1104 ' workplace: %s\n' 1105 ' account: %s\n' 1106 ' access: %s\n' 1107 ' database: %s\n' 1108 ' server: %s\n' 1109 ' PostgreSQL: %s\n' 1110 ) % ( 1111 praxis['praxis'], 1112 praxis['branch'], 1113 praxis.active_workplace, 1114 login.user, 1115 _provider['role'], 1116 login.database, 1117 gmTools.coalesce(login.host, '<localhost>'), 1118 gmPG2.postgresql_version_string 1119 ) 1120 1121 msg += auth 1122 1123 gmGuiHelpers.gm_show_info(msg, _('About database and server'))
1124 1125 #----------------------------------------------
1126 - def __on_show_contributors(self, event):
1127 from Gnumed.wxpython import gmAbout 1128 contribs = gmAbout.cContributorsDlg ( 1129 parent = self, 1130 id = -1, 1131 title = _('GNUmed contributors'), 1132 size = wx.Size(400,600), 1133 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER 1134 ) 1135 contribs.ShowModal() 1136 contribs.Destroy()
1137 1138 #---------------------------------------------- 1139 # GNUmed menu 1140 #----------------------------------------------
1141 - def __on_exit_gnumed(self, event):
1142 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler).""" 1143 _log.debug('gmTopLevelFrame._on_exit_gnumed() start') 1144 self.Close(True) # -> calls wx.EVT_CLOSE handler 1145 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1146 1147 #----------------------------------------------
1148 - def __on_check_for_updates(self, evt):
1150 1151 #----------------------------------------------
1152 - def __on_announce_maintenance(self, evt):
1153 send = gmGuiHelpers.gm_show_question ( 1154 _('This will send a notification about database downtime\n' 1155 'to all GNUmed clients connected to your database.\n' 1156 '\n' 1157 'Do you want to send the notification ?\n' 1158 ), 1159 _('Announcing database maintenance downtime') 1160 ) 1161 if not send: 1162 return 1163 gmPG2.send_maintenance_notification()
1164 #---------------------------------------------- 1165 #----------------------------------------------
1166 - def __on_list_configuration(self, evt):
1168 #---------------------------------------------- 1169 # submenu GNUmed / options / client 1170 #----------------------------------------------
1171 - def __on_configure_export_chunk_size(self, evt):
1172 1173 def is_valid(value): 1174 try: 1175 i = int(value) 1176 except: 1177 return False, value 1178 if i < 0: 1179 return False, value 1180 if i > (1024 * 1024 * 1024 * 10): # 10 GB 1181 return False, value 1182 return True, i
1183 1184 gmCfgWidgets.configure_string_option ( 1185 message = _( 1186 'Some network installations cannot cope with loading\n' 1187 'documents of arbitrary size in one piece from the\n' 1188 'database (mainly observed on older Windows versions)\n.' 1189 '\n' 1190 'Under such circumstances documents need to be retrieved\n' 1191 'in chunks and reassembled on the client.\n' 1192 '\n' 1193 'Here you can set the size (in Bytes) above which\n' 1194 'GNUmed will retrieve documents in chunks. Setting this\n' 1195 'value to 0 will disable the chunking protocol.' 1196 ), 1197 option = 'horstspace.blob_export_chunk_size', 1198 bias = 'workplace', 1199 default_value = 1024 * 1024, 1200 validator = is_valid 1201 )
1202 #---------------------------------------------- 1203 # submenu GNUmed / database 1204 #----------------------------------------------
1205 - def __on_configure_db_lang(self, event):
1206 1207 langs = gmPG2.get_translation_languages() 1208 1209 for lang in [ 1210 gmI18N.system_locale_level['language'], 1211 gmI18N.system_locale_level['country'], 1212 gmI18N.system_locale_level['full'] 1213 ]: 1214 if lang not in langs: 1215 langs.append(lang) 1216 1217 selected_lang = gmPG2.get_current_user_language() 1218 try: 1219 selections = [langs.index(selected_lang)] 1220 except ValueError: 1221 selections = None 1222 1223 language = gmListWidgets.get_choices_from_list ( 1224 parent = self, 1225 msg = _( 1226 'Please select your database language from the list below.\n' 1227 '\n' 1228 'Your current setting is [%s].\n' 1229 '\n' 1230 'This setting will not affect the language the user interface\n' 1231 'is displayed in but rather that of the metadata returned\n' 1232 'from the database such as encounter types, document types,\n' 1233 'and EMR formatting.\n' 1234 '\n' 1235 'To switch back to the default English language unselect all\n' 1236 'pre-selected languages from the list below.' 1237 ) % gmTools.coalesce(selected_lang, _('not configured')), 1238 caption = _('Configuring database language'), 1239 choices = langs, 1240 selections = selections, 1241 columns = [_('Language')], 1242 data = langs, 1243 single_selection = True, 1244 can_return_empty = True 1245 ) 1246 1247 if language is None: 1248 return 1249 1250 if language == []: 1251 language = None 1252 1253 try: 1254 _provider.get_staff().database_language = language 1255 return 1256 except ValueError: 1257 pass 1258 1259 force_language = gmGuiHelpers.gm_show_question ( 1260 _('The database currently holds no translations for\n' 1261 'language [%s]. However, you can add translations\n' 1262 'for things like document or encounter types yourself.\n' 1263 '\n' 1264 'Do you want to force the language setting to [%s] ?' 1265 ) % (language, language), 1266 _('Configuring database language') 1267 ) 1268 if not force_language: 1269 return 1270 1271 gmPG2.force_user_language(language = language)
1272 #----------------------------------------------
1273 - def __on_configure_db_welcome(self, event):
1274 dlg = gmPraxisWidgets.cGreetingEditorDlg(self, -1) 1275 dlg.ShowModal()
1276 #---------------------------------------------- 1277 # submenu GNUmed - config - external tools 1278 #----------------------------------------------
1279 - def __on_configure_ooo_settle_time(self, event):
1280 1281 def is_valid(value): 1282 try: 1283 value = float(value) 1284 return True, value 1285 except: 1286 return False, value
1287 1288 gmCfgWidgets.configure_string_option ( 1289 message = _( 1290 'When GNUmed cannot find an OpenOffice server it\n' 1291 'will try to start one. OpenOffice, however, needs\n' 1292 'some time to fully start up.\n' 1293 '\n' 1294 'Here you can set the time for GNUmed to wait for OOo.\n' 1295 ), 1296 option = 'external.ooo.startup_settle_time', 1297 bias = 'workplace', 1298 default_value = 2.0, 1299 validator = is_valid 1300 ) 1301 #----------------------------------------------
1302 - def __on_configure_drug_data_source(self, evt):
1303 gmSubstanceMgmtWidgets.configure_drug_data_source(parent = self)
1304 1305 #----------------------------------------------
1306 - def __on_configure_adr_url(self, evt):
1307 1308 # http://www.akdae.de/Arzneimittelsicherheit/UAW-Meldung/UAW-Meldung-online.html 1309 german_default = 'https://dcgma.org/uaw/meldung.php' 1310 1311 def is_valid(value): 1312 value = value.strip() 1313 if value == '': 1314 return True, german_default 1315 try: 1316 urllib.request.urlopen(value) 1317 return True, value 1318 except: 1319 return True, value
1320 1321 gmCfgWidgets.configure_string_option ( 1322 message = _( 1323 'GNUmed will use this URL to access a website which lets\n' 1324 'you report an adverse drug reaction (ADR).\n' 1325 '\n' 1326 'If you leave this empty it will fall back\n' 1327 'to an URL for reporting ADRs in Germany.' 1328 ), 1329 option = 'external.urls.report_ADR', 1330 bias = 'user', 1331 default_value = german_default, 1332 validator = is_valid 1333 ) 1334 #----------------------------------------------
1335 - def __on_configure_vaccine_adr_url(self, evt):
1336 1337 german_default = 'http://www.pei.de/cln_042/SharedDocs/Downloads/fachkreise/uaw/meldeboegen/b-ifsg-meldebogen,templateId=raw,property=publicationFile.pdf/b-ifsg-meldebogen.pdf' 1338 1339 def is_valid(value): 1340 value = value.strip() 1341 if value == '': 1342 return True, german_default 1343 try: 1344 urllib.request.urlopen(value) 1345 return True, value 1346 except: 1347 return True, value
1348 1349 gmCfgWidgets.configure_string_option ( 1350 message = _( 1351 'GNUmed will use this URL to access a website which lets\n' 1352 'you report an adverse vaccination reaction (vADR).\n' 1353 '\n' 1354 'If you set it to a specific address that URL must be\n' 1355 'accessible now. If you leave it empty it will fall back\n' 1356 'to the URL for reporting other adverse drug reactions.' 1357 ), 1358 option = 'external.urls.report_vaccine_ADR', 1359 bias = 'user', 1360 default_value = german_default, 1361 validator = is_valid 1362 ) 1363 #----------------------------------------------
1364 - def __on_configure_measurements_url(self, evt):
1365 1366 from Gnumed.business import gmPathLab 1367 german_default = gmPathLab.URL_test_result_information 1368 1369 def is_valid(value): 1370 value = value.strip() 1371 if value == '': 1372 return True, german_default 1373 try: 1374 urllib.request.urlopen(value) 1375 return True, value 1376 except: 1377 return True, value
1378 1379 gmCfgWidgets.configure_string_option ( 1380 message = _( 1381 'GNUmed will use this URL to access an encyclopedia of\n' 1382 'measurement/lab methods from within the measurments grid.\n' 1383 '\n' 1384 'You can leave this empty but to set it to a specific\n' 1385 'address the URL must be accessible now.' 1386 ), 1387 option = 'external.urls.measurements_encyclopedia', 1388 bias = 'user', 1389 default_value = german_default, 1390 validator = is_valid 1391 ) 1392 #----------------------------------------------
1393 - def __on_configure_vaccination_plans_url(self, evt):
1394 1395 from Gnumed.business import gmVaccination 1396 german_default = gmVaccination.URL_vaccination_plan 1397 1398 def is_valid(value): 1399 value = value.strip() 1400 if value == '': 1401 return True, german_default 1402 try: 1403 urllib.request.urlopen(value) 1404 return True, value 1405 except: 1406 return True, value
1407 1408 gmCfgWidgets.configure_string_option ( 1409 message = _( 1410 'GNUmed will use this URL to access a page showing\n' 1411 'vaccination schedules.\n' 1412 '\n' 1413 'You can leave this empty but to set it to a specific\n' 1414 'address the URL must be accessible now.' 1415 ), 1416 option = 'external.urls.vaccination_plans', 1417 bias = 'user', 1418 default_value = german_default, 1419 validator = is_valid 1420 ) 1421 #----------------------------------------------
1422 - def __on_configure_acs_risk_calculator_cmd(self, event):
1423 1424 def is_valid(value): 1425 found, binary = gmShellAPI.detect_external_binary(value) 1426 if not found: 1427 gmDispatcher.send ( 1428 signal = 'statustext', 1429 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1430 beep = True 1431 ) 1432 return False, value 1433 return True, binary
1434 1435 gmCfgWidgets.configure_string_option ( 1436 message = _( 1437 'Enter the shell command with which to start the\n' 1438 'the ACS risk assessment calculator.\n' 1439 '\n' 1440 'GNUmed will try to verify the path which may,\n' 1441 'however, fail if you are using an emulator such\n' 1442 'as Wine. Nevertheless, starting the calculator\n' 1443 'will work as long as the shell command is correct\n' 1444 'despite the failing test.' 1445 ), 1446 option = 'external.tools.acs_risk_calculator_cmd', 1447 bias = 'user', 1448 validator = is_valid 1449 ) 1450 #----------------------------------------------
1451 - def __on_configure_visual_soap_cmd(self, event):
1452 gmVisualProgressNoteWidgets.configure_visual_progress_note_editor()
1453 #----------------------------------------------
1454 - def __on_configure_freediams_cmd(self, event):
1455 1456 def is_valid(value): 1457 found, binary = gmShellAPI.detect_external_binary(value) 1458 if not found: 1459 gmDispatcher.send ( 1460 signal = 'statustext', 1461 msg = _('The command [%s] is not found.') % value, 1462 beep = True 1463 ) 1464 return False, value 1465 return True, binary
1466 #------------------------------------------ 1467 gmCfgWidgets.configure_string_option ( 1468 message = _( 1469 'Enter the shell command with which to start\n' 1470 'the FreeDiams drug database frontend.\n' 1471 '\n' 1472 'GNUmed will try to verify that path.' 1473 ), 1474 option = 'external.tools.freediams_cmd', 1475 bias = 'workplace', 1476 default_value = None, 1477 validator = is_valid 1478 ) 1479 #----------------------------------------------
1480 - def __on_configure_ifap_cmd(self, event):
1481 1482 def is_valid(value): 1483 found, binary = gmShellAPI.detect_external_binary(value) 1484 if not found: 1485 gmDispatcher.send ( 1486 signal = 'statustext', 1487 msg = _('The command [%s] is not found. This may or may not be a problem.') % value, 1488 beep = True 1489 ) 1490 return False, value 1491 return True, binary
1492 1493 gmCfgWidgets.configure_string_option ( 1494 message = _( 1495 'Enter the shell command with which to start the\n' 1496 'the IFAP drug database.\n' 1497 '\n' 1498 'GNUmed will try to verify the path which may,\n' 1499 'however, fail if you are using an emulator such\n' 1500 'as Wine. Nevertheless, starting IFAP will work\n' 1501 'as long as the shell command is correct despite\n' 1502 'the failing test.' 1503 ), 1504 option = 'external.ifap-win.shell_command', 1505 bias = 'workplace', 1506 default_value = 'C:\Ifapwin\WIAMDB.EXE', 1507 validator = is_valid 1508 ) 1509 #---------------------------------------------- 1510 # submenu GNUmed / config / ui 1511 #----------------------------------------------
1512 - def __on_configure_startup_plugin(self, evt):
1513 1514 dbcfg = gmCfg.cCfgSQL() 1515 # get list of possible plugins 1516 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1517 option = 'horstspace.notebook.plugin_load_order', 1518 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1519 bias = 'user' 1520 ), []) 1521 1522 # get current setting 1523 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1524 option = 'horstspace.plugin_to_raise_after_startup', 1525 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1526 bias = 'user' 1527 ), 'gmEMRBrowserPlugin') 1528 try: 1529 selections = [plugin_list.index(initial_plugin)] 1530 except ValueError: 1531 selections = None 1532 1533 # now let user decide 1534 plugin = gmListWidgets.get_choices_from_list ( 1535 parent = self, 1536 msg = _( 1537 'Here you can choose which plugin you want\n' 1538 'GNUmed to display after initial startup.\n' 1539 '\n' 1540 'Note that the plugin must not require any\n' 1541 'patient to be activated.\n' 1542 '\n' 1543 'Select the desired plugin below:' 1544 ), 1545 caption = _('Configuration'), 1546 choices = plugin_list, 1547 selections = selections, 1548 columns = [_('GNUmed Plugin')], 1549 single_selection = True 1550 ) 1551 1552 if plugin is None: 1553 return 1554 1555 dbcfg.set ( 1556 option = 'horstspace.plugin_to_raise_after_startup', 1557 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1558 value = plugin 1559 )
1560 #---------------------------------------------- 1561 # submenu GNUmed / config / ui / patient search 1562 #----------------------------------------------
1563 - def __on_configure_quick_pat_search(self, evt):
1564 gmCfgWidgets.configure_boolean_option ( 1565 parent = self, 1566 question = _( 1567 'If there is only one external patient\n' 1568 'source available do you want GNUmed\n' 1569 'to immediately go ahead and search for\n' 1570 'matching patient records ?\n\n' 1571 'If not GNUmed will let you confirm the source.' 1572 ), 1573 option = 'patient_search.external_sources.immediately_search_if_single_source', 1574 button_tooltips = [ 1575 _('Yes, search for matches immediately.'), 1576 _('No, let me confirm the external patient first.') 1577 ] 1578 )
1579 #----------------------------------------------
1580 - def __on_cfg_default_region(self, evt):
1581 gmAddressWidgets.configure_default_region()
1582 #----------------------------------------------
1583 - def __on_cfg_default_country(self, evt):
1584 gmAddressWidgets.configure_default_country()
1585 #----------------------------------------------
1586 - def __on_configure_dob_reminder_proximity(self, evt):
1587 1588 def is_valid(value): 1589 return gmPG2.is_pg_interval(candidate=value), value
1590 1591 gmCfgWidgets.configure_string_option ( 1592 message = _( 1593 'When a patient is activated GNUmed checks the\n' 1594 "proximity of the patient's birthday.\n" 1595 '\n' 1596 'If the birthday falls within the range of\n' 1597 ' "today %s <the interval you set here>"\n' 1598 'GNUmed will remind you of the recent or\n' 1599 'imminent anniversary.' 1600 ) % '\u2213', 1601 option = 'patient_search.dob_warn_interval', 1602 bias = 'user', 1603 default_value = '1 week', 1604 validator = is_valid 1605 ) 1606 #----------------------------------------------
1607 - def __on_allow_multiple_new_episodes(self, evt):
1608 1609 gmCfgWidgets.configure_boolean_option ( 1610 parent = self, 1611 question = _( 1612 'When adding progress notes do you want to\n' 1613 'allow opening several unassociated, new\n' 1614 'episodes for a patient at once ?\n' 1615 '\n' 1616 'This can be particularly helpful when entering\n' 1617 'progress notes on entirely new patients presenting\n' 1618 'with a multitude of problems on their first visit.' 1619 ), 1620 option = 'horstspace.soap_editor.allow_same_episode_multiple_times', 1621 button_tooltips = [ 1622 _('Yes, allow for multiple new episodes concurrently.'), 1623 _('No, only allow editing one new episode at a time.') 1624 ] 1625 )
1626 #----------------------------------------------
1627 - def __on_allow_auto_open_episodes(self, evt):
1628 1629 gmCfgWidgets.configure_boolean_option ( 1630 parent = self, 1631 question = _( 1632 'When activating a patient, do you want GNUmed to\n' 1633 'auto-open editors for all active problems that were\n' 1634 'touched upon during the current and the most recent\n' 1635 'encounter ?' 1636 ), 1637 option = 'horstspace.soap_editor.auto_open_latest_episodes', 1638 button_tooltips = [ 1639 _('Yes, auto-open editors for all problems of the most recent encounter.'), 1640 _('No, only auto-open one editor for a new, unassociated problem.') 1641 ] 1642 )
1643 1644 #----------------------------------------------
1645 - def __on_use_fields_in_soap_editor(self, evt):
1646 gmCfgWidgets.configure_boolean_option ( 1647 parent = self, 1648 question = _( 1649 'When editing progress notes, do you want GNUmed to\n' 1650 'show individual fields for each of the SOAP categories\n' 1651 'or do you want to use a text-editor like field for\n' 1652 'all SOAP categories which can then be set per line\n' 1653 'of input ?' 1654 ), 1655 option = 'horstspace.soap_editor.use_one_field_per_soap_category', 1656 button_tooltips = [ 1657 _('Yes, show a dedicated field per SOAP category.'), 1658 _('No, use one field for all SOAP categories.') 1659 ] 1660 )
1661 1662 #----------------------------------------------
1663 - def __on_configure_initial_pat_plugin(self, evt):
1664 1665 dbcfg = gmCfg.cCfgSQL() 1666 # get list of possible plugins 1667 plugin_list = gmTools.coalesce(dbcfg.get2 ( 1668 option = 'horstspace.notebook.plugin_load_order', 1669 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1670 bias = 'user' 1671 ), []) 1672 1673 # get current setting 1674 initial_plugin = gmTools.coalesce(dbcfg.get2 ( 1675 option = 'patient_search.plugin_to_raise_after_search', 1676 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1677 bias = 'user' 1678 ), 'gmPatientOverviewPlugin') 1679 try: 1680 selections = [plugin_list.index(initial_plugin)] 1681 except ValueError: 1682 selections = None 1683 1684 # now let user decide 1685 plugin = gmListWidgets.get_choices_from_list ( 1686 parent = self, 1687 msg = _( 1688 'When a patient is activated GNUmed can\n' 1689 'be told to switch to a specific plugin.\n' 1690 '\n' 1691 'Select the desired plugin below:' 1692 ), 1693 caption = _('Configuration'), 1694 choices = plugin_list, 1695 selections = selections, 1696 columns = [_('GNUmed Plugin')], 1697 single_selection = True 1698 ) 1699 1700 if plugin is None: 1701 return 1702 1703 dbcfg.set ( 1704 option = 'patient_search.plugin_to_raise_after_search', 1705 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 1706 value = plugin 1707 )
1708 #---------------------------------------------- 1709 # submenu GNUmed / config / billing 1710 #----------------------------------------------
1711 - def __on_cfg_invoice_template_no_vat(self, evt):
1712 gmBillingWidgets.configure_invoice_template(parent = self, with_vat = False)
1713 #----------------------------------------------
1714 - def __on_cfg_invoice_template_with_vat(self, evt):
1715 gmBillingWidgets.configure_invoice_template(parent = self, with_vat = True)
1716 #----------------------------------------------
1717 - def __on_configure_billing_catalogs_url(self, evt):
1718 german_default = 'http://www.e-bis.de/goae/defaultFrame.htm' 1719 1720 def is_valid(value): 1721 value = value.strip() 1722 if value == '': 1723 return True, german_default 1724 try: 1725 urllib.request.urlopen(value) 1726 return True, value 1727 except: 1728 return True, value
1729 1730 gmCfgWidgets.configure_string_option ( 1731 message = _( 1732 'GNUmed will use this URL to let you browse\n' 1733 'billing catalogs (schedules of fees).\n' 1734 '\n' 1735 'You can leave this empty but to set it to a specific\n' 1736 'address the URL must be accessible now.' 1737 ), 1738 option = 'external.urls.schedules_of_fees', 1739 bias = 'user', 1740 default_value = german_default, 1741 validator = is_valid 1742 ) 1743 #---------------------------------------------- 1744 # submenu GNUmed / config / encounter 1745 #----------------------------------------------
1746 - def __on_cfg_medication_list_template(self, evt):
1747 gmMedicationWidgets.configure_medication_list_template(parent = self)
1748 #----------------------------------------------
1749 - def __on_cfg_prescription_template(self, evt):
1750 gmMedicationWidgets.configure_prescription_template(parent = self)
1751 #----------------------------------------------
1752 - def __on_cfg_prescription_mode(self, evt):
1753 gmCfgWidgets.configure_string_from_list_option ( 1754 parent = self, 1755 message = _('Select the default prescription mode.\n'), 1756 option = 'horst_space.default_prescription_mode', 1757 bias = 'user', 1758 default_value = 'form', 1759 choices = [ _('Formular'), _('Datenbank') ], 1760 columns = [_('Prescription mode')], 1761 data = [ 'form', 'database' ] 1762 )
1763 #----------------------------------------------
1764 - def __on_cfg_default_gnuplot_template(self, evt):
1765 gmMeasurementWidgets.configure_default_gnuplot_template(parent = self)
1766 #----------------------------------------------
1767 - def __on_cfg_fallback_primary_provider(self, evt):
1768 gmPraxisWidgets.configure_fallback_primary_provider(parent = self)
1769 #----------------------------------------------
1770 - def __on_cfg_meds_lab_pnl(self, evt):
1771 gmMedicationWidgets.configure_default_medications_lab_panel(parent = self)
1772 #----------------------------------------------
1773 - def __on_cfg_top_lab_pnl(self, evt):
1774 gmMeasurementWidgets.configure_default_top_lab_panel(parent = self)
1775 #----------------------------------------------
1776 - def __on_cfg_enc_default_type(self, evt):
1777 enc_types = gmEMRStructItems.get_encounter_types() 1778 msg = _( 1779 'Select the default type for new encounters.\n' 1780 '\n' 1781 'Leaving this unset will make GNUmed apply the most commonly used type.\n' 1782 ) 1783 gmCfgWidgets.configure_string_from_list_option ( 1784 parent = self, 1785 message = msg, 1786 option = 'encounter.default_type', 1787 bias = 'user', 1788 # default_value = u'in surgery', 1789 choices = [ e[0] for e in enc_types ], 1790 columns = [_('Encounter type')], 1791 data = [ e[1] for e in enc_types ] 1792 )
1793 #----------------------------------------------
1794 - def __on_cfg_enc_pat_change(self, event):
1795 gmCfgWidgets.configure_boolean_option ( 1796 parent = self, 1797 question = _( 1798 'Do you want GNUmed to show the encounter\n' 1799 'details editor when changing the active patient ?' 1800 ), 1801 option = 'encounter.show_editor_before_patient_change', 1802 button_tooltips = [ 1803 _('Yes, show the encounter editor if it seems appropriate.'), 1804 _('No, never show the encounter editor even if it would seem useful.') 1805 ] 1806 )
1807 #----------------------------------------------
1808 - def __on_cfg_enc_empty_ttl(self, evt):
1809 1810 def is_valid(value): 1811 return gmPG2.is_pg_interval(candidate=value), value
1812 1813 gmCfgWidgets.configure_string_option ( 1814 message = _( 1815 'When a patient is activated GNUmed checks the\n' 1816 'chart for encounters lacking any entries.\n' 1817 '\n' 1818 'Any such encounters older than what you set\n' 1819 'here will be removed from the medical record.\n' 1820 '\n' 1821 'To effectively disable removal of such encounters\n' 1822 'set this option to an improbable value.\n' 1823 ), 1824 option = 'encounter.ttl_if_empty', 1825 bias = 'user', 1826 default_value = '1 week', 1827 validator = is_valid 1828 ) 1829 #----------------------------------------------
1830 - def __on_cfg_enc_min_ttl(self, evt):
1831 1832 def is_valid(value): 1833 return gmPG2.is_pg_interval(candidate=value), value
1834 1835 gmCfgWidgets.configure_string_option ( 1836 message = _( 1837 'When a patient is activated GNUmed checks the\n' 1838 'age of the most recent encounter.\n' 1839 '\n' 1840 'If that encounter is younger than this age\n' 1841 'the existing encounter will be continued.\n' 1842 '\n' 1843 '(If it is really old a new encounter is\n' 1844 ' started, or else GNUmed will ask you.)\n' 1845 ), 1846 option = 'encounter.minimum_ttl', 1847 bias = 'user', 1848 default_value = '1 hour 30 minutes', 1849 validator = is_valid 1850 ) 1851 #----------------------------------------------
1852 - def __on_cfg_enc_max_ttl(self, evt):
1853 1854 def is_valid(value): 1855 return gmPG2.is_pg_interval(candidate=value), value
1856 1857 gmCfgWidgets.configure_string_option ( 1858 message = _( 1859 'When a patient is activated GNUmed checks the\n' 1860 'age of the most recent encounter.\n' 1861 '\n' 1862 'If that encounter is older than this age\n' 1863 'GNUmed will always start a new encounter.\n' 1864 '\n' 1865 '(If it is very recent the existing encounter\n' 1866 ' is continued, or else GNUmed will ask you.)\n' 1867 ), 1868 option = 'encounter.maximum_ttl', 1869 bias = 'user', 1870 default_value = '6 hours', 1871 validator = is_valid 1872 ) 1873 #----------------------------------------------
1874 - def __on_cfg_epi_ttl(self, evt):
1875 1876 def is_valid(value): 1877 try: 1878 value = int(value) 1879 except: 1880 return False, value 1881 return gmPG2.is_pg_interval(candidate=value), value
1882 1883 gmCfgWidgets.configure_string_option ( 1884 message = _( 1885 'At any time there can only be one open (ongoing)\n' 1886 'episode for each health issue.\n' 1887 '\n' 1888 'When you try to open (add data to) an episode on a health\n' 1889 'issue GNUmed will check for an existing open episode on\n' 1890 'that issue. If there is any it will check the age of that\n' 1891 'episode. The episode is closed if it has been dormant (no\n' 1892 'data added, that is) for the period of time (in days) you\n' 1893 'set here.\n' 1894 '\n' 1895 "If the existing episode hasn't been dormant long enough\n" 1896 'GNUmed will consult you what to do.\n' 1897 '\n' 1898 'Enter maximum episode dormancy in DAYS:' 1899 ), 1900 option = 'episode.ttl', 1901 bias = 'user', 1902 default_value = 60, 1903 validator = is_valid 1904 ) 1905 #----------------------------------------------
1906 - def __on_configure_user_email(self, evt):
1907 email = gmPraxis.gmCurrentPraxisBranch().user_email 1908 1909 dlg = wx.TextEntryDialog ( 1910 parent = self, 1911 message = _( 1912 'If you want the GNUmed developers to be able to\n' 1913 'contact you directly - rather than via the public\n' 1914 'mailing list only - you can enter your preferred\n' 1915 'email address here.\n' 1916 '\n' 1917 'This address will then be included with bug reports\n' 1918 'or contributions to the GNUmed community you may\n' 1919 'choose to send from within the GNUmed client.\n' 1920 '\n' 1921 'Leave this blank if you wish to stay anonymous.\n' 1922 ), 1923 caption = _('Please enter your email address.'), 1924 defaultValue = gmTools.coalesce(email, ''), 1925 style = wx.OK | wx.CANCEL | wx.CENTRE 1926 ) 1927 decision = dlg.ShowModal() 1928 if decision == wx.ID_CANCEL: 1929 dlg.Destroy() 1930 return 1931 1932 email = dlg.GetValue().strip() 1933 gmPraxis.gmCurrentPraxisBranch().user_email = email 1934 gmExceptionHandlingWidgets.set_sender_email(email) 1935 dlg.Destroy()
1936 #----------------------------------------------
1937 - def __on_configure_update_check(self, evt):
1938 gmCfgWidgets.configure_boolean_option ( 1939 question = _( 1940 'Do you want GNUmed to check for updates at startup ?\n' 1941 '\n' 1942 'You will still need your system administrator to\n' 1943 'actually install any updates for you.\n' 1944 ), 1945 option = 'horstspace.update.autocheck_at_startup', 1946 button_tooltips = [ 1947 _('Yes, check for updates at startup.'), 1948 _('No, do not check for updates at startup.') 1949 ] 1950 )
1951 #----------------------------------------------
1952 - def __on_configure_update_check_scope(self, evt):
1953 gmCfgWidgets.configure_boolean_option ( 1954 question = _( 1955 'When checking for updates do you want GNUmed to\n' 1956 'look for bug fix updates only or do you want to\n' 1957 'know about features updates, too ?\n' 1958 '\n' 1959 'Minor updates (x.y.z.a -> x.y.z.b) contain bug fixes\n' 1960 'only. They can usually be installed without much\n' 1961 'preparation. They never require a database upgrade.\n' 1962 '\n' 1963 'Major updates (x.y.a -> x..y.b or y.a -> x.b) come\n' 1964 'with new features. They need more preparation and\n' 1965 'often require a database upgrade.\n' 1966 '\n' 1967 'You will still need your system administrator to\n' 1968 'actually install any updates for you.\n' 1969 ), 1970 option = 'horstspace.update.consider_latest_branch', 1971 button_tooltips = [ 1972 _('Yes, check for feature updates, too.'), 1973 _('No, check for bug-fix updates only.') 1974 ] 1975 )
1976 #----------------------------------------------
1977 - def __on_configure_update_url(self, evt):
1978 1979 def is_valid(value): 1980 try: 1981 urllib.request.urlopen(value) 1982 except: 1983 return False, value 1984 1985 return True, value
1986 1987 gmCfgWidgets.configure_string_option ( 1988 message = _( 1989 'GNUmed can check for new releases being available. To do\n' 1990 'so it needs to load version information from an URL.\n' 1991 '\n' 1992 'The default URL is:\n' 1993 '\n' 1994 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n' 1995 '\n' 1996 'but you can configure any other URL locally. Note\n' 1997 'that you must enter the location as a valid URL.\n' 1998 'Depending on the URL the client will need online\n' 1999 'access when checking for updates.' 2000 ), 2001 option = 'horstspace.update.url', 2002 bias = 'workplace', 2003 default_value = 'http://www.gnumed.de/downloads/gnumed-versions.txt', 2004 validator = is_valid 2005 ) 2006 #----------------------------------------------
2007 - def __on_configure_partless_docs(self, evt):
2008 gmCfgWidgets.configure_boolean_option ( 2009 question = _( 2010 'Do you want to allow saving of new documents without\n' 2011 'any parts or do you want GNUmed to enforce that they\n' 2012 'contain at least one part before they can be saved ?\n' 2013 '\n' 2014 'Part-less documents can be useful if you want to build\n' 2015 'up an index of, say, archived documents but do not\n' 2016 'want to scan in all the pages contained therein.' 2017 ), 2018 option = 'horstspace.scan_index.allow_partless_documents', 2019 button_tooltips = [ 2020 _('Yes, allow saving documents without any parts.'), 2021 _('No, require documents to have at least one part.') 2022 ] 2023 )
2024 #----------------------------------------------
2025 - def __on_configure_doc_uuid_dialog(self, evt):
2026 gmCfgWidgets.configure_boolean_option ( 2027 question = _( 2028 'After importing a new document do you\n' 2029 'want GNUmed to display the unique ID\n' 2030 'it auto-generated for that document ?\n' 2031 '\n' 2032 'This can be useful if you want to label the\n' 2033 'originals with that ID for later identification.' 2034 ), 2035 option = 'horstspace.scan_index.show_doc_id', 2036 button_tooltips = [ 2037 _('Yes, display the ID generated for the new document after importing.'), 2038 _('No, do not display the ID generated for the new document after importing.') 2039 ] 2040 )
2041 #----------------------------------------------
2042 - def __on_configure_generate_doc_uuid(self, evt):
2043 gmCfgWidgets.configure_boolean_option ( 2044 question = _( 2045 'After importing a new document do you\n' 2046 'want GNUmed to generate a unique ID\n' 2047 '(UUID) for that document ?\n' 2048 '\n' 2049 'This can be useful if you want to label the\n' 2050 'originals with that ID for later identification.' 2051 ), 2052 option = 'horstspace.scan_index.generate_doc_uuid', 2053 button_tooltips = [ 2054 _('Yes, generate a UUID for the new document after importing.'), 2055 _('No, do not generate a UUID for the new document after importing.') 2056 ] 2057 )
2058 #----------------------------------------------
2059 - def __on_configure_doc_review_dialog(self, evt):
2060 2061 def is_valid(value): 2062 try: 2063 value = int(value) 2064 except: 2065 return False, value 2066 if value not in [0, 1, 2, 3, 4]: 2067 return False, value 2068 return True, value
2069 2070 gmCfgWidgets.configure_string_option ( 2071 message = _( 2072 'GNUmed can show the document review dialog after\n' 2073 'calling the appropriate viewer for that document.\n' 2074 '\n' 2075 'Select the conditions under which you want\n' 2076 'GNUmed to do so:\n' 2077 '\n' 2078 ' 0: never display the review dialog\n' 2079 ' 1: always display the dialog\n' 2080 ' 2: only if there is no previous review by me\n' 2081 ' 3: only if there is no previous review at all\n' 2082 ' 4: only if there is no review by the responsible reviewer\n' 2083 '\n' 2084 'Note that if a viewer is configured to not block\n' 2085 'GNUmed during document display the review dialog\n' 2086 'will actually appear in parallel to the viewer.' 2087 ), 2088 option = 'horstspace.document_viewer.review_after_display', 2089 bias = 'user', 2090 default_value = 3, 2091 validator = is_valid 2092 ) 2093 #----------------------------------------------
2094 - def __on_manage_master_data(self, evt):
2095 2096 # this is how it is sorted 2097 master_data_lists = [ 2098 'adr', 2099 'provinces', 2100 'codes', 2101 'billables', 2102 'ref_data_sources', 2103 'meds_substances', 2104 'meds_doses', 2105 'meds_components', 2106 'meds_drugs', 2107 'meds_vaccines', 2108 'orgs', 2109 'labs', 2110 'meta_test_types', 2111 'test_types', 2112 'test_panels', 2113 'form_templates', 2114 'doc_types', 2115 'enc_types', 2116 'communication_channel_types', 2117 'text_expansions', 2118 'patient_tags', 2119 'hints', 2120 'db_translations', 2121 'workplaces' 2122 ] 2123 2124 master_data_list_names = { 2125 'adr': _('Addresses (likely slow)'), 2126 'hints': _('Dynamic automatic hints'), 2127 'codes': _('Codes and their respective terms'), 2128 'communication_channel_types': _('Communication channel types'), 2129 'orgs': _('Organizations with their units, addresses, and comm channels'), 2130 'labs': _('Measurements: diagnostic organizations (path labs, ...)'), 2131 'test_types': _('Measurements: test types'), 2132 'test_panels': _('Measurements: test panels/profiles/batteries'), 2133 'form_templates': _('Document templates (forms, letters, plots, ...)'), 2134 'doc_types': _('Document types'), 2135 'enc_types': _('Encounter types'), 2136 'text_expansions': _('Keyword based text expansion macros'), 2137 'meta_test_types': _('Measurements: aggregate test types'), 2138 'patient_tags': _('Patient tags'), 2139 'provinces': _('Provinces (counties, territories, states, regions, ...)'), 2140 'db_translations': _('String translations in the database'), 2141 'meds_vaccines': _('Medications: vaccines'), 2142 'meds_substances': _('Medications: base substances'), 2143 'meds_doses': _('Medications: substance dosage'), 2144 'meds_components': _('Medications: drug components'), 2145 'meds_drugs': _('Medications: drug products and generic drugs'), 2146 'workplaces': _('Workplace profiles (which plugins to load)'), 2147 'billables': _('Billable items'), 2148 'ref_data_sources': _('Reference data sources') 2149 } 2150 2151 map_list2handler = { 2152 'form_templates': gmFormWidgets.manage_form_templates, 2153 'doc_types': gmDocumentWidgets.manage_document_types, 2154 'text_expansions': gmKeywordExpansionWidgets.configure_keyword_text_expansion, 2155 'db_translations': gmI18nWidgets.manage_translations, 2156 'codes': gmCodingWidgets.browse_coded_terms, 2157 'enc_types': gmEncounterWidgets.manage_encounter_types, 2158 'provinces': gmAddressWidgets.manage_regions, 2159 'workplaces': gmPraxisWidgets.configure_workplace_plugins, 2160 'labs': gmMeasurementWidgets.manage_measurement_orgs, 2161 'test_types': gmMeasurementWidgets.manage_measurement_types, 2162 'meta_test_types': gmMeasurementWidgets.manage_meta_test_types, 2163 'orgs': gmOrganizationWidgets.manage_orgs, 2164 'adr': gmAddressWidgets.manage_addresses, 2165 'meds_substances': gmSubstanceMgmtWidgets.manage_substances, 2166 'meds_doses': gmSubstanceMgmtWidgets.manage_substance_doses, 2167 'meds_components': gmSubstanceMgmtWidgets.manage_drug_components, 2168 'meds_drugs': gmSubstanceMgmtWidgets.manage_drug_products, 2169 'meds_vaccines': gmVaccWidgets.manage_vaccines, 2170 'patient_tags': gmDemographicsWidgets.manage_tag_images, 2171 'communication_channel_types': gmContactWidgets.manage_comm_channel_types, 2172 'billables': gmBillingWidgets.manage_billables, 2173 'ref_data_sources': gmCodingWidgets.browse_data_sources, 2174 'hints': gmAutoHintWidgets.manage_dynamic_hints, 2175 'test_panels': gmMeasurementWidgets.manage_test_panels 2176 } 2177 2178 #--------------------------------- 2179 def edit(item): 2180 try: map_list2handler[item](parent = self) 2181 except KeyError: pass 2182 return False
2183 #--------------------------------- 2184 2185 gmListWidgets.get_choices_from_list ( 2186 parent = self, 2187 caption = _('Master data management'), 2188 choices = [ master_data_list_names[lst] for lst in master_data_lists], 2189 data = master_data_lists, 2190 columns = [_('Select the list you want to manage:')], 2191 edit_callback = edit, 2192 single_selection = True, 2193 ignore_OK_button = True 2194 ) 2195 #----------------------------------------------
2196 - def __on_manage_praxis(self, evt):
2197 gmPraxisWidgets.manage_praxis_branches(parent = self)
2198 #----------------------------------------------
2199 - def __on_dicom_viewer(self, evt):
2200 2201 found, cmd = gmShellAPI.detect_external_binary(binary = 'ginkgocadx') 2202 if found: 2203 gmShellAPI.run_command_in_shell(cmd, blocking=False) 2204 return 2205 2206 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK): 2207 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking = False) 2208 return 2209 2210 for viewer in ['aeskulap', 'amide', 'dicomscope', 'xmedcon']: 2211 found, cmd = gmShellAPI.detect_external_binary(binary = viewer) 2212 if found: 2213 gmShellAPI.run_command_in_shell(cmd, blocking = False) 2214 return 2215 2216 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
2217 #----------------------------------------------
2218 - def __on_arriba(self, evt):
2219 2220 curr_pat = gmPerson.gmCurrentPatient() 2221 2222 arriba = gmArriba.cArriba() 2223 pat = gmTools.bool2subst(curr_pat.connected, curr_pat, None) 2224 if not arriba.run(patient = pat, debug = _cfg.get(option = 'debug')): 2225 return 2226 2227 # FIXME: try to find patient 2228 if curr_pat is None: 2229 return 2230 2231 if arriba.pdf_result is None: 2232 return 2233 2234 doc = gmDocumentWidgets.save_file_as_new_document ( 2235 parent = self, 2236 filename = arriba.pdf_result, 2237 document_type = _('risk assessment'), 2238 pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit'] 2239 ) 2240 2241 try: os.remove(arriba.pdf_result) 2242 except Exception: _log.exception('cannot remove [%s]', arriba.pdf_result) 2243 2244 if doc is None: 2245 return 2246 2247 doc['comment'] = 'arriba: %s' % _('cardiovascular risk assessment') 2248 doc.save() 2249 2250 try: 2251 open(arriba.xml_result).close() 2252 part = doc.add_part(file = arriba.xml_result) 2253 except Exception: 2254 _log.exception('error accessing [%s]', arriba.xml_result) 2255 gmDispatcher.send(signal = 'statustext', msg = _('[arriba] XML result not found in [%s]') % arriba.xml_result, beep = False) 2256 2257 if part is None: 2258 return 2259 2260 part['obj_comment'] = 'XML-Daten' 2261 part['filename'] = 'arriba-result.xml' 2262 part.save()
2263 #----------------------------------------------
2264 - def __on_acs_risk_assessment(self, evt):
2265 2266 dbcfg = gmCfg.cCfgSQL() 2267 cmd = dbcfg.get2 ( 2268 option = 'external.tools.acs_risk_calculator_cmd', 2269 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 2270 bias = 'user' 2271 ) 2272 2273 if cmd is None: 2274 gmDispatcher.send(signal = 'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True) 2275 return 2276 2277 cwd = os.path.expanduser(os.path.join('~', '.gnumed')) 2278 try: 2279 subprocess.check_call ( 2280 args = (cmd,), 2281 close_fds = True, 2282 cwd = cwd 2283 ) 2284 except (OSError, ValueError, subprocess.CalledProcessError): 2285 _log.exception('there was a problem executing [%s]', cmd) 2286 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True) 2287 return 2288 2289 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d'))) 2290 for pdf in pdfs: 2291 try: 2292 open(pdf).close() 2293 except: 2294 _log.exception('error accessing [%s]', pdf) 2295 gmDispatcher.send(signal = 'statustext', msg = _('There was a problem accessing the [arriba] result in [%s] !') % pdf, beep = True) 2296 continue 2297 2298 doc = gmDocumentWidgets.save_file_as_new_document ( 2299 parent = self, 2300 filename = pdf, 2301 document_type = 'risk assessment', 2302 pk_org_unit = gmPraxis.gmCurrentPraxisBranch()['pk_org_unit'] 2303 ) 2304 2305 try: 2306 os.remove(pdf) 2307 except Exception: 2308 _log.exception('cannot remove [%s]', pdf) 2309 2310 if doc is None: 2311 continue 2312 doc['comment'] = 'arriba: %s' % _('cardiovascular risk assessment') 2313 doc.save() 2314 2315 return
2316 2317 #----------------------------------------------
2318 - def __on_show_hl7(self, evt):
2319 # from Gnumed.business import gmClinicalCalculator 2320 # calc = gmClinicalCalculator.cClinicalCalculator(patient = gmPerson.gmCurrentPatient()) 2321 # result = calc.eGFR_CKD_EPI 2322 # print(u'%s' % result.format(with_formula = True, with_warnings = True, with_variables = True, with_sub_results = True, with_hints = True)) 2323 # return 2324 gmMeasurementWidgets.show_hl7_file(parent = self)
2325 #----------------------------------------------
2326 - def __on_unwrap_hl7_from_xml(self, evt):
2327 gmMeasurementWidgets.unwrap_HL7_from_XML(parent = self)
2328 #----------------------------------------------
2329 - def __on_stage_hl7(self, evt):
2330 gmMeasurementWidgets.stage_hl7_file(parent = self)
2331 #----------------------------------------------
2332 - def __on_incoming(self, evt):
2333 gmMeasurementWidgets.browse_incoming_unmatched(parent = self)
2334 #----------------------------------------------
2335 - def __on_snellen(self, evt):
2336 dlg = gmSnellen.cSnellenCfgDlg() 2337 if dlg.ShowModal() != wx.ID_OK: 2338 return 2339 2340 frame = gmSnellen.cSnellenChart ( 2341 width = dlg.vals[0], 2342 height = dlg.vals[1], 2343 alpha = dlg.vals[2], 2344 mirr = dlg.vals[3], 2345 parent = None 2346 ) 2347 frame.CentreOnScreen(wx.BOTH) 2348 # self.SetTopWindow(frame) 2349 # frame.Destroy = frame.DestroyWhenApp 2350 frame.Show(True)
2351 #---------------------------------------------- 2352 #---------------------------------------------- 2355 2356 #----------------------------------------------
2357 - def __on_jump_to_drug_db(self, evt):
2358 curr_pat = gmPerson.gmCurrentPatient() 2359 if not curr_pat.connected: 2360 curr_pat = None 2361 gmSubstanceMgmtWidgets.jump_to_drug_database(patient = curr_pat)
2362 2363 #----------------------------------------------
2364 - def __on_kompendium_ch(self, evt):
2365 gmNetworkTools.open_url_in_browser(url = 'http://www.kompendium.ch')
2366 2367 #---------------------------------------------- 2368 # Office 2369 #----------------------------------------------
2370 - def __on_display_audit_trail(self, evt):
2371 gmPraxisWidgets.show_audit_trail(parent = self)
2372 2373 #----------------------------------------------
2374 - def __on_show_all_bills(self, evt):
2375 gmBillingWidgets.manage_bills(parent = self)
2376 2377 #----------------------------------------------
2378 - def __on_manage_orgs(self, evt):
2379 gmOrganizationWidgets.manage_orgs(parent = self)
2380 2381 #---------------------------------------------- 2382 # Help / Debugging 2383 #----------------------------------------------
2384 - def __on_save_screenshot(self, evt):
2385 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') 2386 self.__save_screenshot_to_file(filename = fname)
2387 2388 #----------------------------------------------
2389 - def __on_test_exception(self, evt):
2390 raise ValueError('raised ValueError to test exception handling')
2391 2392 #----------------------------------------------
2393 - def __on_test_segfault(self, evt):
2394 import faulthandler 2395 _log.debug('testing faulthandler via SIGSEGV') 2396 faulthandler._sigsegv()
2397 2398 #----------------------------------------------
2399 - def __on_test_placeholders(self, evt):
2400 from Gnumed.wxpython.gmMacro import test_placeholders 2401 test_placeholders()
2402 2403 #----------------------------------------------
2404 - def __on_test_access_violation(self, evt):
2405 raise gmExceptions.AccessDenied ( 2406 _('[-9999]: <access violation test error>'), 2407 source = 'GNUmed code', 2408 code = -9999, 2409 details = _('This is a deliberate AccessDenied exception thrown to test the handling of access violations by means of a decorator.') 2410 )
2411 #---------------------------------------------- 2412 @gmAccessPermissionWidgets.verify_minimum_required_role('admin', activity = _('testing access check for non-existant <admin> role'))
2413 - def __on_test_access_checking(self, evt):
2414 raise gmExceptions.AccessDenied ( 2415 _('[-9999]: <access violation test error>'), 2416 source = 'GNUmed code', 2417 code = -9999, 2418 details = _('This is a deliberate AccessDenied exception. You should not see this message because the role is checked in a decorator.') 2419 )
2420 #----------------------------------------------
2421 - def __on_invoke_inspector(self, evt):
2422 import wx.lib.inspection 2423 wx.lib.inspection.InspectionTool().Show()
2424 #----------------------------------------------
2425 - def __on_display_bugtracker(self, evt):
2426 gmNetworkTools.open_url_in_browser(url = 'https://bugs.launchpad.net/gnumed/')
2427 #----------------------------------------------
2428 - def __on_display_wiki(self, evt):
2429 gmNetworkTools.open_url_in_browser(url = 'http://wiki.gnumed.de')
2430 #----------------------------------------------
2431 - def __on_display_user_manual_online(self, evt):
2432 gmNetworkTools.open_url_in_browser(url = 'http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual#UserGuideInManual')
2433 #----------------------------------------------
2434 - def __on_menu_reference(self, evt):
2435 gmNetworkTools.open_url_in_browser(url = 'http://wiki.gnumed.de/bin/view/Gnumed/MenuReference')
2436 #----------------------------------------------
2437 - def __on_pgadmin3(self, evt):
2438 found, cmd = gmShellAPI.detect_external_binary(binary = 'pgadmin3') 2439 if found: 2440 gmShellAPI.run_command_in_shell(cmd, blocking = False) 2441 return 2442 gmDispatcher.send(signal = 'statustext', msg = _('pgAdmin III not found.'), beep = True)
2443 #----------------------------------------------
2444 - def __on_reload_hook_script(self, evt):
2445 if not gmHooks.import_hook_module(reimport = True): 2446 gmDispatcher.send(signal = 'statustext', msg = _('Error reloading hook script.'))
2447 #----------------------------------------------
2448 - def __on_unblock_cursor(self, evt):
2449 wx.EndBusyCursor()
2450 #----------------------------------------------
2451 - def __on_clear_status_line(self, evt):
2452 gmDispatcher.send(signal = 'statustext', msg = '')
2453 #----------------------------------------------
2454 - def __on_toggle_patient_lock(self, evt):
2455 curr_pat = gmPerson.gmCurrentPatient() 2456 if curr_pat.locked: 2457 curr_pat.force_unlock() 2458 else: 2459 curr_pat.locked = True
2460 #----------------------------------------------
2461 - def __on_show_log_file(self, evt):
2462 gmLog2.flush() 2463 gmMimeLib.call_viewer_on_file(gmLog2._logfile_name, block = False)
2464 #----------------------------------------------
2465 - def __on_backup_log_file(self, evt):
2466 name = os.path.basename(gmLog2._logfile_name) 2467 name, ext = os.path.splitext(name) 2468 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 2469 new_path = os.path.expanduser(os.path.join('~', 'gnumed')) 2470 2471 dlg = wx.FileDialog ( 2472 parent = self, 2473 message = _("Save current log as..."), 2474 defaultDir = new_path, 2475 defaultFile = new_name, 2476 wildcard = "%s (*.log)|*.log" % _("log files"), 2477 style = wx.FD_SAVE 2478 ) 2479 choice = dlg.ShowModal() 2480 new_name = dlg.GetPath() 2481 dlg.Destroy() 2482 if choice != wx.ID_OK: 2483 return True 2484 2485 _log.warning('syncing log file for backup to [%s]', new_name) 2486 gmLog2.flush() 2487 shutil.copy2(gmLog2._logfile_name, new_name) 2488 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2489 #----------------------------------------------
2490 - def __on_email_log_file(self, evt):
2491 gmExceptionHandlingWidgets.mail_log(parent = self)
2492 2493 #----------------------------------------------
2494 - def __on_browse_tmp_dir(self, evt):
2495 gmMimeLib.call_viewer_on_file(gmTools.gmPaths().tmp_dir, block = False)
2496 2497 #----------------------------------------------
2498 - def __on_browse_work_dir(self, evt):
2499 gmMimeLib.call_viewer_on_file(os.path.join(gmTools.gmPaths().home_dir, 'gnumed'), block = False)
2500 2501 #----------------------------------------------
2502 - def __on_browse_internal_work_dir(self, evt):
2503 gmMimeLib.call_viewer_on_file(os.path.join(gmTools.gmPaths().home_dir, '.gnumed'), block = False)
2504 2505 #---------------------------------------------- 2506 # GNUmed / 2507 #----------------------------------------------
2508 - def OnClose(self, event):
2509 """This is the wx.EVT_CLOSE handler. 2510 2511 - framework still functional 2512 """ 2513 _log.debug('gmTopLevelFrame.OnClose() start') 2514 self._clean_exit() 2515 self.Destroy() 2516 _log.debug('gmTopLevelFrame.OnClose() end') 2517 return True
2518 2519 #----------------------------------------------
2520 - def __dermtool (self, event):
2521 import Gnumed.wxpython.gmDermTool as DT 2522 frame = DT.DermToolDialog(None, -1) 2523 frame.Show(True)
2524 2525 #----------------------------------------------
2526 - def __on_start_new_encounter(self, evt):
2527 pat = gmPerson.gmCurrentPatient() 2528 if not pat.connected: 2529 gmDispatcher.send(signal = 'statustext', msg = _('Cannot start new encounter. No active patient.')) 2530 return False 2531 emr = pat.emr 2532 gmEncounterWidgets.start_new_encounter(emr = emr)
2533 #----------------------------------------------
2534 - def __on_list_encounters(self, evt):
2535 pat = gmPerson.gmCurrentPatient() 2536 if not pat.connected: 2537 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 2538 return False 2539 gmEncounterWidgets.select_encounters()
2540 #----------------------------------------------
2541 - def __on_add_health_issue(self, event):
2542 pat = gmPerson.gmCurrentPatient() 2543 if not pat.connected: 2544 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add health issue. No active patient.')) 2545 return False 2546 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
2547 #----------------------------------------------
2548 - def __on_add_episode(self, event):
2549 pat = gmPerson.gmCurrentPatient() 2550 if not pat.connected: 2551 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add episode. No active patient.')) 2552 return False 2553 gmEMRStructWidgets.edit_episode(parent = self, episode = None)
2554 #----------------------------------------------
2555 - def __on_add_medication(self, evt):
2556 pat = gmPerson.gmCurrentPatient() 2557 if not pat.connected: 2558 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add medication. No active patient.')) 2559 return False 2560 2561 gmMedicationWidgets.edit_intake_of_substance(parent = self, substance = None) 2562 2563 evt.Skip()
2564 #----------------------------------------------
2565 - def __on_manage_allergies(self, evt):
2566 pat = gmPerson.gmCurrentPatient() 2567 if not pat.connected: 2568 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add allergy. No active patient.')) 2569 return False 2570 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 2571 dlg.ShowModal()
2572 #----------------------------------------------
2573 - def __on_manage_performed_procedures(self, evt):
2574 pat = gmPerson.gmCurrentPatient() 2575 if not pat.connected: 2576 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage performed procedures. No active patient.')) 2577 return False 2578 gmProcedureWidgets.manage_performed_procedures(parent = self) 2579 evt.Skip()
2580 #----------------------------------------------
2581 - def __on_manage_hospital_stays(self, evt):
2582 pat = gmPerson.gmCurrentPatient() 2583 if not pat.connected: 2584 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage hospitalizations. No active patient.')) 2585 return False 2586 gmHospitalStayWidgets.manage_hospital_stays(parent = self) 2587 evt.Skip()
2588 #----------------------------------------------
2589 - def __on_manage_external_care(self, evt):
2590 pat = gmPerson.gmCurrentPatient() 2591 if not pat.connected: 2592 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage external care. No active patient.')) 2593 return False 2594 gmExternalCareWidgets.manage_external_care(parent = self) 2595 evt.Skip()
2596 #----------------------------------------------
2597 - def __on_edit_occupation(self, evt):
2598 pat = gmPerson.gmCurrentPatient() 2599 if not pat.connected: 2600 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit occupation. No active patient.')) 2601 return False 2602 gmDemographicsWidgets.edit_occupation() 2603 evt.Skip()
2604 2605 #---------------------------------------------- 2606 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage vaccinations'))
2607 - def __on_manage_vaccination(self, evt):
2608 pat = gmPerson.gmCurrentPatient() 2609 if not pat.connected: 2610 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add vaccinations. No active patient.')) 2611 return False 2612 2613 gmVaccWidgets.manage_vaccinations(parent = self, latest_only = False) 2614 evt.Skip()
2615 2616 #---------------------------------------------- 2617 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage vaccinations'))
2618 - def __on_show_latest_vaccinations(self, evt):
2619 pat = gmPerson.gmCurrentPatient() 2620 if not pat.connected: 2621 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage vaccinations. No active patient.')) 2622 return False 2623 2624 gmVaccWidgets.manage_vaccinations(parent = self, latest_only = True) 2625 evt.Skip()
2626 2627 #---------------------------------------------- 2628 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage vaccinations'))
2629 - def __on_show_all_vaccinations_by_indication(self, evt):
2630 pat = gmPerson.gmCurrentPatient() 2631 if not pat.connected: 2632 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage vaccinations. No active patient.')) 2633 return False 2634 2635 gmVaccWidgets.manage_vaccinations(parent = self, latest_only = False, expand_indications = True) 2636 evt.Skip()
2637 2638 #---------------------------------------------- 2639 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage family history'))
2640 - def __on_manage_fhx(self, evt):
2641 pat = gmPerson.gmCurrentPatient() 2642 if not pat.connected: 2643 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage family history. No active patient.')) 2644 return False 2645 2646 gmFamilyHistoryWidgets.manage_family_history(parent = self) 2647 evt.Skip()
2648 #---------------------------------------------- 2649 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage vaccinations'))
2650 - def __on_manage_measurements(self, evt):
2651 pat = gmPerson.gmCurrentPatient() 2652 if not pat.connected: 2653 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage measurements. No active patient.')) 2654 return False 2655 gmMeasurementWidgets.manage_measurements(parent = self, single_selection = True, emr = pat.emr)
2656 #---------------------------------------------- 2657 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('calculate EDC'))
2658 - def __on_calc_edc(self, evt):
2659 pat = gmPerson.gmCurrentPatient() 2660 gmPregWidgets.calculate_edc(parent = self, patient = pat)
2661 2662 #---------------------------------------------- 2663 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage suppressed hints'))
2664 - def __on_manage_suppressed_hints(self, evt):
2665 pat = gmPerson.gmCurrentPatient() 2666 if not pat.connected: 2667 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage suppressed hints. No active patient.')) 2668 return False 2669 gmAutoHintWidgets.manage_suppressed_hints(parent = self, pk_identity = pat.ID)
2670 2671 #----------------------------------------------
2672 - def __on_manage_substance_abuse(self, evt):
2673 pat = gmPerson.gmCurrentPatient() 2674 if not pat.connected: 2675 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage smoking status. No active patient.')) 2676 return False 2677 gmHabitWidgets.manage_substance_abuse(parent = self, patient = pat)
2678 2679 #----------------------------------------------
2680 - def __on_show_emr_summary(self, event):
2681 pat = gmPerson.gmCurrentPatient() 2682 if not pat.connected: 2683 gmDispatcher.send(signal = 'statustext', msg = _('Cannot show EMR summary. No active patient.')) 2684 return False 2685 2686 emr = pat.emr 2687 dlg = wx.MessageDialog ( 2688 parent = self, 2689 message = emr.format_statistics(), 2690 caption = _('EMR Summary'), 2691 style = wx.OK | wx.STAY_ON_TOP 2692 ) 2693 dlg.ShowModal() 2694 dlg.Destroy() 2695 return True
2696 #----------------------------------------------
2697 - def __on_search_emr(self, event):
2698 return gmNarrativeWorkflows.search_narrative_in_emr(parent=self)
2699 #----------------------------------------------
2700 - def __on_search_across_emrs(self, event):
2701 gmNarrativeWorkflows.search_narrative_across_emrs(parent=self)
2702 2703 #----------------------------------------------
2704 - def __export_emr_as_textfile(self, event):
2705 pat = gmPerson.gmCurrentPatient() 2706 if not pat.connected: 2707 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR. No active patient.')) 2708 return False 2709 from Gnumed.exporters import gmPatientExporter 2710 exporter = gmPatientExporter.cEmrExport(patient = pat) 2711 fname = gmTools.get_unique_filename(prefix = 'gm-exp-', suffix = '.txt') 2712 output_file = io.open(fname, mode = 'wt', encoding = 'utf8', errors = 'replace') 2713 exporter.set_output_file(output_file) 2714 exporter.dump_constraints() 2715 exporter.dump_demographic_record(True) 2716 exporter.dump_clinical_record() 2717 exporter.dump_med_docs() 2718 output_file.close() 2719 pat.export_area.add_file(filename = fname, hint = _('EMR as text document'))
2720 2721 #----------------------------------------------
2722 - def __export_emr_as_timeline_xml(self, event):
2723 pat = gmPerson.gmCurrentPatient() 2724 if not pat.connected: 2725 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR. No active patient.')) 2726 return False 2727 wx.BeginBusyCursor() 2728 try: 2729 from Gnumed.exporters import gmTimelineExporter 2730 fname = gmTimelineExporter.create_timeline_file(patient = pat) 2731 pat.export_area.add_file(filename = fname, hint = _('EMR as timeline file (XML)')) 2732 except Exception: 2733 raise 2734 finally: 2735 wx.EndBusyCursor()
2736 2737 #----------------------------------------------
2738 - def __export_emr_as_care_structure(self, event):
2739 pat = gmPerson.gmCurrentPatient() 2740 if not pat.connected: 2741 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR. No active patient.')) 2742 return False 2743 wx.BeginBusyCursor() 2744 try: 2745 fname = gmEMRStructItems.export_emr_structure(patient = pat) 2746 pat.export_area.add_file(filename = fname, hint = _('EMR as care structure file')) 2747 except Exception: 2748 raise 2749 finally: 2750 wx.EndBusyCursor()
2751 2752 #---------------------------------------------- 2753 # def __on_save_emr_by_last_mod(self, event): 2754 # # sanity checks 2755 # pat = gmPerson.gmCurrentPatient() 2756 # if not pat.connected: 2757 # gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal by last modification time. No active patient.')) 2758 # return False 2759 # 2760 # # get file name 2761 # aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 2762 # aDefDir = os.path.expanduser(os.path.join('~', 'gnumed')) 2763 # fname = '%s-%s_%s.txt' % (_('journal_by_last_mod_time'), pat['lastnames'], pat['firstnames']) 2764 # dlg = wx.FileDialog ( 2765 # parent = self, 2766 # message = _("Save patient's EMR journal as..."), 2767 # defaultDir = aDefDir, 2768 # defaultFile = fname, 2769 # wildcard = aWildcard, 2770 # style = wx.FD_SAVE 2771 # ) 2772 # choice = dlg.ShowModal() 2773 # fname = dlg.GetPath() 2774 # dlg.Destroy() 2775 # if choice != wx.ID_OK: 2776 # return True 2777 # 2778 # _log.debug('exporting EMR journal (by last mod) to [%s]' % fname) 2779 # 2780 # exporter = gmPatientExporter.cEMRJournalExporter() 2781 # 2782 # wx.BeginBusyCursor() 2783 # try: 2784 # fname = exporter.save_to_file_by_mod_time(filename = fname, patient = pat) 2785 # except: 2786 # wx.EndBusyCursor() 2787 # _log.exception('error exporting EMR') 2788 # gmGuiHelpers.gm_show_error ( 2789 # _('Error exporting patient EMR as journal by last modification time.'), 2790 # _('EMR journal export') 2791 # ) 2792 # return 2793 # wx.EndBusyCursor() 2794 # 2795 # gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as journal by last modification time into file [%s].') % fname, beep=False) 2796 # 2797 # return True 2798 2799 # #---------------------------------------------- 2800 # def __on_save_emr_as_journal(self, event): 2801 # # sanity checks 2802 # pat = gmPerson.gmCurrentPatient() 2803 # if not pat.connected: 2804 # gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.')) 2805 # return False 2806 # # get file name 2807 # aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 2808 # aDefDir = os.path.expanduser(os.path.join('~', 'gnumed')) 2809 # fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames']) 2810 # dlg = wx.FileDialog ( 2811 # parent = self, 2812 # message = _("Save patient's EMR journal as..."), 2813 # defaultDir = aDefDir, 2814 # defaultFile = fname, 2815 # wildcard = aWildcard, 2816 # style = wx.FD_SAVE 2817 # ) 2818 # choice = dlg.ShowModal() 2819 # fname = dlg.GetPath() 2820 # dlg.Destroy() 2821 # if choice != wx.ID_OK: 2822 # return True 2823 # 2824 # _log.debug('exporting EMR journal to [%s]' % fname) 2825 # # instantiate exporter 2826 # exporter = gmPatientExporter.cEMRJournalExporter() 2827 # 2828 # wx.BeginBusyCursor() 2829 # try: 2830 # fname = exporter.save_to_file_by_encounter(filename = fname, patient = pat) 2831 # except: 2832 # wx.EndBusyCursor() 2833 # _log.exception('error exporting EMR') 2834 # gmGuiHelpers.gm_show_error ( 2835 # _('Error exporting patient EMR as chronological journal.'), 2836 # _('EMR journal export') 2837 # ) 2838 # return 2839 # wx.EndBusyCursor() 2840 # 2841 # gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False) 2842 # 2843 # return True 2844 2845 #----------------------------------------------
2846 - def __on_export_emr_by_last_mod(self, event):
2847 # sanity checks 2848 pat = gmPerson.gmCurrentPatient() 2849 if not pat.connected: 2850 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal by last modification time. No active patient.')) 2851 return False 2852 2853 exporter = gmPatientExporter.cEMRJournalExporter() 2854 wx.BeginBusyCursor() 2855 try: 2856 fname = exporter.save_to_file_by_mod_time(patient = pat) 2857 except: 2858 wx.EndBusyCursor() 2859 _log.exception('error exporting EMR') 2860 gmGuiHelpers.gm_show_error ( 2861 _('Error exporting patient EMR as journal by last modification time.'), 2862 _('EMR journal export') 2863 ) 2864 return 2865 wx.EndBusyCursor() 2866 2867 pat.export_area.add_file(filename = fname, hint = _('EMR journal by last modification time')) 2868 2869 return True
2870 2871 #----------------------------------------------
2872 - def __on_export_emr_as_journal(self, event):
2873 # sanity checks 2874 pat = gmPerson.gmCurrentPatient() 2875 if not pat.connected: 2876 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.')) 2877 return False 2878 2879 exporter = gmPatientExporter.cEMRJournalExporter() 2880 wx.BeginBusyCursor() 2881 try: 2882 fname = exporter.save_to_file_by_encounter(patient = pat) 2883 except: 2884 wx.EndBusyCursor() 2885 _log.exception('error exporting EMR') 2886 gmGuiHelpers.gm_show_error ( 2887 _('Error exporting patient EMR as chronological journal.'), 2888 _('EMR journal export') 2889 ) 2890 return 2891 wx.EndBusyCursor() 2892 2893 pat.export_area.add_file(filename = fname, hint = _('EMR journal by encounter')) 2894 2895 return True
2896 2897 #----------------------------------------------
2898 - def __on_export_for_medistar(self, event):
2899 gmNarrativeWorkflows.export_narrative_for_medistar_import ( 2900 parent = self, 2901 soap_cats = 'soapu', 2902 encounter = None # IOW, the current one 2903 )
2904 2905 #----------------------------------------------
2906 - def __on_add_tag2person(self, event):
2907 curr_pat = gmPerson.gmCurrentPatient() 2908 if not curr_pat.connected: 2909 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.')) 2910 return 2911 2912 tag = gmDemographicsWidgets.manage_tag_images(parent = self) 2913 if tag is None: 2914 return 2915 2916 tag = curr_pat.add_tag(tag['pk_tag_image']) 2917 msg = _('Edit the comment on tag [%s]') % tag['l10n_description'] 2918 comment = wx.GetTextFromUser ( 2919 message = msg, 2920 caption = _('Editing tag comment'), 2921 default_value = gmTools.coalesce(tag['comment'], ''), 2922 parent = self 2923 ) 2924 2925 if comment == '': 2926 return 2927 2928 if comment.strip() == tag['comment']: 2929 return 2930 2931 if comment == ' ': 2932 tag['comment'] = None 2933 else: 2934 tag['comment'] = comment.strip() 2935 2936 tag.save()
2937 2938 #----------------------------------------------
2939 - def __on_load_external_patient(self, event):
2940 dbcfg = gmCfg.cCfgSQL() 2941 search_immediately = bool(dbcfg.get2 ( 2942 option = 'patient_search.external_sources.immediately_search_if_single_source', 2943 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 2944 bias = 'user', 2945 default = 0 2946 )) 2947 gmPatSearchWidgets.get_person_from_external_sources(parent = self, search_immediately = search_immediately, activate_immediately = True)
2948 2949 #----------------------------------------------
2950 - def __on_export_gdt2clipboard(self, event):
2951 curr_pat = gmPerson.gmCurrentPatient() 2952 if not curr_pat.connected: 2953 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.')) 2954 return False 2955 enc = 'cp850' # FIXME: configurable 2956 gdt_name = curr_pat.export_as_gdt(encoding = enc) 2957 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics as GDT to clipboard.')) 2958 gmGuiHelpers.file2clipboard(filename = gdt_name, announce_result = True)
2959 2960 #----------------------------------------------
2961 - def __on_export_vcard2clipboard(self, event):
2962 curr_pat = gmPerson.gmCurrentPatient() 2963 if not curr_pat.connected: 2964 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as VCARD. No active patient.')) 2965 return False 2966 vcf_name = curr_pat.export_as_vcard() 2967 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics as VCARD to clipboard.')) 2968 gmGuiHelpers.file2clipboard(filename = vcf_name, announce_result = True)
2969 2970 #----------------------------------------------
2971 - def __on_export_linuxmednews_xml2clipboard(self, event):
2972 curr_pat = gmPerson.gmCurrentPatient() 2973 if not curr_pat.connected: 2974 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as XML (LinuxMedNews). No active patient.')) 2975 return False 2976 fname = curr_pat.export_as_xml_linuxmednews() 2977 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to XML file [%s].') % fname) 2978 gmGuiHelpers.file2clipboard(filename = fname, announce_result = True)
2979 2980 #----------------------------------------------
2981 - def __on_export_as_gdt(self, event):
2982 curr_pat = gmPerson.gmCurrentPatient() 2983 if not curr_pat.connected: 2984 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.')) 2985 return False 2986 enc = 'cp850' # FIXME: configurable 2987 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'current-patient.gdt')) 2988 curr_pat.export_as_gdt(filename = fname, encoding = enc) 2989 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2990 2991 #----------------------------------------------
2992 - def __on_export_as_vcard(self, event):
2993 curr_pat = gmPerson.gmCurrentPatient() 2994 if not curr_pat.connected: 2995 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as VCARD. No active patient.')) 2996 return False 2997 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'current-patient.vcf')) 2998 curr_pat.export_as_vcard(filename = fname) 2999 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to VCARD file [%s].') % fname)
3000 3001 #----------------------------------------------
3002 - def __on_import_xml_linuxmednews(self, evt):
3003 gmPatSearchWidgets.load_person_from_xml_linuxmednews_via_clipboard()
3004 3005 #----------------------------------------------
3006 - def __on_import_vcard_from_clipboard(self, evt):
3007 gmPatSearchWidgets.load_person_from_vcard_via_clipboard()
3008 3009 #----------------------------------------------
3010 - def __on_import_vcard_from_file(self, evt):
3011 gmPatSearchWidgets.load_person_from_vcard_file()
3012 3013 #----------------------------------------------
3014 - def __on_search_person(self, evt):
3015 gmDispatcher.send(signal = 'focus_patient_search')
3016 #----------------------------------------------
3017 - def __on_create_new_patient(self, evt):
3018 gmPersonCreationWidgets.create_new_person(parent = self, activate = True)
3019 #----------------------------------------------
3020 - def __on_enlist_patient_as_staff(self, event):
3021 pat = gmPerson.gmCurrentPatient() 3022 if not pat.connected: 3023 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add staff member. No active patient.')) 3024 return False 3025 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 3026 dlg.ShowModal()
3027 #----------------------------------------------
3028 - def __on_delete_patient(self, event):
3029 pat = gmPerson.gmCurrentPatient() 3030 if not pat.connected: 3031 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete patient. No patient active.')) 3032 return False 3033 gmDemographicsWidgets.disable_identity(identity = pat) 3034 return True
3035 #----------------------------------------------
3036 - def __on_merge_patients(self, event):
3037 gmPatSearchWidgets.merge_patients(parent=self)
3038 #----------------------------------------------
3039 - def __on_add_new_staff(self, event):
3040 """Create new person and add it as staff.""" 3041 if not gmPersonCreationWidgets.create_new_person(parent = self, activate = True): 3042 return 3043 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 3044 dlg.ShowModal()
3045 #----------------------------------------------
3046 - def __on_edit_staff_list(self, event):
3047 dlg = gmStaffWidgets.cEditStaffListDlg(parent=self, id=-1) 3048 dlg.ShowModal()
3049 #----------------------------------------------
3050 - def __on_edit_gmdbowner_password(self, evt):
3051 gmAuthWidgets.change_gmdbowner_password()
3052 #----------------------------------------------
3053 - def __on_update_loinc(self, evt):
3054 gmLOINCWidgets.update_loinc_reference_data()
3055 3056 #----------------------------------------------
3057 - def __on_update_atc(self, evt):
3058 gmATCWidgets.update_atc_reference_data()
3059 3060 #----------------------------------------------
3061 - def __on_install_data_packs(self, evt):
3062 gmDataPackWidgets.manage_data_packs(parent = self)
3063 3064 #----------------------------------------------
3065 - def __on_generate_vaccines(self, evt):
3066 gmVaccWidgets.regenerate_generic_vaccines()
3067 3068 #----------------------------------------------
3069 - def _clean_exit(self):
3070 """Cleanup helper. 3071 3072 - should ALWAYS be called when this program is 3073 to be terminated 3074 - ANY code that should be executed before a 3075 regular shutdown should go in here 3076 - framework still functional 3077 """ 3078 _log.debug('gmTopLevelFrame._clean_exit() start') 3079 3080 # shut down backend notifications listener 3081 listener = gmBackendListener.gmBackendListener() 3082 try: 3083 listener.shutdown() 3084 except: 3085 _log.exception('cannot stop backend notifications listener thread') 3086 3087 # shutdown application scripting listener 3088 if _scripting_listener is not None: 3089 try: 3090 _scripting_listener.shutdown() 3091 except: 3092 _log.exception('cannot stop scripting listener thread') 3093 3094 # shutdown timers 3095 self.clock_update_timer.Stop() 3096 gmTimer.shutdown() 3097 gmPhraseWheel.shutdown() 3098 3099 # run synchronous pre-exit callback 3100 for call_back in self.__pre_exit_callbacks: 3101 try: 3102 call_back() 3103 except: 3104 print('*** pre-exit callback failed ***') 3105 print('%s' % call_back) 3106 _log.exception('callback [%s] failed', call_back) 3107 3108 # signal imminent demise to plugins 3109 gmDispatcher.send('application_closing') 3110 3111 # do not show status line messages anymore 3112 gmDispatcher.disconnect(self._on_set_statustext, 'statustext') 3113 3114 # remember GUI size 3115 curr_width, curr_height = self.GetClientSize() 3116 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height)) 3117 if 0 not in [curr_width, curr_height]: 3118 dbcfg = gmCfg.cCfgSQL() 3119 try: 3120 dbcfg.set ( 3121 option = 'main.window.width', 3122 value = curr_width, 3123 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace 3124 ) 3125 dbcfg.set ( 3126 option = 'main.window.height', 3127 value = curr_height, 3128 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace 3129 ) 3130 except: 3131 _log.exception('cannot save current client window size') 3132 3133 if _cfg.get(option = 'debug'): 3134 print('---=== GNUmed shutdown ===---') 3135 try: 3136 print(_('You have to manually close this window to finalize shutting down GNUmed.')) 3137 print(_('This is so that you can inspect the console output at your leisure.')) 3138 except UnicodeEncodeError: 3139 print('You have to manually close this window to finalize shutting down GNUmed.') 3140 print('This is so that you can inspect the console output at your leisure.') 3141 print('---=== GNUmed shutdown ===---') 3142 3143 # shutdown GUI exception handling 3144 gmExceptionHandlingWidgets.uninstall_wx_exception_handler() 3145 3146 # are we clean ? 3147 import threading 3148 _log.debug("%s active threads", threading.activeCount()) 3149 for t in threading.enumerate(): 3150 _log.debug('thread %s', t) 3151 if t.name == 'MainThread': 3152 continue 3153 print('GNUmed: waiting for thread [%s] to finish' % t.name) 3154 3155 _log.debug('gmTopLevelFrame._clean_exit() end')
3156 3157 #---------------------------------------------- 3158 # internal API 3159 #----------------------------------------------
3160 - def __set_window_title_template(self):
3161 3162 if _cfg.get(option = 'slave'): 3163 self.__title_template = 'GMdS: %%(pat)s [%%(prov)s@%%(wp)s in %%(site)s of %%(prax)s] (%s:%s)' % ( 3164 _cfg.get(option = 'slave personality'), 3165 _cfg.get(option = 'xml-rpc port') 3166 ) 3167 else: 3168 self.__title_template = 'GMd: %(pat)s [%(prov)s@%(wp)s in %(site)s of %(prax)s]'
3169 #----------------------------------------------
3170 - def __update_window_title(self):
3171 """Update title of main window based on template. 3172 3173 This gives nice tooltips on iconified GNUmed instances. 3174 3175 User research indicates that in the title bar people want 3176 the date of birth, not the age, so please stick to this 3177 convention. 3178 """ 3179 args = {} 3180 3181 pat = gmPerson.gmCurrentPatient() 3182 if pat.connected: 3183 args['pat'] = '%s %s %s (%s) #%d' % ( 3184 gmTools.coalesce(pat['title'], '', '%.4s'), 3185 pat['firstnames'], 3186 pat['lastnames'], 3187 pat.get_formatted_dob(format = '%Y %b %d'), 3188 pat['pk_identity'] 3189 ) 3190 else: 3191 args['pat'] = _('no patient') 3192 3193 args['prov'] = '%s%s.%s' % ( 3194 gmTools.coalesce(_provider['title'], '', '%s '), 3195 _provider['firstnames'][:1], 3196 _provider['lastnames'] 3197 ) 3198 3199 praxis = gmPraxis.gmCurrentPraxisBranch() 3200 args['wp'] = praxis.active_workplace 3201 args['site'] = praxis['branch'] 3202 args['prax'] = praxis['praxis'] 3203 3204 self.SetTitle(self.__title_template % args)
3205 3206 #----------------------------------------------
3207 - def __save_screenshot_to_file(self, filename=None):
3208 3209 time.sleep(0.5) 3210 3211 rect = self.GetRect() 3212 3213 # adjust for window decoration on Linux 3214 if sys.platform == 'linux2': 3215 client_x, client_y = self.ClientToScreen((0, 0)) 3216 border_width = client_x - rect.x 3217 title_bar_height = client_y - rect.y 3218 # If the window has a menu bar, remove it from the title bar height. 3219 if self.GetMenuBar(): 3220 title_bar_height /= 2 3221 rect.width += (border_width * 2) 3222 rect.height += title_bar_height + border_width 3223 3224 scr_dc = wx.ScreenDC() 3225 mem_dc = wx.MemoryDC() 3226 img = wx.Bitmap(rect.width, rect.height) 3227 mem_dc.SelectObject(img) 3228 mem_dc.Blit ( # copy ... 3229 0, 0, # ... to here in the target ... 3230 rect.width, rect.height, # ... that much from ... 3231 scr_dc, # ... the source ... 3232 rect.x, rect.y # ... starting here 3233 ) 3234 3235 # FIXME: improve filename with patient/workplace/provider, allow user to select/change 3236 if filename is None: 3237 filename = gmTools.get_unique_filename ( 3238 prefix = 'gm-screenshot-%s-' % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), 3239 suffix = '.png' 3240 ) 3241 3242 img.SaveFile(filename, wx.BITMAP_TYPE_PNG) 3243 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % filename) 3244 3245 return filename
3246 3247 #----------------------------------------------
3248 - def setup_statusbar(self):
3249 sb = self.CreateStatusBar(2, wx.STB_SIZEGRIP | wx.STB_SHOW_TIPS) 3250 sb.SetStatusWidths([-1, 225]) 3251 # add time and date display to the right corner of the status bar 3252 self.clock_update_timer = wx.PyTimer(self._cb_update_clock) 3253 self._cb_update_clock() 3254 # update every second 3255 self.clock_update_timer.Start(milliseconds = 1000)
3256 3257 #----------------------------------------------
3258 - def _cb_update_clock(self):
3259 """Displays date and local time in the second slot of the status bar""" 3260 t = time.localtime(time.time()) 3261 st = time.strftime('%Y %b %d %H:%M:%S', t) 3262 self.SetStatusText(st, 1)
3263 3264 #------------------------------------------------
3265 - def Lock(self):
3266 """Lock GNUmed client against unauthorized access""" 3267 # FIXME 3268 # for i in range(1, self.nb.GetPageCount()): 3269 # self.nb.GetPage(i).Enable(False) 3270 return
3271 3272 #----------------------------------------------
3273 - def Unlock(self):
3274 """Unlock the main notebook widgets 3275 As long as we are not logged into the database backend, 3276 all pages but the 'login' page of the main notebook widget 3277 are locked; i.e. not accessible by the user 3278 """ 3279 #unlock notebook pages 3280 # for i in range(1, self.nb.GetPageCount()): 3281 # self.nb.GetPage(i).Enable(True) 3282 # go straight to patient selection 3283 # self.nb.AdvanceSelection() 3284 return
3285 #-----------------------------------------------
3286 - def OnPanelSize (self, event):
3287 wx.LayoutAlgorithm().LayoutWindow(self.LayoutMgr, self.nb)
3288
3289 #============================================================================== 3290 -class gmApp(wx.App):
3291
3292 - def OnInit(self):
3293 3294 if _cfg.get(option = 'debug'): 3295 self.SetAssertMode(wx.APP_ASSERT_EXCEPTION | wx.APP_ASSERT_LOG) 3296 else: 3297 self.SetAssertMode(wx.APP_ASSERT_SUPPRESS) 3298 3299 self.__starting_up = True 3300 3301 gmExceptionHandlingWidgets.install_wx_exception_handler() 3302 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version')) 3303 3304 self.SetAppName('gnumed') # set this so things like "wx.StandardPaths.GetDataDir()" work as expected 3305 self.SetVendorName('gnumed_community') 3306 try: 3307 self.SetAppDisplayName('GNUmed %s' % _cfg.get(option = 'client_version')) 3308 except AttributeError: 3309 _log.info('SetAppDisplayName() not supported') 3310 try: 3311 self.SetVendorDisplayName('The GNUmed Development Community.') 3312 except AttributeError: 3313 _log.info('SetVendorDisplayName() not supported') 3314 paths = gmTools.gmPaths(app_name = 'gnumed', wx = wx) 3315 paths.init_paths(wx = wx, app_name = 'gnumed') 3316 3317 # warn users running on Python < 2.7 3318 # for transitioning to Python 3 3319 # in GNUmed 1.6 make this fail startup 3320 # unless --debug is given 3321 if sys.hexversion < 0x02070000: 3322 _log.debug('Python version < 2.7') 3323 gmGuiHelpers.gm_show_warning ( 3324 aTitle = _('Python version check'), 3325 aMessage = _( 3326 'You are running Python version\n' 3327 ' %s\n' 3328 '\n' 3329 'However, GNUmed wants Python 2.7 to\n' 3330 'facilitate migration to Python 3.\n' 3331 '\n' 3332 'Please upgrade your Python interpreter !' 3333 ) % sys.version 3334 ) 3335 3336 if not self.__setup_prefs_file(): 3337 return False 3338 3339 gmExceptionHandlingWidgets.set_sender_email(gmPraxis.gmCurrentPraxisBranch().user_email) 3340 3341 self.__guibroker = gmGuiBroker.GuiBroker() 3342 self.__setup_platform() 3343 3344 if not self.__establish_backend_connection(): 3345 return False 3346 if not self.__verify_db_account(): 3347 return False 3348 if not self.__verify_praxis_branch(): 3349 return False 3350 3351 self.__check_db_lang() 3352 self.__update_workplace_list() 3353 3354 if not _cfg.get(option = 'skip-update-check'): 3355 self.__check_for_updates() 3356 3357 if _cfg.get(option = 'slave'): 3358 if not self.__setup_scripting_listener(): 3359 return False 3360 3361 # FIXME: load last position from backend 3362 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440)) 3363 frame.CentreOnScreen(wx.BOTH) 3364 self.SetTopWindow(frame) 3365 frame.Show(True) 3366 3367 if _cfg.get(option = 'debug'): 3368 self.RedirectStdio() 3369 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window')) 3370 # print this so people know what this window is for 3371 # and don't get suprised when it pops up later 3372 print('---=== GNUmed startup ===---') 3373 print(_('redirecting STDOUT/STDERR to this log window')) 3374 print('---=== GNUmed startup ===---') 3375 3376 self.__setup_user_activity_timer() 3377 self.__register_events() 3378 3379 wx.CallAfter(self._do_after_init) 3380 3381 return True
3382 #----------------------------------------------
3383 - def OnExit(self):
3384 """Called internally by wxPython after EVT_CLOSE has been handled on last frame. 3385 3386 - after destroying all application windows and controls 3387 - before wx.Windows internal cleanup 3388 """ 3389 _log.debug('gmApp.OnExit() start') 3390 3391 self.__shutdown_user_activity_timer() 3392 3393 if _cfg.get(option = 'debug'): 3394 self.RestoreStdio() 3395 sys.stdin = sys.__stdin__ 3396 sys.stdout = sys.__stdout__ 3397 sys.stderr = sys.__stderr__ 3398 3399 top_wins = wx.GetTopLevelWindows() 3400 if len(top_wins) > 0: 3401 _log.debug('%s top level windows still around in <app>.OnExit()', len(top_wins)) 3402 _log.debug(top_wins) 3403 for win in top_wins: 3404 _log.debug('destroying: %s', win) 3405 win.Destroy() 3406 3407 _log.debug('gmApp.OnExit() end') 3408 return 0
3409 3410 #----------------------------------------------
3411 - def _on_query_end_session(self, *args, **kwargs):
3412 wx.Bell() 3413 wx.Bell() 3414 wx.Bell() 3415 _log.warning('unhandled event detected: QUERY_END_SESSION') 3416 _log.info('we should be saving ourselves from here') 3417 gmLog2.flush() 3418 print('unhandled event detected: QUERY_END_SESSION')
3419 #----------------------------------------------
3420 - def _on_end_session(self, *args, **kwargs):
3421 wx.Bell() 3422 wx.Bell() 3423 wx.Bell() 3424 _log.warning('unhandled event detected: END_SESSION') 3425 gmLog2.flush() 3426 print('unhandled event detected: END_SESSION')
3427 #----------------------------------------------
3428 - def _on_app_activated(self, evt):
3429 if evt.GetActive(): 3430 if self.__starting_up: 3431 gmHooks.run_hook_script(hook = 'app_activated_startup') 3432 else: 3433 gmHooks.run_hook_script(hook = 'app_activated') 3434 else: 3435 gmHooks.run_hook_script(hook = 'app_deactivated') 3436 3437 evt.Skip()
3438 #----------------------------------------------
3439 - def _on_user_activity(self, evt):
3440 self.user_activity_detected = True 3441 evt.Skip()
3442 #----------------------------------------------
3443 - def _on_user_activity_timer_expired(self, cookie=None):
3444 3445 if self.user_activity_detected: 3446 self.elapsed_inactivity_slices = 0 3447 self.user_activity_detected = False 3448 self.elapsed_inactivity_slices += 1 3449 else: 3450 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices: 3451 # print("User was inactive for 30 seconds.") 3452 pass 3453 3454 self.user_activity_timer.Start(oneShot = True)
3455 #---------------------------------------------- 3456 # internal helpers 3457 #----------------------------------------------
3458 - def _do_after_init(self):
3459 self.__starting_up = False 3460 #gmClinicalRecord.set_func_ask_user(a_func = gmEncounterWidgets.ask_for_encounter_continuation) 3461 self.__guibroker['horstspace.top_panel']._TCTRL_patient_selector.SetFocus() 3462 gmHooks.run_hook_script(hook = 'startup-after-GUI-init')
3463 3464 #----------------------------------------------
3466 self.user_activity_detected = True 3467 self.elapsed_inactivity_slices = 0 3468 # FIXME: make configurable 3469 self.max_user_inactivity_slices = 15 # 15 * 2000ms == 30 seconds 3470 self.user_activity_timer = gmTimer.cTimer ( 3471 callback = self._on_user_activity_timer_expired, 3472 delay = 2000 # hence a minimum of 2 and max of 3.999... seconds after which inactivity is detected 3473 ) 3474 self.user_activity_timer.Start(oneShot=True)
3475 3476 #----------------------------------------------
3478 try: 3479 self.user_activity_timer.Stop() 3480 del self.user_activity_timer 3481 except: 3482 pass
3483 3484 #----------------------------------------------
3485 - def __register_events(self):
3486 self.Bind(wx.EVT_QUERY_END_SESSION, self._on_query_end_session) 3487 self.Bind(wx.EVT_END_SESSION, self._on_end_session) 3488 3489 # You can bind your app to wx.EVT_ACTIVATE_APP which will fire when your 3490 # app gets/looses focus, or you can wx.EVT_ACTIVATE with any of your 3491 # toplevel windows and call evt.GetActive() in the handler to see whether 3492 # it is gaining or loosing focus. 3493 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated) 3494 3495 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity) 3496 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
3497 3498 #----------------------------------------------
3499 - def __check_for_updates(self):
3500 3501 dbcfg = gmCfg.cCfgSQL() 3502 do_check = bool(dbcfg.get2 ( 3503 option = 'horstspace.update.autocheck_at_startup', 3504 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace, 3505 bias = 'workplace', 3506 default = True 3507 )) 3508 if not do_check: 3509 return 3510 3511 gmCfgWidgets.check_for_updates(async = True)
3512 3513 #----------------------------------------------
3515 """Handle all the database related tasks necessary for startup.""" 3516 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')]) 3517 from Gnumed.wxpython import gmAuthWidgets 3518 connected = gmAuthWidgets.connect_to_database ( 3519 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')], 3520 require_version = not override 3521 ) 3522 if connected: 3523 return True 3524 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection") 3525 return False
3526 #----------------------------------------------
3527 - def __verify_db_account(self):
3528 # check account <-> staff member association 3529 global _provider 3530 try: 3531 _provider = gmStaff.gmCurrentProvider(provider = gmStaff.cStaff()) 3532 except ValueError: 3533 account = gmPG2.get_current_user() 3534 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account) 3535 msg = _( 3536 'The database account [%s] cannot be used as a\n' 3537 'staff member login for GNUmed. There was an\n' 3538 'error retrieving staff details for it.\n\n' 3539 'Please ask your administrator for help.\n' 3540 ) % account 3541 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions')) 3542 return False 3543 3544 # improve exception handler setup 3545 tmp = '%s%s %s (%s = %s)' % ( 3546 gmTools.coalesce(_provider['title'], ''), 3547 _provider['firstnames'], 3548 _provider['lastnames'], 3549 _provider['short_alias'], 3550 _provider['db_user'] 3551 ) 3552 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp) 3553 3554 return True
3555 #----------------------------------------------
3556 - def __verify_praxis_branch(self):
3557 3558 if not gmPraxisWidgets.set_active_praxis_branch(no_parent = True): 3559 return False 3560 3561 login = gmPG2.get_default_login() 3562 msg = '\n' 3563 msg += _('Database <%s> on <%s>') % ( 3564 login.database, 3565 gmTools.coalesce(login.host, 'localhost') 3566 ) 3567 msg += '\n\n' 3568 3569 praxis = gmPraxis.gmCurrentPraxisBranch() 3570 msg += _('Branch "%s" of praxis "%s"\n') % ( 3571 praxis['branch'], 3572 praxis['praxis'] 3573 ) 3574 msg += '\n\n' 3575 3576 banner = praxis.db_logon_banner 3577 if banner.strip() == '': 3578 return True 3579 msg += banner 3580 msg += '\n\n' 3581 3582 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 3583 None, #self.GetTopWindow(), # freezes 3584 -1, 3585 caption = _('Verifying database'), 3586 question = gmTools.wrap(msg, 60, initial_indent = ' ', subsequent_indent = ' '), 3587 button_defs = [ 3588 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True}, 3589 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False} 3590 ] 3591 ) 3592 log_on = dlg.ShowModal() 3593 dlg.Destroy() 3594 if log_on == wx.ID_YES: 3595 return True 3596 _log.info('user decided to not connect to this database') 3597 return False
3598 #----------------------------------------------
3599 - def __update_workplace_list(self):
3600 wps = gmPraxis.gmCurrentPraxisBranch().workplaces 3601 if len(wps) == 0: 3602 return 3603 login = gmPG2.get_default_login() 3604 prefs_file = _cfg.get(option = 'user_preferences_file') 3605 gmCfg2.set_option_in_INI_file ( 3606 filename = prefs_file, 3607 group = 'profile %s' % login.backend_profile, 3608 option = 'last known workplaces', 3609 value = wps 3610 ) 3611 _cfg.reload_file_source(file = prefs_file)
3612 #----------------------------------------------
3613 - def __setup_prefs_file(self):
3614 """Setup access to a config file for storing preferences.""" 3615 3616 paths = gmTools.gmPaths(app_name = 'gnumed', wx = wx) 3617 3618 candidates = [] 3619 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')]) 3620 if explicit_file is not None: 3621 candidates.append(explicit_file) 3622 # provide a few fallbacks in the event the --conf-file isn't writable 3623 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf')) 3624 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf')) 3625 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf')) 3626 3627 prefs_file = None 3628 for candidate in candidates: 3629 try: 3630 open(candidate, 'a+').close() 3631 prefs_file = candidate 3632 break 3633 except IOError: 3634 continue 3635 3636 if prefs_file is None: 3637 msg = _( 3638 'Cannot find configuration file in any of:\n' 3639 '\n' 3640 ' %s\n' 3641 'You may need to use the comand line option\n' 3642 '\n' 3643 ' --conf-file=<FILE>' 3644 ) % '\n '.join(candidates) 3645 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files')) 3646 return False 3647 3648 _cfg.set_option(option = 'user_preferences_file', value = prefs_file) 3649 _log.info('user preferences file: %s', prefs_file) 3650 3651 return True
3652 #----------------------------------------------
3653 - def __setup_scripting_listener(self):
3654 3655 from socket import error as SocketError 3656 from Gnumed.pycommon import gmScriptingListener 3657 from Gnumed.wxpython import gmMacro 3658 3659 slave_personality = gmTools.coalesce ( 3660 _cfg.get ( 3661 group = 'workplace', 3662 option = 'slave personality', 3663 source_order = [ 3664 ('explicit', 'return'), 3665 ('workbase', 'return'), 3666 ('user', 'return'), 3667 ('system', 'return') 3668 ] 3669 ), 3670 'gnumed-client' 3671 ) 3672 _cfg.set_option(option = 'slave personality', value = slave_personality) 3673 3674 # FIXME: handle port via /var/run/ 3675 port = int ( 3676 gmTools.coalesce ( 3677 _cfg.get ( 3678 group = 'workplace', 3679 option = 'xml-rpc port', 3680 source_order = [ 3681 ('explicit', 'return'), 3682 ('workbase', 'return'), 3683 ('user', 'return'), 3684 ('system', 'return') 3685 ] 3686 ), 3687 9999 3688 ) 3689 ) 3690 _cfg.set_option(option = 'xml-rpc port', value = port) 3691 3692 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality) 3693 global _scripting_listener 3694 try: 3695 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor) 3696 except SocketError as e: 3697 _log.exception('cannot start GNUmed XML-RPC server') 3698 gmGuiHelpers.gm_show_error ( 3699 aMessage = ( 3700 'Cannot start the GNUmed server:\n' 3701 '\n' 3702 ' [%s]' 3703 ) % e, 3704 aTitle = _('GNUmed startup') 3705 ) 3706 return False 3707 3708 return True
3709 #----------------------------------------------
3710 - def __setup_platform(self):
3711 3712 import wx.lib.colourdb 3713 wx.lib.colourdb.updateColourDB() 3714 3715 traits = self.GetTraits() 3716 try: 3717 _log.info('desktop environment: [%s]', traits.GetDesktopEnvironment()) 3718 except: 3719 pass 3720 3721 if wx.Platform == '__WXMSW__': 3722 _log.info('running on MS Windows') 3723 elif wx.Platform == '__WXGTK__': 3724 _log.info('running on GTK (probably Linux)') 3725 elif wx.Platform == '__WXMAC__': 3726 _log.info('running on Mac OS') 3727 wx.SystemOptions.SetOptionInt('mac.textcontrol-use-spell-checker', 1) 3728 else: 3729 _log.info('running on an unknown platform (%s)' % wx.Platform)
3730 #----------------------------------------------
3731 - def __check_db_lang(self):
3732 if gmI18N.system_locale is None or gmI18N.system_locale == '': 3733 _log.warning("system locale is undefined (probably meaning 'C')") 3734 return True 3735 3736 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': "select i18n.get_curr_lang() as lang"}]) 3737 curr_db_lang = rows[0]['lang'] 3738 _log.debug("current database locale: [%s]" % curr_db_lang) 3739 3740 if curr_db_lang is None: 3741 # try setting (only possible if translation exists) 3742 cmd = 'select i18n.set_curr_lang(%s)' 3743 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]: 3744 if len(lang) == 0: 3745 continue 3746 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [lang]}], return_data = True) 3747 if rows[0][0]: 3748 _log.debug("Successfully set database language to [%s]." % lang) 3749 return True 3750 _log.error('Cannot set database language to [%s].' % lang) 3751 3752 return True 3753 3754 if curr_db_lang == gmI18N.system_locale_level['full']: 3755 _log.debug('Database locale (%s) up to date.' % curr_db_lang) 3756 return True 3757 if curr_db_lang == gmI18N.system_locale_level['country']: 3758 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (curr_db_lang, gmI18N.system_locale)) 3759 return True 3760 if curr_db_lang == gmI18N.system_locale_level['language']: 3761 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (curr_db_lang, gmI18N.system_locale)) 3762 return True 3763 3764 _log.warning('database locale [%s] does not match system locale [%s]' % (curr_db_lang, gmI18N.system_locale)) 3765 3766 sys_lang2ignore = _cfg.get ( 3767 group = 'backend', 3768 option = 'ignored mismatching system locale', 3769 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')] 3770 ) 3771 if gmI18N.system_locale == sys_lang2ignore: 3772 _log.info('configured to ignore system-to-database locale mismatch') 3773 return True 3774 3775 # no match, not ignoring 3776 msg = _( 3777 "The currently selected database language ('%s') does\n" 3778 "not match the current system language ('%s').\n" 3779 "\n" 3780 "Do you want to set the database language to '%s' ?\n" 3781 ) % (curr_db_lang, gmI18N.system_locale, gmI18N.system_locale) 3782 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 3783 None, 3784 -1, 3785 caption = _('Checking database language settings'), 3786 question = msg, 3787 button_defs = [ 3788 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True}, 3789 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False} 3790 ], 3791 show_checkbox = True, 3792 checkbox_msg = _('Remember to ignore language mismatch'), 3793 checkbox_tooltip = _( 3794 'Checking this will make GNUmed remember your decision\n' 3795 'until the system language is changed.\n' 3796 '\n' 3797 'You can also reactivate this inquiry by removing the\n' 3798 'corresponding "ignore" option from the configuration file\n' 3799 '\n' 3800 ' [%s]' 3801 ) % _cfg.get(option = 'user_preferences_file') 3802 ) 3803 decision = dlg.ShowModal() 3804 remember2ignore_this_mismatch = dlg._CHBOX_dont_ask_again.GetValue() 3805 dlg.Destroy() 3806 3807 if decision == wx.ID_NO: 3808 if not remember2ignore_this_mismatch: 3809 return True 3810 _log.info('User did not want to set database locale. Ignoring mismatch next time.') 3811 gmCfg2.set_option_in_INI_file ( 3812 filename = _cfg.get(option = 'user_preferences_file'), 3813 group = 'backend', 3814 option = 'ignored mismatching system locale', 3815 value = gmI18N.system_locale 3816 ) 3817 return True 3818 3819 # try setting database language (only possible if translation exists) 3820 cmd = 'select i18n.set_curr_lang(%s)' 3821 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]: 3822 if len(lang) == 0: 3823 continue 3824 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [lang]}], return_data = True) 3825 if rows[0][0]: 3826 _log.debug("Successfully set database language to [%s]." % lang) 3827 return True 3828 _log.error('Cannot set database language to [%s].' % lang) 3829 3830 # no match found but user wanted to set language anyways, so force it 3831 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country']) 3832 gmPG2.run_rw_queries(queries = [{ 3833 'cmd': 'select i18n.force_curr_lang(%s)', 3834 'args': [gmI18N.system_locale_level['country']] 3835 }]) 3836 3837 return True
3838
3839 #============================================================================== 3840 -def _signal_debugging_monitor(*args, **kwargs):
3841 try: 3842 kwargs['originated_in_database'] 3843 print('==> got notification from database "%s":' % kwargs['signal']) 3844 except KeyError: 3845 print('==> received signal from client: "%s"' % kwargs['signal']) 3846 3847 del kwargs['signal'] 3848 for key in kwargs: 3849 # careful because of possibly limited console output encoding 3850 try: print(' [%s]: %s' % (key, kwargs[key])) 3851 except: print('cannot print signal information')
3852
3853 #============================================================================== 3854 -def _safe_wxEndBusyCursor():
3855 try: _original_wxEndBusyCursor() 3856 except wx.wxAssertionError: pass
3857
3858 #------------------------------------------------------------------------------ 3859 -def setup_safe_wxEndBusyCursor():
3860 # monkey patch wxPython, needed on Windows ... 3861 if os.name != 'nt': 3862 return 3863 print('GNUmed startup: Monkey patching wx.EndBusyCursor...') 3864 global _original_wxEndBusyCursor 3865 _original_wxEndBusyCursor = wx.EndBusyCursor 3866 wx.EndBusyCursor = _safe_wxEndBusyCursor 3867 _log.debug('monkey patched wx.EndBusyCursor:') 3868 _log.debug('[%s] -> [%s]', _original_wxEndBusyCursor, _safe_wxEndBusyCursor)
3869
3870 #============================================================================== 3871 -def setup_callbacks():
3872 gmPerson.set_yielder(wx.Yield) 3873 gmClinicalRecord.set_delayed_executor(wx.CallAfter)
3874
3875 #============================================================================== 3876 -def main():
3877 3878 # make sure signals end up in the main thread, 3879 # no matter the thread they came from 3880 gmDispatcher.set_main_thread_caller(wx.CallAfter) 3881 3882 if _cfg.get(option = 'debug'): 3883 gmDispatcher.connect(receiver = _signal_debugging_monitor) 3884 _log.debug('gmDispatcher signal monitor activated') 3885 3886 setup_safe_wxEndBusyCursor() 3887 3888 setup_callbacks() 3889 3890 # create an instance of our GNUmed main application 3891 # - do not redirect stdio (yet) 3892 # - allow signals to be delivered 3893 app = gmApp(redirect = False, clearSigInt = False) 3894 app.MainLoop()
3895 3896 #============================================================================== 3897 # Main 3898 #============================================================================== 3899 if __name__ == '__main__': 3900 3901 from GNUmed.pycommon import gmI18N 3902 gmI18N.activate_locale() 3903 gmI18N.install_domain() 3904 3905 _log.info('Starting up as main module.') 3906 main() 3907 3908 #============================================================================== 3909