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