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