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