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