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

Source Code for Module Gnumed.wxpython.gmGuiMain

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