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