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

Source Code for Module Gnumed.wxpython.gmGuiMain

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