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

Source Code for Module Gnumed.wxpython.gmGuiMain

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