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