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