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 webbrowser, 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 
  52   
  53  from Gnumed.business import gmPerson, gmClinicalRecord, gmSurgery, gmEMRStructItems 
  54  from Gnumed.business import gmVaccination 
  55  from Gnumed.business import gmArriba 
  56   
  57  from Gnumed.exporters import gmPatientExporter 
  58   
  59  from Gnumed.wxpython import gmGuiHelpers, gmHorstSpace, gmEMRBrowser 
  60  from Gnumed.wxpython import gmDemographicsWidgets, gmEMRStructWidgets 
  61  from Gnumed.wxpython import gmPatSearchWidgets, gmAllergyWidgets, gmListWidgets 
  62  from Gnumed.wxpython import gmProviderInboxWidgets, gmCfgWidgets, gmExceptionHandlingWidgets 
  63  from Gnumed.wxpython import gmNarrativeWidgets, gmPhraseWheel, gmMedicationWidgets 
  64  from Gnumed.wxpython import gmStaffWidgets, gmDocumentWidgets, gmTimer, gmMeasurementWidgets 
  65  from Gnumed.wxpython import gmFormWidgets, gmSnellen 
  66  from Gnumed.wxpython import gmVaccWidgets 
  67  from Gnumed.wxpython import gmPersonContactWidgets 
  68  from Gnumed.wxpython import gmI18nWidgets 
  69  from Gnumed.wxpython import gmCodingWidgets 
  70  from Gnumed.wxpython import gmOrganizationWidgets 
  71  from Gnumed.wxpython import gmAuthWidgets 
  72  from Gnumed.wxpython import gmFamilyHistoryWidgets 
  73  from Gnumed.wxpython import gmDataPackWidgets 
  74  from Gnumed.wxpython import gmContactWidgets 
  75  from Gnumed.wxpython import gmAddressWidgets 
  76   
  77   
  78  try: 
  79          _('dummy-no-need-to-translate-but-make-epydoc-happy') 
  80  except NameError: 
  81          _ = lambda x:x 
  82   
  83  _cfg = gmCfg2.gmCfgData() 
  84  _provider = None 
  85  _scripting_listener = None 
  86   
  87  _log = logging.getLogger('gm.main') 
  88  _log.info(__version__) 
  89  _log.info('wxPython GUI framework: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo)) 
  90   
  91  #============================================================================== 
92 -class gmTopLevelFrame(wx.Frame):
93 """GNUmed client's main windows frame. 94 95 This is where it all happens. Avoid popping up any other windows. 96 Most user interaction should happen to and from widgets within this frame 97 """ 98 #----------------------------------------------
99 - def __init__(self, parent, id, title, size=wx.DefaultSize):
100 """You'll have to browse the source to understand what the constructor does 101 """ 102 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE) 103 104 self.__setup_font() 105 106 self.__gb = gmGuiBroker.GuiBroker() 107 self.__pre_exit_callbacks = [] 108 self.bar_width = -1 109 self.menu_id2plugin = {} 110 111 _log.info('workplace is >>>%s<<<', gmSurgery.gmCurrentPractice().active_workplace) 112 113 self.__setup_main_menu() 114 self.setup_statusbar() 115 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % ( 116 gmTools.coalesce(_provider['title'], ''), 117 _provider['firstnames'][:1], 118 _provider['lastnames'], 119 _provider['short_alias'], 120 _provider['db_user'] 121 )) 122 123 self.__set_window_title_template() 124 self.__update_window_title() 125 126 #icon_bundle = wx.IconBundle() 127 #icon_bundle.AddIcon(wx.Icon("my_icon_16_16.ico", wx.BITMAP_TYPE_ICO)) 128 #icon_bundle.AddIcon(wx.Icon("my_icon_32_32.ico", wx.BITMAP_TYPE_ICO)) 129 #self.SetIcons(icon_bundle) 130 self.SetIcon(gmTools.get_icon(wx = wx)) 131 132 self.__register_events() 133 134 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1) 135 self.vbox = wx.BoxSizer(wx.VERTICAL) 136 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1) 137 138 self.SetAutoLayout(True) 139 self.SetSizerAndFit(self.vbox) 140 141 # don't allow the window to get too small 142 # setsizehints only allows minimum size, therefore window can't become small enough 143 # effectively we need the font size to be configurable according to screen size 144 #self.vbox.SetSizeHints(self) 145 self.__set_GUI_size()
146 147 #----------------------------------------------
148 - def __setup_font(self):
149 150 font = self.GetFont() 151 _log.debug('system default font is [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc()) 152 153 desired_font_face = _cfg.get ( 154 group = u'workplace', 155 option = u'client font', 156 source_order = [ 157 ('explicit', 'return'), 158 ('workbase', 'return'), 159 ('local', 'return'), 160 ('user', 'return'), 161 ('system', 'return') 162 ] 163 ) 164 165 fonts2try = [] 166 if desired_font_face is not None: 167 _log.info('client is configured to use font [%s]', desired_font_face) 168 fonts2try.append(desired_font_face) 169 170 if wx.Platform == '__WXMSW__': 171 sane_font_face = u'DejaVu Sans' 172 _log.info('MS Windows: appending fallback font candidate [%s]', sane_font_face) 173 fonts2try.append(sane_font_face) 174 175 if len(fonts2try) == 0: 176 return 177 178 for font_face in fonts2try: 179 success = font.SetFaceName(font_face) 180 if success: 181 self.SetFont(font) 182 _log.debug('switched font to [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc()) 183 return 184 font = self.GetFont() 185 _log.error('cannot switch font from [%s] (%s) to [%s]', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc(), font_face) 186 187 return
188 #----------------------------------------------
189 - def __set_GUI_size(self):
190 """Try to get previous window size from backend.""" 191 192 cfg = gmCfg.cCfgSQL() 193 194 # width 195 width = int(cfg.get2 ( 196 option = 'main.window.width', 197 workplace = gmSurgery.gmCurrentPractice().active_workplace, 198 bias = 'workplace', 199 default = 800 200 )) 201 202 # height 203 height = int(cfg.get2 ( 204 option = 'main.window.height', 205 workplace = gmSurgery.gmCurrentPractice().active_workplace, 206 bias = 'workplace', 207 default = 600 208 )) 209 210 dw = wx.DisplaySize()[0] 211 dh = wx.DisplaySize()[1] 212 213 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 214 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM())) 215 _log.debug('previous GUI size [%s:%s]', width, height) 216 217 # max size 218 if width > dw: 219 _log.debug('adjusting GUI width from %s to %s', width, dw) 220 width = dw 221 222 if height > dh: 223 _log.debug('adjusting GUI height from %s to %s', height, dh) 224 height = dh 225 226 # min size 227 if width < 100: 228 _log.debug('adjusting GUI width to minimum of 100 pixel') 229 width = 100 230 if height < 100: 231 _log.debug('adjusting GUI height to minimum of 100 pixel') 232 height = 100 233 234 _log.info('setting GUI to size [%s:%s]', width, height) 235 236 self.SetClientSize(wx.Size(width, height))
237 #----------------------------------------------
238 - def __setup_main_menu(self):
239 """Create the main menu entries. 240 241 Individual entries are farmed out to the modules. 242 243 menu item template: 244 245 item = menu_emr_edit.Append(-1, _(''), _('')) 246 self.Bind(wx.EVT_MENU, self__on_, item) 247 """ 248 global wx 249 self.mainmenu = wx.MenuBar() 250 self.__gb['main.mainmenu'] = self.mainmenu 251 252 # -- menu "GNUmed" ----------------- 253 menu_gnumed = wx.Menu() 254 255 self.menu_plugins = wx.Menu() 256 menu_gnumed.AppendMenu(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins) 257 258 ID = wx.NewId() 259 menu_gnumed.Append(ID, _('Check for updates'), _('Check for new releases of the GNUmed client.')) 260 wx.EVT_MENU(self, ID, self.__on_check_for_updates) 261 262 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.')) 263 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item) 264 265 # -- 266 menu_gnumed.AppendSeparator() 267 268 # GNUmed / Preferences 269 menu_config = wx.Menu() 270 271 item = menu_config.Append(-1, _('List configuration'), _('List all configuration items stored in the database.')) 272 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item) 273 274 # GNUmed / Preferences / Database 275 menu_cfg_db = wx.Menu() 276 277 ID = wx.NewId() 278 menu_cfg_db.Append(ID, _('Language'), _('Configure the database language')) 279 wx.EVT_MENU(self, ID, self.__on_configure_db_lang) 280 281 ID = wx.NewId() 282 menu_cfg_db.Append(ID, _('Welcome message'), _('Configure the database welcome message (all users).')) 283 wx.EVT_MENU(self, ID, self.__on_configure_db_welcome) 284 285 menu_config.AppendMenu(wx.NewId(), _('Database ...'), menu_cfg_db) 286 287 # GNUmed / Preferences / Client 288 menu_cfg_client = wx.Menu() 289 290 ID = wx.NewId() 291 menu_cfg_client.Append(ID, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.')) 292 wx.EVT_MENU(self, ID, self.__on_configure_export_chunk_size) 293 294 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.')) 295 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item) 296 297 menu_config.AppendMenu(wx.NewId(), _('Client parameters ...'), menu_cfg_client) 298 299 # GNUmed / Preferences / User Interface 300 menu_cfg_ui = wx.Menu() 301 302 # -- submenu gnumed / config / ui / docs 303 menu_cfg_doc = wx.Menu() 304 305 ID = wx.NewId() 306 menu_cfg_doc.Append(ID, _('Review dialog'), _('Configure review dialog after document display.')) 307 wx.EVT_MENU(self, ID, self.__on_configure_doc_review_dialog) 308 309 ID = wx.NewId() 310 menu_cfg_doc.Append(ID, _('UUID display'), _('Configure unique ID dialog on document import.')) 311 wx.EVT_MENU(self, ID, self.__on_configure_doc_uuid_dialog) 312 313 ID = wx.NewId() 314 menu_cfg_doc.Append(ID, _('Empty documents'), _('Whether to allow saving documents without parts.')) 315 wx.EVT_MENU(self, ID, self.__on_configure_partless_docs) 316 317 item = menu_cfg_doc.Append(-1, _('Generate UUID'), _('Whether to generate UUIDs for new documents.')) 318 self.Bind(wx.EVT_MENU, self.__on_configure_generate_doc_uuid, item) 319 320 menu_cfg_ui.AppendMenu(wx.NewId(), _('Document handling ...'), menu_cfg_doc) 321 322 # -- submenu gnumed / config / ui / updates 323 menu_cfg_update = wx.Menu() 324 325 ID = wx.NewId() 326 menu_cfg_update.Append(ID, _('Auto-check'), _('Whether to auto-check for updates at startup.')) 327 wx.EVT_MENU(self, ID, self.__on_configure_update_check) 328 329 ID = wx.NewId() 330 menu_cfg_update.Append(ID, _('Check scope'), _('When checking for updates, consider latest branch, too ?')) 331 wx.EVT_MENU(self, ID, self.__on_configure_update_check_scope) 332 333 ID = wx.NewId() 334 menu_cfg_update.Append(ID, _('URL'), _('The URL to retrieve version information from.')) 335 wx.EVT_MENU(self, ID, self.__on_configure_update_url) 336 337 menu_cfg_ui.AppendMenu(wx.NewId(), _('Update handling ...'), menu_cfg_update) 338 339 # -- submenu gnumed / config / ui / patient 340 menu_cfg_pat_search = wx.Menu() 341 342 ID = wx.NewId() 343 menu_cfg_pat_search.Append(ID, _('Birthday reminder'), _('Configure birthday reminder proximity interval.')) 344 wx.EVT_MENU(self, ID, self.__on_configure_dob_reminder_proximity) 345 346 ID = wx.NewId() 347 menu_cfg_pat_search.Append(ID, _('Immediate source activation'), _('Configure immediate activation of single external person.')) 348 wx.EVT_MENU(self, ID, self.__on_configure_quick_pat_search) 349 350 ID = wx.NewId() 351 menu_cfg_pat_search.Append(ID, _('Initial plugin'), _('Configure which plugin to show right after person activation.')) 352 wx.EVT_MENU(self, ID, self.__on_configure_initial_pat_plugin) 353 354 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default province/region/state for person creation.')) 355 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item) 356 357 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.')) 358 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item) 359 360 menu_cfg_ui.AppendMenu(wx.NewId(), _('Person ...'), menu_cfg_pat_search) 361 362 # -- submenu gnumed / config / ui / soap handling 363 menu_cfg_soap_editing = wx.Menu() 364 365 ID = wx.NewId() 366 menu_cfg_soap_editing.Append(ID, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.')) 367 wx.EVT_MENU(self, ID, self.__on_allow_multiple_new_episodes) 368 369 item = menu_cfg_soap_editing.Append(-1, _('Auto-open editors'), _('Configure auto-opening editors for recent problems.')) 370 self.Bind(wx.EVT_MENU, self.__on_allow_auto_open_episodes, item) 371 372 menu_cfg_ui.AppendMenu(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing) 373 374 menu_config.AppendMenu(wx.NewId(), _('User interface ...'), menu_cfg_ui) 375 376 # GNUmed / Preferences / External tools 377 menu_cfg_ext_tools = wx.Menu() 378 379 # ID = wx.NewId() 380 # menu_cfg_ext_tools.Append(ID, _('IFAP command'), _('Set the command to start IFAP.')) 381 # wx.EVT_MENU(self, ID, self.__on_configure_ifap_cmd) 382 383 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.')) 384 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item) 385 386 ID = wx.NewId() 387 menu_cfg_ext_tools.Append(ID, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.')) 388 wx.EVT_MENU(self, ID, self.__on_configure_ooo_settle_time) 389 390 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.')) 391 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item) 392 393 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.')) 394 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item) 395 396 item = menu_cfg_ext_tools.Append(-1, _('FreeDiams path'), _('Set the path for the FreeDiams binary.')) 397 self.Bind(wx.EVT_MENU, self.__on_configure_freediams_cmd, item) 398 399 item = menu_cfg_ext_tools.Append(-1, _('ADR URL'), _('URL for reporting Adverse Drug Reactions.')) 400 self.Bind(wx.EVT_MENU, self.__on_configure_adr_url, item) 401 402 item = menu_cfg_ext_tools.Append(-1, _('vaccADR URL'), _('URL for reporting Adverse Drug Reactions to *vaccines*.')) 403 self.Bind(wx.EVT_MENU, self.__on_configure_vaccine_adr_url, item) 404 405 item = menu_cfg_ext_tools.Append(-1, _('Vacc plans URL'), _('URL for vaccination plans.')) 406 self.Bind(wx.EVT_MENU, self.__on_configure_vaccination_plans_url, item) 407 408 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.')) 409 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item) 410 411 menu_config.AppendMenu(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools) 412 413 # -- submenu gnumed / config / emr 414 menu_cfg_emr = wx.Menu() 415 416 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.')) 417 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item) 418 419 item = menu_cfg_emr.Append(-1, _('Primary doctor'), _('Select the primary doctor to fall back to for patients without one.')) 420 self.Bind(wx.EVT_MENU, self.__on_cfg_fallback_primary_provider, item) 421 422 # -- submenu gnumed / config / emr / encounter 423 menu_cfg_encounter = wx.Menu() 424 425 ID = wx.NewId() 426 menu_cfg_encounter.Append(ID, _('Edit before patient change'), _('Edit encounter details before change of patient.')) 427 wx.EVT_MENU(self, ID, self.__on_cfg_enc_pat_change) 428 429 ID = wx.NewId() 430 menu_cfg_encounter.Append(ID, _('Minimum duration'), _('Minimum duration of an encounter.')) 431 wx.EVT_MENU(self, ID, self.__on_cfg_enc_min_ttl) 432 433 ID = wx.NewId() 434 menu_cfg_encounter.Append(ID, _('Maximum duration'), _('Maximum duration of an encounter.')) 435 wx.EVT_MENU(self, ID, self.__on_cfg_enc_max_ttl) 436 437 ID = wx.NewId() 438 menu_cfg_encounter.Append(ID, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.')) 439 wx.EVT_MENU(self, ID, self.__on_cfg_enc_empty_ttl) 440 441 ID = wx.NewId() 442 menu_cfg_encounter.Append(ID, _('Default type'), _('Default type for new encounters.')) 443 wx.EVT_MENU(self, ID, self.__on_cfg_enc_default_type) 444 445 menu_cfg_emr.AppendMenu(wx.NewId(), _('Encounter ...'), menu_cfg_encounter) 446 447 # -- submenu gnumed / config / emr / episode 448 menu_cfg_episode = wx.Menu() 449 450 ID = wx.NewId() 451 menu_cfg_episode.Append(ID, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.')) 452 wx.EVT_MENU(self, ID, self.__on_cfg_epi_ttl) 453 454 menu_cfg_emr.AppendMenu(wx.NewId(), _('Episode ...'), menu_cfg_episode) 455 menu_config.AppendMenu(wx.NewId(), _('EMR ...'), menu_cfg_emr) 456 menu_gnumed.AppendMenu(wx.NewId(), _('Preferences ...'), menu_config) 457 458 # -- submenu gnumed / master data 459 menu_master_data = wx.Menu() 460 461 item = menu_master_data.Append(-1, _('Manage lists'), _('Manage various lists of master data.')) 462 self.Bind(wx.EVT_MENU, self.__on_manage_master_data, item) 463 464 item = menu_master_data.Append(-1, _('Install data packs'), _('Install reference data from data packs.')) 465 self.Bind(wx.EVT_MENU, self.__on_install_data_packs, item) 466 467 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.')) 468 self.Bind(wx.EVT_MENU, self.__on_update_atc, item) 469 470 # item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.')) 471 # self.Bind(wx.EVT_MENU, self.__on_update_loinc, item) 472 473 item = menu_master_data.Append(-1, _('Create fake vaccines'), _('Re-create fake generic vaccines.')) 474 self.Bind(wx.EVT_MENU, self.__on_generate_vaccines, item) 475 476 menu_gnumed.AppendMenu(wx.NewId(), _('&Master data ...'), menu_master_data) 477 478 # -- submenu gnumed / users 479 menu_users = wx.Menu() 480 481 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user')) 482 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item) 483 484 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users')) 485 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item) 486 487 item = menu_users.Append(-1, _('&Change DB owner PWD'), _('Change the password of the GNUmed database owner')) 488 self.Bind(wx.EVT_MENU, self.__on_edit_gmdbowner_password, item) 489 490 menu_gnumed.AppendMenu(wx.NewId(), _('&Users ...'), menu_users) 491 492 # -- 493 menu_gnumed.AppendSeparator() 494 495 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.')) 496 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item) 497 498 self.mainmenu.Append(menu_gnumed, '&GNUmed') 499 500 # -- menu "Person" --------------------------- 501 menu_person = wx.Menu() 502 503 ID_CREATE_PATIENT = wx.NewId() 504 menu_person.Append(ID_CREATE_PATIENT, _('&Register person'), _("Register a new person with GNUmed")) 505 wx.EVT_MENU(self, ID_CREATE_PATIENT, self.__on_create_new_patient) 506 507 ID_LOAD_EXT_PAT = wx.NewId() 508 menu_person.Append(ID_LOAD_EXT_PAT, _('&Load external'), _('Load and possibly create person from an external source.')) 509 wx.EVT_MENU(self, ID_LOAD_EXT_PAT, self.__on_load_external_patient) 510 511 item = menu_person.Append(-1, _('Add &tag'), _('Add a text/image tag to this person.')) 512 self.Bind(wx.EVT_MENU, self.__on_add_tag2person, item) 513 514 ID_DEL_PAT = wx.NewId() 515 menu_person.Append(ID_DEL_PAT, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.')) 516 wx.EVT_MENU(self, ID_DEL_PAT, self.__on_delete_patient) 517 518 item = menu_person.Append(-1, _('&Merge persons'), _('Merge two persons into one.')) 519 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item) 520 521 menu_person.AppendSeparator() 522 523 ID_ENLIST_PATIENT_AS_STAFF = wx.NewId() 524 menu_person.Append(ID_ENLIST_PATIENT_AS_STAFF, _('Enlist as user'), _('Enlist current person as GNUmed user')) 525 wx.EVT_MENU(self, ID_ENLIST_PATIENT_AS_STAFF, self.__on_enlist_patient_as_staff) 526 527 # FIXME: temporary until external program framework is active 528 ID = wx.NewId() 529 menu_person.Append(ID, _('Export to GDT'), _('Export demographics of currently active person into GDT file.')) 530 wx.EVT_MENU(self, ID, self.__on_export_as_gdt) 531 532 menu_person.AppendSeparator() 533 534 self.mainmenu.Append(menu_person, '&Person') 535 self.__gb['main.patientmenu'] = menu_person 536 537 # -- menu "EMR" --------------------------- 538 menu_emr = wx.Menu() 539 540 # - EMR / Show as / 541 menu_emr_show = wx.Menu() 542 543 item = menu_emr_show.Append(-1, _('Summary'), _('Show a high-level summary of the EMR.')) 544 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item) 545 546 menu_emr.AppendMenu(wx.NewId(), _('Show as ...'), menu_emr_show) 547 self.__gb['main.emr_showmenu'] = menu_emr_show 548 549 # - EMR / 550 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient')) 551 self.Bind(wx.EVT_MENU, self.__on_search_emr, item) 552 553 item = menu_emr.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients')) 554 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item) 555 556 # -- EMR / Add, Edit 557 menu_emr_edit = wx.Menu() 558 559 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')) 560 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item) 561 562 item = menu_emr_edit.Append(-1, _('&Episode'), _('Add an episode of illness to the EMR of the active patient')) 563 self.Bind(wx.EVT_MENU, self.__on_add_episode, item) 564 565 item = menu_emr_edit.Append(-1, _('&Medication'), _('Add medication / substance use entry.')) 566 self.Bind(wx.EVT_MENU, self.__on_add_medication, item) 567 568 item = menu_emr_edit.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.')) 569 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item) 570 571 item = menu_emr_edit.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.')) 572 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item) 573 574 item = menu_emr_edit.Append(-1, _('&Hospitalizations'), _('Manage hospital stays.')) 575 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item) 576 577 item = menu_emr_edit.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.')) 578 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item) 579 580 item = menu_emr_edit.Append(-1, _('&Measurement(s)'), _('Add (a) measurement result(s) for the current patient.')) 581 self.Bind(wx.EVT_MENU, self.__on_add_measurement, item) 582 583 item = menu_emr_edit.Append(-1, _('&Vaccination(s)'), _('Add (a) vaccination(s) for the current patient.')) 584 self.Bind(wx.EVT_MENU, self.__on_add_vaccination, item) 585 586 item = menu_emr_edit.Append(-1, _('&Family history (FHx)'), _('Manage family history.')) 587 self.Bind(wx.EVT_MENU, self.__on_manage_fhx, item) 588 589 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit) 590 591 # -- EMR / 592 593 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.')) 594 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item) 595 596 item = menu_emr.Append(-1, _('&Encounters list'), _('List all encounters including empty ones.')) 597 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item) 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 #---------------------------------------------- 2218 #----------------------------------------------
2219 - def __on_jump_to_drug_db(self, evt):
2220 gmMedicationWidgets.jump_to_drug_database()
2221 #----------------------------------------------
2222 - def __on_kompendium_ch(self, evt):
2223 webbrowser.open ( 2224 url = 'http://www.kompendium.ch', 2225 new = False, 2226 autoraise = True 2227 )
2228 #---------------------------------------------- 2229 # Office 2230 #----------------------------------------------
2231 - def __on_display_audit_trail(self, evt):
2232 gmProviderInboxWidgets.show_audit_trail(parent = self) 2233 evt.Skip()
2234 #---------------------------------------------- 2235 # Help / Debugging 2236 #----------------------------------------------
2237 - def __on_save_screenshot(self, evt):
2238 wx.CallAfter(self.__save_screenshot) 2239 evt.Skip()
2240 #----------------------------------------------
2241 - def __save_screenshot(self):
2242 2243 time.sleep(0.5) 2244 2245 rect = self.GetRect() 2246 2247 # adjust for window decoration on Linux 2248 if sys.platform == 'linux2': 2249 client_x, client_y = self.ClientToScreen((0, 0)) 2250 border_width = client_x - rect.x 2251 title_bar_height = client_y - rect.y 2252 # If the window has a menu bar, remove it from the title bar height. 2253 if self.GetMenuBar(): 2254 title_bar_height /= 2 2255 rect.width += (border_width * 2) 2256 rect.height += title_bar_height + border_width 2257 2258 wdc = wx.ScreenDC() 2259 mdc = wx.MemoryDC() 2260 img = wx.EmptyBitmap(rect.width, rect.height) 2261 mdc.SelectObject(img) 2262 mdc.Blit ( # copy ... 2263 0, 0, # ... to here in the target ... 2264 rect.width, rect.height, # ... that much from ... 2265 wdc, # ... the source ... 2266 rect.x, rect.y # ... starting here 2267 ) 2268 2269 # FIXME: improve filename with patient/workplace/provider, allow user to select/change 2270 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S') 2271 img.SaveFile(fname, wx.BITMAP_TYPE_PNG) 2272 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
2273 #----------------------------------------------
2274 - def __on_test_exception(self, evt):
2275 #import nonexistant_module 2276 raise ValueError('raised ValueError to test exception handling')
2277 #----------------------------------------------
2278 - def __on_invoke_inspector(self, evt):
2279 import wx.lib.inspection 2280 wx.lib.inspection.InspectionTool().Show()
2281 #----------------------------------------------
2282 - def __on_display_bugtracker(self, evt):
2283 webbrowser.open ( 2284 url = 'https://bugs.launchpad.net/gnumed/', 2285 new = False, 2286 autoraise = True 2287 )
2288 #----------------------------------------------
2289 - def __on_display_wiki(self, evt):
2290 webbrowser.open ( 2291 url = 'http://wiki.gnumed.de', 2292 new = False, 2293 autoraise = True 2294 )
2295 #----------------------------------------------
2296 - def __on_display_user_manual_online(self, evt):
2297 webbrowser.open ( 2298 url = 'http://wiki.gnumed.de/bin/view/Gnumed/GnumedManual#UserGuideInManual', 2299 new = False, 2300 autoraise = True 2301 )
2302 #----------------------------------------------
2303 - def __on_menu_reference(self, evt):
2304 webbrowser.open ( 2305 url = 'http://wiki.gnumed.de/bin/view/Gnumed/MenuReference', 2306 new = False, 2307 autoraise = True 2308 )
2309 #----------------------------------------------
2310 - def __on_pgadmin3(self, evt):
2311 found, cmd = gmShellAPI.detect_external_binary(binary = 'pgadmin3') 2312 if found: 2313 gmShellAPI.run_command_in_shell(cmd, blocking=False) 2314 return 2315 gmDispatcher.send(signal = 'statustext', msg = _('pgAdmin III not found.'), beep = True)
2316 #----------------------------------------------
2317 - def __on_reload_hook_script(self, evt):
2318 if not gmHooks.import_hook_module(reimport = True): 2319 gmDispatcher.send(signal = 'statustext', msg = _('Error reloading hook script.'))
2320 #----------------------------------------------
2321 - def __on_unblock_cursor(self, evt):
2322 wx.EndBusyCursor()
2323 #----------------------------------------------
2324 - def __on_toggle_patient_lock(self, evt):
2325 curr_pat = gmPerson.gmCurrentPatient() 2326 if curr_pat.locked: 2327 curr_pat.force_unlock() 2328 else: 2329 curr_pat.locked = True
2330 #----------------------------------------------
2331 - def __on_show_log_file(self, evt):
2332 from Gnumed.pycommon import gmMimeLib 2333 gmLog2.flush() 2334 gmMimeLib.call_viewer_on_file(gmLog2._logfile_name, block = False)
2335 #----------------------------------------------
2336 - def __on_backup_log_file(self, evt):
2337 name = os.path.basename(gmLog2._logfile_name) 2338 name, ext = os.path.splitext(name) 2339 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext) 2340 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs')) 2341 2342 dlg = wx.FileDialog ( 2343 parent = self, 2344 message = _("Save current log as..."), 2345 defaultDir = new_path, 2346 defaultFile = new_name, 2347 wildcard = "%s (*.log)|*.log" % _("log files"), 2348 style = wx.SAVE 2349 ) 2350 choice = dlg.ShowModal() 2351 new_name = dlg.GetPath() 2352 dlg.Destroy() 2353 if choice != wx.ID_OK: 2354 return True 2355 2356 _log.warning('syncing log file for backup to [%s]', new_name) 2357 gmLog2.flush() 2358 shutil.copy2(gmLog2._logfile_name, new_name) 2359 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2360 #----------------------------------------------
2361 - def __on_email_log_file(self, evt):
2362 gmExceptionHandlingWidgets.mail_log(parent = self)
2363 #---------------------------------------------- 2364 # GNUmed / 2365 #----------------------------------------------
2366 - def OnClose(self, event):
2367 """This is the wx.EVT_CLOSE handler. 2368 2369 - framework still functional 2370 """ 2371 _log.debug('gmTopLevelFrame.OnClose() start') 2372 self._clean_exit() 2373 self.Destroy() 2374 _log.debug('gmTopLevelFrame.OnClose() end') 2375 return True
2376 #----------------------------------------------
2377 - def OnExportEMR(self, event):
2378 """ 2379 Export selected patient EMR to a file 2380 """ 2381 gmEMRBrowser.export_emr_to_ascii(parent=self)
2382 #----------------------------------------------
2383 - def __dermtool (self, event):
2384 import Gnumed.wxpython.gmDermTool as DT 2385 frame = DT.DermToolDialog(None, -1) 2386 frame.Show(True)
2387 #----------------------------------------------
2388 - def __on_start_new_encounter(self, evt):
2389 pat = gmPerson.gmCurrentPatient() 2390 if not pat.connected: 2391 gmDispatcher.send(signal = 'statustext', msg = _('Cannot start new encounter. No active patient.')) 2392 return False 2393 emr = pat.get_emr() 2394 gmEMRStructWidgets.start_new_encounter(emr = emr)
2395 #----------------------------------------------
2396 - def __on_list_encounters(self, evt):
2397 pat = gmPerson.gmCurrentPatient() 2398 if not pat.connected: 2399 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.')) 2400 return False 2401 gmEMRStructWidgets.select_encounters()
2402 #----------------------------------------------
2403 - def __on_add_health_issue(self, event):
2404 pat = gmPerson.gmCurrentPatient() 2405 if not pat.connected: 2406 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add health issue. No active patient.')) 2407 return False 2408 gmEMRStructWidgets.edit_health_issue(parent = self, issue = None)
2409 #----------------------------------------------
2410 - def __on_add_episode(self, event):
2411 pat = gmPerson.gmCurrentPatient() 2412 if not pat.connected: 2413 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add episode. No active patient.')) 2414 return False 2415 gmEMRStructWidgets.edit_episode(parent = self, episode = None)
2416 #----------------------------------------------
2417 - def __on_add_medication(self, evt):
2418 pat = gmPerson.gmCurrentPatient() 2419 if not pat.connected: 2420 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add medication. No active patient.')) 2421 return False 2422 2423 gmMedicationWidgets.edit_intake_of_substance(parent = self, substance = None) 2424 2425 evt.Skip()
2426 #----------------------------------------------
2427 - def __on_manage_allergies(self, evt):
2428 pat = gmPerson.gmCurrentPatient() 2429 if not pat.connected: 2430 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add allergy. No active patient.')) 2431 return False 2432 dlg = gmAllergyWidgets.cAllergyManagerDlg(parent=self, id=-1) 2433 dlg.ShowModal()
2434 #----------------------------------------------
2435 - def __on_manage_performed_procedures(self, evt):
2436 pat = gmPerson.gmCurrentPatient() 2437 if not pat.connected: 2438 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage performed procedures. No active patient.')) 2439 return False 2440 gmEMRStructWidgets.manage_performed_procedures(parent = self) 2441 evt.Skip()
2442 #----------------------------------------------
2443 - def __on_manage_hospital_stays(self, evt):
2444 pat = gmPerson.gmCurrentPatient() 2445 if not pat.connected: 2446 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage hospital stays. No active patient.')) 2447 return False 2448 gmEMRStructWidgets.manage_hospital_stays(parent = self) 2449 evt.Skip()
2450 #----------------------------------------------
2451 - def __on_edit_occupation(self, evt):
2452 pat = gmPerson.gmCurrentPatient() 2453 if not pat.connected: 2454 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit occupation. No active patient.')) 2455 return False 2456 gmDemographicsWidgets.edit_occupation() 2457 evt.Skip()
2458 #----------------------------------------------
2459 - def __on_add_vaccination(self, evt):
2460 pat = gmPerson.gmCurrentPatient() 2461 if not pat.connected: 2462 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add vaccinations. No active patient.')) 2463 return False 2464 2465 gmVaccWidgets.manage_vaccinations(parent = self) 2466 evt.Skip()
2467 #----------------------------------------------
2468 - def __on_manage_fhx(self, evt):
2469 pat = gmPerson.gmCurrentPatient() 2470 if not pat.connected: 2471 gmDispatcher.send(signal = 'statustext', msg = _('Cannot manage family history. No active patient.')) 2472 return False 2473 2474 gmFamilyHistoryWidgets.manage_family_history(parent = self) 2475 evt.Skip()
2476 #----------------------------------------------
2477 - def __on_add_measurement(self, evt):
2478 pat = gmPerson.gmCurrentPatient() 2479 if not pat.connected: 2480 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add measurement. No active patient.')) 2481 return False 2482 gmMeasurementWidgets.edit_measurement(parent = self, measurement = None) 2483 evt.Skip()
2484 #----------------------------------------------
2485 - def __on_show_emr_summary(self, event):
2486 pat = gmPerson.gmCurrentPatient() 2487 if not pat.connected: 2488 gmDispatcher.send(signal = 'statustext', msg = _('Cannot show EMR summary. No active patient.')) 2489 return False 2490 2491 emr = pat.get_emr() 2492 dlg = wx.MessageDialog ( 2493 parent = self, 2494 message = emr.format_statistics(), 2495 caption = _('EMR Summary'), 2496 style = wx.OK | wx.STAY_ON_TOP 2497 ) 2498 dlg.ShowModal() 2499 dlg.Destroy() 2500 return True
2501 #----------------------------------------------
2502 - def __on_search_emr(self, event):
2503 return gmNarrativeWidgets.search_narrative_in_emr(parent=self)
2504 #----------------------------------------------
2505 - def __on_search_across_emrs(self, event):
2506 gmNarrativeWidgets.search_narrative_across_emrs(parent=self)
2507 #----------------------------------------------
2508 - def __on_export_emr_as_journal(self, event):
2509 # sanity checks 2510 pat = gmPerson.gmCurrentPatient() 2511 if not pat.connected: 2512 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.')) 2513 return False 2514 # get file name 2515 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files")) 2516 # FIXME: make configurable 2517 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname'])) 2518 gmTools.mkdir(aDefDir) 2519 # FIXME: make configurable 2520 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames']) 2521 dlg = wx.FileDialog ( 2522 parent = self, 2523 message = _("Save patient's EMR journal as..."), 2524 defaultDir = aDefDir, 2525 defaultFile = fname, 2526 wildcard = aWildcard, 2527 style = wx.SAVE 2528 ) 2529 choice = dlg.ShowModal() 2530 fname = dlg.GetPath() 2531 dlg.Destroy() 2532 if choice != wx.ID_OK: 2533 return True 2534 2535 _log.debug('exporting EMR journal to [%s]' % fname) 2536 # instantiate exporter 2537 exporter = gmPatientExporter.cEMRJournalExporter() 2538 2539 wx.BeginBusyCursor() 2540 try: 2541 fname = exporter.export_to_file(filename = fname) 2542 except: 2543 wx.EndBusyCursor() 2544 gmGuiHelpers.gm_show_error ( 2545 _('Error exporting patient EMR as chronological journal.'), 2546 _('EMR journal export') 2547 ) 2548 raise 2549 wx.EndBusyCursor() 2550 2551 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False) 2552 2553 return True
2554 #----------------------------------------------
2555 - def __on_export_for_medistar(self, event):
2556 gmNarrativeWidgets.export_narrative_for_medistar_import ( 2557 parent = self, 2558 soap_cats = u'soap', 2559 encounter = None # IOW, the current one 2560 )
2561 #----------------------------------------------
2562 - def __on_add_tag2person(self, event):
2563 curr_pat = gmPerson.gmCurrentPatient() 2564 if not curr_pat.connected: 2565 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.')) 2566 return 2567 2568 tag = gmDemographicsWidgets.manage_tag_images(parent = self) 2569 if tag is None: 2570 return 2571 2572 tag = curr_pat.add_tag(tag['pk_tag_image']) 2573 msg = _('Edit the comment on tag [%s]') % tag['l10n_description'] 2574 comment = wx.GetTextFromUser ( 2575 message = msg, 2576 caption = _('Editing tag comment'), 2577 default_value = gmTools.coalesce(tag['comment'], u''), 2578 parent = self 2579 ) 2580 2581 if comment == u'': 2582 return 2583 2584 if comment.strip() == tag['comment']: 2585 return 2586 2587 if comment == u' ': 2588 tag['comment'] = None 2589 else: 2590 tag['comment'] = comment.strip() 2591 2592 tag.save()
2593 #----------------------------------------------
2594 - def __on_load_external_patient(self, event):
2595 dbcfg = gmCfg.cCfgSQL() 2596 search_immediately = bool(dbcfg.get2 ( 2597 option = 'patient_search.external_sources.immediately_search_if_single_source', 2598 workplace = gmSurgery.gmCurrentPractice().active_workplace, 2599 bias = 'user', 2600 default = 0 2601 )) 2602 gmPatSearchWidgets.get_person_from_external_sources(parent=self, search_immediately=search_immediately, activate_immediately=True)
2603 #----------------------------------------------
2604 - def __on_export_as_gdt(self, event):
2605 curr_pat = gmPerson.gmCurrentPatient() 2606 if not curr_pat.connected: 2607 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.')) 2608 return False 2609 # FIXME: configurable 2610 enc = 'cp850' 2611 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt')) 2612 curr_pat.export_as_gdt(filename = fname, encoding = enc) 2613 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2614 #----------------------------------------------
2615 - def __on_create_new_patient(self, evt):
2616 gmDemographicsWidgets.create_new_person(parent = self, activate = True)
2617 #----------------------------------------------
2618 - def __on_enlist_patient_as_staff(self, event):
2619 pat = gmPerson.gmCurrentPatient() 2620 if not pat.connected: 2621 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add staff member. No active patient.')) 2622 return False 2623 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2624 dlg.ShowModal()
2625 #----------------------------------------------
2626 - def __on_delete_patient(self, event):
2627 pat = gmPerson.gmCurrentPatient() 2628 if not pat.connected: 2629 gmDispatcher.send(signal = 'statustext', msg = _('Cannot delete patient. No patient active.')) 2630 return False 2631 gmDemographicsWidgets.disable_identity(identity=pat) 2632 return True
2633 #----------------------------------------------
2634 - def __on_merge_patients(self, event):
2635 gmPatSearchWidgets.merge_patients(parent=self)
2636 #----------------------------------------------
2637 - def __on_add_new_staff(self, event):
2638 """Create new person and add it as staff.""" 2639 if not gmDemographicsWidgets.create_new_person(parent = self, activate = True): 2640 return 2641 dlg = gmStaffWidgets.cAddPatientAsStaffDlg(parent=self, id=-1) 2642 dlg.ShowModal()
2643 #----------------------------------------------
2644 - def __on_edit_staff_list(self, event):
2645 dlg = gmStaffWidgets.cEditStaffListDlg(parent=self, id=-1) 2646 dlg.ShowModal()
2647 #----------------------------------------------
2648 - def __on_edit_gmdbowner_password(self, evt):
2649 gmAuthWidgets.change_gmdbowner_password()
2650 #---------------------------------------------- 2651 # def __on_update_loinc(self, evt): 2652 # gmMeasurementWidgets.update_loinc_reference_data() 2653 #----------------------------------------------
2654 - def __on_update_atc(self, evt):
2655 gmMedicationWidgets.update_atc_reference_data()
2656 #----------------------------------------------
2657 - def __on_install_data_packs(self, evt):
2658 gmDataPackWidgets.manage_data_packs(parent = self)
2659 #----------------------------------------------
2660 - def __on_generate_vaccines(self, evt):
2661 wx.BeginBusyCursor() 2662 gmVaccination.regenerate_generic_vaccines() 2663 wx.EndBusyCursor()
2664 #----------------------------------------------
2665 - def _clean_exit(self):
2666 """Cleanup helper. 2667 2668 - should ALWAYS be called when this program is 2669 to be terminated 2670 - ANY code that should be executed before a 2671 regular shutdown should go in here 2672 - framework still functional 2673 """ 2674 _log.debug('gmTopLevelFrame._clean_exit() start') 2675 2676 # shut down backend notifications listener 2677 listener = gmBackendListener.gmBackendListener() 2678 try: 2679 listener.shutdown() 2680 except: 2681 _log.exception('cannot stop backend notifications listener thread') 2682 2683 # shutdown application scripting listener 2684 if _scripting_listener is not None: 2685 try: 2686 _scripting_listener.shutdown() 2687 except: 2688 _log.exception('cannot stop scripting listener thread') 2689 2690 # shutdown timers 2691 self.clock_update_timer.Stop() 2692 gmTimer.shutdown() 2693 gmPhraseWheel.shutdown() 2694 2695 # run synchronous pre-exit callback 2696 for call_back in self.__pre_exit_callbacks: 2697 try: 2698 call_back() 2699 except: 2700 print "*** pre-exit callback failed ***" 2701 print call_back 2702 _log.exception('callback [%s] failed', call_back) 2703 2704 # signal imminent demise to plugins 2705 gmDispatcher.send(u'application_closing') 2706 2707 # do not show status line messages anymore 2708 gmDispatcher.disconnect(self._on_set_statustext, 'statustext') 2709 2710 # remember GUI size 2711 curr_width, curr_height = self.GetClientSizeTuple() 2712 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height)) 2713 dbcfg = gmCfg.cCfgSQL() 2714 dbcfg.set ( 2715 option = 'main.window.width', 2716 value = curr_width, 2717 workplace = gmSurgery.gmCurrentPractice().active_workplace 2718 ) 2719 dbcfg.set ( 2720 option = 'main.window.height', 2721 value = curr_height, 2722 workplace = gmSurgery.gmCurrentPractice().active_workplace 2723 ) 2724 2725 if _cfg.get(option = 'debug'): 2726 print '---=== GNUmed shutdown ===---' 2727 try: 2728 print _('You have to manually close this window to finalize shutting down GNUmed.') 2729 print _('This is so that you can inspect the console output at your leisure.') 2730 except UnicodeEncodeError: 2731 print 'You have to manually close this window to finalize shutting down GNUmed.' 2732 print 'This is so that you can inspect the console output at your leisure.' 2733 print '---=== GNUmed shutdown ===---' 2734 2735 # shutdown GUI exception handling 2736 gmExceptionHandlingWidgets.uninstall_wx_exception_handler() 2737 2738 # are we clean ? 2739 import threading 2740 _log.debug("%s active threads", threading.activeCount()) 2741 for t in threading.enumerate(): 2742 _log.debug('thread %s', t) 2743 2744 _log.debug('gmTopLevelFrame._clean_exit() end')
2745 #---------------------------------------------- 2746 # internal API 2747 #----------------------------------------------
2748 - def __set_window_title_template(self):
2749 2750 if _cfg.get(option = 'slave'): 2751 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % ( 2752 _cfg.get(option = 'slave personality'), 2753 _cfg.get(option = 'xml-rpc port') 2754 ) 2755 else: 2756 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2757 #----------------------------------------------
2758 - def __update_window_title(self):
2759 """Update title of main window based on template. 2760 2761 This gives nice tooltips on iconified GNUmed instances. 2762 2763 User research indicates that in the title bar people want 2764 the date of birth, not the age, so please stick to this 2765 convention. 2766 """ 2767 args = {} 2768 2769 pat = gmPerson.gmCurrentPatient() 2770 if pat.connected: 2771 args['pat'] = u'%s %s %s (%s) #%d' % ( 2772 gmTools.coalesce(pat['title'], u'', u'%.4s'), 2773 pat['firstnames'], 2774 pat['lastnames'], 2775 pat.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()), 2776 pat['pk_identity'] 2777 ) 2778 else: 2779 args['pat'] = _('no patient') 2780 2781 args['prov'] = u'%s%s.%s' % ( 2782 gmTools.coalesce(_provider['title'], u'', u'%s '), 2783 _provider['firstnames'][:1], 2784 _provider['lastnames'] 2785 ) 2786 2787 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace 2788 2789 self.SetTitle(self.__title_template % args)
2790 #---------------------------------------------- 2791 #----------------------------------------------
2792 - def setup_statusbar(self):
2793 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP) 2794 sb.SetStatusWidths([-1, 225]) 2795 # add time and date display to the right corner of the status bar 2796 self.clock_update_timer = wx.PyTimer(self._cb_update_clock) 2797 self._cb_update_clock() 2798 # update every second 2799 self.clock_update_timer.Start(milliseconds = 1000)
2800 #----------------------------------------------
2801 - def _cb_update_clock(self):
2802 """Displays date and local time in the second slot of the status bar""" 2803 t = time.localtime(time.time()) 2804 st = time.strftime('%c', t).decode(gmI18N.get_encoding()) 2805 self.SetStatusText(st,1)
2806 #------------------------------------------------
2807 - def Lock(self):
2808 """Lock GNUmed client against unauthorized access""" 2809 # FIXME 2810 # for i in range(1, self.nb.GetPageCount()): 2811 # self.nb.GetPage(i).Enable(False) 2812 return
2813 #----------------------------------------------
2814 - def Unlock(self):
2815 """Unlock the main notebook widgets 2816 As long as we are not logged into the database backend, 2817 all pages but the 'login' page of the main notebook widget 2818 are locked; i.e. not accessible by the user 2819 """ 2820 #unlock notebook pages 2821 # for i in range(1, self.nb.GetPageCount()): 2822 # self.nb.GetPage(i).Enable(True) 2823 # go straight to patient selection 2824 # self.nb.AdvanceSelection() 2825 return
2826 #-----------------------------------------------
2827 - def OnPanelSize (self, event):
2828 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
2829 #==============================================================================
2830 -class gmApp(wx.App):
2831
2832 - def OnInit(self):
2833 2834 self.__starting_up = True 2835 2836 gmExceptionHandlingWidgets.install_wx_exception_handler() 2837 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version')) 2838 2839 # _log.info('display: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y))) 2840 2841 # set this so things like "wx.StandardPaths.GetDataDir()" work as expected 2842 self.SetAppName(u'gnumed') 2843 self.SetVendorName(u'The GNUmed Development Community.') 2844 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 2845 paths.init_paths(wx = wx, app_name = u'gnumed') 2846 2847 if not self.__setup_prefs_file(): 2848 return False 2849 2850 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email) 2851 2852 self.__guibroker = gmGuiBroker.GuiBroker() 2853 self.__setup_platform() 2854 2855 if not self.__establish_backend_connection(): 2856 return False 2857 2858 if not _cfg.get(option = 'skip-update-check'): 2859 self.__check_for_updates() 2860 2861 if _cfg.get(option = 'slave'): 2862 if not self.__setup_scripting_listener(): 2863 return False 2864 2865 # FIXME: load last position from backend 2866 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440)) 2867 frame.CentreOnScreen(wx.BOTH) 2868 self.SetTopWindow(frame) 2869 frame.Show(True) 2870 2871 if _cfg.get(option = 'debug'): 2872 self.RedirectStdio() 2873 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window')) 2874 # print this so people know what this window is for 2875 # and don't get suprised when it pops up later 2876 print '---=== GNUmed startup ===---' 2877 print _('redirecting STDOUT/STDERR to this log window') 2878 print '---=== GNUmed startup ===---' 2879 2880 self.__setup_user_activity_timer() 2881 self.__register_events() 2882 2883 wx.CallAfter(self._do_after_init) 2884 2885 return True
2886 #----------------------------------------------
2887 - def OnExit(self):
2888 """Called internally by wxPython after EVT_CLOSE has been handled on last frame. 2889 2890 - after destroying all application windows and controls 2891 - before wx.Windows internal cleanup 2892 """ 2893 _log.debug('gmApp.OnExit() start') 2894 2895 self.__shutdown_user_activity_timer() 2896 2897 if _cfg.get(option = 'debug'): 2898 self.RestoreStdio() 2899 sys.stdin = sys.__stdin__ 2900 sys.stdout = sys.__stdout__ 2901 sys.stderr = sys.__stderr__ 2902 2903 _log.debug('gmApp.OnExit() end')
2904 #----------------------------------------------
2905 - def _on_query_end_session(self, *args, **kwargs):
2906 wx.Bell() 2907 wx.Bell() 2908 wx.Bell() 2909 _log.warning('unhandled event detected: QUERY_END_SESSION') 2910 _log.info('we should be saving ourselves from here') 2911 gmLog2.flush() 2912 print "unhandled event detected: QUERY_END_SESSION"
2913 #----------------------------------------------
2914 - def _on_end_session(self, *args, **kwargs):
2915 wx.Bell() 2916 wx.Bell() 2917 wx.Bell() 2918 _log.warning('unhandled event detected: END_SESSION') 2919 gmLog2.flush() 2920 print "unhandled event detected: END_SESSION"
2921 #----------------------------------------------
2922 - def _on_app_activated(self, evt):
2923 if evt.GetActive(): 2924 if self.__starting_up: 2925 gmHooks.run_hook_script(hook = u'app_activated_startup') 2926 else: 2927 gmHooks.run_hook_script(hook = u'app_activated') 2928 else: 2929 gmHooks.run_hook_script(hook = u'app_deactivated') 2930 2931 evt.Skip()
2932 #----------------------------------------------
2933 - def _on_user_activity(self, evt):
2934 self.user_activity_detected = True 2935 evt.Skip()
2936 #----------------------------------------------
2937 - def _on_user_activity_timer_expired(self, cookie=None):
2938 2939 if self.user_activity_detected: 2940 self.elapsed_inactivity_slices = 0 2941 self.user_activity_detected = False 2942 self.elapsed_inactivity_slices += 1 2943 else: 2944 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices: 2945 # print "User was inactive for 30 seconds." 2946 pass 2947 2948 self.user_activity_timer.Start(oneShot = True)
2949 #---------------------------------------------- 2950 # internal helpers 2951 #----------------------------------------------
2952 - def _signal_debugging_monitor(*args, **kwargs):
2953 try: 2954 kwargs['originated_in_database'] 2955 print '==> got notification from database "%s":' % kwargs['signal'] 2956 except KeyError: 2957 print '==> received signal from client: "%s"' % kwargs['signal'] 2958 2959 del kwargs['signal'] 2960 for key in kwargs.keys(): 2961 print ' [%s]: %s' % (key, kwargs[key])
2962 #----------------------------------------------
2963 - def _signal_debugging_monitor_pubsub(self, msg):
2964 print "wx.lib.pubsub message:" 2965 print msg.topic 2966 print msg.data
2967 #----------------------------------------------
2968 - def _do_after_init(self):
2969 self.__starting_up = False 2970 gmClinicalRecord.set_func_ask_user(a_func = gmEMRStructWidgets.ask_for_encounter_continuation) 2971 self.__guibroker['horstspace.top_panel'].patient_selector.SetFocus() 2972 gmHooks.run_hook_script(hook = u'startup-after-GUI-init')
2973 #----------------------------------------------
2975 self.user_activity_detected = True 2976 self.elapsed_inactivity_slices = 0 2977 # FIXME: make configurable 2978 self.max_user_inactivity_slices = 15 # 15 * 2000ms == 30 seconds 2979 self.user_activity_timer = gmTimer.cTimer ( 2980 callback = self._on_user_activity_timer_expired, 2981 delay = 2000 # hence a minimum of 2 and max of 3.999... seconds after which inactivity is detected 2982 ) 2983 self.user_activity_timer.Start(oneShot=True)
2984 #----------------------------------------------
2986 try: 2987 self.user_activity_timer.Stop() 2988 del self.user_activity_timer 2989 except: 2990 pass
2991 #----------------------------------------------
2992 - def __register_events(self):
2993 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session) 2994 wx.EVT_END_SESSION(self, self._on_end_session) 2995 2996 # You can bind your app to wx.EVT_ACTIVATE_APP which will fire when your 2997 # app gets/looses focus, or you can wx.EVT_ACTIVATE with any of your 2998 # toplevel windows and call evt.GetActive() in the handler to see whether 2999 # it is gaining or loosing focus. 3000 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated) 3001 3002 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity) 3003 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity) 3004 3005 if _cfg.get(option = 'debug'): 3006 gmDispatcher.connect(receiver = self._signal_debugging_monitor) 3007 _log.debug('connected old signal monitor') 3008 wx.lib.pubsub.Publisher().subscribe(listener = self._signal_debugging_monitor_pubsub) 3009 _log.debug('connected wx.lib.pubsub based signal monitor for all topics')
3010 3011 # wx.lib.pubsub.Publisher().subscribe ( 3012 # listener = self._signal_debugging_monitor_pubsub, 3013 # topic = wx.lib.pubsub.getStrAllTopics() # does not exist anymore in later versions of pubsub 3014 # ) 3015 #----------------------------------------------
3016 - def __check_for_updates(self):
3017 3018 dbcfg = gmCfg.cCfgSQL() 3019 3020 do_check = bool(dbcfg.get2 ( 3021 option = u'horstspace.update.autocheck_at_startup', 3022 workplace = gmSurgery.gmCurrentPractice().active_workplace, 3023 bias = 'workplace', 3024 default = True 3025 )) 3026 3027 if not do_check: 3028 return 3029 3030 gmCfgWidgets.check_for_updates()
3031 #----------------------------------------------
3033 """Handle all the database related tasks necessary for startup.""" 3034 3035 # log on 3036 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')]) 3037 3038 from Gnumed.wxpython import gmAuthWidgets 3039 connected = gmAuthWidgets.connect_to_database ( 3040 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')], 3041 require_version = not override 3042 ) 3043 if not connected: 3044 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection") 3045 return False 3046 3047 # check account <-> staff member association 3048 try: 3049 global _provider 3050 _provider = gmPerson.gmCurrentProvider(provider = gmPerson.cStaff()) 3051 except ValueError: 3052 account = gmPG2.get_current_user() 3053 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account) 3054 msg = _( 3055 'The database account [%s] cannot be used as a\n' 3056 'staff member login for GNUmed. There was an\n' 3057 'error retrieving staff details for it.\n\n' 3058 'Please ask your administrator for help.\n' 3059 ) % account 3060 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions')) 3061 return False 3062 3063 # improve exception handler setup 3064 tmp = '%s%s %s (%s = %s)' % ( 3065 gmTools.coalesce(_provider['title'], ''), 3066 _provider['firstnames'], 3067 _provider['lastnames'], 3068 _provider['short_alias'], 3069 _provider['db_user'] 3070 ) 3071 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp) 3072 3073 # display database banner 3074 surgery = gmSurgery.gmCurrentPractice() 3075 msg = surgery.db_logon_banner 3076 if msg.strip() != u'': 3077 3078 login = gmPG2.get_default_login() 3079 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % ( 3080 login.database, 3081 gmTools.coalesce(login.host, u'localhost') 3082 )) 3083 msg = auth + msg + u'\n\n' 3084 3085 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 3086 None, 3087 #self.GetTopWindow(), # freezes 3088 -1, 3089 caption = _('Verifying database'), 3090 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '), 3091 button_defs = [ 3092 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True}, 3093 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False} 3094 ] 3095 ) 3096 go_on = dlg.ShowModal() 3097 dlg.Destroy() 3098 if go_on != wx.ID_YES: 3099 _log.info('user decided to not connect to this database') 3100 return False 3101 3102 # check database language settings 3103 self.__check_db_lang() 3104 3105 return True
3106 #----------------------------------------------
3107 - def __setup_prefs_file(self):
3108 """Setup access to a config file for storing preferences.""" 3109 3110 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx) 3111 3112 candidates = [] 3113 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')]) 3114 if explicit_file is not None: 3115 candidates.append(explicit_file) 3116 # provide a few fallbacks in the event the --conf-file isn't writable 3117 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf')) 3118 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf')) 3119 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf')) 3120 3121 prefs_file = None 3122 for candidate in candidates: 3123 try: 3124 open(candidate, 'a+').close() 3125 prefs_file = candidate 3126 break 3127 except IOError: 3128 continue 3129 3130 if prefs_file is None: 3131 msg = _( 3132 'Cannot find configuration file in any of:\n' 3133 '\n' 3134 ' %s\n' 3135 'You may need to use the comand line option\n' 3136 '\n' 3137 ' --conf-file=<FILE>' 3138 ) % '\n '.join(candidates) 3139 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files')) 3140 return False 3141 3142 _cfg.set_option(option = u'user_preferences_file', value = prefs_file) 3143 _log.info('user preferences file: %s', prefs_file) 3144 3145 return True
3146 #----------------------------------------------
3147 - def __setup_scripting_listener(self):
3148 3149 from socket import error as SocketError 3150 from Gnumed.pycommon import gmScriptingListener 3151 from Gnumed.wxpython import gmMacro 3152 3153 slave_personality = gmTools.coalesce ( 3154 _cfg.get ( 3155 group = u'workplace', 3156 option = u'slave personality', 3157 source_order = [ 3158 ('explicit', 'return'), 3159 ('workbase', 'return'), 3160 ('user', 'return'), 3161 ('system', 'return') 3162 ] 3163 ), 3164 u'gnumed-client' 3165 ) 3166 _cfg.set_option(option = 'slave personality', value = slave_personality) 3167 3168 # FIXME: handle port via /var/run/ 3169 port = int ( 3170 gmTools.coalesce ( 3171 _cfg.get ( 3172 group = u'workplace', 3173 option = u'xml-rpc port', 3174 source_order = [ 3175 ('explicit', 'return'), 3176 ('workbase', 'return'), 3177 ('user', 'return'), 3178 ('system', 'return') 3179 ] 3180 ), 3181 9999 3182 ) 3183 ) 3184 _cfg.set_option(option = 'xml-rpc port', value = port) 3185 3186 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality) 3187 global _scripting_listener 3188 try: 3189 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor) 3190 except SocketError, e: 3191 _log.exception('cannot start GNUmed XML-RPC server') 3192 gmGuiHelpers.gm_show_error ( 3193 aMessage = ( 3194 'Cannot start the GNUmed server:\n' 3195 '\n' 3196 ' [%s]' 3197 ) % e, 3198 aTitle = _('GNUmed startup') 3199 ) 3200 return False 3201 3202 return True
3203 #----------------------------------------------
3204 - def __setup_platform(self):
3205 3206 import wx.lib.colourdb 3207 wx.lib.colourdb.updateColourDB() 3208 3209 traits = self.GetTraits() 3210 try: 3211 _log.info('desktop environment: [%s]', traits.GetDesktopEnvironment()) 3212 except: 3213 pass 3214 3215 if wx.Platform == '__WXMSW__': 3216 _log.info('running on MS Windows') 3217 elif wx.Platform == '__WXGTK__': 3218 _log.info('running on GTK (probably Linux)') 3219 elif wx.Platform == '__WXMAC__': 3220 _log.info('running on Mac OS') 3221 wx.SystemOptions.SetOptionInt('mac.textcontrol-use-spell-checker', 1) 3222 else: 3223 _log.info('running on an unknown platform (%s)' % wx.Platform)
3224 #----------------------------------------------
3225 - def __check_db_lang(self):
3226 if gmI18N.system_locale is None or gmI18N.system_locale == '': 3227 _log.warning("system locale is undefined (probably meaning 'C')") 3228 return True 3229 3230 # get current database locale 3231 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}]) 3232 db_lang = rows[0]['lang'] 3233 3234 if db_lang is None: 3235 _log.debug("database locale currently not set") 3236 msg = _( 3237 "There is no language selected in the database for user [%s].\n" 3238 "Your system language is currently set to [%s].\n\n" 3239 "Do you want to set the database language to '%s' ?\n\n" 3240 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale) 3241 checkbox_msg = _('Remember to ignore missing language') 3242 else: 3243 _log.debug("current database locale: [%s]" % db_lang) 3244 msg = _( 3245 "The currently selected database language ('%s') does\n" 3246 "not match the current system language ('%s').\n" 3247 "\n" 3248 "Do you want to set the database language to '%s' ?\n" 3249 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale) 3250 checkbox_msg = _('Remember to ignore language mismatch') 3251 3252 # check if we can match up system and db language somehow 3253 if db_lang == gmI18N.system_locale_level['full']: 3254 _log.debug('Database locale (%s) up to date.' % db_lang) 3255 return True 3256 if db_lang == gmI18N.system_locale_level['country']: 3257 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale)) 3258 return True 3259 if db_lang == gmI18N.system_locale_level['language']: 3260 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale)) 3261 return True 3262 # no match 3263 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale)) 3264 3265 # returns either None or a locale string 3266 ignored_sys_lang = _cfg.get ( 3267 group = u'backend', 3268 option = u'ignored mismatching system locale', 3269 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')] 3270 ) 3271 3272 # are we to ignore *this* mismatch ? 3273 if gmI18N.system_locale == ignored_sys_lang: 3274 _log.info('configured to ignore system-to-database locale mismatch') 3275 return True 3276 3277 # no, so ask user 3278 dlg = gmGuiHelpers.c2ButtonQuestionDlg ( 3279 None, 3280 -1, 3281 caption = _('Checking database language settings'), 3282 question = msg, 3283 button_defs = [ 3284 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True}, 3285 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False} 3286 ], 3287 show_checkbox = True, 3288 checkbox_msg = checkbox_msg, 3289 checkbox_tooltip = _( 3290 'Checking this will make GNUmed remember your decision\n' 3291 'until the system language is changed.\n' 3292 '\n' 3293 'You can also reactivate this inquiry by removing the\n' 3294 'corresponding "ignore" option from the configuration file\n' 3295 '\n' 3296 ' [%s]' 3297 ) % _cfg.get(option = 'user_preferences_file') 3298 ) 3299 decision = dlg.ShowModal() 3300 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue() 3301 dlg.Destroy() 3302 3303 if decision == wx.ID_NO: 3304 if not remember_ignoring_problem: 3305 return True 3306 _log.info('User did not want to set database locale. Ignoring mismatch next time.') 3307 gmCfg2.set_option_in_INI_file ( 3308 filename = _cfg.get(option = 'user_preferences_file'), 3309 group = 'backend', 3310 option = 'ignored mismatching system locale', 3311 value = gmI18N.system_locale 3312 ) 3313 return True 3314 3315 # try setting database language (only possible if translation exists) 3316 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]: 3317 if len(lang) > 0: 3318 # users are getting confused, so don't show these "errors", 3319 # they really are just notices about us being nice 3320 rows, idx = gmPG2.run_rw_queries ( 3321 link_obj = None, 3322 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}], 3323 return_data = True 3324 ) 3325 if rows[0][0]: 3326 _log.debug("Successfully set database language to [%s]." % lang) 3327 else: 3328 _log.error('Cannot set database language to [%s].' % lang) 3329 continue 3330 return True 3331 3332 # no match found but user wanted to set language anyways, so force it 3333 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country']) 3334 gmPG2.run_rw_queries(queries = [{ 3335 'cmd': u'select i18n.force_curr_lang(%s)', 3336 'args': [gmI18N.system_locale_level['country']] 3337 }]) 3338 3339 return True
3340 #==============================================================================
3341 -def _signal_debugging_monitor(*args, **kwargs):
3342 try: 3343 kwargs['originated_in_database'] 3344 print '==> got notification from database "%s":' % kwargs['signal'] 3345 except KeyError: 3346 print '==> received signal from client: "%s"' % kwargs['signal'] 3347 3348 del kwargs['signal'] 3349 for key in kwargs.keys(): 3350 # careful because of possibly limited console output encoding 3351 try: print ' [%s]: %s' % (key, kwargs[key]) 3352 except: print 'cannot print signal information'
3353 #------------------------------------------------------------------------------
3354 -def _signal_debugging_monitor_pubsub(msg):
3355 # careful because of possibly limited console output encoding 3356 try: 3357 print '==> received wx.lib.pubsub message: "%s"' % msg.topic 3358 print ' data: %s' % msg.data 3359 print msg 3360 except: print 'problem printing pubsub message information'
3361 #==============================================================================
3362 -def main():
3363 3364 if _cfg.get(option = 'debug'): 3365 gmDispatcher.connect(receiver = _signal_debugging_monitor) 3366 _log.debug('gmDispatcher signal monitor activated') 3367 wx.lib.pubsub.Publisher().subscribe ( 3368 listener = _signal_debugging_monitor_pubsub 3369 # , topic = wx.lib.pubsub.getStrAllTopics() # not available in some implementations 3370 ) 3371 _log.debug('wx.lib.pubsub signal monitor activated') 3372 3373 wx.InitAllImageHandlers() 3374 # create an instance of our GNUmed main application 3375 # - do not redirect stdio (yet) 3376 # - allow signals to be delivered 3377 app = gmApp(redirect = False, clearSigInt = False) 3378 app.MainLoop()
3379 #============================================================================== 3380 # Main 3381 #============================================================================== 3382 if __name__ == '__main__': 3383 3384 from GNUmed.pycommon import gmI18N 3385 gmI18N.activate_locale() 3386 gmI18N.install_domain() 3387 3388 _log.info('Starting up as main module.') 3389 main() 3390 3391 #============================================================================== 3392