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