Package Gnumed :: Package wxpython :: Module gmGuiMain
[frames] | no frames]

Source Code for Module Gnumed.wxpython.gmGuiMain

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