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