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 self.menu_tools.AppendSeparator()
759
760 self.mainmenu.Append(self.menu_tools, _("&Tools"))
761 self.__gb['main.toolsmenu'] = self.menu_tools
762
763
764 menu_knowledge = wx.Menu()
765
766
767 menu_drug_dbs = wx.Menu()
768
769 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.'))
770 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item)
771
772
773
774
775
776
777 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs)
778
779 menu_id = wx.NewId()
780 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)'))
781 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch)
782
783
784
785
786 ID_MEDICAL_LINKS = wx.NewId()
787 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.'))
788 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links)
789
790 self.mainmenu.Append(menu_knowledge, _('&Knowledge'))
791 self.__gb['main.knowledgemenu'] = menu_knowledge
792
793
794 self.menu_office = wx.Menu()
795
796 item = self.menu_office.Append(-1, _('Audit trail'), _('Display database audit trail.'))
797 self.Bind(wx.EVT_MENU, self.__on_display_audit_trail, item)
798
799 self.menu_office.AppendSeparator()
800
801 item = self.menu_office.Append(-1, _('List bills'), _('List all bills across all patients.'))
802 self.Bind(wx.EVT_MENU, self.__on_show_all_bills, item)
803
804 self.mainmenu.Append(self.menu_office, _('&Office'))
805 self.__gb['main.officemenu'] = self.menu_office
806
807
808 help_menu = wx.Menu()
809
810 ID = wx.NewId()
811 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.'))
812 wx.EVT_MENU(self, ID, self.__on_display_wiki)
813
814 ID = wx.NewId()
815 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.'))
816 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online)
817
818 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.'))
819 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item)
820
821 item = help_menu.Append(-1, _('&Clear status line'), _('Clear out the status line.'))
822 self.Bind(wx.EVT_MENU, self.__on_clear_status_line, item)
823
824 menu_debugging = wx.Menu()
825
826 ID_SCREENSHOT = wx.NewId()
827 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.'))
828 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot)
829
830 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.'))
831 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item)
832
833 ID = wx.NewId()
834 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.'))
835 wx.EVT_MENU(self, ID, self.__on_backup_log_file)
836
837 item = menu_debugging.Append(-1, _('Email log file'), _('Send the log file to the authors for help.'))
838 self.Bind(wx.EVT_MENU, self.__on_email_log_file, item)
839
840 ID = wx.NewId()
841 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.'))
842 wx.EVT_MENU(self, ID, self.__on_display_bugtracker)
843
844 ID_UNBLOCK = wx.NewId()
845 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.'))
846 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor)
847
848 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.'))
849 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item)
850
851
852
853
854 if _cfg.get(option = 'debug'):
855 ID_TOGGLE_PAT_LOCK = wx.NewId()
856 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient search'), _('Lock/unlock patient search - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !'))
857 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock)
858
859 ID_TEST_EXCEPTION = wx.NewId()
860 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.'))
861 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception)
862
863 item = menu_debugging.Append(-1, _('Test access violation exception'), _('Simulate an access violation exception.'))
864 self.Bind(wx.EVT_MENU, self.__on_test_access_violation, item)
865
866 item = menu_debugging.Append(-1, _('Test access checking'), _('Simulate a failing access check.'))
867 self.Bind(wx.EVT_MENU, self.__on_test_access_checking, item)
868
869 ID = wx.NewId()
870 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).'))
871 wx.EVT_MENU(self, ID, self.__on_invoke_inspector)
872 try:
873 import wx.lib.inspection
874 except ImportError:
875 menu_debugging.Enable(id = ID, enable = False)
876
877 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging)
878
879 help_menu.AppendSeparator()
880
881 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "")
882 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout)
883
884 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.'))
885 self.Bind(wx.EVT_MENU, self.__on_about_database, item)
886
887 item = help_menu.Append(-1, _('About contributors'), _('Show GNUmed contributors'))
888 self.Bind(wx.EVT_MENU, self.__on_show_contributors, item)
889
890 help_menu.AppendSeparator()
891
892 self.mainmenu.Append(help_menu, _("&Help"))
893
894 self.__gb['main.helpmenu'] = help_menu
895
896
897 self.SetMenuBar(self.mainmenu)
898
901
902
903
905 """register events we want to react to"""
906
907 wx.EVT_CLOSE(self, self.OnClose)
908 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
909 wx.EVT_END_SESSION(self, self._on_end_session)
910
911 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
912 gmDispatcher.connect(signal = u'name_mod_db', receiver = self._on_pat_name_changed)
913 gmDispatcher.connect(signal = u'identity_mod_db', receiver = self._on_pat_name_changed)
914 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext)
915 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention)
916 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning)
917 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback)
918 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded)
919
920 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
921
922 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
923
924 _log.debug('registering plugin with menu system')
925 _log.debug(' generic name: %s', plugin_name)
926 _log.debug(' class name: %s', class_name)
927 _log.debug(' specific menu: %s', menu_name)
928 _log.debug(' menu item: %s', menu_item_name)
929
930
931 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name)
932 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
933 self.menu_id2plugin[item.Id] = class_name
934
935
936 if menu_name is not None:
937 menu = self.__gb['main.%smenu' % menu_name]
938 item = menu.Append(-1, menu_item_name, menu_help_string)
939 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
940 self.menu_id2plugin[item.Id] = class_name
941
942 return True
943
945 gmDispatcher.send (
946 signal = u'display_widget',
947 name = self.menu_id2plugin[evt.Id]
948 )
949
951 wx.Bell()
952 wx.Bell()
953 wx.Bell()
954 _log.warning('unhandled event detected: QUERY_END_SESSION')
955 _log.info('we should be saving ourselves from here')
956 gmLog2.flush()
957 print "unhandled event detected: QUERY_END_SESSION"
958
960 wx.Bell()
961 wx.Bell()
962 wx.Bell()
963 _log.warning('unhandled event detected: END_SESSION')
964 gmLog2.flush()
965 print "unhandled event detected: END_SESSION"
966
968 if not callable(callback):
969 raise TypeError(u'callback [%s] not callable' % callback)
970
971 self.__pre_exit_callbacks.append(callback)
972
973 - def _on_set_statustext_pubsub(self, context=None):
974 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg'])
975 wx.CallAfter(self.SetStatusText, msg)
976
977 try:
978 if context.data['beep']:
979 wx.Bell()
980 except KeyError:
981 pass
982
983 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
984
985 if msg is None:
986 msg = _('programmer forgot to specify status message')
987
988 if loglevel is not None:
989 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' '))
990
991 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg)
992 wx.CallAfter(self.SetStatusText, msg)
993
994 if beep:
995 wx.Bell()
996
998 wx.CallAfter(self.__on_db_maintenance_warning)
999
1001
1002 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.'))
1003 wx.Bell()
1004 if not wx.GetApp().IsActive():
1005 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1006
1007 gmHooks.run_hook_script(hook = u'db_maintenance_warning')
1008
1009 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
1010 None,
1011 -1,
1012 caption = _('Database shutdown warning'),
1013 question = _(
1014 'The database will be shut down for maintenance\n'
1015 'in a few minutes.\n'
1016 '\n'
1017 'In order to not suffer any loss of data you\n'
1018 'will need to save your current work and log\n'
1019 'out of this GNUmed client.\n'
1020 ),
1021 button_defs = [
1022 {
1023 u'label': _('Close now'),
1024 u'tooltip': _('Close this GNUmed client immediately.'),
1025 u'default': False
1026 },
1027 {
1028 u'label': _('Finish work'),
1029 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'),
1030 u'default': True
1031 }
1032 ]
1033 )
1034 decision = dlg.ShowModal()
1035 if decision == wx.ID_YES:
1036 top_win = wx.GetApp().GetTopWindow()
1037 wx.CallAfter(top_win.Close)
1038
1040 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
1041
1043
1044 if not wx.GetApp().IsActive():
1045 if urgent:
1046 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1047 else:
1048 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO)
1049
1050 if msg is not None:
1051 self.SetStatusText(msg)
1052
1053 if urgent:
1054 wx.Bell()
1055
1056 gmHooks.run_hook_script(hook = u'request_user_attention')
1057
1059 wx.CallAfter(self.__on_pat_name_changed)
1060
1062 self.__update_window_title()
1063
1064 - def _on_post_patient_selection(self, **kwargs):
1065 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
1066
1068 self.__update_window_title()
1069 gmDispatcher.send(signal = 'statustext', msg = u'')
1070 try:
1071 gmHooks.run_hook_script(hook = u'post_patient_activation')
1072 except:
1073 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.'))
1074 raise
1075
1077 return self.__sanity_check_encounter()
1078
1140
1141
1142
1145
1152
1156
1157
1158
1174
1199
1201 from Gnumed.wxpython import gmAbout
1202 contribs = gmAbout.cContributorsDlg (
1203 parent = self,
1204 id = -1,
1205 title = _('GNUmed contributors'),
1206 size = wx.Size(400,600),
1207 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1208 )
1209 contribs.ShowModal()
1210 del contribs
1211 del gmAbout
1212
1213
1214
1216 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler)."""
1217 _log.debug('gmTopLevelFrame._on_exit_gnumed() start')
1218 self.Close(True)
1219 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1220
1223
1225 send = gmGuiHelpers.gm_show_question (
1226 _('This will send a notification about database downtime\n'
1227 'to all GNUmed clients connected to your database.\n'
1228 '\n'
1229 'Do you want to send the notification ?\n'
1230 ),
1231 _('Announcing database maintenance downtime')
1232 )
1233 if not send:
1234 return
1235 gmPG2.send_maintenance_notification()
1236
1237
1240
1241
1242
1255
1256 gmCfgWidgets.configure_string_option (
1257 message = _(
1258 'Some network installations cannot cope with loading\n'
1259 'documents of arbitrary size in one piece from the\n'
1260 'database (mainly observed on older Windows versions)\n.'
1261 '\n'
1262 'Under such circumstances documents need to be retrieved\n'
1263 'in chunks and reassembled on the client.\n'
1264 '\n'
1265 'Here you can set the size (in Bytes) above which\n'
1266 'GNUmed will retrieve documents in chunks. Setting this\n'
1267 'value to 0 will disable the chunking protocol.'
1268 ),
1269 option = 'horstspace.blob_export_chunk_size',
1270 bias = 'workplace',
1271 default_value = 1024 * 1024,
1272 validator = is_valid
1273 )
1274
1275
1276
1344
1348
1349
1350
1359
1360 gmCfgWidgets.configure_string_option (
1361 message = _(
1362 'When GNUmed cannot find an OpenOffice server it\n'
1363 'will try to start one. OpenOffice, however, needs\n'
1364 'some time to fully start up.\n'
1365 '\n'
1366 'Here you can set the time for GNUmed to wait for OOo.\n'
1367 ),
1368 option = 'external.ooo.startup_settle_time',
1369 bias = 'workplace',
1370 default_value = 2.0,
1371 validator = is_valid
1372 )
1373
1376
1391
1392 gmCfgWidgets.configure_string_option (
1393 message = _(
1394 'GNUmed will use this URL to access a website which lets\n'
1395 'you report an adverse drug reaction (ADR).\n'
1396 '\n'
1397 'If you leave this empty it will fall back\n'
1398 'to an URL for reporting ADRs in Germany.'
1399 ),
1400 option = 'external.urls.report_ADR',
1401 bias = 'user',
1402 default_value = german_default,
1403 validator = is_valid
1404 )
1405
1419
1420 gmCfgWidgets.configure_string_option (
1421 message = _(
1422 'GNUmed will use this URL to access a website which lets\n'
1423 'you report an adverse vaccination reaction (vADR).\n'
1424 '\n'
1425 'If you set it to a specific address that URL must be\n'
1426 'accessible now. If you leave it empty it will fall back\n'
1427 'to the URL for reporting other adverse drug reactions.'
1428 ),
1429 option = 'external.urls.report_vaccine_ADR',
1430 bias = 'user',
1431 default_value = german_default,
1432 validator = is_valid
1433 )
1434
1448
1449 gmCfgWidgets.configure_string_option (
1450 message = _(
1451 'GNUmed will use this URL to access an encyclopedia of\n'
1452 'measurement/lab methods from within the measurments grid.\n'
1453 '\n'
1454 'You can leave this empty but to set it to a specific\n'
1455 'address the URL must be accessible now.'
1456 ),
1457 option = 'external.urls.measurements_encyclopedia',
1458 bias = 'user',
1459 default_value = german_default,
1460 validator = is_valid
1461 )
1462
1476
1477 gmCfgWidgets.configure_string_option (
1478 message = _(
1479 'GNUmed will use this URL to access a page showing\n'
1480 'vaccination schedules.\n'
1481 '\n'
1482 'You can leave this empty but to set it to a specific\n'
1483 'address the URL must be accessible now.'
1484 ),
1485 option = 'external.urls.vaccination_plans',
1486 bias = 'user',
1487 default_value = german_default,
1488 validator = is_valid
1489 )
1490
1503
1504 gmCfgWidgets.configure_string_option (
1505 message = _(
1506 'Enter the shell command with which to start the\n'
1507 'the ACS risk assessment calculator.\n'
1508 '\n'
1509 'GNUmed will try to verify the path which may,\n'
1510 'however, fail if you are using an emulator such\n'
1511 'as Wine. Nevertheless, starting the calculator\n'
1512 'will work as long as the shell command is correct\n'
1513 'despite the failing test.'
1514 ),
1515 option = 'external.tools.acs_risk_calculator_cmd',
1516 bias = 'user',
1517 validator = is_valid
1518 )
1519
1522
1535
1536 gmCfgWidgets.configure_string_option (
1537 message = _(
1538 'Enter the shell command with which to start\n'
1539 'the FreeDiams drug database frontend.\n'
1540 '\n'
1541 'GNUmed will try to verify that path.'
1542 ),
1543 option = 'external.tools.freediams_cmd',
1544 bias = 'workplace',
1545 default_value = None,
1546 validator = is_valid
1547 )
1548
1561
1562 gmCfgWidgets.configure_string_option (
1563 message = _(
1564 'Enter the shell command with which to start the\n'
1565 'the IFAP drug database.\n'
1566 '\n'
1567 'GNUmed will try to verify the path which may,\n'
1568 'however, fail if you are using an emulator such\n'
1569 'as Wine. Nevertheless, starting IFAP will work\n'
1570 'as long as the shell command is correct despite\n'
1571 'the failing test.'
1572 ),
1573 option = 'external.ifap-win.shell_command',
1574 bias = 'workplace',
1575 default_value = 'C:\Ifapwin\WIAMDB.EXE',
1576 validator = is_valid
1577 )
1578
1579
1580
1629
1630
1631
1648
1651
1654
1659
1660 gmCfgWidgets.configure_string_option (
1661 message = _(
1662 'When a patient is activated GNUmed checks the\n'
1663 "proximity of the patient's birthday.\n"
1664 '\n'
1665 'If the birthday falls within the range of\n'
1666 ' "today %s <the interval you set here>"\n'
1667 'GNUmed will remind you of the recent or\n'
1668 'imminent anniversary.'
1669 ) % u'\u2213',
1670 option = u'patient_search.dob_warn_interval',
1671 bias = 'user',
1672 default_value = '1 week',
1673 validator = is_valid
1674 )
1675
1677
1678 gmCfgWidgets.configure_boolean_option (
1679 parent = self,
1680 question = _(
1681 'When adding progress notes do you want to\n'
1682 'allow opening several unassociated, new\n'
1683 'episodes for a patient at once ?\n'
1684 '\n'
1685 'This can be particularly helpful when entering\n'
1686 'progress notes on entirely new patients presenting\n'
1687 'with a multitude of problems on their first visit.'
1688 ),
1689 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
1690 button_tooltips = [
1691 _('Yes, allow for multiple new episodes concurrently.'),
1692 _('No, only allow editing one new episode at a time.')
1693 ]
1694 )
1695
1697
1698 gmCfgWidgets.configure_boolean_option (
1699 parent = self,
1700 question = _(
1701 'When activating a patient, do you want GNUmed to\n'
1702 'auto-open editors for all active problems that were\n'
1703 'touched upon during the current and the most recent\n'
1704 'encounter ?'
1705 ),
1706 option = u'horstspace.soap_editor.auto_open_latest_episodes',
1707 button_tooltips = [
1708 _('Yes, auto-open editors for all problems of the most recent encounter.'),
1709 _('No, only auto-open one editor for a new, unassociated problem.')
1710 ]
1711 )
1712
1758
1759
1760
1763
1766
1779
1780 gmCfgWidgets.configure_string_option (
1781 message = _(
1782 'GNUmed will use this URL to let you browse\n'
1783 'billing catalogs (schedules of fees).\n'
1784 '\n'
1785 'You can leave this empty but to set it to a specific\n'
1786 'address the URL must be accessible now.'
1787 ),
1788 option = 'external.urls.schedules_of_fees',
1789 bias = 'user',
1790 default_value = german_default,
1791 validator = is_valid
1792 )
1793
1794
1795
1798
1801
1803 gmCfgWidgets.configure_string_from_list_option (
1804 parent = self,
1805 message = _('Select the default prescription mode.\n'),
1806 option = 'horst_space.default_prescription_mode',
1807 bias = 'user',
1808 default_value = u'form',
1809 choices = [ _('Formular'), _('Datenbank') ],
1810 columns = [_('Prescription mode')],
1811 data = [ u'form', u'database' ]
1812 )
1813
1816
1819
1833
1835 gmCfgWidgets.configure_boolean_option (
1836 parent = self,
1837 question = _(
1838 'Do you want GNUmed to show the encounter\n'
1839 'details editor when changing the active patient ?'
1840 ),
1841 option = 'encounter.show_editor_before_patient_change',
1842 button_tooltips = [
1843 _('Yes, show the encounter editor if it seems appropriate.'),
1844 _('No, never show the encounter editor even if it would seem useful.')
1845 ]
1846 )
1847
1852
1853 gmCfgWidgets.configure_string_option (
1854 message = _(
1855 'When a patient is activated GNUmed checks the\n'
1856 'chart for encounters lacking any entries.\n'
1857 '\n'
1858 'Any such encounters older than what you set\n'
1859 'here will be removed from the medical record.\n'
1860 '\n'
1861 'To effectively disable removal of such encounters\n'
1862 'set this option to an improbable value.\n'
1863 ),
1864 option = 'encounter.ttl_if_empty',
1865 bias = 'user',
1866 default_value = '1 week',
1867 validator = is_valid
1868 )
1869
1874
1875 gmCfgWidgets.configure_string_option (
1876 message = _(
1877 'When a patient is activated GNUmed checks the\n'
1878 'age of the most recent encounter.\n'
1879 '\n'
1880 'If that encounter is younger than this age\n'
1881 'the existing encounter will be continued.\n'
1882 '\n'
1883 '(If it is really old a new encounter is\n'
1884 ' started, or else GNUmed will ask you.)\n'
1885 ),
1886 option = 'encounter.minimum_ttl',
1887 bias = 'user',
1888 default_value = '1 hour 30 minutes',
1889 validator = is_valid
1890 )
1891
1896
1897 gmCfgWidgets.configure_string_option (
1898 message = _(
1899 'When a patient is activated GNUmed checks the\n'
1900 'age of the most recent encounter.\n'
1901 '\n'
1902 'If that encounter is older than this age\n'
1903 'GNUmed will always start a new encounter.\n'
1904 '\n'
1905 '(If it is very recent the existing encounter\n'
1906 ' is continued, or else GNUmed will ask you.)\n'
1907 ),
1908 option = 'encounter.maximum_ttl',
1909 bias = 'user',
1910 default_value = '6 hours',
1911 validator = is_valid
1912 )
1913
1922
1923 gmCfgWidgets.configure_string_option (
1924 message = _(
1925 'At any time there can only be one open (ongoing)\n'
1926 'episode for each health issue.\n'
1927 '\n'
1928 'When you try to open (add data to) an episode on a health\n'
1929 'issue GNUmed will check for an existing open episode on\n'
1930 'that issue. If there is any it will check the age of that\n'
1931 'episode. The episode is closed if it has been dormant (no\n'
1932 'data added, that is) for the period of time (in days) you\n'
1933 'set here.\n'
1934 '\n'
1935 "If the existing episode hasn't been dormant long enough\n"
1936 'GNUmed will consult you what to do.\n'
1937 '\n'
1938 'Enter maximum episode dormancy in DAYS:'
1939 ),
1940 option = 'episode.ttl',
1941 bias = 'user',
1942 default_value = 60,
1943 validator = is_valid
1944 )
1945
1976
1991
2016
2028
2029 gmCfgWidgets.configure_string_option (
2030 message = _(
2031 'GNUmed can check for new releases being available. To do\n'
2032 'so it needs to load version information from an URL.\n'
2033 '\n'
2034 'The default URL is:\n'
2035 '\n'
2036 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n'
2037 '\n'
2038 'but you can configure any other URL locally. Note\n'
2039 'that you must enter the location as a valid URL.\n'
2040 'Depending on the URL the client will need online\n'
2041 'access when checking for updates.'
2042 ),
2043 option = u'horstspace.update.url',
2044 bias = u'workplace',
2045 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt',
2046 validator = is_valid
2047 )
2048
2066
2083
2100
2111
2112 gmCfgWidgets.configure_string_option (
2113 message = _(
2114 'GNUmed can show the document review dialog after\n'
2115 'calling the appropriate viewer for that document.\n'
2116 '\n'
2117 'Select the conditions under which you want\n'
2118 'GNUmed to do so:\n'
2119 '\n'
2120 ' 0: never display the review dialog\n'
2121 ' 1: always display the dialog\n'
2122 ' 2: only if there is no previous review by me\n'
2123 ' 3: only if there is no previous review at all\n'
2124 ' 4: only if there is no review by the responsible reviewer\n'
2125 '\n'
2126 'Note that if a viewer is configured to not block\n'
2127 'GNUmed during document display the review dialog\n'
2128 'will actually appear in parallel to the viewer.'
2129 ),
2130 option = u'horstspace.document_viewer.review_after_display',
2131 bias = u'user',
2132 default_value = 3,
2133 validator = is_valid
2134 )
2135
2137
2138
2139 master_data_lists = [
2140 'adr',
2141 'billables',
2142 'drugs',
2143 'hints',
2144 'codes',
2145 'communication_channel_types',
2146 'substances_in_brands',
2147 'substances',
2148 'labs',
2149 'form_templates',
2150 'doc_types',
2151 'enc_types',
2152 'text_expansions',
2153 'meta_test_types',
2154 'orgs',
2155 'patient_tags',
2156 'provinces',
2157 'db_translations',
2158 'ref_data_sources',
2159 'test_types',
2160 'test_panels',
2161 'vacc_indications',
2162 'vaccines',
2163 'workplaces'
2164 ]
2165
2166 master_data_list_names = {
2167 'adr': _('Addresses (likely slow)'),
2168 'drugs': _('Branded drugs (as marketed)'),
2169 'hints': _('Clinical hints'),
2170 'codes': _('Codes and their respective terms'),
2171 'communication_channel_types': _('Communication channel types'),
2172 'substances_in_brands': _('Components of branded drugs (substances in brands)'),
2173 'labs': _('Diagnostic organizations (path labs, ...)'),
2174 'form_templates': _('Document templates (forms, letters, plots, ...)'),
2175 'doc_types': _('Document types'),
2176 'enc_types': _('Encounter types'),
2177 'text_expansions': _('Keyword based text expansion macros'),
2178 'meta_test_types': _('Meta test/measurement types'),
2179 'orgs': _('Organizations with their units, addresses, and comm channels'),
2180 'patient_tags': _('Patient tags'),
2181 'provinces': _('Provinces (counties, territories, states, regions, ...)'),
2182 'db_translations': _('String translations in the database'),
2183 'test_types': _('Test/measurement types'),
2184 'vacc_indications': _('Vaccination targets (conditions known to be preventable by vaccination)'),
2185 'vaccines': _('Vaccines'),
2186 'workplaces': _('Workplace profiles (which plugins to load)'),
2187 'substances': _('Consumable substances'),
2188 'billables': _('Billable items'),
2189 'ref_data_sources': _('Reference data sources'),
2190 'test_panels': _('Test/measurement panels/profiles')
2191 }
2192
2193 map_list2handler = {
2194 'form_templates': gmFormWidgets.manage_form_templates,
2195 'doc_types': gmDocumentWidgets.manage_document_types,
2196 'text_expansions': gmKeywordExpansionWidgets.configure_keyword_text_expansion,
2197 'db_translations': gmI18nWidgets.manage_translations,
2198 'codes': gmCodingWidgets.browse_coded_terms,
2199 'enc_types': gmEMRStructWidgets.manage_encounter_types,
2200 'provinces': gmAddressWidgets.manage_provinces,
2201 'workplaces': gmProviderInboxWidgets.configure_workplace_plugins,
2202 'drugs': gmMedicationWidgets.manage_branded_drugs,
2203 'substances_in_brands': gmMedicationWidgets.manage_drug_components,
2204 'labs': gmMeasurementWidgets.manage_measurement_orgs,
2205 'test_types': gmMeasurementWidgets.manage_measurement_types,
2206 'meta_test_types': gmMeasurementWidgets.manage_meta_test_types,
2207 'vaccines': gmVaccWidgets.manage_vaccines,
2208 'vacc_indications': gmVaccWidgets.manage_vaccination_indications,
2209 'orgs': gmOrganizationWidgets.manage_orgs,
2210 'adr': gmAddressWidgets.manage_addresses,
2211 'substances': gmMedicationWidgets.manage_consumable_substances,
2212 'patient_tags': gmDemographicsWidgets.manage_tag_images,
2213 'communication_channel_types': gmContactWidgets.manage_comm_channel_types,
2214 'billables': gmBillingWidgets.manage_billables,
2215 'ref_data_sources': gmCodingWidgets.browse_data_sources,
2216 'hints': gmProviderInboxWidgets.browse_dynamic_hints,
2217 'test_panels': gmMeasurementWidgets.manage_test_panels
2218 }
2219
2220
2221 def edit(item):
2222 try: map_list2handler[item](parent = self)
2223 except KeyError: pass
2224 return False
2225
2226
2227 gmListWidgets.get_choices_from_list (
2228 parent = self,
2229 caption = _('Master data management'),
2230 choices = [ master_data_list_names[lst] for lst in master_data_lists],
2231 data = master_data_lists,
2232 columns = [_('Select the list you want to manage:')],
2233 edit_callback = edit,
2234 single_selection = True,
2235 ignore_OK_button = True
2236 )
2237
2239
2240 found, cmd = gmShellAPI.detect_external_binary(binary = 'ginkgocadx')
2241 if found:
2242 gmShellAPI.run_command_in_shell(cmd, blocking=False)
2243 return
2244
2245 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
2246 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking = False)
2247 return
2248
2249 for viewer in ['aeskulap', 'amide', 'dicomscope', 'xmedcon']:
2250 found, cmd = gmShellAPI.detect_external_binary(binary = viewer)
2251 if found:
2252 gmShellAPI.run_command_in_shell(cmd, blocking = False)
2253 return
2254
2255 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
2256
2258
2259 curr_pat = gmPerson.gmCurrentPatient()
2260
2261 arriba = gmArriba.cArriba()
2262 pat = gmTools.bool2subst(curr_pat.connected, curr_pat, None)
2263 if not arriba.run(patient = pat, debug = _cfg.get(option = 'debug')):
2264 return
2265
2266
2267 if curr_pat is None:
2268 return
2269
2270 if arriba.pdf_result is None:
2271 return
2272
2273 doc = gmDocumentWidgets.save_file_as_new_document (
2274 parent = self,
2275 filename = arriba.pdf_result,
2276 document_type = _('risk assessment')
2277 )
2278
2279 try: os.remove(arriba.pdf_result)
2280 except StandardError: _log.exception('cannot remove [%s]', arriba.pdf_result)
2281
2282 if doc is None:
2283 return
2284
2285 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2286 doc.save()
2287
2288 try:
2289 open(arriba.xml_result).close()
2290 part = doc.add_part(file = arriba.xml_result)
2291 except StandardError:
2292 _log.exception('error accessing [%s]', arriba.xml_result)
2293 gmDispatcher.send(signal = u'statustext', msg = _('[arriba] XML result not found in [%s]') % arriba.xml_result, beep = False)
2294
2295 if part is None:
2296 return
2297
2298 part['obj_comment'] = u'XML-Daten'
2299 part['filename'] = u'arriba-result.xml'
2300 part.save()
2301
2303
2304 dbcfg = gmCfg.cCfgSQL()
2305 cmd = dbcfg.get2 (
2306 option = u'external.tools.acs_risk_calculator_cmd',
2307 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2308 bias = 'user'
2309 )
2310
2311 if cmd is None:
2312 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True)
2313 return
2314
2315 cwd = os.path.expanduser(os.path.join('~', '.gnumed'))
2316 try:
2317 subprocess.check_call (
2318 args = (cmd,),
2319 close_fds = True,
2320 cwd = cwd
2321 )
2322 except (OSError, ValueError, subprocess.CalledProcessError):
2323 _log.exception('there was a problem executing [%s]', cmd)
2324 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True)
2325 return
2326
2327 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d')))
2328 for pdf in pdfs:
2329 try:
2330 open(pdf).close()
2331 except:
2332 _log.exception('error accessing [%s]', pdf)
2333 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the [arriba] result in [%s] !') % pdf, beep = True)
2334 continue
2335
2336 doc = gmDocumentWidgets.save_file_as_new_document (
2337 parent = self,
2338 filename = pdf,
2339 document_type = u'risk assessment'
2340 )
2341
2342 try:
2343 os.remove(pdf)
2344 except StandardError:
2345 _log.exception('cannot remove [%s]', pdf)
2346
2347 if doc is None:
2348 continue
2349 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2350 doc.save()
2351
2352 return
2353
2355 dlg = gmSnellen.cSnellenCfgDlg()
2356 if dlg.ShowModal() != wx.ID_OK:
2357 return
2358
2359 frame = gmSnellen.cSnellenChart (
2360 width = dlg.vals[0],
2361 height = dlg.vals[1],
2362 alpha = dlg.vals[2],
2363 mirr = dlg.vals[3],
2364 parent = None
2365 )
2366 frame.CentreOnScreen(wx.BOTH)
2367
2368
2369 frame.Show(True)
2370
2371
2374
2377
2380
2381
2382
2386
2389
2390
2391
2393 wx.CallAfter(self.__save_screenshot)
2394 evt.Skip()
2395
2397
2398 time.sleep(0.5)
2399
2400 rect = self.GetRect()
2401
2402
2403 if sys.platform == 'linux2':
2404 client_x, client_y = self.ClientToScreen((0, 0))
2405 border_width = client_x - rect.x
2406 title_bar_height = client_y - rect.y
2407
2408 if self.GetMenuBar():
2409 title_bar_height /= 2
2410 rect.width += (border_width * 2)
2411 rect.height += title_bar_height + border_width
2412
2413 wdc = wx.ScreenDC()
2414 mdc = wx.MemoryDC()
2415 img = wx.EmptyBitmap(rect.width, rect.height)
2416 mdc.SelectObject(img)
2417 mdc.Blit (
2418 0, 0,
2419 rect.width, rect.height,
2420 wdc,
2421 rect.x, rect.y
2422 )
2423
2424
2425 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
2426 img.SaveFile(fname, wx.BITMAP_TYPE_PNG)
2427 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
2428
2430
2431 raise ValueError('raised ValueError to test exception handling')
2432
2434 raise gmExceptions.AccessDenied (
2435 _('[-9999]: <access violation test error>'),
2436 source = u'GNUmed code',
2437 code = -9999,
2438 details = _('This is a deliberate AcessDenied exception thrown to test the handling of access violations by means of a decorator.')
2439 )
2440
2441 @gmAccessPermissionWidgets.verify_minimum_required_role('admin', activity = _('testing access check for non-existant <admin> role'))
2443 raise gmExceptions.AccessDenied (
2444 _('[-9999]: <access violation test error>'),
2445 source = u'GNUmed code',
2446 code = -9999,
2447 details = _('This is a deliberate AcessDenied exception. You should not see this message because the role is checked in a decorator.')
2448 )
2449
2451 import wx.lib.inspection
2452 wx.lib.inspection.InspectionTool().Show()
2453
2456
2459
2462
2465
2472
2476
2479
2482
2489
2494
2496 name = os.path.basename(gmLog2._logfile_name)
2497 name, ext = os.path.splitext(name)
2498 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
2499 new_path = os.path.expanduser(os.path.join('~', 'gnumed', 'logs'))
2500
2501 dlg = wx.FileDialog (
2502 parent = self,
2503 message = _("Save current log as..."),
2504 defaultDir = new_path,
2505 defaultFile = new_name,
2506 wildcard = "%s (*.log)|*.log" % _("log files"),
2507 style = wx.SAVE
2508 )
2509 choice = dlg.ShowModal()
2510 new_name = dlg.GetPath()
2511 dlg.Destroy()
2512 if choice != wx.ID_OK:
2513 return True
2514
2515 _log.warning('syncing log file for backup to [%s]', new_name)
2516 gmLog2.flush()
2517 shutil.copy2(gmLog2._logfile_name, new_name)
2518 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2519
2522
2523
2524
2526 """This is the wx.EVT_CLOSE handler.
2527
2528 - framework still functional
2529 """
2530 _log.debug('gmTopLevelFrame.OnClose() start')
2531 self._clean_exit()
2532 self.Destroy()
2533 _log.debug('gmTopLevelFrame.OnClose() end')
2534 return True
2535
2541
2546
2554
2561
2568
2575
2585
2593
2601
2609
2617
2618 @gmAccessPermissionWidgets.verify_minimum_required_role('doctor', activity = _('manage vaccinations'))
2627
2636
2644
2651
2668
2671
2674
2676
2677 pat = gmPerson.gmCurrentPatient()
2678 if not pat.connected:
2679 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.'))
2680 return False
2681
2682 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
2683
2684 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'EMR', pat['dirname']))
2685 gmTools.mkdir(aDefDir)
2686
2687 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames'])
2688 dlg = wx.FileDialog (
2689 parent = self,
2690 message = _("Save patient's EMR journal as..."),
2691 defaultDir = aDefDir,
2692 defaultFile = fname,
2693 wildcard = aWildcard,
2694 style = wx.SAVE
2695 )
2696 choice = dlg.ShowModal()
2697 fname = dlg.GetPath()
2698 dlg.Destroy()
2699 if choice != wx.ID_OK:
2700 return True
2701
2702 _log.debug('exporting EMR journal to [%s]' % fname)
2703
2704 exporter = gmPatientExporter.cEMRJournalExporter()
2705
2706 wx.BeginBusyCursor()
2707 try:
2708 fname = exporter.export_to_file(filename = fname)
2709 except:
2710 wx.EndBusyCursor()
2711 gmGuiHelpers.gm_show_error (
2712 _('Error exporting patient EMR as chronological journal.'),
2713 _('EMR journal export')
2714 )
2715 raise
2716 wx.EndBusyCursor()
2717
2718 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False)
2719
2720 return True
2721
2728
2730 curr_pat = gmPerson.gmCurrentPatient()
2731 if not curr_pat.connected:
2732 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.'))
2733 return
2734
2735 tag = gmDemographicsWidgets.manage_tag_images(parent = self)
2736 if tag is None:
2737 return
2738
2739 tag = curr_pat.add_tag(tag['pk_tag_image'])
2740 msg = _('Edit the comment on tag [%s]') % tag['l10n_description']
2741 comment = wx.GetTextFromUser (
2742 message = msg,
2743 caption = _('Editing tag comment'),
2744 default_value = gmTools.coalesce(tag['comment'], u''),
2745 parent = self
2746 )
2747
2748 if comment == u'':
2749 return
2750
2751 if comment.strip() == tag['comment']:
2752 return
2753
2754 if comment == u' ':
2755 tag['comment'] = None
2756 else:
2757 tag['comment'] = comment.strip()
2758
2759 tag.save()
2760
2770
2772 curr_pat = gmPerson.gmCurrentPatient()
2773 if not curr_pat.connected:
2774 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.'))
2775 return False
2776
2777 enc = 'cp850'
2778 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'export', 'xDT', 'current-patient.gdt'))
2779 curr_pat.export_as_gdt(filename = fname, encoding = enc)
2780 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2781
2784
2787
2795
2803
2806
2813
2817
2820
2823
2826
2829
2834
2836 """Cleanup helper.
2837
2838 - should ALWAYS be called when this program is
2839 to be terminated
2840 - ANY code that should be executed before a
2841 regular shutdown should go in here
2842 - framework still functional
2843 """
2844 _log.debug('gmTopLevelFrame._clean_exit() start')
2845
2846
2847 listener = gmBackendListener.gmBackendListener()
2848 try:
2849 listener.shutdown()
2850 except:
2851 _log.exception('cannot stop backend notifications listener thread')
2852
2853
2854 if _scripting_listener is not None:
2855 try:
2856 _scripting_listener.shutdown()
2857 except:
2858 _log.exception('cannot stop scripting listener thread')
2859
2860
2861 self.clock_update_timer.Stop()
2862 gmTimer.shutdown()
2863 gmPhraseWheel.shutdown()
2864
2865
2866 for call_back in self.__pre_exit_callbacks:
2867 try:
2868 call_back()
2869 except:
2870 print "*** pre-exit callback failed ***"
2871 print call_back
2872 _log.exception('callback [%s] failed', call_back)
2873
2874
2875 gmDispatcher.send(u'application_closing')
2876
2877
2878 gmDispatcher.disconnect(self._on_set_statustext, 'statustext')
2879
2880
2881 curr_width, curr_height = self.GetClientSizeTuple()
2882 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height))
2883 dbcfg = gmCfg.cCfgSQL()
2884 dbcfg.set (
2885 option = 'main.window.width',
2886 value = curr_width,
2887 workplace = gmSurgery.gmCurrentPractice().active_workplace
2888 )
2889 dbcfg.set (
2890 option = 'main.window.height',
2891 value = curr_height,
2892 workplace = gmSurgery.gmCurrentPractice().active_workplace
2893 )
2894
2895 if _cfg.get(option = 'debug'):
2896 print '---=== GNUmed shutdown ===---'
2897 try:
2898 print _('You have to manually close this window to finalize shutting down GNUmed.')
2899 print _('This is so that you can inspect the console output at your leisure.')
2900 except UnicodeEncodeError:
2901 print 'You have to manually close this window to finalize shutting down GNUmed.'
2902 print 'This is so that you can inspect the console output at your leisure.'
2903 print '---=== GNUmed shutdown ===---'
2904
2905
2906 gmExceptionHandlingWidgets.uninstall_wx_exception_handler()
2907
2908
2909 import threading
2910 _log.debug("%s active threads", threading.activeCount())
2911 for t in threading.enumerate():
2912 _log.debug('thread %s', t)
2913
2914 _log.debug('gmTopLevelFrame._clean_exit() end')
2915
2916
2917
2919
2920 if _cfg.get(option = 'slave'):
2921 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s] (%s:%s)' % (
2922 _cfg.get(option = 'slave personality'),
2923 _cfg.get(option = 'xml-rpc port')
2924 )
2925 else:
2926 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s]'
2927
2929 """Update title of main window based on template.
2930
2931 This gives nice tooltips on iconified GNUmed instances.
2932
2933 User research indicates that in the title bar people want
2934 the date of birth, not the age, so please stick to this
2935 convention.
2936 """
2937 args = {}
2938
2939 pat = gmPerson.gmCurrentPatient()
2940 if pat.connected:
2941 args['pat'] = u'%s %s %s (%s) #%d' % (
2942 gmTools.coalesce(pat['title'], u'', u'%.4s'),
2943 pat['firstnames'],
2944 pat['lastnames'],
2945 pat.get_formatted_dob(format = '%x', encoding = gmI18N.get_encoding()),
2946 pat['pk_identity']
2947 )
2948 else:
2949 args['pat'] = _('no patient')
2950
2951 args['prov'] = u'%s%s.%s' % (
2952 gmTools.coalesce(_provider['title'], u'', u'%s '),
2953 _provider['firstnames'][:1],
2954 _provider['lastnames']
2955 )
2956
2957 args['wp'] = gmSurgery.gmCurrentPractice().active_workplace
2958
2959 self.SetTitle(self.__title_template % args)
2960
2961
2963 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
2964 sb.SetStatusWidths([-1, 225])
2965
2966 self.clock_update_timer = wx.PyTimer(self._cb_update_clock)
2967 self._cb_update_clock()
2968
2969 self.clock_update_timer.Start(milliseconds = 1000)
2970
2972 """Displays date and local time in the second slot of the status bar"""
2973 t = time.localtime(time.time())
2974 st = time.strftime('%c', t).decode(gmI18N.get_encoding(), 'replace')
2975 self.SetStatusText(st, 1)
2976
2978 """Lock GNUmed client against unauthorized access"""
2979
2980
2981
2982 return
2983
2985 """Unlock the main notebook widgets
2986 As long as we are not logged into the database backend,
2987 all pages but the 'login' page of the main notebook widget
2988 are locked; i.e. not accessible by the user
2989 """
2990
2991
2992
2993
2994
2995 return
2996
2998 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
2999
3001
3003
3004 self.__starting_up = True
3005
3006 gmExceptionHandlingWidgets.install_wx_exception_handler()
3007 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version'))
3008
3009
3010 self.SetAppName(u'gnumed')
3011 self.SetVendorName(u'The GNUmed Development Community.')
3012 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3013 paths.init_paths(wx = wx, app_name = u'gnumed')
3014
3015 if not self.__setup_prefs_file():
3016 return False
3017
3018 gmExceptionHandlingWidgets.set_sender_email(gmSurgery.gmCurrentPractice().user_email)
3019
3020 self.__guibroker = gmGuiBroker.GuiBroker()
3021 self.__setup_platform()
3022
3023 if not self.__establish_backend_connection():
3024 return False
3025
3026 self.__update_workplace_list()
3027
3028 if not _cfg.get(option = 'skip-update-check'):
3029 self.__check_for_updates()
3030
3031 if _cfg.get(option = 'slave'):
3032 if not self.__setup_scripting_listener():
3033 return False
3034
3035
3036 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440))
3037 frame.CentreOnScreen(wx.BOTH)
3038 self.SetTopWindow(frame)
3039 frame.Show(True)
3040
3041 if _cfg.get(option = 'debug'):
3042 self.RedirectStdio()
3043 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window'))
3044
3045
3046 print '---=== GNUmed startup ===---'
3047 print _('redirecting STDOUT/STDERR to this log window')
3048 print '---=== GNUmed startup ===---'
3049
3050 self.__setup_user_activity_timer()
3051 self.__register_events()
3052
3053 wx.CallAfter(self._do_after_init)
3054
3055 return True
3056
3058 """Called internally by wxPython after EVT_CLOSE has been handled on last frame.
3059
3060 - after destroying all application windows and controls
3061 - before wx.Windows internal cleanup
3062 """
3063 _log.debug('gmApp.OnExit() start')
3064
3065 self.__shutdown_user_activity_timer()
3066
3067 if _cfg.get(option = 'debug'):
3068 self.RestoreStdio()
3069 sys.stdin = sys.__stdin__
3070 sys.stdout = sys.__stdout__
3071 sys.stderr = sys.__stderr__
3072
3073 _log.debug('gmApp.OnExit() end')
3074
3076 wx.Bell()
3077 wx.Bell()
3078 wx.Bell()
3079 _log.warning('unhandled event detected: QUERY_END_SESSION')
3080 _log.info('we should be saving ourselves from here')
3081 gmLog2.flush()
3082 print "unhandled event detected: QUERY_END_SESSION"
3083
3085 wx.Bell()
3086 wx.Bell()
3087 wx.Bell()
3088 _log.warning('unhandled event detected: END_SESSION')
3089 gmLog2.flush()
3090 print "unhandled event detected: END_SESSION"
3091
3102
3104 self.user_activity_detected = True
3105 evt.Skip()
3106
3108
3109 if self.user_activity_detected:
3110 self.elapsed_inactivity_slices = 0
3111 self.user_activity_detected = False
3112 self.elapsed_inactivity_slices += 1
3113 else:
3114 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices:
3115
3116 pass
3117
3118 self.user_activity_timer.Start(oneShot = True)
3119
3120
3121
3123 try:
3124 kwargs['originated_in_database']
3125 print '==> got notification from database "%s":' % kwargs['signal']
3126 except KeyError:
3127 print '==> received signal from client: "%s"' % kwargs['signal']
3128
3129 del kwargs['signal']
3130 for key in kwargs.keys():
3131 print ' [%s]: %s' % (key, kwargs[key])
3132
3139
3141 self.user_activity_detected = True
3142 self.elapsed_inactivity_slices = 0
3143
3144 self.max_user_inactivity_slices = 15
3145 self.user_activity_timer = gmTimer.cTimer (
3146 callback = self._on_user_activity_timer_expired,
3147 delay = 2000
3148 )
3149 self.user_activity_timer.Start(oneShot=True)
3150
3152 try:
3153 self.user_activity_timer.Stop()
3154 del self.user_activity_timer
3155 except:
3156 pass
3157
3159 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
3160 wx.EVT_END_SESSION(self, self._on_end_session)
3161
3162
3163
3164
3165
3166 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated)
3167
3168 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity)
3169 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
3170
3171 if _cfg.get(option = 'debug'):
3172 gmDispatcher.connect(receiver = self._signal_debugging_monitor)
3173 _log.debug('connected signal monitor')
3174
3190
3192 """Handle all the database related tasks necessary for startup."""
3193
3194
3195 override = _cfg.get(option = '--override-schema-check', source_order = [('cli', 'return')])
3196
3197 from Gnumed.wxpython import gmAuthWidgets
3198 connected = gmAuthWidgets.connect_to_database (
3199 expected_version = gmPG2.map_client_branch2required_db_version[_cfg.get(option = 'client_branch')],
3200 require_version = not override
3201 )
3202 if not connected:
3203 _log.warning("Login attempt unsuccessful. Can't run GNUmed without database connection")
3204 return False
3205
3206
3207 try:
3208 global _provider
3209 _provider = gmStaff.gmCurrentProvider(provider = gmStaff.cStaff())
3210 except ValueError:
3211 account = gmPG2.get_current_user()
3212 _log.exception('DB account [%s] cannot be used as a GNUmed staff login', account)
3213 msg = _(
3214 'The database account [%s] cannot be used as a\n'
3215 'staff member login for GNUmed. There was an\n'
3216 'error retrieving staff details for it.\n\n'
3217 'Please ask your administrator for help.\n'
3218 ) % account
3219 gmGuiHelpers.gm_show_error(msg, _('Checking access permissions'))
3220 return False
3221
3222
3223 tmp = '%s%s %s (%s = %s)' % (
3224 gmTools.coalesce(_provider['title'], ''),
3225 _provider['firstnames'],
3226 _provider['lastnames'],
3227 _provider['short_alias'],
3228 _provider['db_user']
3229 )
3230 gmExceptionHandlingWidgets.set_staff_name(staff_name = tmp)
3231
3232
3233 surgery = gmSurgery.gmCurrentPractice()
3234 msg = surgery.db_logon_banner
3235 if msg.strip() != u'':
3236
3237 login = gmPG2.get_default_login()
3238 auth = u'\n%s\n\n' % (_('Database <%s> on <%s>') % (
3239 login.database,
3240 gmTools.coalesce(login.host, u'localhost')
3241 ))
3242 msg = auth + msg + u'\n\n'
3243
3244 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3245 None,
3246
3247 -1,
3248 caption = _('Verifying database'),
3249 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '),
3250 button_defs = [
3251 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True},
3252 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False}
3253 ]
3254 )
3255 go_on = dlg.ShowModal()
3256 dlg.Destroy()
3257 if go_on != wx.ID_YES:
3258 _log.info('user decided to not connect to this database')
3259 return False
3260
3261
3262 self.__check_db_lang()
3263
3264 return True
3265
3279
3281 """Setup access to a config file for storing preferences."""
3282
3283 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3284
3285 candidates = []
3286 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')])
3287 if explicit_file is not None:
3288 candidates.append(explicit_file)
3289
3290 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf'))
3291 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf'))
3292 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf'))
3293
3294 prefs_file = None
3295 for candidate in candidates:
3296 try:
3297 open(candidate, 'a+').close()
3298 prefs_file = candidate
3299 break
3300 except IOError:
3301 continue
3302
3303 if prefs_file is None:
3304 msg = _(
3305 'Cannot find configuration file in any of:\n'
3306 '\n'
3307 ' %s\n'
3308 'You may need to use the comand line option\n'
3309 '\n'
3310 ' --conf-file=<FILE>'
3311 ) % '\n '.join(candidates)
3312 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files'))
3313 return False
3314
3315 _cfg.set_option(option = u'user_preferences_file', value = prefs_file)
3316 _log.info('user preferences file: %s', prefs_file)
3317
3318 return True
3319
3321
3322 from socket import error as SocketError
3323 from Gnumed.pycommon import gmScriptingListener
3324 from Gnumed.wxpython import gmMacro
3325
3326 slave_personality = gmTools.coalesce (
3327 _cfg.get (
3328 group = u'workplace',
3329 option = u'slave personality',
3330 source_order = [
3331 ('explicit', 'return'),
3332 ('workbase', 'return'),
3333 ('user', 'return'),
3334 ('system', 'return')
3335 ]
3336 ),
3337 u'gnumed-client'
3338 )
3339 _cfg.set_option(option = 'slave personality', value = slave_personality)
3340
3341
3342 port = int (
3343 gmTools.coalesce (
3344 _cfg.get (
3345 group = u'workplace',
3346 option = u'xml-rpc port',
3347 source_order = [
3348 ('explicit', 'return'),
3349 ('workbase', 'return'),
3350 ('user', 'return'),
3351 ('system', 'return')
3352 ]
3353 ),
3354 9999
3355 )
3356 )
3357 _cfg.set_option(option = 'xml-rpc port', value = port)
3358
3359 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality)
3360 global _scripting_listener
3361 try:
3362 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor)
3363 except SocketError, e:
3364 _log.exception('cannot start GNUmed XML-RPC server')
3365 gmGuiHelpers.gm_show_error (
3366 aMessage = (
3367 'Cannot start the GNUmed server:\n'
3368 '\n'
3369 ' [%s]'
3370 ) % e,
3371 aTitle = _('GNUmed startup')
3372 )
3373 return False
3374
3375 return True
3376
3397
3399 if gmI18N.system_locale is None or gmI18N.system_locale == '':
3400 _log.warning("system locale is undefined (probably meaning 'C')")
3401 return True
3402
3403
3404 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}])
3405 db_lang = rows[0]['lang']
3406
3407 if db_lang is None:
3408 _log.debug("database locale currently not set")
3409 msg = _(
3410 "There is no language selected in the database for user [%s].\n"
3411 "Your system language is currently set to [%s].\n\n"
3412 "Do you want to set the database language to '%s' ?\n\n"
3413 ) % (_provider['db_user'], gmI18N.system_locale, gmI18N.system_locale)
3414 checkbox_msg = _('Remember to ignore missing language')
3415 else:
3416 _log.debug("current database locale: [%s]" % db_lang)
3417 msg = _(
3418 "The currently selected database language ('%s') does\n"
3419 "not match the current system language ('%s').\n"
3420 "\n"
3421 "Do you want to set the database language to '%s' ?\n"
3422 ) % (db_lang, gmI18N.system_locale, gmI18N.system_locale)
3423 checkbox_msg = _('Remember to ignore language mismatch')
3424
3425
3426 if db_lang == gmI18N.system_locale_level['full']:
3427 _log.debug('Database locale (%s) up to date.' % db_lang)
3428 return True
3429 if db_lang == gmI18N.system_locale_level['country']:
3430 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (db_lang, gmI18N.system_locale))
3431 return True
3432 if db_lang == gmI18N.system_locale_level['language']:
3433 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (db_lang, gmI18N.system_locale))
3434 return True
3435
3436 _log.warning('database locale [%s] does not match system locale [%s]' % (db_lang, gmI18N.system_locale))
3437
3438
3439 ignored_sys_lang = _cfg.get (
3440 group = u'backend',
3441 option = u'ignored mismatching system locale',
3442 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')]
3443 )
3444
3445
3446 if gmI18N.system_locale == ignored_sys_lang:
3447 _log.info('configured to ignore system-to-database locale mismatch')
3448 return True
3449
3450
3451 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3452 None,
3453 -1,
3454 caption = _('Checking database language settings'),
3455 question = msg,
3456 button_defs = [
3457 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True},
3458 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False}
3459 ],
3460 show_checkbox = True,
3461 checkbox_msg = checkbox_msg,
3462 checkbox_tooltip = _(
3463 'Checking this will make GNUmed remember your decision\n'
3464 'until the system language is changed.\n'
3465 '\n'
3466 'You can also reactivate this inquiry by removing the\n'
3467 'corresponding "ignore" option from the configuration file\n'
3468 '\n'
3469 ' [%s]'
3470 ) % _cfg.get(option = 'user_preferences_file')
3471 )
3472 decision = dlg.ShowModal()
3473 remember_ignoring_problem = dlg._CHBOX_dont_ask_again.GetValue()
3474 dlg.Destroy()
3475
3476 if decision == wx.ID_NO:
3477 if not remember_ignoring_problem:
3478 return True
3479 _log.info('User did not want to set database locale. Ignoring mismatch next time.')
3480 gmCfg2.set_option_in_INI_file (
3481 filename = _cfg.get(option = 'user_preferences_file'),
3482 group = 'backend',
3483 option = 'ignored mismatching system locale',
3484 value = gmI18N.system_locale
3485 )
3486 return True
3487
3488
3489 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]:
3490 if len(lang) > 0:
3491
3492
3493 rows, idx = gmPG2.run_rw_queries (
3494 link_obj = None,
3495 queries = [{'cmd': u'select i18n.set_curr_lang(%s)', 'args': [lang]}],
3496 return_data = True
3497 )
3498 if rows[0][0]:
3499 _log.debug("Successfully set database language to [%s]." % lang)
3500 else:
3501 _log.error('Cannot set database language to [%s].' % lang)
3502 continue
3503 return True
3504
3505
3506 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country'])
3507 gmPG2.run_rw_queries(queries = [{
3508 'cmd': u'select i18n.force_curr_lang(%s)',
3509 'args': [gmI18N.system_locale_level['country']]
3510 }])
3511
3512 return True
3513
3515 try:
3516 kwargs['originated_in_database']
3517 print '==> got notification from database "%s":' % kwargs['signal']
3518 except KeyError:
3519 print '==> received signal from client: "%s"' % kwargs['signal']
3520
3521 del kwargs['signal']
3522 for key in kwargs.keys():
3523
3524 try: print ' [%s]: %s' % (key, kwargs[key])
3525 except: print 'cannot print signal information'
3526
3530
3541
3543
3544 if _cfg.get(option = 'debug'):
3545 gmDispatcher.connect(receiver = _signal_debugging_monitor)
3546 _log.debug('gmDispatcher signal monitor activated')
3547
3548 setup_safe_wxEndBusyCursor()
3549
3550 wx.InitAllImageHandlers()
3551
3552
3553
3554 app = gmApp(redirect = False, clearSigInt = False)
3555 app.MainLoop()
3556
3557
3558
3559 if __name__ == '__main__':
3560
3561 from GNUmed.pycommon import gmI18N
3562 gmI18N.activate_locale()
3563 gmI18N.install_domain()
3564
3565 _log.info('Starting up as main module.')
3566 main()
3567
3568
3569