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