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