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