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

Source Code for Module Gnumed.wxpython.gmGuiMain

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