1
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 copyright: authors
10 """
11
12 __author__ = "H. Herb <hherb@gnumed.net>,\
13 K. Hilbert <Karsten.Hilbert@gmx.net>,\
14 I. Haywood <i.haywood@ugrad.unimelb.edu.au>"
15 __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
16
17
18 import sys
19 import time
20 import os
21 import os.path
22 import datetime as pyDT
23 import shutil
24 import logging
25 import urllib2
26 import subprocess
27 import glob
28
29
30
31
32 if not hasattr(sys, 'frozen'):
33 import wxversion
34 wxversion.ensureMinimal('2.8-unicode', optionsRequired=True)
35
36 try:
37 import wx
38 except ImportError:
39 print "GNUmed startup: Cannot import wxPython library."
40 print "GNUmed startup: Make sure wxPython is installed."
41 print 'CRITICAL ERROR: Error importing wxPython. Halted.'
42 raise
43
44
45
46 version = int(u'%s%s' % (wx.MAJOR_VERSION, wx.MINOR_VERSION))
47 if (version < 28) or ('unicode' not in wx.PlatformInfo):
48 print "GNUmed startup: Unsupported wxPython version (%s: %s)." % (wx.VERSION_STRING, wx.PlatformInfo)
49 print "GNUmed startup: wxPython 2.8+ with unicode support is required."
50 print 'CRITICAL ERROR: Proper wxPython version not found. Halted.'
51 raise ValueError('wxPython 2.8+ with unicode support not found')
52
53
54
55 from Gnumed.pycommon import gmCfg
56 from Gnumed.pycommon import gmPG2
57 from Gnumed.pycommon import gmDispatcher
58 from Gnumed.pycommon import gmGuiBroker
59 from Gnumed.pycommon import gmI18N
60 from Gnumed.pycommon import gmExceptions
61 from Gnumed.pycommon import gmShellAPI
62 from Gnumed.pycommon import gmTools
63 from Gnumed.pycommon import gmDateTime
64 from Gnumed.pycommon import gmHooks
65 from Gnumed.pycommon import gmBackendListener
66 from Gnumed.pycommon import gmCfg2
67 from Gnumed.pycommon import gmLog2
68 from Gnumed.pycommon import gmNetworkTools
69
70 from Gnumed.business import gmPerson
71 from Gnumed.business import gmClinicalRecord
72 from Gnumed.business import gmSurgery
73 from Gnumed.business import gmEMRStructItems
74 from Gnumed.business import gmVaccination
75 from Gnumed.business import gmArriba
76 from Gnumed.business import gmStaff
77
78 from Gnumed.exporters import gmPatientExporter
79
80 from Gnumed.wxpython import gmGuiHelpers
81 from Gnumed.wxpython import gmHorstSpace
82 from Gnumed.wxpython import gmEMRBrowser
83 from Gnumed.wxpython import gmDemographicsWidgets
84 from Gnumed.wxpython import gmEMRStructWidgets
85 from Gnumed.wxpython import gmPatSearchWidgets
86 from Gnumed.wxpython import gmAllergyWidgets
87 from Gnumed.wxpython import gmListWidgets
88 from Gnumed.wxpython import gmProviderInboxWidgets
89 from Gnumed.wxpython import gmCfgWidgets
90 from Gnumed.wxpython import gmExceptionHandlingWidgets
91 from Gnumed.wxpython import gmNarrativeWidgets
92 from Gnumed.wxpython import gmPhraseWheel
93 from Gnumed.wxpython import gmMedicationWidgets
94 from Gnumed.wxpython import gmStaffWidgets
95 from Gnumed.wxpython import gmDocumentWidgets
96 from Gnumed.wxpython import gmTimer
97 from Gnumed.wxpython import gmMeasurementWidgets
98 from Gnumed.wxpython import gmFormWidgets
99 from Gnumed.wxpython import gmSnellen
100 from Gnumed.wxpython import gmVaccWidgets
101 from Gnumed.wxpython import gmPersonContactWidgets
102 from Gnumed.wxpython import gmI18nWidgets
103 from Gnumed.wxpython import gmCodingWidgets
104 from Gnumed.wxpython import gmOrganizationWidgets
105 from Gnumed.wxpython import gmAuthWidgets
106 from Gnumed.wxpython import gmFamilyHistoryWidgets
107 from Gnumed.wxpython import gmDataPackWidgets
108 from Gnumed.wxpython import gmContactWidgets
109 from Gnumed.wxpython import gmAddressWidgets
110 from Gnumed.wxpython import gmBillingWidgets
111 from Gnumed.wxpython import gmKeywordExpansionWidgets
112 from Gnumed.wxpython import gmAccessPermissionWidgets
113
114
115 try:
116 _('dummy-no-need-to-translate-but-make-epydoc-happy')
117 except NameError:
118 _ = lambda x:x
119
120 _cfg = gmCfg2.gmCfgData()
121 _provider = None
122 _scripting_listener = None
123 _original_wxEndBusyCursor = None
124
125 _log = logging.getLogger('gm.main')
126 _log.info('wxPython GUI framework: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo))
130 """GNUmed client's main windows frame.
131
132 This is where it all happens. Avoid popping up any other windows.
133 Most user interaction should happen to and from widgets within this frame
134 """
135
136 - def __init__(self, parent, id, title, size=wx.DefaultSize):
137 """You'll have to browse the source to understand what the constructor does
138 """
139 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE)
140
141 self.__setup_font()
142
143 self.__gb = gmGuiBroker.GuiBroker()
144 self.__pre_exit_callbacks = []
145 self.bar_width = -1
146 self.menu_id2plugin = {}
147
148 _log.info('workplace is >>>%s<<<', gmSurgery.gmCurrentPractice().active_workplace)
149
150 self.__setup_main_menu()
151 self.setup_statusbar()
152 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % (
153 gmTools.coalesce(_provider['title'], ''),
154 _provider['firstnames'][:1],
155 _provider['lastnames'],
156 _provider['short_alias'],
157 _provider['db_user']
158 ))
159
160 self.__set_window_title_template()
161 self.__update_window_title()
162
163
164
165
166
167 self.SetIcon(gmTools.get_icon(wx = wx))
168
169 self.__register_events()
170
171 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1)
172 self.vbox = wx.BoxSizer(wx.VERTICAL)
173 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1)
174
175 self.SetAutoLayout(True)
176 self.SetSizerAndFit(self.vbox)
177
178
179
180
181
182 self.__set_GUI_size()
183
184
186
187 font = self.GetFont()
188 _log.debug('system default font is [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc())
189
190 desired_font_face = _cfg.get (
191 group = u'workplace',
192 option = u'client font',
193 source_order = [
194 ('explicit', 'return'),
195 ('workbase', 'return'),
196 ('local', 'return'),
197 ('user', 'return'),
198 ('system', 'return')
199 ]
200 )
201
202 fonts2try = []
203 if desired_font_face is not None:
204 _log.info('client is configured to use font [%s]', desired_font_face)
205 fonts2try.append(desired_font_face)
206
207 if wx.Platform == '__WXMSW__':
208 sane_font_face = u'DejaVu Sans'
209 _log.info('MS Windows: appending fallback font candidate [%s]', sane_font_face)
210 fonts2try.append(sane_font_face)
211
212 if len(fonts2try) == 0:
213 return
214
215 for font_face in fonts2try:
216 success = font.SetFaceName(font_face)
217 if success:
218 self.SetFont(font)
219 _log.debug('switched font to [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc())
220 return
221 font = self.GetFont()
222 _log.error('cannot switch font from [%s] (%s) to [%s]', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc(), font_face)
223
224 return
225
227 """Try to get previous window size from backend."""
228
229 cfg = gmCfg.cCfgSQL()
230
231
232 width = int(cfg.get2 (
233 option = 'main.window.width',
234 workplace = gmSurgery.gmCurrentPractice().active_workplace,
235 bias = 'workplace',
236 default = 800
237 ))
238
239
240 height = int(cfg.get2 (
241 option = 'main.window.height',
242 workplace = gmSurgery.gmCurrentPractice().active_workplace,
243 bias = 'workplace',
244 default = 600
245 ))
246
247 dw = wx.DisplaySize()[0]
248 dh = wx.DisplaySize()[1]
249
250 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)))
251 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM()))
252 _log.debug('previous GUI size [%s:%s]', width, height)
253
254
255 if width > dw:
256 _log.debug('adjusting GUI width from %s to %s', width, dw)
257 width = dw
258
259 if height > dh:
260 _log.debug('adjusting GUI height from %s to %s', height, dh)
261 height = dh
262
263
264 if width < 100:
265 _log.debug('adjusting GUI width to minimum of 100 pixel')
266 width = 100
267 if height < 100:
268 _log.debug('adjusting GUI height to minimum of 100 pixel')
269 height = 100
270
271 _log.info('setting GUI to size [%s:%s]', width, height)
272
273 self.SetClientSize(wx.Size(width, height))
274
276 """Create the main menu entries.
277
278 Individual entries are farmed out to the modules.
279
280 menu item template:
281
282 item = menu_emr_edit.Append(-1, _(''), _(''))
283 self.Bind(wx.EVT_MENU, self__on_, item)
284 """
285 global wx
286 self.mainmenu = wx.MenuBar()
287 self.__gb['main.mainmenu'] = self.mainmenu
288
289
290 menu_gnumed = wx.Menu()
291
292 self.menu_plugins = wx.Menu()
293 menu_gnumed.AppendMenu(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins)
294
295 ID = wx.NewId()
296 menu_gnumed.Append(ID, _('Check for updates'), _('Check for new releases of the GNUmed client.'))
297 wx.EVT_MENU(self, ID, self.__on_check_for_updates)
298
299 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.'))
300 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item)
301
302
303 menu_gnumed.AppendSeparator()
304
305
306 menu_config = wx.Menu()
307
308 item = menu_config.Append(-1, _('All options'), _('List all options as configured in the database.'))
309 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item)
310
311
312 menu_cfg_db = wx.Menu()
313
314 ID = wx.NewId()
315 menu_cfg_db.Append(ID, _('Language'), _('Configure the database language'))
316 wx.EVT_MENU(self, ID, self.__on_configure_db_lang)
317
318 ID = wx.NewId()
319 menu_cfg_db.Append(ID, _('Welcome message'), _('Configure the database welcome message (all users).'))
320 wx.EVT_MENU(self, ID, self.__on_configure_db_welcome)
321
322 menu_config.AppendMenu(wx.NewId(), _('Database ...'), menu_cfg_db)
323
324
325 menu_cfg_client = wx.Menu()
326
327 ID = wx.NewId()
328 menu_cfg_client.Append(ID, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.'))
329 wx.EVT_MENU(self, ID, self.__on_configure_export_chunk_size)
330
331 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.'))
332 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item)
333
334 menu_config.AppendMenu(wx.NewId(), _('Client parameters ...'), menu_cfg_client)
335
336
337 menu_cfg_ui = wx.Menu()
338
339
340 menu_cfg_doc = wx.Menu()
341
342 ID = wx.NewId()
343 menu_cfg_doc.Append(ID, _('Review dialog'), _('Configure review dialog after document display.'))
344 wx.EVT_MENU(self, ID, self.__on_configure_doc_review_dialog)
345
346 ID = wx.NewId()
347 menu_cfg_doc.Append(ID, _('UUID display'), _('Configure unique ID dialog on document import.'))
348 wx.EVT_MENU(self, ID, self.__on_configure_doc_uuid_dialog)
349
350 ID = wx.NewId()
351 menu_cfg_doc.Append(ID, _('Empty documents'), _('Whether to allow saving documents without parts.'))
352 wx.EVT_MENU(self, ID, self.__on_configure_partless_docs)
353
354 item = menu_cfg_doc.Append(-1, _('Generate UUID'), _('Whether to generate UUIDs for new documents.'))
355 self.Bind(wx.EVT_MENU, self.__on_configure_generate_doc_uuid, item)
356
357 menu_cfg_ui.AppendMenu(wx.NewId(), _('Document handling ...'), menu_cfg_doc)
358
359
360 menu_cfg_update = wx.Menu()
361
362 ID = wx.NewId()
363 menu_cfg_update.Append(ID, _('Auto-check'), _('Whether to auto-check for updates at startup.'))
364 wx.EVT_MENU(self, ID, self.__on_configure_update_check)
365
366 ID = wx.NewId()
367 menu_cfg_update.Append(ID, _('Check scope'), _('When checking for updates, consider latest branch, too ?'))
368 wx.EVT_MENU(self, ID, self.__on_configure_update_check_scope)
369
370 ID = wx.NewId()
371 menu_cfg_update.Append(ID, _('URL'), _('The URL to retrieve version information from.'))
372 wx.EVT_MENU(self, ID, self.__on_configure_update_url)
373
374 menu_cfg_ui.AppendMenu(wx.NewId(), _('Update handling ...'), menu_cfg_update)
375
376
377 menu_cfg_pat_search = wx.Menu()
378
379 ID = wx.NewId()
380 menu_cfg_pat_search.Append(ID, _('Birthday reminder'), _('Configure birthday reminder proximity interval.'))
381 wx.EVT_MENU(self, ID, self.__on_configure_dob_reminder_proximity)
382
383 ID = wx.NewId()
384 menu_cfg_pat_search.Append(ID, _('Immediate source activation'), _('Configure immediate activation of single external person.'))
385 wx.EVT_MENU(self, ID, self.__on_configure_quick_pat_search)
386
387 ID = wx.NewId()
388 menu_cfg_pat_search.Append(ID, _('Initial plugin'), _('Configure which plugin to show right after person activation.'))
389 wx.EVT_MENU(self, ID, self.__on_configure_initial_pat_plugin)
390
391 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default province/region/state for person creation.'))
392 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item)
393
394 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.'))
395 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item)
396
397 menu_cfg_ui.AppendMenu(wx.NewId(), _('Person ...'), menu_cfg_pat_search)
398
399
400 menu_cfg_soap_editing = wx.Menu()
401
402 ID = wx.NewId()
403 menu_cfg_soap_editing.Append(ID, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.'))
404 wx.EVT_MENU(self, ID, self.__on_allow_multiple_new_episodes)
405
406 item = menu_cfg_soap_editing.Append(-1, _('Auto-open editors'), _('Configure auto-opening editors for recent problems.'))
407 self.Bind(wx.EVT_MENU, self.__on_allow_auto_open_episodes, item)
408
409 menu_cfg_ui.AppendMenu(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing)
410
411 menu_config.AppendMenu(wx.NewId(), _('User interface ...'), menu_cfg_ui)
412
413
414 menu_cfg_ext_tools = wx.Menu()
415
416
417
418
419
420 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.'))
421 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item)
422
423 ID = wx.NewId()
424 menu_cfg_ext_tools.Append(ID, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.'))
425 wx.EVT_MENU(self, ID, self.__on_configure_ooo_settle_time)
426
427 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.'))
428 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item)
429
430 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.'))
431 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item)
432
433
434
435
436 item = menu_cfg_ext_tools.Append(-1, _('ADR URL'), _('URL for reporting Adverse Drug Reactions.'))
437 self.Bind(wx.EVT_MENU, self.__on_configure_adr_url, item)
438
439 item = menu_cfg_ext_tools.Append(-1, _('vaccADR URL'), _('URL for reporting Adverse Drug Reactions to *vaccines*.'))
440 self.Bind(wx.EVT_MENU, self.__on_configure_vaccine_adr_url, item)
441
442 item = menu_cfg_ext_tools.Append(-1, _('Vacc plans URL'), _('URL for vaccination plans.'))
443 self.Bind(wx.EVT_MENU, self.__on_configure_vaccination_plans_url, item)
444
445 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.'))
446 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item)
447
448 menu_config.AppendMenu(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools)
449
450
451 menu_cfg_bill = wx.Menu()
452
453 item = menu_cfg_bill.Append(-1, _('Invoice template (no VAT)'), _('Select the template for printing an invoice without VAT.'))
454 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_no_vat, item)
455
456 item = menu_cfg_bill.Append(-1, _('Invoice template (with VAT)'), _('Select the template for printing an invoice with VAT.'))
457 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_with_vat, item)
458
459 item = menu_cfg_bill.Append(-1, _('Catalogs URL'), _('URL for billing catalogs (schedules of fees).'))
460 self.Bind(wx.EVT_MENU, self.__on_configure_billing_catalogs_url, item)
461
462
463 menu_cfg_emr = wx.Menu()
464
465 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.'))
466 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item)
467
468 item = menu_cfg_emr.Append(-1, _('Prescription mode'), _('Select the default mode for creating a prescription.'))
469 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_mode, item)
470
471 item = menu_cfg_emr.Append(-1, _('Prescription template'), _('Select the template for printing a prescription.'))
472 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_template, item)
473
474 item = menu_cfg_emr.Append(-1, _('Default Gnuplot template'), _('Select the default template for plotting test results.'))
475 self.Bind(wx.EVT_MENU, self.__on_cfg_default_gnuplot_template, item)
476
477 item = menu_cfg_emr.Append(-1, _('Fallback provider'), _('Select the doctor to fall back to for patients without a primary provider.'))
478 self.Bind(wx.EVT_MENU, self.__on_cfg_fallback_primary_provider, item)
479
480
481 menu_cfg_encounter = wx.Menu()
482
483 ID = wx.NewId()
484 menu_cfg_encounter.Append(ID, _('Edit before patient change'), _('Edit encounter details before change of patient.'))
485 wx.EVT_MENU(self, ID, self.__on_cfg_enc_pat_change)
486
487 ID = wx.NewId()
488 menu_cfg_encounter.Append(ID, _('Minimum duration'), _('Minimum duration of an encounter.'))
489 wx.EVT_MENU(self, ID, self.__on_cfg_enc_min_ttl)
490
491 ID = wx.NewId()
492 menu_cfg_encounter.Append(ID, _('Maximum duration'), _('Maximum duration of an encounter.'))
493 wx.EVT_MENU(self, ID, self.__on_cfg_enc_max_ttl)
494
495 ID = wx.NewId()
496 menu_cfg_encounter.Append(ID, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.'))
497 wx.EVT_MENU(self, ID, self.__on_cfg_enc_empty_ttl)
498
499 ID = wx.NewId()
500 menu_cfg_encounter.Append(ID, _('Default type'), _('Default type for new encounters.'))
501 wx.EVT_MENU(self, ID, self.__on_cfg_enc_default_type)
502
503 menu_cfg_emr.AppendMenu(wx.NewId(), _('Encounter ...'), menu_cfg_encounter)
504
505
506 menu_cfg_episode = wx.Menu()
507
508 ID = wx.NewId()
509 menu_cfg_episode.Append(ID, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.'))
510 wx.EVT_MENU(self, ID, self.__on_cfg_epi_ttl)
511
512 menu_cfg_emr.AppendMenu(wx.NewId(), _('Episode ...'), menu_cfg_episode)
513
514 menu_config.AppendMenu(wx.NewId(), _('EMR ...'), menu_cfg_emr)
515 menu_config.AppendMenu(wx.NewId(), _('Billing ...'), menu_cfg_bill)
516 menu_gnumed.AppendMenu(wx.NewId(), _('Preferences ...'), menu_config)
517
518
519 menu_master_data = wx.Menu()
520
521 item = menu_master_data.Append(-1, _('Manage lists'), _('Manage various lists of master data.'))
522 self.Bind(wx.EVT_MENU, self.__on_manage_master_data, item)
523
524 item = menu_master_data.Append(-1, _('Install data packs'), _('Install reference data from data packs.'))
525 self.Bind(wx.EVT_MENU, self.__on_install_data_packs, item)
526
527 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.'))
528 self.Bind(wx.EVT_MENU, self.__on_update_atc, item)
529
530 item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.'))
531 self.Bind(wx.EVT_MENU, self.__on_update_loinc, item)
532
533 item = menu_master_data.Append(-1, _('Create fake vaccines'), _('Re-create fake generic vaccines.'))
534 self.Bind(wx.EVT_MENU, self.__on_generate_vaccines, item)
535
536 menu_gnumed.AppendMenu(wx.NewId(), _('&Master data ...'), menu_master_data)
537
538
539 menu_users = wx.Menu()
540
541 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user'))
542 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item)
543
544 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users'))
545 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item)
546
547 item = menu_users.Append(-1, _('&Change DB owner PWD'), _('Change the password of the GNUmed database owner'))
548 self.Bind(wx.EVT_MENU, self.__on_edit_gmdbowner_password, item)
549
550 menu_gnumed.AppendMenu(wx.NewId(), _('&Users ...'), menu_users)
551
552
553 menu_gnumed.AppendSeparator()
554
555 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.'))
556 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item)
557
558 self.mainmenu.Append(menu_gnumed, '&GNUmed')
559
560
561 menu_person = wx.Menu()
562
563 item = menu_person.Append(-1, _('Search'), _('Search for a person.'))
564 self.Bind(wx.EVT_MENU, self.__on_search_person, item)
565 acc_tab = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, item.GetId())])
566 self.SetAcceleratorTable(acc_tab)
567
568 ID_CREATE_PATIENT = wx.NewId()
569 menu_person.Append(ID_CREATE_PATIENT, _('&Register person'), _("Register a new person with GNUmed"))
570 wx.EVT_MENU(self, ID_CREATE_PATIENT, self.__on_create_new_patient)
571
572 ID_LOAD_EXT_PAT = wx.NewId()
573 menu_person.Append(ID_LOAD_EXT_PAT, _('&Load external'), _('Load and possibly create person from an external source.'))
574 wx.EVT_MENU(self, ID_LOAD_EXT_PAT, self.__on_load_external_patient)
575
576 item = menu_person.Append(-1, _('Add &tag'), _('Add a text/image tag to this person.'))
577 self.Bind(wx.EVT_MENU, self.__on_add_tag2person, item)
578
579 ID_DEL_PAT = wx.NewId()
580 menu_person.Append(ID_DEL_PAT, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.'))
581 wx.EVT_MENU(self, ID_DEL_PAT, self.__on_delete_patient)
582
583 item = menu_person.Append(-1, _('&Merge persons'), _('Merge two persons into one.'))
584 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item)
585
586 menu_person.AppendSeparator()
587
588 ID_ENLIST_PATIENT_AS_STAFF = wx.NewId()
589 menu_person.Append(ID_ENLIST_PATIENT_AS_STAFF, _('Enlist as user'), _('Enlist current person as GNUmed user'))
590 wx.EVT_MENU(self, ID_ENLIST_PATIENT_AS_STAFF, self.__on_enlist_patient_as_staff)
591
592
593 ID = wx.NewId()
594 menu_person.Append(ID, _('Export to GDT'), _('Export demographics of currently active person into GDT file.'))
595 wx.EVT_MENU(self, ID, self.__on_export_as_gdt)
596
597 menu_person.AppendSeparator()
598
599 self.mainmenu.Append(menu_person, '&Person')
600 self.__gb['main.patientmenu'] = menu_person
601
602
603 menu_emr = wx.Menu()
604
605
606 menu_emr_edit = wx.Menu()
607
608 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'))
609 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item)
610
611 item = menu_emr_edit.Append(-1, _('&Episode'), _('Add an episode of illness to the EMR of the active patient'))
612 self.Bind(wx.EVT_MENU, self.__on_add_episode, item)
613
614 item = menu_emr_edit.Append(-1, _('&Medication'), _('Add medication / substance use entry.'))
615 self.Bind(wx.EVT_MENU, self.__on_add_medication, item)
616
617 item = menu_emr_edit.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.'))
618 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item)
619
620 item = menu_emr_edit.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.'))
621 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item)
622
623 item = menu_emr_edit.Append(-1, _('&Hospitalizations'), _('Manage hospitalizations.'))
624 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item)
625
626 item = menu_emr_edit.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.'))
627 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item)
628
629 item = menu_emr_edit.Append(-1, _('&Measurements'), _('Add (a) measurement result(s) for the current patient.'))
630 self.Bind(wx.EVT_MENU, self.__on_add_measurement, item)
631
632 item = menu_emr_edit.Append(-1, _('&Vaccinations'), _('Add (a) vaccination(s) for the current patient.'))
633 self.Bind(wx.EVT_MENU, self.__on_add_vaccination, item)
634
635 item = menu_emr_edit.Append(-1, _('&Family history (FHx)'), _('Manage family history.'))
636 self.Bind(wx.EVT_MENU, self.__on_manage_fhx, item)
637
638 item = menu_emr_edit.Append(-1, _('&Encounters'), _('List all encounters including empty ones.'))
639 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item)
640
641 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit)
642
643
644 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient'))
645 self.Bind(wx.EVT_MENU, self.__on_search_emr, item)
646
647 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.'))
648 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item)
649
650
651
652
653
654 item = menu_emr.Append(-1, _('Statistics'), _('Show a high-level statistic summary of the EMR.'))
655 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item)
656
657
658
659
660 menu_emr.AppendSeparator()
661
662
663 menu_emr_export = wx.Menu()
664
665 ID_EXPORT_EMR_ASCII = wx.NewId()
666 menu_emr_export.Append (
667 ID_EXPORT_EMR_ASCII,
668 _('Text document'),
669 _("Export the EMR of the active patient into a text file")
670 )
671 wx.EVT_MENU(self, ID_EXPORT_EMR_ASCII, self.OnExportEMR)
672
673 ID_EXPORT_EMR_JOURNAL = wx.NewId()
674 menu_emr_export.Append (
675 ID_EXPORT_EMR_JOURNAL,
676 _('Journal'),
677 _("Export the EMR of the active patient as a chronological journal into a text file")
678 )
679 wx.EVT_MENU(self, ID_EXPORT_EMR_JOURNAL, self.__on_export_emr_as_journal)
680
681 ID_EXPORT_MEDISTAR = wx.NewId()
682 menu_emr_export.Append (
683 ID_EXPORT_MEDISTAR,
684 _('MEDISTAR import format'),
685 _("GNUmed -> MEDISTAR. Export progress notes of active patient's active encounter into a text file.")
686 )
687 wx.EVT_MENU(self, ID_EXPORT_MEDISTAR, self.__on_export_for_medistar)
688
689 menu_emr.AppendMenu(wx.NewId(), _('Export as ...'), menu_emr_export)
690
691 menu_emr.AppendSeparator()
692
693 self.mainmenu.Append(menu_emr, _("&EMR"))
694 self.__gb['main.emrmenu'] = menu_emr
695
696
697 menu_paperwork = wx.Menu()
698
699 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.'))
700 self.Bind(wx.EVT_MENU, self.__on_new_letter, item)
701
702 self.mainmenu.Append(menu_paperwork, _('&Correspondence'))
703
704
705 self.menu_tools = wx.Menu()
706
707 item = self.menu_tools.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients'))
708 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item)
709
710 ID_DICOM_VIEWER = wx.NewId()
711 viewer = _('no viewer installed')
712 if gmShellAPI.detect_external_binary(binary = 'ginkgocadx')[0]:
713 viewer = u'Ginkgo CADx'
714 elif os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
715 viewer = u'OsiriX'
716 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]:
717 viewer = u'Aeskulap'
718 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]:
719 viewer = u'AMIDE'
720 elif gmShellAPI.detect_external_binary(binary = 'dicomscope')[0]:
721 viewer = u'DicomScope'
722 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]:
723 viewer = u'(x)medcon'
724 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)
725 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_dicom_viewer)
726 if viewer == _('no viewer installed'):
727 _log.info('neither of Ginkgo CADx / OsiriX / Aeskulap / AMIDE / DicomScope / xmedcon found, disabling "DICOM viewer" menu item')
728 self.menu_tools.Enable(id=ID_DICOM_VIEWER, enable=False)
729
730
731
732
733
734 ID = wx.NewId()
735 self.menu_tools.Append(ID, _('Snellen chart'), _('Display fullscreen snellen chart.'))
736 wx.EVT_MENU(self, ID, self.__on_snellen)
737
738 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.'))
739 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item)
740
741 ID_DICOM_VIEWER = wx.NewId()
742 self.menu_tools.Append(ID_DICOM_VIEWER, u'arriba', _('arriba: cardiovascular risk assessment (%s).') % u'www.arriba-hausarzt.de')
743 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_arriba)
744 if not gmShellAPI.detect_external_binary(binary = 'arriba')[0]:
745 _log.info('<arriba> not found, disabling "arriba" menu item')
746 self.menu_tools.Enable(id = ID_DICOM_VIEWER, enable = False)
747
748
749
750 self.menu_tools.AppendSeparator()
751
752 self.mainmenu.Append(self.menu_tools, _("&Tools"))
753 self.__gb['main.toolsmenu'] = self.menu_tools
754
755
756 menu_knowledge = wx.Menu()
757
758
759 menu_drug_dbs = wx.Menu()
760
761 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.'))
762 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item)
763
764
765
766
767
768
769 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs)
770
771 menu_id = wx.NewId()
772 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)'))
773 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch)
774
775
776
777
778 ID_MEDICAL_LINKS = wx.NewId()
779 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.'))
780 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links)
781
782 self.mainmenu.Append(menu_knowledge, _('&Knowledge'))
783 self.__gb['main.knowledgemenu'] = menu_knowledge
784
785
786 self.menu_office = wx.Menu()
787
788 item = self.menu_office.Append(-1, _('Audit trail'), _('Display database audit trail.'))
789 self.Bind(wx.EVT_MENU, self.__on_display_audit_trail, item)
790
791 self.menu_office.AppendSeparator()
792
793 item = self.menu_office.Append(-1, _('List bills'), _('List all bills across all patients.'))
794 self.Bind(wx.EVT_MENU, self.__on_show_all_bills, item)
795
796 self.mainmenu.Append(self.menu_office, _('&Office'))
797 self.__gb['main.officemenu'] = self.menu_office
798
799
800 help_menu = wx.Menu()
801
802 ID = wx.NewId()
803 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.'))
804 wx.EVT_MENU(self, ID, self.__on_display_wiki)
805
806 ID = wx.NewId()
807 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.'))
808 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online)
809
810 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.'))
811 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item)
812
813 item = help_menu.Append(-1, _('&Clear status line'), _('Clear out the status line.'))
814 self.Bind(wx.EVT_MENU, self.__on_clear_status_line, item)
815
816 menu_debugging = wx.Menu()
817
818 ID_SCREENSHOT = wx.NewId()
819 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.'))
820 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot)
821
822 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.'))
823 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item)
824
825 ID = wx.NewId()
826 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.'))
827 wx.EVT_MENU(self, ID, self.__on_backup_log_file)
828
829 item = menu_debugging.Append(-1, _('Email log file'), _('Send the log file to the authors for help.'))
830 self.Bind(wx.EVT_MENU, self.__on_email_log_file, item)
831
832 ID = wx.NewId()
833 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.'))
834 wx.EVT_MENU(self, ID, self.__on_display_bugtracker)
835
836 ID_UNBLOCK = wx.NewId()
837 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.'))
838 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor)
839
840 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.'))
841 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item)
842
843
844
845
846 if _cfg.get(option = 'debug'):
847 ID_TOGGLE_PAT_LOCK = wx.NewId()
848 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient search'), _('Lock/unlock patient search - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !'))
849 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock)
850
851 ID_TEST_EXCEPTION = wx.NewId()
852 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.'))
853 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception)
854
855 item = menu_debugging.Append(-1, _('Test access violation exception'), _('Simulate an access violation exception.'))
856 self.Bind(wx.EVT_MENU, self.__on_test_access_violation, item)
857
858 item = menu_debugging.Append(-1, _('Test access checking'), _('Simulate a failing access check.'))
859 self.Bind(wx.EVT_MENU, self.__on_test_access_checking, item)
860
861 ID = wx.NewId()
862 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).'))
863 wx.EVT_MENU(self, ID, self.__on_invoke_inspector)
864 try:
865 import wx.lib.inspection
866 except ImportError:
867 menu_debugging.Enable(id = ID, enable = False)
868
869 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging)
870
871 help_menu.AppendSeparator()
872
873 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "")
874 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout)
875
876 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.'))
877 self.Bind(wx.EVT_MENU, self.__on_about_database, item)
878
879 item = help_menu.Append(-1, _('About contributors'), _('Show GNUmed contributors'))
880 self.Bind(wx.EVT_MENU, self.__on_show_contributors, item)
881
882 help_menu.AppendSeparator()
883
884 self.mainmenu.Append(help_menu, _("&Help"))
885
886 self.__gb['main.helpmenu'] = help_menu
887
888
889 self.SetMenuBar(self.mainmenu)
890
893
894
895
897 """register events we want to react to"""
898
899 wx.EVT_CLOSE(self, self.OnClose)
900 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
901 wx.EVT_END_SESSION(self, self._on_end_session)
902
903 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
904 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_pat_name_changed)
905 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_pat_name_changed)
906 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext)
907 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention)
908 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning)
909 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback)
910 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded)
911
912 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
913
914 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
915
916 _log.debug('registering plugin with menu system')
917 _log.debug(' generic name: %s', plugin_name)
918 _log.debug(' class name: %s', class_name)
919 _log.debug(' specific menu: %s', menu_name)
920 _log.debug(' menu item: %s', menu_item_name)
921
922
923 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name)
924 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
925 self.menu_id2plugin[item.Id] = class_name
926
927
928 if menu_name is not None:
929 menu = self.__gb['main.%smenu' % menu_name]
930 item = menu.Append(-1, menu_item_name, menu_help_string)
931 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
932 self.menu_id2plugin[item.Id] = class_name
933
934 return True
935
937 gmDispatcher.send (
938 signal = u'display_widget',
939 name = self.menu_id2plugin[evt.Id]
940 )
941
943 wx.Bell()
944 wx.Bell()
945 wx.Bell()
946 _log.warning('unhandled event detected: QUERY_END_SESSION')
947 _log.info('we should be saving ourselves from here')
948 gmLog2.flush()
949 print "unhandled event detected: QUERY_END_SESSION"
950
952 wx.Bell()
953 wx.Bell()
954 wx.Bell()
955 _log.warning('unhandled event detected: END_SESSION')
956 gmLog2.flush()
957 print "unhandled event detected: END_SESSION"
958
960 if not callable(callback):
961 raise TypeError(u'callback [%s] not callable' % callback)
962
963 self.__pre_exit_callbacks.append(callback)
964
965 - def _on_set_statustext_pubsub(self, context=None):
966 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg'])
967 wx.CallAfter(self.SetStatusText, msg)
968
969 try:
970 if context.data['beep']:
971 wx.Bell()
972 except KeyError:
973 pass
974
975 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
976
977 if msg is None:
978 msg = _('programmer forgot to specify status message')
979
980 if loglevel is not None:
981 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' '))
982
983 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg)
984 wx.CallAfter(self.SetStatusText, msg)
985
986 if beep:
987 wx.Bell()
988
990 wx.CallAfter(self.__on_db_maintenance_warning)
991
993
994 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.'))
995 wx.Bell()
996 if not wx.GetApp().IsActive():
997 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
998
999 gmHooks.run_hook_script(hook = u'db_maintenance_warning')
1000
1001 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
1002 None,
1003 -1,
1004 caption = _('Database shutdown warning'),
1005 question = _(
1006 'The database will be shut down for maintenance\n'
1007 'in a few minutes.\n'
1008 '\n'
1009 'In order to not suffer any loss of data you\n'
1010 'will need to save your current work and log\n'
1011 'out of this GNUmed client.\n'
1012 ),
1013 button_defs = [
1014 {
1015 u'label': _('Close now'),
1016 u'tooltip': _('Close this GNUmed client immediately.'),
1017 u'default': False
1018 },
1019 {
1020 u'label': _('Finish work'),
1021 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'),
1022 u'default': True
1023 }
1024 ]
1025 )
1026 decision = dlg.ShowModal()
1027 if decision == wx.ID_YES:
1028 top_win = wx.GetApp().GetTopWindow()
1029 wx.CallAfter(top_win.Close)
1030
1032 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
1033
1035
1036 if not wx.GetApp().IsActive():
1037 if urgent:
1038 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1039 else:
1040 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO)
1041
1042 if msg is not None:
1043 self.SetStatusText(msg)
1044
1045 if urgent:
1046 wx.Bell()
1047
1048 gmHooks.run_hook_script(hook = u'request_user_attention')
1049
1051 wx.CallAfter(self.__on_pat_name_changed)
1052
1054 self.__update_window_title()
1055
1056 - def _on_post_patient_selection(self, **kwargs):
1057 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
1058
1060 self.__update_window_title()
1061 gmDispatcher.send(signal = 'statustext', msg = u'')
1062 try:
1063 gmHooks.run_hook_script(hook = u'post_patient_activation')
1064 except:
1065 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.'))
1066 raise
1067
1069 return self.__sanity_check_encounter()
1070
1132
1133
1134
1137
1144
1145
1146
1162
1187
1189 from Gnumed.wxpython import gmAbout
1190 contribs = gmAbout.cContributorsDlg (
1191 parent = self,
1192 id = -1,
1193 title = _('GNUmed contributors'),
1194 size = wx.Size(400,600),
1195 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1196 )
1197 contribs.ShowModal()
1198 del contribs
1199 del gmAbout
1200
1201
1202
1204 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler)."""
1205 _log.debug('gmTopLevelFrame._on_exit_gnumed() start')
1206 self.Close(True)
1207 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1208
1211
1213 send = gmGuiHelpers.gm_show_question (
1214 _('This will send a notification about database downtime\n'
1215 'to all GNUmed clients connected to your database.\n'
1216 '\n'
1217 'Do you want to send the notification ?\n'
1218 ),
1219 _('Announcing database maintenance downtime')
1220 )
1221 if not send:
1222 return
1223 gmPG2.send_maintenance_notification()
1224
1225
1228
1229
1230
1243
1244 gmCfgWidgets.configure_string_option (
1245 message = _(
1246 'Some network installations cannot cope with loading\n'
1247 'documents of arbitrary size in one piece from the\n'
1248 'database (mainly observed on older Windows versions)\n.'
1249 '\n'
1250 'Under such circumstances documents need to be retrieved\n'
1251 'in chunks and reassembled on the client.\n'
1252 '\n'
1253 'Here you can set the size (in Bytes) above which\n'
1254 'GNUmed will retrieve documents in chunks. Setting this\n'
1255 'value to 0 will disable the chunking protocol.'
1256 ),
1257 option = 'horstspace.blob_export_chunk_size',
1258 bias = 'workplace',
1259 default_value = 1024 * 1024,
1260 validator = is_valid
1261 )
1262
1263
1264
1332
1336
1337
1338
1347
1348 gmCfgWidgets.configure_string_option (
1349 message = _(
1350 'When GNUmed cannot find an OpenOffice server it\n'
1351 'will try to start one. OpenOffice, however, needs\n'
1352 'some time to fully start up.\n'
1353 '\n'
1354 'Here you can set the time for GNUmed to wait for OOo.\n'
1355 ),
1356 option = 'external.ooo.startup_settle_time',
1357 bias = 'workplace',
1358 default_value = 2.0,
1359 validator = is_valid
1360 )
1361
1364
1379
1380 gmCfgWidgets.configure_string_option (
1381 message = _(
1382 'GNUmed will use this URL to access a website which lets\n'
1383 'you report an adverse drug reaction (ADR).\n'
1384 '\n'
1385 'If you leave this empty it will fall back\n'
1386 'to an URL for reporting ADRs in Germany.'
1387 ),
1388 option = 'external.urls.report_ADR',
1389 bias = 'user',
1390 default_value = german_default,
1391 validator = is_valid
1392 )
1393
1407
1408 gmCfgWidgets.configure_string_option (
1409 message = _(
1410 'GNUmed will use this URL to access a website which lets\n'
1411 'you report an adverse vaccination reaction (vADR).\n'
1412 '\n'
1413 'If you set it to a specific address that URL must be\n'
1414 'accessible now. If you leave it empty it will fall back\n'
1415 'to the URL for reporting other adverse drug reactions.'
1416 ),
1417 option = 'external.urls.report_vaccine_ADR',
1418 bias = 'user',
1419 default_value = german_default,
1420 validator = is_valid
1421 )
1422
1436
1437 gmCfgWidgets.configure_string_option (
1438 message = _(
1439 'GNUmed will use this URL to access an encyclopedia of\n'
1440 'measurement/lab methods from within the measurments grid.\n'
1441 '\n'
1442 'You can leave this empty but to set it to a specific\n'
1443 'address the URL must be accessible now.'
1444 ),
1445 option = 'external.urls.measurements_encyclopedia',
1446 bias = 'user',
1447 default_value = german_default,
1448 validator = is_valid
1449 )
1450
1464
1465 gmCfgWidgets.configure_string_option (
1466 message = _(
1467 'GNUmed will use this URL to access a page showing\n'
1468 'vaccination schedules.\n'
1469 '\n'
1470 'You can leave this empty but to set it to a specific\n'
1471 'address the URL must be accessible now.'
1472 ),
1473 option = 'external.urls.vaccination_plans',
1474 bias = 'user',
1475 default_value = german_default,
1476 validator = is_valid
1477 )
1478
1491
1492 gmCfgWidgets.configure_string_option (
1493 message = _(
1494 'Enter the shell command with which to start the\n'
1495 'the ACS risk assessment calculator.\n'
1496 '\n'
1497 'GNUmed will try to verify the path which may,\n'
1498 'however, fail if you are using an emulator such\n'
1499 'as Wine. Nevertheless, starting the calculator\n'
1500 'will work as long as the shell command is correct\n'
1501 'despite the failing test.'
1502 ),
1503 option = 'external.tools.acs_risk_calculator_cmd',
1504 bias = 'user',
1505 validator = is_valid
1506 )
1507
1510
1523
1524 gmCfgWidgets.configure_string_option (
1525 message = _(
1526 'Enter the shell command with which to start\n'
1527 'the FreeDiams drug database frontend.\n'
1528 '\n'
1529 'GNUmed will try to verify that path.'
1530 ),
1531 option = 'external.tools.freediams_cmd',
1532 bias = 'workplace',
1533 default_value = None,
1534 validator = is_valid
1535 )
1536
1549
1550 gmCfgWidgets.configure_string_option (
1551 message = _(
1552 'Enter the shell command with which to start the\n'
1553 'the IFAP drug database.\n'
1554 '\n'
1555 'GNUmed will try to verify the path which may,\n'
1556 'however, fail if you are using an emulator such\n'
1557 'as Wine. Nevertheless, starting IFAP will work\n'
1558 'as long as the shell command is correct despite\n'
1559 'the failing test.'
1560 ),
1561 option = 'external.ifap-win.shell_command',
1562 bias = 'workplace',
1563 default_value = 'C:\Ifapwin\WIAMDB.EXE',
1564 validator = is_valid
1565 )
1566
1567
1568
1617
1618
1619
1636
1639
1642
1647
1648 gmCfgWidgets.configure_string_option (
1649 message = _(
1650 'When a patient is activated GNUmed checks the\n'
1651 "proximity of the patient's birthday.\n"
1652 '\n'
1653 'If the birthday falls within the range of\n'
1654 ' "today %s <the interval you set here>"\n'
1655 'GNUmed will remind you of the recent or\n'
1656 'imminent anniversary.'
1657 ) % u'\u2213',
1658 option = u'patient_search.dob_warn_interval',
1659 bias = 'user',
1660 default_value = '1 week',
1661 validator = is_valid
1662 )
1663
1665
1666 gmCfgWidgets.configure_boolean_option (
1667 parent = self,
1668 question = _(
1669 'When adding progress notes do you want to\n'
1670 'allow opening several unassociated, new\n'
1671 'episodes for a patient at once ?\n'
1672 '\n'
1673 'This can be particularly helpful when entering\n'
1674 'progress notes on entirely new patients presenting\n'
1675 'with a multitude of problems on their first visit.'
1676 ),
1677 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
1678 button_tooltips = [
1679 _('Yes, allow for multiple new episodes concurrently.'),
1680 _('No, only allow editing one new episode at a time.')
1681 ]
1682 )
1683
1685
1686 gmCfgWidgets.configure_boolean_option (
1687 parent = self,
1688 question = _(
1689 'When activating a patient, do you want GNUmed to\n'
1690 'auto-open editors for all active problems that were\n'
1691 'touched upon during the current and the most recent\n'
1692 'encounter ?'
1693 ),
1694 option = u'horstspace.soap_editor.auto_open_latest_episodes',
1695 button_tooltips = [
1696 _('Yes, auto-open editors for all problems of the most recent encounter.'),
1697 _('No, only auto-open one editor for a new, unassociated problem.')
1698 ]
1699 )
1700
1746
1747
1748
1751
1754
1767
1768 gmCfgWidgets.configure_string_option (
1769 message = _(
1770 'GNUmed will use this URL to let you browse\n'
1771 'billing catalogs (schedules of fees).\n'
1772 '\n'
1773 'You can leave this empty but to set it to a specific\n'
1774 'address the URL must be accessible now.'
1775 ),
1776 option = 'external.urls.schedules_of_fees',
1777 bias = 'user',
1778 default_value = german_default,
1779 validator = is_valid
1780 )
1781
1782
1783
1786
1789
1791 gmCfgWidgets.configure_string_from_list_option (
1792 parent = self,
1793 message = _('Select the default prescription mode.\n'),
1794 option = 'horst_space.default_prescription_mode',
1795 bias = 'user',
1796 default_value = u'form',
1797 choices = [ _('Formular'), _('Datenbank') ],
1798 columns = [_('Prescription mode')],
1799 data = [ u'form', u'database' ]
1800 )
1801
1804
1807
1821
1823 gmCfgWidgets.configure_boolean_option (
1824 parent = self,
1825 question = _(
1826 'Do you want GNUmed to show the encounter\n'
1827 'details editor when changing the active patient ?'
1828 ),
1829 option = 'encounter.show_editor_before_patient_change',
1830 button_tooltips = [
1831 _('Yes, show the encounter editor if it seems appropriate.'),
1832 _('No, never show the encounter editor even if it would seem useful.')
1833 ]
1834 )
1835
1840
1841 gmCfgWidgets.configure_string_option (
1842 message = _(
1843 'When a patient is activated GNUmed checks the\n'
1844 'chart for encounters lacking any entries.\n'
1845 '\n'
1846 'Any such encounters older than what you set\n'
1847 'here will be removed from the medical record.\n'
1848 '\n'
1849 'To effectively disable removal of such encounters\n'
1850 'set this option to an improbable value.\n'
1851 ),
1852 option = 'encounter.ttl_if_empty',
1853 bias = 'user',
1854 default_value = '1 week',
1855 validator = is_valid
1856 )
1857
1862
1863 gmCfgWidgets.configure_string_option (
1864 message = _(
1865 'When a patient is activated GNUmed checks the\n'
1866 'age of the most recent encounter.\n'
1867 '\n'
1868 'If that encounter is younger than this age\n'
1869 'the existing encounter will be continued.\n'
1870 '\n'
1871 '(If it is really old a new encounter is\n'
1872 ' started, or else GNUmed will ask you.)\n'
1873 ),
1874 option = 'encounter.minimum_ttl',
1875 bias = 'user',
1876 default_value = '1 hour 30 minutes',
1877 validator = is_valid
1878 )
1879
1884
1885 gmCfgWidgets.configure_string_option (
1886 message = _(
1887 'When a patient is activated GNUmed checks the\n'
1888 'age of the most recent encounter.\n'
1889 '\n'
1890 'If that encounter is older than this age\n'
1891 'GNUmed will always start a new encounter.\n'
1892 '\n'
1893 '(If it is very recent the existing encounter\n'
1894 ' is continued, or else GNUmed will ask you.)\n'
1895 ),
1896 option = 'encounter.maximum_ttl',
1897 bias = 'user',
1898 default_value = '6 hours',
1899 validator = is_valid
1900 )
1901
1910
1911 gmCfgWidgets.configure_string_option (
1912 message = _(
1913 'At any time there can only be one open (ongoing)\n'
1914 'episode for each health issue.\n'
1915 '\n'
1916 'When you try to open (add data to) an episode on a health\n'
1917 'issue GNUmed will check for an existing open episode on\n'
1918 'that issue. If there is any it will check the age of that\n'
1919 'episode. The episode is closed if it has been dormant (no\n'
1920 'data added, that is) for the period of time (in days) you\n'
1921 'set here.\n'
1922 '\n'
1923 "If the existing episode hasn't been dormant long enough\n"
1924 'GNUmed will consult you what to do.\n'
1925 '\n'
1926 'Enter maximum episode dormancy in DAYS:'
1927 ),
1928 option = 'episode.ttl',
1929 bias = 'user',
1930 default_value = 60,
1931 validator = is_valid
1932 )
1933
1964
1979
2004
2016
2017 gmCfgWidgets.configure_string_option (
2018 message = _(
2019 'GNUmed can check for new releases being available. To do\n'
2020 'so it needs to load version information from an URL.\n'
2021 '\n'
2022 'The default URL is:\n'
2023 '\n'
2024 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n'
2025 '\n'
2026 'but you can configure any other URL locally. Note\n'
2027 'that you must enter the location as a valid URL.\n'
2028 'Depending on the URL the client will need online\n'
2029 'access when checking for updates.'
2030 ),
2031 option = u'horstspace.update.url',
2032 bias = u'workplace',
2033 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt',
2034 validator = is_valid
2035 )
2036
2054
2071
2088
2099
2100 gmCfgWidgets.configure_string_option (
2101 message = _(
2102 'GNUmed can show the document review dialog after\n'
2103 'calling the appropriate viewer for that document.\n'
2104 '\n'
2105 'Select the conditions under which you want\n'
2106 'GNUmed to do so:\n'
2107 '\n'
2108 ' 0: never display the review dialog\n'
2109 ' 1: always display the dialog\n'
2110 ' 2: only if there is no previous review by me\n'
2111 ' 3: only if there is no previous review at all\n'
2112 ' 4: only if there is no review by the responsible reviewer\n'
2113 '\n'
2114 'Note that if a viewer is configured to not block\n'
2115 'GNUmed during document display the review dialog\n'
2116 'will actually appear in parallel to the viewer.'
2117 ),
2118 option = u'horstspace.document_viewer.review_after_display',
2119 bias = u'user',
2120 default_value = 3,
2121 validator = is_valid
2122 )
2123
2125
2126
2127 master_data_lists = [
2128 'adr',
2129 'billables',
2130 'drugs',
2131 'hints',
2132 'codes',
2133 'communication_channel_types',
2134 'substances_in_brands',
2135 'substances',
2136 'labs',
2137 'form_templates',
2138 'doc_types',
2139 'enc_types',
2140 'text_expansions',
2141 'meta_test_types',
2142 'orgs',
2143 'patient_tags',
2144 'provinces',
2145 'db_translations',
2146 'ref_data_sources',
2147 'test_types',
2148 'vacc_indications',
2149 'vaccines',
2150 'workplaces'
2151 ]
2152
2153 master_data_list_names = {
2154 'adr': _('Addresses (likely slow)'),
2155 'drugs': _('Branded drugs (as marketed)'),
2156 'hints': _('Clinical hints'),
2157 'codes': _('Codes and their respective terms'),
2158 'communication_channel_types': _('Communication channel types'),
2159 'substances_in_brands': _('Components of branded drugs (substances in brands)'),
2160 'labs': _('Diagnostic organizations (path labs, ...)'),
2161 'form_templates': _('Document templates (forms, letters, plots, ...)'),
2162 'doc_types': _('Document types'),
2163 'enc_types': _('Encounter types'),
2164 'text_expansions': _('Keyword based text expansion macros'),
2165 'meta_test_types': _('Meta test/measurement types'),
2166 'orgs': _('Organizations with their units, addresses, and comm channels'),
2167 'patient_tags': _('Patient tags'),
2168 'provinces': _('Provinces (counties, territories, states, regions, ...)'),
2169 'db_translations': _('String translations in the database'),
2170 'test_types': _('Test/measurement types'),
2171 'vacc_indications': _('Vaccination targets (conditions known to be preventable by vaccination)'),
2172 'vaccines': _('Vaccines'),
2173 'workplaces': _('Workplace profiles (which plugins to load)'),
2174 'substances': _('Consumable substances'),
2175 'billables': _('Billable items'),
2176 'ref_data_sources': _('Reference data sources')
2177 }
2178
2179 map_list2handler = {
2180 'form_templates': gmFormWidgets.manage_form_templates,
2181 'doc_types': gmDocumentWidgets.manage_document_types,
2182 'text_expansions': gmKeywordExpansionWidgets.configure_keyword_text_expansion,
2183 'db_translations': gmI18nWidgets.manage_translations,
2184 'codes': gmCodingWidgets.browse_coded_terms,
2185 'enc_types': gmEMRStructWidgets.manage_encounter_types,
2186 'provinces': gmAddressWidgets.manage_provinces,
2187 'workplaces': gmProviderInboxWidgets.configure_workplace_plugins,
2188 'drugs': gmMedicationWidgets.manage_branded_drugs,
2189 'substances_in_brands': gmMedicationWidgets.manage_drug_components,
2190 'labs': gmMeasurementWidgets.manage_measurement_orgs,
2191 'test_types': gmMeasurementWidgets.manage_measurement_types,
2192 'meta_test_types': gmMeasurementWidgets.manage_meta_test_types,
2193 'vaccines': gmVaccWidgets.manage_vaccines,
2194 'vacc_indications': gmVaccWidgets.manage_vaccination_indications,
2195 'orgs': gmOrganizationWidgets.manage_orgs,
2196 'adr': gmAddressWidgets.manage_addresses,
2197 'substances': gmMedicationWidgets.manage_consumable_substances,
2198 'patient_tags': gmDemographicsWidgets.manage_tag_images,
2199 'communication_channel_types': gmContactWidgets.manage_comm_channel_types,
2200 'billables': gmBillingWidgets.manage_billables,
2201 'ref_data_sources': gmCodingWidgets.browse_data_sources,
2202 'hints': gmProviderInboxWidgets.browse_dynamic_hints,
2203 }
2204
2205
2206 def edit(item):
2207 try: map_list2handler[item](parent = self)
2208 except KeyError: pass
2209 return False
2210
2211
2212 gmListWidgets.get_choices_from_list (
2213 parent = self,
2214 caption = _('Master data management'),
2215 choices = [ master_data_list_names[lst] for lst in master_data_lists],
2216 data = master_data_lists,
2217 columns = [_('Select the list you want to manage:')],
2218 edit_callback = edit,
2219 single_selection = True,
2220 ignore_OK_button = True
2221 )
2222
2224
2225 found, cmd = gmShellAPI.detect_external_binary(binary = 'ginkgocadx')
2226 if found:
2227 gmShellAPI.run_command_in_shell(cmd, blocking=False)
2228 return
2229
2230 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
2231 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking = False)
2232 return
2233
2234 for viewer in ['aeskulap', 'amide', 'dicomscope', 'xmedcon']:
2235 found, cmd = gmShellAPI.detect_external_binary(binary = viewer)
2236 if found:
2237 gmShellAPI.run_command_in_shell(cmd, blocking = False)
2238 return
2239
2240 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
2241
2243
2244 curr_pat = gmPerson.gmCurrentPatient()
2245
2246 arriba = gmArriba.cArriba()
2247 pat = gmTools.bool2subst(curr_pat.connected, curr_pat, None)
2248 if not arriba.run(patient = pat, debug = _cfg.get(option = 'debug')):
2249 return
2250
2251
2252 if curr_pat is None:
2253 return
2254
2255 if arriba.pdf_result is None:
2256 return
2257
2258 doc = gmDocumentWidgets.save_file_as_new_document (
2259 parent = self,
2260 filename = arriba.pdf_result,
2261 document_type = _('risk assessment')
2262 )
2263
2264 try: os.remove(arriba.pdf_result)
2265 except StandardError: _log.exception('cannot remove [%s]', arriba.pdf_result)
2266
2267 if doc is None:
2268 return
2269
2270 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2271 doc.save()
2272
2273 try:
2274 open(arriba.xml_result).close()
2275 part = doc.add_part(file = arriba.xml_result)
2276 except StandardError:
2277 _log.exception('error accessing [%s]', arriba.xml_result)
2278 gmDispatcher.send(signal = u'statustext', msg = _('[arriba] XML result not found in [%s]') % arriba.xml_result, beep = False)
2279
2280 if part is None:
2281 return
2282
2283 part['obj_comment'] = u'XML-Daten'
2284 part['filename'] = u'arriba-result.xml'
2285 part.save()
2286
2288
2289 dbcfg = gmCfg.cCfgSQL()
2290 cmd = dbcfg.get2 (
2291 option = u'external.tools.acs_risk_calculator_cmd',
2292 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2293 bias = 'user'
2294 )
2295
2296 if cmd is None:
2297 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True)
2298 return
2299
2300 cwd = os.path.expanduser(os.path.join('~', '.gnumed'))
2301 try:
2302 subprocess.check_call (
2303 args = (cmd,),
2304 close_fds = True,
2305 cwd = cwd
2306 )
2307 except (OSError, ValueError, subprocess.CalledProcessError):
2308 _log.exception('there was a problem executing [%s]', cmd)
2309 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True)
2310 return
2311
2312 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d')))
2313 for pdf in pdfs:
2314 try:
2315 open(pdf).close()
2316 except:
2317 _log.exception('error accessing [%s]', pdf)
2318 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the [arriba] result in [%s] !') % pdf, beep = True)
2319 continue
2320
2321 doc = gmDocumentWidgets.save_file_as_new_document (
2322 parent = self,
2323 filename = pdf,
2324 document_type = u'risk assessment'
2325 )
2326
2327 try:
2328 os.remove(pdf)
2329 except StandardError:
2330 _log.exception('cannot remove [%s]', pdf)
2331
2332 if doc is None:
2333 continue
2334 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2335 doc.save()
2336
2337 return
2338
2340 dlg = gmSnellen.cSnellenCfgDlg()
2341 if dlg.ShowModal() != wx.ID_OK:
2342 return
2343
2344 frame = gmSnellen.cSnellenChart (
2345 width = dlg.vals[0],
2346 height = dlg.vals[1],
2347 alpha = dlg.vals[2],
2348 mirr = dlg.vals[3],
2349 parent = None
2350 )
2351 frame.CentreOnScreen(wx.BOTH)
2352
2353
2354 frame.Show(True)
2355
2356
2359
2362
2365
2366
2367
2371
2374
2375
2376
2378 wx.CallAfter(self.__save_screenshot)
2379 evt.Skip()
2380
2382
2383 time.sleep(0.5)
2384
2385 rect = self.GetRect()
2386
2387
2388 if sys.platform == 'linux2':
2389 client_x, client_y = self.ClientToScreen((0, 0))
2390 border_width = client_x - rect.x
2391 title_bar_height = client_y - rect.y
2392
2393 if self.GetMenuBar():
2394 title_bar_height /= 2
2395 rect.width += (border_width * 2)
2396 rect.height += title_bar_height + border_width
2397
2398 wdc = wx.ScreenDC()
2399 mdc = wx.MemoryDC()
2400 img = wx.EmptyBitmap(rect.width, rect.height)
2401 mdc.SelectObject(img)
2402 mdc.Blit (
2403 0, 0,
2404 rect.width, rect.height,
2405 wdc,
2406 rect.x, rect.y
2407 )
2408
2409
2410 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
2411 img.SaveFile(fname, wx.BITMAP_TYPE_PNG)
2412 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
2413
2415
2416 raise ValueError('raised ValueError to test exception handling')
2417
2419 raise gmExceptions.AccessDenied (
2420 _('[-9999]: <access violation test error>'),
2421 source = u'GNUmed code',
2422 code = -9999,
2423 details = _('This is a deliberate AcessDenied exception thrown to test the handling of access violations by means of a decorator.')
2424 )
2425
2426 @gmAccessPermissionWidgets.verify_minimum_required_role('admin', activity = _('testing access check for non-existant <admin> role'))
2428 raise gmExceptions.AccessDenied (
2429 _('[-9999]: <access violation test error>'),
2430 source = u'GNUmed code',
2431 code = -9999,
2432 details = _('This is a deliberate AcessDenied exception. You should not see this message because the role is checked in a decorator.')
2433 )
2434
2436 import wx.lib.inspection
2437 wx.lib.inspection.InspectionTool().Show()
2438
2441
2444
2447
2450
2457
2461
2464
2467
2474
2479
2481 name = os.path.basename(gmLog2._logfile_name)
2482 name, ext = os.path.splitext(name)
2483 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
2484 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs'))
2485
2486 dlg = wx.FileDialog (
2487 parent = self,
2488 message = _("Save current log as..."),
2489 defaultDir = new_path,
2490 defaultFile = new_name,
2491 wildcard = "%s (*.log)|*.log" % _("log files"),
2492 style = wx.SAVE
2493 )
2494 choice = dlg.ShowModal()
2495 new_name = dlg.GetPath()
2496 dlg.Destroy()
2497 if choice != wx.ID_OK:
2498 return True
2499
2500 _log.warning('syncing log file for backup to [%s]', new_name)
2501 gmLog2.flush()
2502 shutil.copy2(gmLog2._logfile_name, new_name)
2503 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2504
2507
2508
2509
2511 """This is the wx.EVT_CLOSE handler.
2512
2513 - framework still functional
2514 """
2515 _log.debug('gmTopLevelFrame.OnClose() start')
2516 self._clean_exit()
2517 self.Destroy()
2518 _log.debug('gmTopLevelFrame.OnClose() end')
2519 return True
2520
2526
2531
2539
2546
2553
2560
2570
2578
2586
2594
2602
2603 @gmAccessPermissionWidgets.verify_minimum_required_role('doctor', activity = _('manage vaccinations'))
2612
2621
2629
2646
2649
2652
2654
2655 pat = gmPerson.gmCurrentPatient()
2656 if not pat.connected:
2657 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.'))
2658 return False
2659
2660 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
2661
2662 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname']))
2663 gmTools.mkdir(aDefDir)
2664
2665 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames'])
2666 dlg = wx.FileDialog (
2667 parent = self,
2668 message = _("Save patient's EMR journal as..."),
2669 defaultDir = aDefDir,
2670 defaultFile = fname,
2671 wildcard = aWildcard,
2672 style = wx.SAVE
2673 )
2674 choice = dlg.ShowModal()
2675 fname = dlg.GetPath()
2676 dlg.Destroy()
2677 if choice != wx.ID_OK:
2678 return True
2679
2680 _log.debug('exporting EMR journal to [%s]' % fname)
2681
2682 exporter = gmPatientExporter.cEMRJournalExporter()
2683
2684 wx.BeginBusyCursor()
2685 try:
2686 fname = exporter.export_to_file(filename = fname)
2687 except:
2688 wx.EndBusyCursor()
2689 gmGuiHelpers.gm_show_error (
2690 _('Error exporting patient EMR as chronological journal.'),
2691 _('EMR journal export')
2692 )
2693 raise
2694 wx.EndBusyCursor()
2695
2696 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False)
2697
2698 return True
2699
2706
2708 curr_pat = gmPerson.gmCurrentPatient()
2709 if not curr_pat.connected:
2710 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.'))
2711 return
2712
2713 tag = gmDemographicsWidgets.manage_tag_images(parent = self)
2714 if tag is None:
2715 return
2716
2717 tag = curr_pat.add_tag(tag['pk_tag_image'])
2718 msg = _('Edit the comment on tag [%s]') % tag['l10n_description']
2719 comment = wx.GetTextFromUser (
2720 message = msg,
2721 caption = _('Editing tag comment'),
2722 default_value = gmTools.coalesce(tag['comment'], u''),
2723 parent = self
2724 )
2725
2726 if comment == u'':
2727 return
2728
2729 if comment.strip() == tag['comment']:
2730 return
2731
2732 if comment == u' ':
2733 tag['comment'] = None
2734 else:
2735 tag['comment'] = comment.strip()
2736
2737 tag.save()
2738
2748
2750 curr_pat = gmPerson.gmCurrentPatient()
2751 if not curr_pat.connected:
2752 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.'))
2753 return False
2754
2755 enc = 'cp850'
2756 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt'))
2757 curr_pat.export_as_gdt(filename = fname, encoding = enc)
2758 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2759
2762
2765
2773
2781
2784
2791
2795
2798
2801
2804
2807
2812
2814 """Cleanup helper.
2815
2816 - should ALWAYS be called when this program is
2817 to be terminated
2818 - ANY code that should be executed before a
2819 regular shutdown should go in here
2820 - framework still functional
2821 """
2822 _log.debug('gmTopLevelFrame._clean_exit() start')
2823
2824
2825 listener = gmBackendListener.gmBackendListener()
2826 try:
2827 listener.shutdown()
2828 except:
2829 _log.exception('cannot stop backend notifications listener thread')
2830
2831
2832 if _scripting_listener is not None:
2833 try:
2834 _scripting_listener.shutdown()
2835 except:
2836 _log.exception('cannot stop scripting listener thread')
2837
2838
2839 self.clock_update_timer.Stop()
2840 gmTimer.shutdown()
2841 gmPhraseWheel.shutdown()
2842
2843
2844 for call_back in self.__pre_exit_callbacks:
2845 try:
2846 call_back()
2847 except:
2848 print "*** pre-exit callback failed ***"
2849 print call_back
2850 _log.exception('callback [%s] failed', call_back)
2851
2852
2853 gmDispatcher.send(u'application_closing')
2854
2855
2856 gmDispatcher.disconnect(self._on_set_statustext, 'statustext')
2857
2858
2859 curr_width, curr_height = self.GetClientSizeTuple()
2860 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height))
2861 dbcfg = gmCfg.cCfgSQL()
2862 dbcfg.set (
2863 option = 'main.window.width',
2864 value = curr_width,
2865 workplace = gmSurgery.gmCurrentPractice().active_workplace
2866 )
2867 dbcfg.set (
2868 option = 'main.window.height',
2869 value = curr_height,
2870 workplace = gmSurgery.gmCurrentPractice().active_workplace
2871 )
2872
2873 if _cfg.get(option = 'debug'):
2874 print '---=== GNUmed shutdown ===---'
2875 try:
2876 print _('You have to manually close this window to finalize shutting down GNUmed.')
2877 print _('This is so that you can inspect the console output at your leisure.')
2878 except UnicodeEncodeError:
2879 print 'You have to manually close this window to finalize shutting down GNUmed.'
2880 print 'This is so that you can inspect the console output at your leisure.'
2881 print '---=== GNUmed shutdown ===---'
2882
2883
2884 gmExceptionHandlingWidgets.uninstall_wx_exception_handler()
2885
2886
2887 import threading
2888 _log.debug("%s active threads", threading.activeCount())
2889 for t in threading.enumerate():
2890 _log.debug('thread %s', t)
2891
2892 _log.debug('gmTopLevelFrame._clean_exit() end')
2893
2894
2895
2897
2898 if _cfg.get(option = 'slave'):
2899 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % (
2900 _cfg.get(option = 'slave personality'),
2901 _cfg.get(option = 'xml-rpc port')
2902 )
2903 else:
2904 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2905
2907 """Update title of main window based on template.
2908
2909 This gives nice tooltips on iconified GNUmed instances.
2910
2911 User research indicates that in the title bar people want
2912 the date of birth, not the age, so please stick to this
2913 convention.
2914 """
2915 args = {}
2916
2917 pat = gmPerson.gmCurrentPatient()
2918 if pat.connected:
2919 args['pat'] = u'%s %s %s (%s) #%d' % (
2920 gmTools.coalesce(pat['title'], u'', u'%.4s'),
2921 pat['firstnames'],
2922 pat['lastnames'],
2923 pat.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()),
2924 pat['pk_identity']
2925 )
2926 else:
2927 args['pat'] = _('no patient')
2928
2929 args['prov'] = u'%s%s.%s' % (
2930 gmTools.coalesce(_provider['title'], u'', u'%s '),
2931 _provider['firstnames'][:1],
2932 _provider['lastnames']
2933 )
2934
2935 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace
2936
2937 self.SetTitle(self.__title_template % args)
2938
2939
2941 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
2942 sb.SetStatusWidths([-1, 225])
2943
2944 self.clock_update_timer = wx.PyTimer(self._cb_update_clock)
2945 self._cb_update_clock()
2946
2947 self.clock_update_timer.Start(milliseconds = 1000)
2948
2950 """Displays date and local time in the second slot of the status bar"""
2951 t = time.localtime(time.time())
2952 st = time.strftime('%c', t).decode(gmI18N.get_encoding(), 'replace')
2953 self.SetStatusText(st, 1)
2954
2956 """Lock GNUmed client against unauthorized access"""
2957
2958
2959
2960 return
2961
2963 """Unlock the main notebook widgets
2964 As long as we are not logged into the database backend,
2965 all pages but the 'login' page of the main notebook widget
2966 are locked; i.e. not accessible by the user
2967 """
2968
2969
2970
2971
2972
2973 return
2974
2976 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
2977
2979
2981
2982 self.__starting_up = True
2983
2984 gmExceptionHandlingWidgets.install_wx_exception_handler()
2985 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version'))
2986
2987
2988
2989
2990 self.SetAppName(u'gnumed')
2991 self.SetVendorName(u'The GNUmed Development Community.')
2992 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
2993 paths.init_paths(wx = wx, app_name = u'gnumed')
2994
2995 if not self.__setup_prefs_file():
2996 return False
2997
2998 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email)
2999
3000 self.__guibroker = gmGuiBroker.GuiBroker()
3001 self.__setup_platform()
3002
3003 if not self.__establish_backend_connection():
3004 return False
3005
3006 if not _cfg.get(option = 'skip-update-check'):
3007 self.__check_for_updates()
3008
3009 if _cfg.get(option = 'slave'):
3010 if not self.__setup_scripting_listener():
3011 return False
3012
3013
3014 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440))
3015 frame.CentreOnScreen(wx.BOTH)
3016 self.SetTopWindow(frame)
3017 frame.Show(True)
3018
3019 if _cfg.get(option = 'debug'):
3020 self.RedirectStdio()
3021 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window'))
3022
3023
3024 print '---=== GNUmed startup ===---'
3025 print _('redirecting STDOUT/STDERR to this log window')
3026 print '---=== GNUmed startup ===---'
3027
3028 self.__setup_user_activity_timer()
3029 self.__register_events()
3030
3031 wx.CallAfter(self._do_after_init)
3032
3033 return True
3034
3036 """Called internally by wxPython after EVT_CLOSE has been handled on last frame.
3037
3038 - after destroying all application windows and controls
3039 - before wx.Windows internal cleanup
3040 """
3041 _log.debug('gmApp.OnExit() start')
3042
3043 self.__shutdown_user_activity_timer()
3044
3045 if _cfg.get(option = 'debug'):
3046 self.RestoreStdio()
3047 sys.stdin = sys.__stdin__
3048 sys.stdout = sys.__stdout__
3049 sys.stderr = sys.__stderr__
3050
3051 _log.debug('gmApp.OnExit() end')
3052
3054 wx.Bell()
3055 wx.Bell()
3056 wx.Bell()
3057 _log.warning('unhandled event detected: QUERY_END_SESSION')
3058 _log.info('we should be saving ourselves from here')
3059 gmLog2.flush()
3060 print "unhandled event detected: QUERY_END_SESSION"
3061
3063 wx.Bell()
3064 wx.Bell()
3065 wx.Bell()
3066 _log.warning('unhandled event detected: END_SESSION')
3067 gmLog2.flush()
3068 print "unhandled event detected: END_SESSION"
3069
3080
3082 self.user_activity_detected = True
3083 evt.Skip()
3084
3086
3087 if self.user_activity_detected:
3088 self.elapsed_inactivity_slices = 0
3089 self.user_activity_detected = False
3090 self.elapsed_inactivity_slices += 1
3091 else:
3092 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices:
3093
3094 pass
3095
3096 self.user_activity_timer.Start(oneShot = True)
3097
3098
3099
3101 try:
3102 kwargs['originated_in_database']
3103 print '==> got notification from database "%s":' % kwargs['signal']
3104 except KeyError:
3105 print '==> received signal from client: "%s"' % kwargs['signal']
3106
3107 del kwargs['signal']
3108 for key in kwargs.keys():
3109 print ' [%s]: %s' % (key, kwargs[key])
3110
3117
3119 self.user_activity_detected = True
3120 self.elapsed_inactivity_slices = 0
3121
3122 self.max_user_inactivity_slices = 15
3123 self.user_activity_timer = gmTimer.cTimer (
3124 callback = self._on_user_activity_timer_expired,
3125 delay = 2000
3126 )
3127 self.user_activity_timer.Start(oneShot=True)
3128
3130 try:
3131 self.user_activity_timer.Stop()
3132 del self.user_activity_timer
3133 except:
3134 pass
3135
3137 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
3138 wx.EVT_END_SESSION(self, self._on_end_session)
3139
3140
3141
3142
3143
3144 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated)
3145
3146 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity)
3147 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
3148
3149 if _cfg.get(option = 'debug'):
3150 gmDispatcher.connect(receiver = self._signal_debugging_monitor)
3151 _log.debug('connected signal monitor')
3152
3168
3170 """Handle all the database related tasks necessary for startup."""
3171
3172
3173 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')])
3174
3175 from Gnumed.wxpython import gmAuthWidgets
3176 connected = gmAuthWidgets.connect_to_database (
3177 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')],
3178 require_version = not override
3179 )
3180 if not connected:
3181 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection")
3182 return False
3183
3184
3185 try:
3186 global _provider
3187 _provider = gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
3188 except ValueError:
3189 account = gmPG2.get_current_user()
3190 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account)
3191 msg = _(
3192 'The database account [%s] cannot be used as a\n'
3193 'staff member login for GNUmed. There was an\n'
3194 'error retrieving staff details for it.\n\n'
3195 'Please ask your administrator for help.\n'
3196 ) % account
3197 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions'))
3198 return False
3199
3200
3201 tmp = '%s%s %s (%s = %s)' % (
3202 gmTools.coalesce(_provider['title'], ''),
3203 _provider['firstnames'],
3204 _provider['lastnames'],
3205 _provider['short_alias'],
3206 _provider['db_user']
3207 )
3208 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp)
3209
3210
3211 surgery = gmSurgery.gmCurrentPractice()
3212 msg = surgery.db_logon_banner
3213 if msg.strip() != u'':
3214
3215 login = gmPG2.get_default_login()
3216 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % (
3217 login.database,
3218 gmTools.coalesce(login.host, u'localhost')
3219 ))
3220 msg = auth + msg + u'\n\n'
3221
3222 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3223 None,
3224
3225 -1,
3226 caption = _('Verifying database'),
3227 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '),
3228 button_defs = [
3229 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True},
3230 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False}
3231 ]
3232 )
3233 go_on = dlg.ShowModal()
3234 dlg.Destroy()
3235 if go_on != wx.ID_YES:
3236 _log.info('user decided to not connect to this database')
3237 return False
3238
3239
3240 self.__check_db_lang()
3241
3242 return True
3243
3245 """Setup access to a config file for storing preferences."""
3246
3247 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3248
3249 candidates = []
3250 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')])
3251 if explicit_file is not None:
3252 candidates.append(explicit_file)
3253
3254 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf'))
3255 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf'))
3256 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf'))
3257
3258 prefs_file = None
3259 for candidate in candidates:
3260 try:
3261 open(candidate, 'a+').close()
3262 prefs_file = candidate
3263 break
3264 except IOError:
3265 continue
3266
3267 if prefs_file is None:
3268 msg = _(
3269 'Cannot find configuration file in any of:\n'
3270 '\n'
3271 ' %s\n'
3272 'You may need to use the comand line option\n'
3273 '\n'
3274 ' --conf-file=<FILE>'
3275 ) % '\n '.join(candidates)
3276 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files'))
3277 return False
3278
3279 _cfg.set_option(option = u'user_preferences_file', value = prefs_file)
3280 _log.info('user preferences file: %s', prefs_file)
3281
3282 return True
3283
3285
3286 from socket import error as SocketError
3287 from Gnumed.pycommon import gmScriptingListener
3288 from Gnumed.wxpython import gmMacro
3289
3290 slave_personality = gmTools.coalesce (
3291 _cfg.get (
3292 group = u'workplace',
3293 option = u'slave personality',
3294 source_order = [
3295 ('explicit', 'return'),
3296 ('workbase', 'return'),
3297 ('user', 'return'),
3298 ('system', 'return')
3299 ]
3300 ),
3301 u'gnumed-client'
3302 )
3303 _cfg.set_option(option = 'slave personality', value = slave_personality)
3304
3305
3306 port = int (
3307 gmTools.coalesce (
3308 _cfg.get (
3309 group = u'workplace',
3310 option = u'xml-rpc port',
3311 source_order = [
3312 ('explicit', 'return'),
3313 ('workbase', 'return'),
3314 ('user', 'return'),
3315 ('system', 'return')
3316 ]
3317 ),
3318 9999
3319 )
3320 )
3321 _cfg.set_option(option = 'xml-rpc port', value = port)
3322
3323 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality)
3324 global _scripting_listener
3325 try:
3326 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor)
3327 except SocketError, e:
3328 _log.exception('cannot start GNUmed XML-RPC server')
3329 gmGuiHelpers.gm_show_error (
3330 aMessage = (
3331 'Cannot start the GNUmed server:\n'
3332 '\n'
3333 ' [%s]'
3334 ) % e,
3335 aTitle = _('GNUmed startup')
3336 )
3337 return False
3338
3339 return True
3340
3361
3363 if gmI18N.system_locale is None or gmI18N.system_locale == '':
3364 _log.warning("system locale is undefined (probably meaning 'C')")
3365 return True
3366
3367
3368 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}])
3369 db_lang = rows[0]['lang']
3370
3371 if db_lang is None:
3372 _log.debug("database locale currently not set")
3373 msg = _(
3374 "There is no language selected in the database for user [%s].\n"
3375 "Your system language is currently set to [%s].\n\n"
3376 "Do you want to set the database language to '%s' ?\n\n"
3377 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale)
3378 checkbox_msg = _('Remember to ignore missing language')
3379 else:
3380 _log.debug("current database locale: [%s]" % db_lang)
3381 msg = _(
3382 "The currently selected database language ('%s') does\n"
3383 "not match the current system language ('%s').\n"
3384 "\n"
3385 "Do you want to set the database language to '%s' ?\n"
3386 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale)
3387 checkbox_msg = _('Remember to ignore language mismatch')
3388
3389
3390 if db_lang == gmI18N.system_locale_level['full']:
3391 _log.debug('Database locale (%s) up to date.' % db_lang)
3392 return True
3393 if db_lang == gmI18N.system_locale_level['country']:
3394 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale))
3395 return True
3396 if db_lang == gmI18N.system_locale_level['language']:
3397 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale))
3398 return True
3399
3400 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale))
3401
3402
3403 ignored_sys_lang = _cfg.get (
3404 group = u'backend',
3405 option = u'ignored mismatching system locale',
3406 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')]
3407 )
3408
3409
3410 if gmI18N.system_locale == ignored_sys_lang:
3411 _log.info('configured to ignore system-to-database locale mismatch')
3412 return True
3413
3414
3415 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3416 None,
3417 -1,
3418 caption = _('Checking database language settings'),
3419 question = msg,
3420 button_defs = [
3421 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True},
3422 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False}
3423 ],
3424 show_checkbox = True,
3425 checkbox_msg = checkbox_msg,
3426 checkbox_tooltip = _(
3427 'Checking this will make GNUmed remember your decision\n'
3428 'until the system language is changed.\n'
3429 '\n'
3430 'You can also reactivate this inquiry by removing the\n'
3431 'corresponding "ignore" option from the configuration file\n'
3432 '\n'
3433 ' [%s]'
3434 ) % _cfg.get(option = 'user_preferences_file')
3435 )
3436 decision = dlg.ShowModal()
3437 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue()
3438 dlg.Destroy()
3439
3440 if decision == wx.ID_NO:
3441 if not remember_ignoring_problem:
3442 return True
3443 _log.info('User did not want to set database locale. Ignoring mismatch next time.')
3444 gmCfg2.set_option_in_INI_file (
3445 filename = _cfg.get(option = 'user_preferences_file'),
3446 group = 'backend',
3447 option = 'ignored mismatching system locale',
3448 value = gmI18N.system_locale
3449 )
3450 return True
3451
3452
3453 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]:
3454 if len(lang) > 0:
3455
3456
3457 rows, idx = gmPG2.run_rw_queries (
3458 link_obj = None,
3459 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}],
3460 return_data = True
3461 )
3462 if rows[0][0]:
3463 _log.debug("Successfully set database language to [%s]." % lang)
3464 else:
3465 _log.error('Cannot set database language to [%s].' % lang)
3466 continue
3467 return True
3468
3469
3470 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country'])
3471 gmPG2.run_rw_queries(queries = [{
3472 'cmd': u'select i18n.force_curr_lang(%s)',
3473 'args': [gmI18N.system_locale_level['country']]
3474 }])
3475
3476 return True
3477
3479 try:
3480 kwargs['originated_in_database']
3481 print '==> got notification from database "%s":' % kwargs['signal']
3482 except KeyError:
3483 print '==> received signal from client: "%s"' % kwargs['signal']
3484
3485 del kwargs['signal']
3486 for key in kwargs.keys():
3487
3488 try: print ' [%s]: %s' % (key, kwargs[key])
3489 except: print 'cannot print signal information'
3490
3494
3505
3507
3508 if _cfg.get(option = 'debug'):
3509 gmDispatcher.connect(receiver = _signal_debugging_monitor)
3510 _log.debug('gmDispatcher signal monitor activated')
3511
3512 setup_safe_wxEndBusyCursor()
3513
3514 wx.InitAllImageHandlers()
3515
3516
3517
3518 app = gmApp(redirect = False, clearSigInt = False)
3519 app.MainLoop()
3520
3521
3522
3523 if __name__ == '__main__':
3524
3525 from GNUmed.pycommon import gmI18N
3526 gmI18N.activate_locale()
3527 gmI18N.install_domain()
3528
3529 _log.info('Starting up as main module.')
3530 main()
3531
3532
3533