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