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, _('&Measurements'), _('Manage measurement results for the current patient.'))
633 self.Bind(wx.EVT_MENU, self.__on_manage_measurements, item)
634
635 item = menu_emr_edit.Append(-1, _('&Vaccinations'), _('Add (a) vaccination(s) for the current patient.'))
636 self.Bind(wx.EVT_MENU, self.__on_add_vaccination, item)
637
638 item = menu_emr_edit.Append(-1, _('&Family history (FHx)'), _('Manage family history.'))
639 self.Bind(wx.EVT_MENU, self.__on_manage_fhx, item)
640
641 item = menu_emr_edit.Append(-1, _('&Encounters'), _('List all encounters including empty ones.'))
642 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item)
643
644 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit)
645
646
647 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient'))
648 self.Bind(wx.EVT_MENU, self.__on_search_emr, item)
649
650 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.'))
651 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item)
652
653
654
655
656
657 item = menu_emr.Append(-1, _('Statistics'), _('Show a high-level statistic summary of the EMR.'))
658 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item)
659
660
661
662
663 menu_emr.AppendSeparator()
664
665
666 menu_emr_export = wx.Menu()
667
668 ID_EXPORT_EMR_ASCII = wx.NewId()
669 menu_emr_export.Append (
670 ID_EXPORT_EMR_ASCII,
671 _('Text document'),
672 _("Export the EMR of the active patient into a text file")
673 )
674 wx.EVT_MENU(self, ID_EXPORT_EMR_ASCII, self.OnExportEMR)
675
676 ID_EXPORT_EMR_JOURNAL = wx.NewId()
677 menu_emr_export.Append (
678 ID_EXPORT_EMR_JOURNAL,
679 _('Journal'),
680 _("Export the EMR of the active patient as a chronological journal into a text file")
681 )
682 wx.EVT_MENU(self, ID_EXPORT_EMR_JOURNAL, self.__on_export_emr_as_journal)
683
684 ID_EXPORT_MEDISTAR = wx.NewId()
685 menu_emr_export.Append (
686 ID_EXPORT_MEDISTAR,
687 _('MEDISTAR import format'),
688 _("GNUmed -> MEDISTAR. Export progress notes of active patient's active encounter into a text file.")
689 )
690 wx.EVT_MENU(self, ID_EXPORT_MEDISTAR, self.__on_export_for_medistar)
691
692 menu_emr.AppendMenu(wx.NewId(), _('Export as ...'), menu_emr_export)
693
694 menu_emr.AppendSeparator()
695
696 self.mainmenu.Append(menu_emr, _("&EMR"))
697 self.__gb['main.emrmenu'] = menu_emr
698
699
700 menu_paperwork = wx.Menu()
701
702 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.'))
703 self.Bind(wx.EVT_MENU, self.__on_new_letter, item)
704
705 menu_paperwork.AppendSeparator()
706
707 item = menu_paperwork.Append(-1, _('List Placeholders'), _('Show a list of all placeholders.'))
708 self.Bind(wx.EVT_MENU, self.__on_show_placeholders, item)
709
710 self.mainmenu.Append(menu_paperwork, _('&Correspondence'))
711
712
713 self.menu_tools = wx.Menu()
714
715 item = self.menu_tools.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients'))
716 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item)
717
718 ID_DICOM_VIEWER = wx.NewId()
719 viewer = _('no viewer installed')
720 if gmShellAPI.detect_external_binary(binary = 'ginkgocadx')[0]:
721 viewer = u'Ginkgo CADx'
722 elif os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
723 viewer = u'OsiriX'
724 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]:
725 viewer = u'Aeskulap'
726 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]:
727 viewer = u'AMIDE'
728 elif gmShellAPI.detect_external_binary(binary = 'dicomscope')[0]:
729 viewer = u'DicomScope'
730 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]:
731 viewer = u'(x)medcon'
732 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)
733 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_dicom_viewer)
734 if viewer == _('no viewer installed'):
735 _log.info('neither of Ginkgo CADx / OsiriX / Aeskulap / AMIDE / DicomScope / xmedcon found, disabling "DICOM viewer" menu item')
736 self.menu_tools.Enable(id=ID_DICOM_VIEWER, enable=False)
737
738
739
740
741
742 ID = wx.NewId()
743 self.menu_tools.Append(ID, _('Snellen chart'), _('Display fullscreen snellen chart.'))
744 wx.EVT_MENU(self, ID, self.__on_snellen)
745
746 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.'))
747 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item)
748
749 ID_DICOM_VIEWER = wx.NewId()
750 self.menu_tools.Append(ID_DICOM_VIEWER, u'arriba', _('arriba: cardiovascular risk assessment (%s).') % u'www.arriba-hausarzt.de')
751 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_arriba)
752 if not gmShellAPI.detect_external_binary(binary = 'arriba')[0]:
753 _log.info('<arriba> not found, disabling "arriba" menu item')
754 self.menu_tools.Enable(id = ID_DICOM_VIEWER, enable = False)
755
756
757
758 item = self.menu_tools.Append(-1, u'HL7 (Exelleris)', 'Stage Excelleris HL7')
759 self.Bind(wx.EVT_MENU, self.__on_excelleris, item)
760
761 item = self.menu_tools.Append(-1, u'HL7', 'Stage generic HL7')
762 self.Bind(wx.EVT_MENU, self.__on_hl7, item)
763
764 item = self.menu_tools.Append(-1, u'Incoming', 'Browse incoming data')
765 self.Bind(wx.EVT_MENU, self.__on_incoming, item)
766
767 self.menu_tools.AppendSeparator()
768
769 self.mainmenu.Append(self.menu_tools, _("&Tools"))
770 self.__gb['main.toolsmenu'] = self.menu_tools
771
772
773 menu_knowledge = wx.Menu()
774
775
776 menu_drug_dbs = wx.Menu()
777
778 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.'))
779 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item)
780
781
782
783
784
785
786 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs)
787
788 menu_id = wx.NewId()
789 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)'))
790 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch)
791
792
793
794
795 ID_MEDICAL_LINKS = wx.NewId()
796 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.'))
797 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links)
798
799 self.mainmenu.Append(menu_knowledge, _('&Knowledge'))
800 self.__gb['main.knowledgemenu'] = menu_knowledge
801
802
803 self.menu_office = wx.Menu()
804
805 item = self.menu_office.Append(-1, _('Audit trail'), _('Display database audit trail.'))
806 self.Bind(wx.EVT_MENU, self.__on_display_audit_trail, item)
807
808 self.menu_office.AppendSeparator()
809
810 item = self.menu_office.Append(-1, _('List bills'), _('List all bills across all patients.'))
811 self.Bind(wx.EVT_MENU, self.__on_show_all_bills, item)
812
813 self.mainmenu.Append(self.menu_office, _('&Office'))
814 self.__gb['main.officemenu'] = self.menu_office
815
816
817 help_menu = wx.Menu()
818
819 ID = wx.NewId()
820 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.'))
821 wx.EVT_MENU(self, ID, self.__on_display_wiki)
822
823 ID = wx.NewId()
824 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.'))
825 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online)
826
827 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.'))
828 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item)
829
830 item = help_menu.Append(-1, _('&Clear status line'), _('Clear out the status line.'))
831 self.Bind(wx.EVT_MENU, self.__on_clear_status_line, item)
832
833 menu_debugging = wx.Menu()
834
835 ID_SCREENSHOT = wx.NewId()
836 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.'))
837 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot)
838
839 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.'))
840 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item)
841
842 ID = wx.NewId()
843 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.'))
844 wx.EVT_MENU(self, ID, self.__on_backup_log_file)
845
846 item = menu_debugging.Append(-1, _('Email log file'), _('Send the log file to the authors for help.'))
847 self.Bind(wx.EVT_MENU, self.__on_email_log_file, item)
848
849 ID = wx.NewId()
850 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.'))
851 wx.EVT_MENU(self, ID, self.__on_display_bugtracker)
852
853 ID_UNBLOCK = wx.NewId()
854 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.'))
855 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor)
856
857 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.'))
858 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item)
859
860
861
862
863 if _cfg.get(option = 'debug'):
864 ID_TOGGLE_PAT_LOCK = wx.NewId()
865 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient search'), _('Lock/unlock patient search - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !'))
866 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock)
867
868 ID_TEST_EXCEPTION = wx.NewId()
869 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.'))
870 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception)
871
872 item = menu_debugging.Append(-1, _('Test access violation exception'), _('Simulate an access violation exception.'))
873 self.Bind(wx.EVT_MENU, self.__on_test_access_violation, item)
874
875 item = menu_debugging.Append(-1, _('Test access checking'), _('Simulate a failing access check.'))
876 self.Bind(wx.EVT_MENU, self.__on_test_access_checking, item)
877
878 ID = wx.NewId()
879 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).'))
880 wx.EVT_MENU(self, ID, self.__on_invoke_inspector)
881 try:
882 import wx.lib.inspection
883 except ImportError:
884 menu_debugging.Enable(id = ID, enable = False)
885
886 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging)
887
888 help_menu.AppendSeparator()
889
890 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "")
891 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout)
892
893 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.'))
894 self.Bind(wx.EVT_MENU, self.__on_about_database, item)
895
896 item = help_menu.Append(-1, _('About contributors'), _('Show GNUmed contributors'))
897 self.Bind(wx.EVT_MENU, self.__on_show_contributors, item)
898
899 help_menu.AppendSeparator()
900
901 self.mainmenu.Append(help_menu, _("&Help"))
902
903 self.__gb['main.helpmenu'] = help_menu
904
905
906 self.SetMenuBar(self.mainmenu)
907
910
911
912
914 """register events we want to react to"""
915
916 wx.EVT_CLOSE(self, self.OnClose)
917 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
918 wx.EVT_END_SESSION(self, self._on_end_session)
919
920 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
921 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_pat_name_changed)
922 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_pat_name_changed)
923 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext)
924 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention)
925 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning)
926 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback)
927 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded)
928
929 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
930
931 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
932
933 _log.debug('registering plugin with menu system')
934 _log.debug(' generic name: %s', plugin_name)
935 _log.debug(' class name: %s', class_name)
936 _log.debug(' specific menu: %s', menu_name)
937 _log.debug(' menu item: %s', menu_item_name)
938
939
940 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name)
941 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
942 self.menu_id2plugin[item.Id] = class_name
943
944
945 if menu_name is not None:
946 menu = self.__gb['main.%smenu' % menu_name]
947 item = menu.Append(-1, menu_item_name, menu_help_string)
948 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
949 self.menu_id2plugin[item.Id] = class_name
950
951 return True
952
954 gmDispatcher.send (
955 signal = u'display_widget',
956 name = self.menu_id2plugin[evt.Id]
957 )
958
960 wx.Bell()
961 wx.Bell()
962 wx.Bell()
963 _log.warning('unhandled event detected: QUERY_END_SESSION')
964 _log.info('we should be saving ourselves from here')
965 gmLog2.flush()
966 print "unhandled event detected: QUERY_END_SESSION"
967
969 wx.Bell()
970 wx.Bell()
971 wx.Bell()
972 _log.warning('unhandled event detected: END_SESSION')
973 gmLog2.flush()
974 print "unhandled event detected: END_SESSION"
975
977 if not callable(callback):
978 raise TypeError(u'callback [%s] not callable' % callback)
979
980 self.__pre_exit_callbacks.append(callback)
981
982 - def _on_set_statustext_pubsub(self, context=None):
983 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg'])
984 wx.CallAfter(self.SetStatusText, msg)
985
986 try:
987 if context.data['beep']:
988 wx.Bell()
989 except KeyError:
990 pass
991
992 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
993
994 if msg is None:
995 msg = _('programmer forgot to specify status message')
996
997 if loglevel is not None:
998 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' '))
999
1000 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg)
1001 wx.CallAfter(self.SetStatusText, msg)
1002
1003 if beep:
1004 wx.Bell()
1005
1007 wx.CallAfter(self.__on_db_maintenance_warning)
1008
1010
1011 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.'))
1012 wx.Bell()
1013 if not wx.GetApp().IsActive():
1014 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1015
1016 gmHooks.run_hook_script(hook = u'db_maintenance_warning')
1017
1018 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
1019 None,
1020 -1,
1021 caption = _('Database shutdown warning'),
1022 question = _(
1023 'The database will be shut down for maintenance\n'
1024 'in a few minutes.\n'
1025 '\n'
1026 'In order to not suffer any loss of data you\n'
1027 'will need to save your current work and log\n'
1028 'out of this GNUmed client.\n'
1029 ),
1030 button_defs = [
1031 {
1032 u'label': _('Close now'),
1033 u'tooltip': _('Close this GNUmed client immediately.'),
1034 u'default': False
1035 },
1036 {
1037 u'label': _('Finish work'),
1038 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'),
1039 u'default': True
1040 }
1041 ]
1042 )
1043 decision = dlg.ShowModal()
1044 if decision == wx.ID_YES:
1045 top_win = wx.GetApp().GetTopWindow()
1046 wx.CallAfter(top_win.Close)
1047
1049 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
1050
1052
1053 if not wx.GetApp().IsActive():
1054 if urgent:
1055 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1056 else:
1057 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO)
1058
1059 if msg is not None:
1060 self.SetStatusText(msg)
1061
1062 if urgent:
1063 wx.Bell()
1064
1065 gmHooks.run_hook_script(hook = u'request_user_attention')
1066
1068 wx.CallAfter(self.__on_pat_name_changed)
1069
1071 self.__update_window_title()
1072
1073 - def _on_post_patient_selection(self, **kwargs):
1074 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
1075
1077 self.__update_window_title()
1078 gmDispatcher.send(signal = 'statustext', msg = u'')
1079 try:
1080 gmHooks.run_hook_script(hook = u'post_patient_activation')
1081 except:
1082 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.'))
1083 raise
1084
1086 return self.__sanity_check_encounter()
1087
1149
1150
1151
1154
1161
1165
1166
1167
1183
1208
1210 from Gnumed.wxpython import gmAbout
1211 contribs = gmAbout.cContributorsDlg (
1212 parent = self,
1213 id = -1,
1214 title = _('GNUmed contributors'),
1215 size = wx.Size(400,600),
1216 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1217 )
1218 contribs.ShowModal()
1219 del contribs
1220 del gmAbout
1221
1222
1223
1225 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler)."""
1226 _log.debug('gmTopLevelFrame._on_exit_gnumed() start')
1227 self.Close(True)
1228 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1229
1232
1234 send = gmGuiHelpers.gm_show_question (
1235 _('This will send a notification about database downtime\n'
1236 'to all GNUmed clients connected to your database.\n'
1237 '\n'
1238 'Do you want to send the notification ?\n'
1239 ),
1240 _('Announcing database maintenance downtime')
1241 )
1242 if not send:
1243 return
1244 gmPG2.send_maintenance_notification()
1245
1246
1249
1250
1251
1264
1265 gmCfgWidgets.configure_string_option (
1266 message = _(
1267 'Some network installations cannot cope with loading\n'
1268 'documents of arbitrary size in one piece from the\n'
1269 'database (mainly observed on older Windows versions)\n.'
1270 '\n'
1271 'Under such circumstances documents need to be retrieved\n'
1272 'in chunks and reassembled on the client.\n'
1273 '\n'
1274 'Here you can set the size (in Bytes) above which\n'
1275 'GNUmed will retrieve documents in chunks. Setting this\n'
1276 'value to 0 will disable the chunking protocol.'
1277 ),
1278 option = 'horstspace.blob_export_chunk_size',
1279 bias = 'workplace',
1280 default_value = 1024 * 1024,
1281 validator = is_valid
1282 )
1283
1284
1285
1353
1357
1358
1359
1368
1369 gmCfgWidgets.configure_string_option (
1370 message = _(
1371 'When GNUmed cannot find an OpenOffice server it\n'
1372 'will try to start one. OpenOffice, however, needs\n'
1373 'some time to fully start up.\n'
1374 '\n'
1375 'Here you can set the time for GNUmed to wait for OOo.\n'
1376 ),
1377 option = 'external.ooo.startup_settle_time',
1378 bias = 'workplace',
1379 default_value = 2.0,
1380 validator = is_valid
1381 )
1382
1385
1400
1401 gmCfgWidgets.configure_string_option (
1402 message = _(
1403 'GNUmed will use this URL to access a website which lets\n'
1404 'you report an adverse drug reaction (ADR).\n'
1405 '\n'
1406 'If you leave this empty it will fall back\n'
1407 'to an URL for reporting ADRs in Germany.'
1408 ),
1409 option = 'external.urls.report_ADR',
1410 bias = 'user',
1411 default_value = german_default,
1412 validator = is_valid
1413 )
1414
1428
1429 gmCfgWidgets.configure_string_option (
1430 message = _(
1431 'GNUmed will use this URL to access a website which lets\n'
1432 'you report an adverse vaccination reaction (vADR).\n'
1433 '\n'
1434 'If you set it to a specific address that URL must be\n'
1435 'accessible now. If you leave it empty it will fall back\n'
1436 'to the URL for reporting other adverse drug reactions.'
1437 ),
1438 option = 'external.urls.report_vaccine_ADR',
1439 bias = 'user',
1440 default_value = german_default,
1441 validator = is_valid
1442 )
1443
1457
1458 gmCfgWidgets.configure_string_option (
1459 message = _(
1460 'GNUmed will use this URL to access an encyclopedia of\n'
1461 'measurement/lab methods from within the measurments grid.\n'
1462 '\n'
1463 'You can leave this empty but to set it to a specific\n'
1464 'address the URL must be accessible now.'
1465 ),
1466 option = 'external.urls.measurements_encyclopedia',
1467 bias = 'user',
1468 default_value = german_default,
1469 validator = is_valid
1470 )
1471
1485
1486 gmCfgWidgets.configure_string_option (
1487 message = _(
1488 'GNUmed will use this URL to access a page showing\n'
1489 'vaccination schedules.\n'
1490 '\n'
1491 'You can leave this empty but to set it to a specific\n'
1492 'address the URL must be accessible now.'
1493 ),
1494 option = 'external.urls.vaccination_plans',
1495 bias = 'user',
1496 default_value = german_default,
1497 validator = is_valid
1498 )
1499
1512
1513 gmCfgWidgets.configure_string_option (
1514 message = _(
1515 'Enter the shell command with which to start the\n'
1516 'the ACS risk assessment calculator.\n'
1517 '\n'
1518 'GNUmed will try to verify the path which may,\n'
1519 'however, fail if you are using an emulator such\n'
1520 'as Wine. Nevertheless, starting the calculator\n'
1521 'will work as long as the shell command is correct\n'
1522 'despite the failing test.'
1523 ),
1524 option = 'external.tools.acs_risk_calculator_cmd',
1525 bias = 'user',
1526 validator = is_valid
1527 )
1528
1531
1544
1545 gmCfgWidgets.configure_string_option (
1546 message = _(
1547 'Enter the shell command with which to start\n'
1548 'the FreeDiams drug database frontend.\n'
1549 '\n'
1550 'GNUmed will try to verify that path.'
1551 ),
1552 option = 'external.tools.freediams_cmd',
1553 bias = 'workplace',
1554 default_value = None,
1555 validator = is_valid
1556 )
1557
1570
1571 gmCfgWidgets.configure_string_option (
1572 message = _(
1573 'Enter the shell command with which to start the\n'
1574 'the IFAP drug database.\n'
1575 '\n'
1576 'GNUmed will try to verify the path which may,\n'
1577 'however, fail if you are using an emulator such\n'
1578 'as Wine. Nevertheless, starting IFAP will work\n'
1579 'as long as the shell command is correct despite\n'
1580 'the failing test.'
1581 ),
1582 option = 'external.ifap-win.shell_command',
1583 bias = 'workplace',
1584 default_value = 'C:\Ifapwin\WIAMDB.EXE',
1585 validator = is_valid
1586 )
1587
1588
1589
1638
1639
1640
1657
1660
1663
1668
1669 gmCfgWidgets.configure_string_option (
1670 message = _(
1671 'When a patient is activated GNUmed checks the\n'
1672 "proximity of the patient's birthday.\n"
1673 '\n'
1674 'If the birthday falls within the range of\n'
1675 ' "today %s <the interval you set here>"\n'
1676 'GNUmed will remind you of the recent or\n'
1677 'imminent anniversary.'
1678 ) % u'\u2213',
1679 option = u'patient_search.dob_warn_interval',
1680 bias = 'user',
1681 default_value = '1 week',
1682 validator = is_valid
1683 )
1684
1686
1687 gmCfgWidgets.configure_boolean_option (
1688 parent = self,
1689 question = _(
1690 'When adding progress notes do you want to\n'
1691 'allow opening several unassociated, new\n'
1692 'episodes for a patient at once ?\n'
1693 '\n'
1694 'This can be particularly helpful when entering\n'
1695 'progress notes on entirely new patients presenting\n'
1696 'with a multitude of problems on their first visit.'
1697 ),
1698 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
1699 button_tooltips = [
1700 _('Yes, allow for multiple new episodes concurrently.'),
1701 _('No, only allow editing one new episode at a time.')
1702 ]
1703 )
1704
1706
1707 gmCfgWidgets.configure_boolean_option (
1708 parent = self,
1709 question = _(
1710 'When activating a patient, do you want GNUmed to\n'
1711 'auto-open editors for all active problems that were\n'
1712 'touched upon during the current and the most recent\n'
1713 'encounter ?'
1714 ),
1715 option = u'horstspace.soap_editor.auto_open_latest_episodes',
1716 button_tooltips = [
1717 _('Yes, auto-open editors for all problems of the most recent encounter.'),
1718 _('No, only auto-open one editor for a new, unassociated problem.')
1719 ]
1720 )
1721
1767
1768
1769
1772
1775
1788
1789 gmCfgWidgets.configure_string_option (
1790 message = _(
1791 'GNUmed will use this URL to let you browse\n'
1792 'billing catalogs (schedules of fees).\n'
1793 '\n'
1794 'You can leave this empty but to set it to a specific\n'
1795 'address the URL must be accessible now.'
1796 ),
1797 option = 'external.urls.schedules_of_fees',
1798 bias = 'user',
1799 default_value = german_default,
1800 validator = is_valid
1801 )
1802
1803
1804
1807
1810
1812 gmCfgWidgets.configure_string_from_list_option (
1813 parent = self,
1814 message = _('Select the default prescription mode.\n'),
1815 option = 'horst_space.default_prescription_mode',
1816 bias = 'user',
1817 default_value = u'form',
1818 choices = [ _('Formular'), _('Datenbank') ],
1819 columns = [_('Prescription mode')],
1820 data = [ u'form', u'database' ]
1821 )
1822
1825
1828
1842
1844 gmCfgWidgets.configure_boolean_option (
1845 parent = self,
1846 question = _(
1847 'Do you want GNUmed to show the encounter\n'
1848 'details editor when changing the active patient ?'
1849 ),
1850 option = 'encounter.show_editor_before_patient_change',
1851 button_tooltips = [
1852 _('Yes, show the encounter editor if it seems appropriate.'),
1853 _('No, never show the encounter editor even if it would seem useful.')
1854 ]
1855 )
1856
1861
1862 gmCfgWidgets.configure_string_option (
1863 message = _(
1864 'When a patient is activated GNUmed checks the\n'
1865 'chart for encounters lacking any entries.\n'
1866 '\n'
1867 'Any such encounters older than what you set\n'
1868 'here will be removed from the medical record.\n'
1869 '\n'
1870 'To effectively disable removal of such encounters\n'
1871 'set this option to an improbable value.\n'
1872 ),
1873 option = 'encounter.ttl_if_empty',
1874 bias = 'user',
1875 default_value = '1 week',
1876 validator = is_valid
1877 )
1878
1883
1884 gmCfgWidgets.configure_string_option (
1885 message = _(
1886 'When a patient is activated GNUmed checks the\n'
1887 'age of the most recent encounter.\n'
1888 '\n'
1889 'If that encounter is younger than this age\n'
1890 'the existing encounter will be continued.\n'
1891 '\n'
1892 '(If it is really old a new encounter is\n'
1893 ' started, or else GNUmed will ask you.)\n'
1894 ),
1895 option = 'encounter.minimum_ttl',
1896 bias = 'user',
1897 default_value = '1 hour 30 minutes',
1898 validator = is_valid
1899 )
1900
1905
1906 gmCfgWidgets.configure_string_option (
1907 message = _(
1908 'When a patient is activated GNUmed checks the\n'
1909 'age of the most recent encounter.\n'
1910 '\n'
1911 'If that encounter is older than this age\n'
1912 'GNUmed will always start a new encounter.\n'
1913 '\n'
1914 '(If it is very recent the existing encounter\n'
1915 ' is continued, or else GNUmed will ask you.)\n'
1916 ),
1917 option = 'encounter.maximum_ttl',
1918 bias = 'user',
1919 default_value = '6 hours',
1920 validator = is_valid
1921 )
1922
1931
1932 gmCfgWidgets.configure_string_option (
1933 message = _(
1934 'At any time there can only be one open (ongoing)\n'
1935 'episode for each health issue.\n'
1936 '\n'
1937 'When you try to open (add data to) an episode on a health\n'
1938 'issue GNUmed will check for an existing open episode on\n'
1939 'that issue. If there is any it will check the age of that\n'
1940 'episode. The episode is closed if it has been dormant (no\n'
1941 'data added, that is) for the period of time (in days) you\n'
1942 'set here.\n'
1943 '\n'
1944 "If the existing episode hasn't been dormant long enough\n"
1945 'GNUmed will consult you what to do.\n'
1946 '\n'
1947 'Enter maximum episode dormancy in DAYS:'
1948 ),
1949 option = 'episode.ttl',
1950 bias = 'user',
1951 default_value = 60,
1952 validator = is_valid
1953 )
1954
1985
2000
2025
2037
2038 gmCfgWidgets.configure_string_option (
2039 message = _(
2040 'GNUmed can check for new releases being available. To do\n'
2041 'so it needs to load version information from an URL.\n'
2042 '\n'
2043 'The default URL is:\n'
2044 '\n'
2045 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n'
2046 '\n'
2047 'but you can configure any other URL locally. Note\n'
2048 'that you must enter the location as a valid URL.\n'
2049 'Depending on the URL the client will need online\n'
2050 'access when checking for updates.'
2051 ),
2052 option = u'horstspace.update.url',
2053 bias = u'workplace',
2054 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt',
2055 validator = is_valid
2056 )
2057
2075
2092
2109
2120
2121 gmCfgWidgets.configure_string_option (
2122 message = _(
2123 'GNUmed can show the document review dialog after\n'
2124 'calling the appropriate viewer for that document.\n'
2125 '\n'
2126 'Select the conditions under which you want\n'
2127 'GNUmed to do so:\n'
2128 '\n'
2129 ' 0: never display the review dialog\n'
2130 ' 1: always display the dialog\n'
2131 ' 2: only if there is no previous review by me\n'
2132 ' 3: only if there is no previous review at all\n'
2133 ' 4: only if there is no review by the responsible reviewer\n'
2134 '\n'
2135 'Note that if a viewer is configured to not block\n'
2136 'GNUmed during document display the review dialog\n'
2137 'will actually appear in parallel to the viewer.'
2138 ),
2139 option = u'horstspace.document_viewer.review_after_display',
2140 bias = u'user',
2141 default_value = 3,
2142 validator = is_valid
2143 )
2144
2146
2147
2148 master_data_lists = [
2149 'adr',
2150 'billables',
2151 'drugs',
2152 'hints',
2153 'codes',
2154 'communication_channel_types',
2155 'substances_in_brands',
2156 'substances',
2157 'labs',
2158 'form_templates',
2159 'doc_types',
2160 'enc_types',
2161 'text_expansions',
2162 'meta_test_types',
2163 'orgs',
2164 'patient_tags',
2165 'provinces',
2166 'db_translations',
2167 'ref_data_sources',
2168 'test_types',
2169 'test_panels',
2170 'vacc_indications',
2171 'vaccines',
2172 'workplaces'
2173 ]
2174
2175 master_data_list_names = {
2176 'adr': _('Addresses (likely slow)'),
2177 'drugs': _('Branded drugs (as marketed)'),
2178 'hints': _('Clinical hints'),
2179 'codes': _('Codes and their respective terms'),
2180 'communication_channel_types': _('Communication channel types'),
2181 'substances_in_brands': _('Components of branded drugs (substances in brands)'),
2182 'labs': _('Diagnostic organizations (path labs, ...)'),
2183 'form_templates': _('Document templates (forms, letters, plots, ...)'),
2184 'doc_types': _('Document types'),
2185 'enc_types': _('Encounter types'),
2186 'text_expansions': _('Keyword based text expansion macros'),
2187 'meta_test_types': _('Meta test/measurement types'),
2188 'orgs': _('Organizations with their units, addresses, and comm channels'),
2189 'patient_tags': _('Patient tags'),
2190 'provinces': _('Provinces (counties, territories, states, regions, ...)'),
2191 'db_translations': _('String translations in the database'),
2192 'test_types': _('Test/measurement types'),
2193 'vacc_indications': _('Vaccination targets (conditions known to be preventable by vaccination)'),
2194 'vaccines': _('Vaccines'),
2195 'workplaces': _('Workplace profiles (which plugins to load)'),
2196 'substances': _('Consumable substances'),
2197 'billables': _('Billable items'),
2198 'ref_data_sources': _('Reference data sources'),
2199 'test_panels': _('Test/measurement panels/profiles')
2200 }
2201
2202 map_list2handler = {
2203 'form_templates': gmFormWidgets.manage_form_templates,
2204 'doc_types': gmDocumentWidgets.manage_document_types,
2205 'text_expansions': gmKeywordExpansionWidgets.configure_keyword_text_expansion,
2206 'db_translations': gmI18nWidgets.manage_translations,
2207 'codes': gmCodingWidgets.browse_coded_terms,
2208 'enc_types': gmEMRStructWidgets.manage_encounter_types,
2209 'provinces': gmAddressWidgets.manage_provinces,
2210 'workplaces': gmProviderInboxWidgets.configure_workplace_plugins,
2211 'drugs': gmMedicationWidgets.manage_branded_drugs,
2212 'substances_in_brands': gmMedicationWidgets.manage_drug_components,
2213 'labs': gmMeasurementWidgets.manage_measurement_orgs,
2214 'test_types': gmMeasurementWidgets.manage_measurement_types,
2215 'meta_test_types': gmMeasurementWidgets.manage_meta_test_types,
2216 'vaccines': gmVaccWidgets.manage_vaccines,
2217 'vacc_indications': gmVaccWidgets.manage_vaccination_indications,
2218 'orgs': gmOrganizationWidgets.manage_orgs,
2219 'adr': gmAddressWidgets.manage_addresses,
2220 'substances': gmMedicationWidgets.manage_consumable_substances,
2221 'patient_tags': gmDemographicsWidgets.manage_tag_images,
2222 'communication_channel_types': gmContactWidgets.manage_comm_channel_types,
2223 'billables': gmBillingWidgets.manage_billables,
2224 'ref_data_sources': gmCodingWidgets.browse_data_sources,
2225 'hints': gmProviderInboxWidgets.browse_dynamic_hints,
2226 'test_panels': gmMeasurementWidgets.manage_test_panels
2227 }
2228
2229
2230 def edit(item):
2231 try: map_list2handler[item](parent = self)
2232 except KeyError: pass
2233 return False
2234
2235
2236 gmListWidgets.get_choices_from_list (
2237 parent = self,
2238 caption = _('Master data management'),
2239 choices = [ master_data_list_names[lst] for lst in master_data_lists],
2240 data = master_data_lists,
2241 columns = [_('Select the list you want to manage:')],
2242 edit_callback = edit,
2243 single_selection = True,
2244 ignore_OK_button = True
2245 )
2246
2248
2249 found, cmd = gmShellAPI.detect_external_binary(binary = 'ginkgocadx')
2250 if found:
2251 gmShellAPI.run_command_in_shell(cmd, blocking=False)
2252 return
2253
2254 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
2255 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking = False)
2256 return
2257
2258 for viewer in ['aeskulap', 'amide', 'dicomscope', 'xmedcon']:
2259 found, cmd = gmShellAPI.detect_external_binary(binary = viewer)
2260 if found:
2261 gmShellAPI.run_command_in_shell(cmd, blocking = False)
2262 return
2263
2264 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
2265
2267
2268 curr_pat = gmPerson.gmCurrentPatient()
2269
2270 arriba = gmArriba.cArriba()
2271 pat = gmTools.bool2subst(curr_pat.connected, curr_pat, None)
2272 if not arriba.run(patient = pat, debug = _cfg.get(option = 'debug')):
2273 return
2274
2275
2276 if curr_pat is None:
2277 return
2278
2279 if arriba.pdf_result is None:
2280 return
2281
2282 doc = gmDocumentWidgets.save_file_as_new_document (
2283 parent = self,
2284 filename = arriba.pdf_result,
2285 document_type = _('risk assessment')
2286 )
2287
2288 try: os.remove(arriba.pdf_result)
2289 except StandardError: _log.exception('cannot remove [%s]', arriba.pdf_result)
2290
2291 if doc is None:
2292 return
2293
2294 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2295 doc.save()
2296
2297 try:
2298 open(arriba.xml_result).close()
2299 part = doc.add_part(file = arriba.xml_result)
2300 except StandardError:
2301 _log.exception('error accessing [%s]', arriba.xml_result)
2302 gmDispatcher.send(signal = u'statustext', msg = _('[arriba] XML result not found in [%s]') % arriba.xml_result, beep = False)
2303
2304 if part is None:
2305 return
2306
2307 part['obj_comment'] = u'XML-Daten'
2308 part['filename'] = u'arriba-result.xml'
2309 part.save()
2310
2312
2313 dbcfg = gmCfg.cCfgSQL()
2314 cmd = dbcfg.get2 (
2315 option = u'external.tools.acs_risk_calculator_cmd',
2316 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2317 bias = 'user'
2318 )
2319
2320 if cmd is None:
2321 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True)
2322 return
2323
2324 cwd = os.path.expanduser(os.path.join('~', '.gnumed'))
2325 try:
2326 subprocess.check_call (
2327 args = (cmd,),
2328 close_fds = True,
2329 cwd = cwd
2330 )
2331 except (OSError, ValueError, subprocess.CalledProcessError):
2332 _log.exception('there was a problem executing [%s]', cmd)
2333 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True)
2334 return
2335
2336 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d')))
2337 for pdf in pdfs:
2338 try:
2339 open(pdf).close()
2340 except:
2341 _log.exception('error accessing [%s]', pdf)
2342 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the [arriba] result in [%s] !') % pdf, beep = True)
2343 continue
2344
2345 doc = gmDocumentWidgets.save_file_as_new_document (
2346 parent = self,
2347 filename = pdf,
2348 document_type = u'risk assessment'
2349 )
2350
2351 try:
2352 os.remove(pdf)
2353 except StandardError:
2354 _log.exception('cannot remove [%s]', pdf)
2355
2356 if doc is None:
2357 continue
2358 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2359 doc.save()
2360
2361 return
2362
2365
2368
2371
2373 dlg = gmSnellen.cSnellenCfgDlg()
2374 if dlg.ShowModal() != wx.ID_OK:
2375 return
2376
2377 frame = gmSnellen.cSnellenChart (
2378 width = dlg.vals[0],
2379 height = dlg.vals[1],
2380 alpha = dlg.vals[2],
2381 mirr = dlg.vals[3],
2382 parent = None
2383 )
2384 frame.CentreOnScreen(wx.BOTH)
2385
2386
2387 frame.Show(True)
2388
2389
2392
2395
2398
2399
2400
2404
2407
2408
2409
2411 wx.CallAfter(self.__save_screenshot)
2412 evt.Skip()
2413
2415
2416 time.sleep(0.5)
2417
2418 rect = self.GetRect()
2419
2420
2421 if sys.platform == 'linux2':
2422 client_x, client_y = self.ClientToScreen((0, 0))
2423 border_width = client_x - rect.x
2424 title_bar_height = client_y - rect.y
2425
2426 if self.GetMenuBar():
2427 title_bar_height /= 2
2428 rect.width += (border_width * 2)
2429 rect.height += title_bar_height + border_width
2430
2431 wdc = wx.ScreenDC()
2432 mdc = wx.MemoryDC()
2433 img = wx.EmptyBitmap(rect.width, rect.height)
2434 mdc.SelectObject(img)
2435 mdc.Blit (
2436 0, 0,
2437 rect.width, rect.height,
2438 wdc,
2439 rect.x, rect.y
2440 )
2441
2442
2443 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
2444 img.SaveFile(fname, wx.BITMAP_TYPE_PNG)
2445 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
2446
2448
2449 raise ValueError('raised ValueError to test exception handling')
2450
2452 raise gmExceptions.AccessDenied (
2453 _('[-9999]: <access violation test error>'),
2454 source = u'GNUmed code',
2455 code = -9999,
2456 details = _('This is a deliberate AcessDenied exception thrown to test the handling of access violations by means of a decorator.')
2457 )
2458
2459 @gmAccessPermissionWidgets.verify_minimum_required_role('admin', activity = _('testing access check for non-existant <admin> role'))
2461 raise gmExceptions.AccessDenied (
2462 _('[-9999]: <access violation test error>'),
2463 source = u'GNUmed code',
2464 code = -9999,
2465 details = _('This is a deliberate AcessDenied exception. You should not see this message because the role is checked in a decorator.')
2466 )
2467
2469 import wx.lib.inspection
2470 wx.lib.inspection.InspectionTool().Show()
2471
2474
2477
2480
2483
2490
2494
2497
2500
2507
2512
2514 name = os.path.basename(gmLog2._logfile_name)
2515 name, ext = os.path.splitext(name)
2516 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
2517 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs'))
2518
2519 dlg = wx.FileDialog (
2520 parent = self,
2521 message = _("Save current log as..."),
2522 defaultDir = new_path,
2523 defaultFile = new_name,
2524 wildcard = "%s (*.log)|*.log" % _("log files"),
2525 style = wx.SAVE
2526 )
2527 choice = dlg.ShowModal()
2528 new_name = dlg.GetPath()
2529 dlg.Destroy()
2530 if choice != wx.ID_OK:
2531 return True
2532
2533 _log.warning('syncing log file for backup to [%s]', new_name)
2534 gmLog2.flush()
2535 shutil.copy2(gmLog2._logfile_name, new_name)
2536 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2537
2540
2541
2542
2544 """This is the wx.EVT_CLOSE handler.
2545
2546 - framework still functional
2547 """
2548 _log.debug('gmTopLevelFrame.OnClose() start')
2549 self._clean_exit()
2550 self.Destroy()
2551 _log.debug('gmTopLevelFrame.OnClose() end')
2552 return True
2553
2559
2564
2572
2579
2586
2593
2603
2611
2619
2627
2635
2636 @gmAccessPermissionWidgets.verify_minimum_required_role('doctor', activity = _('manage vaccinations'))
2645
2654
2662
2669
2686
2689
2692
2694
2695 pat = gmPerson.gmCurrentPatient()
2696 if not pat.connected:
2697 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.'))
2698 return False
2699
2700 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
2701
2702 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname']))
2703 gmTools.mkdir(aDefDir)
2704
2705 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames'])
2706 dlg = wx.FileDialog (
2707 parent = self,
2708 message = _("Save patient's EMR journal as..."),
2709 defaultDir = aDefDir,
2710 defaultFile = fname,
2711 wildcard = aWildcard,
2712 style = wx.SAVE
2713 )
2714 choice = dlg.ShowModal()
2715 fname = dlg.GetPath()
2716 dlg.Destroy()
2717 if choice != wx.ID_OK:
2718 return True
2719
2720 _log.debug('exporting EMR journal to [%s]' % fname)
2721
2722 exporter = gmPatientExporter.cEMRJournalExporter()
2723
2724 wx.BeginBusyCursor()
2725 try:
2726 fname = exporter.export_to_file(filename = fname)
2727 except:
2728 wx.EndBusyCursor()
2729 gmGuiHelpers.gm_show_error (
2730 _('Error exporting patient EMR as chronological journal.'),
2731 _('EMR journal export')
2732 )
2733 raise
2734 wx.EndBusyCursor()
2735
2736 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False)
2737
2738 return True
2739
2746
2748 curr_pat = gmPerson.gmCurrentPatient()
2749 if not curr_pat.connected:
2750 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.'))
2751 return
2752
2753 tag = gmDemographicsWidgets.manage_tag_images(parent = self)
2754 if tag is None:
2755 return
2756
2757 tag = curr_pat.add_tag(tag['pk_tag_image'])
2758 msg = _('Edit the comment on tag [%s]') % tag['l10n_description']
2759 comment = wx.GetTextFromUser (
2760 message = msg,
2761 caption = _('Editing tag comment'),
2762 default_value = gmTools.coalesce(tag['comment'], u''),
2763 parent = self
2764 )
2765
2766 if comment == u'':
2767 return
2768
2769 if comment.strip() == tag['comment']:
2770 return
2771
2772 if comment == u' ':
2773 tag['comment'] = None
2774 else:
2775 tag['comment'] = comment.strip()
2776
2777 tag.save()
2778
2788
2790 curr_pat = gmPerson.gmCurrentPatient()
2791 if not curr_pat.connected:
2792 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.'))
2793 return False
2794
2795 enc = 'cp850'
2796 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt'))
2797 curr_pat.export_as_gdt(filename = fname, encoding = enc)
2798 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2799
2802
2805
2813
2821
2824
2831
2835
2838
2841
2844
2847
2852
2854 """Cleanup helper.
2855
2856 - should ALWAYS be called when this program is
2857 to be terminated
2858 - ANY code that should be executed before a
2859 regular shutdown should go in here
2860 - framework still functional
2861 """
2862 _log.debug('gmTopLevelFrame._clean_exit() start')
2863
2864
2865 listener = gmBackendListener.gmBackendListener()
2866 try:
2867 listener.shutdown()
2868 except:
2869 _log.exception('cannot stop backend notifications listener thread')
2870
2871
2872 if _scripting_listener is not None:
2873 try:
2874 _scripting_listener.shutdown()
2875 except:
2876 _log.exception('cannot stop scripting listener thread')
2877
2878
2879 self.clock_update_timer.Stop()
2880 gmTimer.shutdown()
2881 gmPhraseWheel.shutdown()
2882
2883
2884 for call_back in self.__pre_exit_callbacks:
2885 try:
2886 call_back()
2887 except:
2888 print "*** pre-exit callback failed ***"
2889 print call_back
2890 _log.exception('callback [%s] failed', call_back)
2891
2892
2893 gmDispatcher.send(u'application_closing')
2894
2895
2896 gmDispatcher.disconnect(self._on_set_statustext, 'statustext')
2897
2898
2899 curr_width, curr_height = self.GetClientSizeTuple()
2900 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height))
2901 dbcfg = gmCfg.cCfgSQL()
2902 dbcfg.set (
2903 option = 'main.window.width',
2904 value = curr_width,
2905 workplace = gmSurgery.gmCurrentPractice().active_workplace
2906 )
2907 dbcfg.set (
2908 option = 'main.window.height',
2909 value = curr_height,
2910 workplace = gmSurgery.gmCurrentPractice().active_workplace
2911 )
2912
2913 if _cfg.get(option = 'debug'):
2914 print '---=== GNUmed shutdown ===---'
2915 try:
2916 print _('You have to manually close this window to finalize shutting down GNUmed.')
2917 print _('This is so that you can inspect the console output at your leisure.')
2918 except UnicodeEncodeError:
2919 print 'You have to manually close this window to finalize shutting down GNUmed.'
2920 print 'This is so that you can inspect the console output at your leisure.'
2921 print '---=== GNUmed shutdown ===---'
2922
2923
2924 gmExceptionHandlingWidgets.uninstall_wx_exception_handler()
2925
2926
2927 import threading
2928 _log.debug("%s active threads", threading.activeCount())
2929 for t in threading.enumerate():
2930 _log.debug('thread %s', t)
2931
2932 _log.debug('gmTopLevelFrame._clean_exit() end')
2933
2934
2935
2937
2938 if _cfg.get(option = 'slave'):
2939 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % (
2940 _cfg.get(option = 'slave personality'),
2941 _cfg.get(option = 'xml-rpc port')
2942 )
2943 else:
2944 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2945
2947 """Update title of main window based on template.
2948
2949 This gives nice tooltips on iconified GNUmed instances.
2950
2951 User research indicates that in the title bar people want
2952 the date of birth, not the age, so please stick to this
2953 convention.
2954 """
2955 args = {}
2956
2957 pat = gmPerson.gmCurrentPatient()
2958 if pat.connected:
2959 args['pat'] = u'%s %s %s (%s) #%d' % (
2960 gmTools.coalesce(pat['title'], u'', u'%.4s'),
2961 pat['firstnames'],
2962 pat['lastnames'],
2963 pat.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()),
2964 pat['pk_identity']
2965 )
2966 else:
2967 args['pat'] = _('no patient')
2968
2969 args['prov'] = u'%s%s.%s' % (
2970 gmTools.coalesce(_provider['title'], u'', u'%s '),
2971 _provider['firstnames'][:1],
2972 _provider['lastnames']
2973 )
2974
2975 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace
2976
2977 self.SetTitle(self.__title_template % args)
2978
2979
2981 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
2982 sb.SetStatusWidths([-1, 225])
2983
2984 self.clock_update_timer = wx.PyTimer(self._cb_update_clock)
2985 self._cb_update_clock()
2986
2987 self.clock_update_timer.Start(milliseconds = 1000)
2988
2990 """Displays date and local time in the second slot of the status bar"""
2991 t = time.localtime(time.time())
2992 st = time.strftime('%Y %b %d %H:%M:%S', t).decode(gmI18N.get_encoding(), 'replace')
2993 self.SetStatusText(st, 1)
2994
2996 """Lock GNUmed client against unauthorized access"""
2997
2998
2999
3000 return
3001
3003 """Unlock the main notebook widgets
3004 As long as we are not logged into the database backend,
3005 all pages but the 'login' page of the main notebook widget
3006 are locked; i.e. not accessible by the user
3007 """
3008
3009
3010
3011
3012
3013 return
3014
3016 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
3017
3019
3021
3022 self.__starting_up = True
3023
3024 gmExceptionHandlingWidgets.install_wx_exception_handler()
3025 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version'))
3026
3027
3028 self.SetAppName(u'gnumed')
3029 self.SetVendorName(u'The GNUmed Development Community.')
3030 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3031 paths.init_paths(wx = wx, app_name = u'gnumed')
3032
3033 if not self.__setup_prefs_file():
3034 return False
3035
3036 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email)
3037
3038 self.__guibroker = gmGuiBroker.GuiBroker()
3039 self.__setup_platform()
3040
3041 if not self.__establish_backend_connection():
3042 return False
3043
3044 self.__update_workplace_list()
3045
3046 if not _cfg.get(option = 'skip-update-check'):
3047 self.__check_for_updates()
3048
3049 if _cfg.get(option = 'slave'):
3050 if not self.__setup_scripting_listener():
3051 return False
3052
3053
3054 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440))
3055 frame.CentreOnScreen(wx.BOTH)
3056 self.SetTopWindow(frame)
3057 frame.Show(True)
3058
3059 if _cfg.get(option = 'debug'):
3060 self.RedirectStdio()
3061 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window'))
3062
3063
3064 print '---=== GNUmed startup ===---'
3065 print _('redirecting STDOUT/STDERR to this log window')
3066 print '---=== GNUmed startup ===---'
3067
3068 self.__setup_user_activity_timer()
3069 self.__register_events()
3070
3071 wx.CallAfter(self._do_after_init)
3072
3073 return True
3074
3076 """Called internally by wxPython after EVT_CLOSE has been handled on last frame.
3077
3078 - after destroying all application windows and controls
3079 - before wx.Windows internal cleanup
3080 """
3081 _log.debug('gmApp.OnExit() start')
3082
3083 self.__shutdown_user_activity_timer()
3084
3085 if _cfg.get(option = 'debug'):
3086 self.RestoreStdio()
3087 sys.stdin = sys.__stdin__
3088 sys.stdout = sys.__stdout__
3089 sys.stderr = sys.__stderr__
3090
3091 _log.debug('gmApp.OnExit() end')
3092
3094 wx.Bell()
3095 wx.Bell()
3096 wx.Bell()
3097 _log.warning('unhandled event detected: QUERY_END_SESSION')
3098 _log.info('we should be saving ourselves from here')
3099 gmLog2.flush()
3100 print "unhandled event detected: QUERY_END_SESSION"
3101
3103 wx.Bell()
3104 wx.Bell()
3105 wx.Bell()
3106 _log.warning('unhandled event detected: END_SESSION')
3107 gmLog2.flush()
3108 print "unhandled event detected: END_SESSION"
3109
3120
3122 self.user_activity_detected = True
3123 evt.Skip()
3124
3126
3127 if self.user_activity_detected:
3128 self.elapsed_inactivity_slices = 0
3129 self.user_activity_detected = False
3130 self.elapsed_inactivity_slices += 1
3131 else:
3132 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices:
3133
3134 pass
3135
3136 self.user_activity_timer.Start(oneShot = True)
3137
3138
3139
3141 try:
3142 kwargs['originated_in_database']
3143 print '==> got notification from database "%s":' % kwargs['signal']
3144 except KeyError:
3145 print '==> received signal from client: "%s"' % kwargs['signal']
3146
3147 del kwargs['signal']
3148 for key in kwargs.keys():
3149 print ' [%s]: %s' % (key, kwargs[key])
3150
3157
3159 self.user_activity_detected = True
3160 self.elapsed_inactivity_slices = 0
3161
3162 self.max_user_inactivity_slices = 15
3163 self.user_activity_timer = gmTimer.cTimer (
3164 callback = self._on_user_activity_timer_expired,
3165 delay = 2000
3166 )
3167 self.user_activity_timer.Start(oneShot=True)
3168
3170 try:
3171 self.user_activity_timer.Stop()
3172 del self.user_activity_timer
3173 except:
3174 pass
3175
3177 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
3178 wx.EVT_END_SESSION(self, self._on_end_session)
3179
3180
3181
3182
3183
3184 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated)
3185
3186 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity)
3187 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
3188
3189 if _cfg.get(option = 'debug'):
3190 gmDispatcher.connect(receiver = self._signal_debugging_monitor)
3191 _log.debug('connected signal monitor')
3192
3208
3210 """Handle all the database related tasks necessary for startup."""
3211
3212
3213 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')])
3214
3215 from Gnumed.wxpython import gmAuthWidgets
3216 connected = gmAuthWidgets.connect_to_database (
3217 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')],
3218 require_version = not override
3219 )
3220 if not connected:
3221 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection")
3222 return False
3223
3224
3225 try:
3226 global _provider
3227 _provider = gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
3228 except ValueError:
3229 account = gmPG2.get_current_user()
3230 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account)
3231 msg = _(
3232 'The database account [%s] cannot be used as a\n'
3233 'staff member login for GNUmed. There was an\n'
3234 'error retrieving staff details for it.\n\n'
3235 'Please ask your administrator for help.\n'
3236 ) % account
3237 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions'))
3238 return False
3239
3240
3241 tmp = '%s%s %s (%s = %s)' % (
3242 gmTools.coalesce(_provider['title'], ''),
3243 _provider['firstnames'],
3244 _provider['lastnames'],
3245 _provider['short_alias'],
3246 _provider['db_user']
3247 )
3248 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp)
3249
3250
3251 surgery = gmSurgery.gmCurrentPractice()
3252 msg = surgery.db_logon_banner
3253 if msg.strip() != u'':
3254
3255 login = gmPG2.get_default_login()
3256 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % (
3257 login.database,
3258 gmTools.coalesce(login.host, u'localhost')
3259 ))
3260 msg = auth + msg + u'\n\n'
3261
3262 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3263 None,
3264
3265 -1,
3266 caption = _('Verifying database'),
3267 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '),
3268 button_defs = [
3269 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True},
3270 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False}
3271 ]
3272 )
3273 go_on = dlg.ShowModal()
3274 dlg.Destroy()
3275 if go_on != wx.ID_YES:
3276 _log.info('user decided to not connect to this database')
3277 return False
3278
3279
3280 self.__check_db_lang()
3281
3282 return True
3283
3297
3299 """Setup access to a config file for storing preferences."""
3300
3301 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3302
3303 candidates = []
3304 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')])
3305 if explicit_file is not None:
3306 candidates.append(explicit_file)
3307
3308 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf'))
3309 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf'))
3310 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf'))
3311
3312 prefs_file = None
3313 for candidate in candidates:
3314 try:
3315 open(candidate, 'a+').close()
3316 prefs_file = candidate
3317 break
3318 except IOError:
3319 continue
3320
3321 if prefs_file is None:
3322 msg = _(
3323 'Cannot find configuration file in any of:\n'
3324 '\n'
3325 ' %s\n'
3326 'You may need to use the comand line option\n'
3327 '\n'
3328 ' --conf-file=<FILE>'
3329 ) % '\n '.join(candidates)
3330 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files'))
3331 return False
3332
3333 _cfg.set_option(option = u'user_preferences_file', value = prefs_file)
3334 _log.info('user preferences file: %s', prefs_file)
3335
3336 return True
3337
3339
3340 from socket import error as SocketError
3341 from Gnumed.pycommon import gmScriptingListener
3342 from Gnumed.wxpython import gmMacro
3343
3344 slave_personality = gmTools.coalesce (
3345 _cfg.get (
3346 group = u'workplace',
3347 option = u'slave personality',
3348 source_order = [
3349 ('explicit', 'return'),
3350 ('workbase', 'return'),
3351 ('user', 'return'),
3352 ('system', 'return')
3353 ]
3354 ),
3355 u'gnumed-client'
3356 )
3357 _cfg.set_option(option = 'slave personality', value = slave_personality)
3358
3359
3360 port = int (
3361 gmTools.coalesce (
3362 _cfg.get (
3363 group = u'workplace',
3364 option = u'xml-rpc port',
3365 source_order = [
3366 ('explicit', 'return'),
3367 ('workbase', 'return'),
3368 ('user', 'return'),
3369 ('system', 'return')
3370 ]
3371 ),
3372 9999
3373 )
3374 )
3375 _cfg.set_option(option = 'xml-rpc port', value = port)
3376
3377 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality)
3378 global _scripting_listener
3379 try:
3380 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor)
3381 except SocketError, e:
3382 _log.exception('cannot start GNUmed XML-RPC server')
3383 gmGuiHelpers.gm_show_error (
3384 aMessage = (
3385 'Cannot start the GNUmed server:\n'
3386 '\n'
3387 ' [%s]'
3388 ) % e,
3389 aTitle = _('GNUmed startup')
3390 )
3391 return False
3392
3393 return True
3394
3415
3417 if gmI18N.system_locale is None or gmI18N.system_locale == '':
3418 _log.warning("system locale is undefined (probably meaning 'C')")
3419 return True
3420
3421
3422 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}])
3423 db_lang = rows[0]['lang']
3424
3425 if db_lang is None:
3426 _log.debug("database locale currently not set")
3427 msg = _(
3428 "There is no language selected in the database for user [%s].\n"
3429 "Your system language is currently set to [%s].\n\n"
3430 "Do you want to set the database language to '%s' ?\n\n"
3431 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale)
3432 checkbox_msg = _('Remember to ignore missing language')
3433 else:
3434 _log.debug("current database locale: [%s]" % db_lang)
3435 msg = _(
3436 "The currently selected database language ('%s') does\n"
3437 "not match the current system language ('%s').\n"
3438 "\n"
3439 "Do you want to set the database language to '%s' ?\n"
3440 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale)
3441 checkbox_msg = _('Remember to ignore language mismatch')
3442
3443
3444 if db_lang == gmI18N.system_locale_level['full']:
3445 _log.debug('Database locale (%s) up to date.' % db_lang)
3446 return True
3447 if db_lang == gmI18N.system_locale_level['country']:
3448 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale))
3449 return True
3450 if db_lang == gmI18N.system_locale_level['language']:
3451 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale))
3452 return True
3453
3454 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale))
3455
3456
3457 ignored_sys_lang = _cfg.get (
3458 group = u'backend',
3459 option = u'ignored mismatching system locale',
3460 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')]
3461 )
3462
3463
3464 if gmI18N.system_locale == ignored_sys_lang:
3465 _log.info('configured to ignore system-to-database locale mismatch')
3466 return True
3467
3468
3469 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3470 None,
3471 -1,
3472 caption = _('Checking database language settings'),
3473 question = msg,
3474 button_defs = [
3475 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True},
3476 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False}
3477 ],
3478 show_checkbox = True,
3479 checkbox_msg = checkbox_msg,
3480 checkbox_tooltip = _(
3481 'Checking this will make GNUmed remember your decision\n'
3482 'until the system language is changed.\n'
3483 '\n'
3484 'You can also reactivate this inquiry by removing the\n'
3485 'corresponding "ignore" option from the configuration file\n'
3486 '\n'
3487 ' [%s]'
3488 ) % _cfg.get(option = 'user_preferences_file')
3489 )
3490 decision = dlg.ShowModal()
3491 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue()
3492 dlg.Destroy()
3493
3494 if decision == wx.ID_NO:
3495 if not remember_ignoring_problem:
3496 return True
3497 _log.info('User did not want to set database locale. Ignoring mismatch next time.')
3498 gmCfg2.set_option_in_INI_file (
3499 filename = _cfg.get(option = 'user_preferences_file'),
3500 group = 'backend',
3501 option = 'ignored mismatching system locale',
3502 value = gmI18N.system_locale
3503 )
3504 return True
3505
3506
3507 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]:
3508 if len(lang) > 0:
3509
3510
3511 rows, idx = gmPG2.run_rw_queries (
3512 link_obj = None,
3513 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}],
3514 return_data = True
3515 )
3516 if rows[0][0]:
3517 _log.debug("Successfully set database language to [%s]." % lang)
3518 else:
3519 _log.error('Cannot set database language to [%s].' % lang)
3520 continue
3521 return True
3522
3523
3524 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country'])
3525 gmPG2.run_rw_queries(queries = [{
3526 'cmd': u'select i18n.force_curr_lang(%s)',
3527 'args': [gmI18N.system_locale_level['country']]
3528 }])
3529
3530 return True
3531
3533 try:
3534 kwargs['originated_in_database']
3535 print '==> got notification from database "%s":' % kwargs['signal']
3536 except KeyError:
3537 print '==> received signal from client: "%s"' % kwargs['signal']
3538
3539 del kwargs['signal']
3540 for key in kwargs.keys():
3541
3542 try: print ' [%s]: %s' % (key, kwargs[key])
3543 except: print 'cannot print signal information'
3544
3548
3559
3561
3562 if _cfg.get(option = 'debug'):
3563 gmDispatcher.connect(receiver = _signal_debugging_monitor)
3564 _log.debug('gmDispatcher signal monitor activated')
3565
3566 setup_safe_wxEndBusyCursor()
3567
3568 wx.InitAllImageHandlers()
3569
3570
3571
3572 app = gmApp(redirect = False, clearSigInt = False)
3573 app.MainLoop()
3574
3575
3576
3577 if __name__ == '__main__':
3578
3579 from GNUmed.pycommon import gmI18N
3580 gmI18N.activate_locale()
3581 gmI18N.install_domain()
3582
3583 _log.info('Starting up as main module.')
3584 main()
3585
3586
3587