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