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 gmPraxis
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 gmPersonCreationWidgets
85 from Gnumed.wxpython import gmEMRStructWidgets
86 from Gnumed.wxpython import gmPatSearchWidgets
87 from Gnumed.wxpython import gmAllergyWidgets
88 from Gnumed.wxpython import gmListWidgets
89 from Gnumed.wxpython import gmProviderInboxWidgets
90 from Gnumed.wxpython import gmCfgWidgets
91 from Gnumed.wxpython import gmExceptionHandlingWidgets
92 from Gnumed.wxpython import gmNarrativeWidgets
93 from Gnumed.wxpython import gmPhraseWheel
94 from Gnumed.wxpython import gmMedicationWidgets
95 from Gnumed.wxpython import gmStaffWidgets
96 from Gnumed.wxpython import gmDocumentWidgets
97 from Gnumed.wxpython import gmTimer
98 from Gnumed.wxpython import gmMeasurementWidgets
99 from Gnumed.wxpython import gmFormWidgets
100 from Gnumed.wxpython import gmSnellen
101 from Gnumed.wxpython import gmVaccWidgets
102 from Gnumed.wxpython import gmPersonContactWidgets
103 from Gnumed.wxpython import gmI18nWidgets
104 from Gnumed.wxpython import gmCodingWidgets
105 from Gnumed.wxpython import gmOrganizationWidgets
106 from Gnumed.wxpython import gmAuthWidgets
107 from Gnumed.wxpython import gmFamilyHistoryWidgets
108 from Gnumed.wxpython import gmDataPackWidgets
109 from Gnumed.wxpython import gmContactWidgets
110 from Gnumed.wxpython import gmAddressWidgets
111 from Gnumed.wxpython import gmBillingWidgets
112 from Gnumed.wxpython import gmKeywordExpansionWidgets
113 from Gnumed.wxpython import gmAccessPermissionWidgets
114 from Gnumed.wxpython import gmPraxisWidgets
115 from Gnumed.wxpython import gmEncounterWidgets
116
117
118 try:
119 _('dummy-no-need-to-translate-but-make-epydoc-happy')
120 except NameError:
121 _ = lambda x:x
122
123 _cfg = gmCfg2.gmCfgData()
124 _provider = None
125 _scripting_listener = None
126 _original_wxEndBusyCursor = None
127
128 _log = logging.getLogger('gm.main')
129 _log.info('wxPython GUI framework: %s %s' % (wx.VERSION_STRING, wx.PlatformInfo))
133 """GNUmed client's main windows frame.
134
135 This is where it all happens. Avoid popping up any other windows.
136 Most user interaction should happen to and from widgets within this frame
137 """
138
139 - def __init__(self, parent, id, title, size=wx.DefaultSize):
140 """You'll have to browse the source to understand what the constructor does
141 """
142 wx.Frame.__init__(self, parent, id, title, size, style = wx.DEFAULT_FRAME_STYLE)
143
144 self.__setup_font()
145
146 self.__gb = gmGuiBroker.GuiBroker()
147 self.__pre_exit_callbacks = []
148 self.bar_width = -1
149 self.menu_id2plugin = {}
150
151 _log.info('workplace is >>>%s<<<', gmPraxis.gmCurrentPraxisBranch().active_workplace)
152
153 self.__setup_main_menu()
154 self.setup_statusbar()
155 self.SetStatusText(_('You are logged in as %s%s.%s (%s). DB account <%s>.') % (
156 gmTools.coalesce(_provider['title'], ''),
157 _provider['firstnames'][:1],
158 _provider['lastnames'],
159 _provider['short_alias'],
160 _provider['db_user']
161 ))
162
163 self.__set_window_title_template()
164 self.__update_window_title()
165
166
167
168
169
170 self.SetIcon(gmTools.get_icon(wx = wx))
171
172 self.__register_events()
173
174 self.LayoutMgr = gmHorstSpace.cHorstSpaceLayoutMgr(self, -1)
175 self.vbox = wx.BoxSizer(wx.VERTICAL)
176 self.vbox.Add(self.LayoutMgr, 10, wx.EXPAND | wx.ALL, 1)
177
178 self.SetAutoLayout(True)
179 self.SetSizerAndFit(self.vbox)
180
181
182
183
184
185 self.__set_GUI_size()
186
187
189
190 font = self.GetFont()
191 _log.debug('system default font is [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc())
192
193 desired_font_face = _cfg.get (
194 group = u'workplace',
195 option = u'client font',
196 source_order = [
197 ('explicit', 'return'),
198 ('workbase', 'return'),
199 ('local', 'return'),
200 ('user', 'return'),
201 ('system', 'return')
202 ]
203 )
204
205 fonts2try = []
206 if desired_font_face is not None:
207 _log.info('client is configured to use font [%s]', desired_font_face)
208 fonts2try.append(desired_font_face)
209
210 if wx.Platform == '__WXMSW__':
211 sane_font_face = u'DejaVu Sans'
212 _log.info('MS Windows: appending fallback font candidate [%s]', sane_font_face)
213 fonts2try.append(sane_font_face)
214
215 if len(fonts2try) == 0:
216 return
217
218 for font_face in fonts2try:
219 success = font.SetFaceName(font_face)
220 if success:
221 self.SetFont(font)
222 _log.debug('switched font to [%s] (%s)', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc())
223 return
224 font = self.GetFont()
225 _log.error('cannot switch font from [%s] (%s) to [%s]', font.GetNativeFontInfoUserDesc(), font.GetNativeFontInfoDesc(), font_face)
226
227 return
228
230 """Try to get previous window size from backend."""
231
232 cfg = gmCfg.cCfgSQL()
233
234
235 width = int(cfg.get2 (
236 option = 'main.window.width',
237 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
238 bias = 'workplace',
239 default = 800
240 ))
241
242
243 height = int(cfg.get2 (
244 option = 'main.window.height',
245 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
246 bias = 'workplace',
247 default = 600
248 ))
249
250 dw = wx.DisplaySize()[0]
251 dh = wx.DisplaySize()[1]
252
253 _log.info('display size: %s:%s' % (wx.SystemSettings.GetMetric(wx.SYS_SCREEN_X), wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y)))
254 _log.debug('display size: %s:%s %s mm', dw, dh, str(wx.DisplaySizeMM()))
255 _log.debug('previous GUI size [%s:%s]', width, height)
256
257
258 if width > dw:
259 _log.debug('adjusting GUI width from %s to %s', width, dw)
260 width = dw
261
262 if height > dh:
263 _log.debug('adjusting GUI height from %s to %s', height, dh)
264 height = dh
265
266
267 if width < 100:
268 _log.debug('adjusting GUI width to minimum of 100 pixel')
269 width = 100
270 if height < 100:
271 _log.debug('adjusting GUI height to minimum of 100 pixel')
272 height = 100
273
274 _log.info('setting GUI to size [%s:%s]', width, height)
275
276 self.SetClientSize(wx.Size(width, height))
277
279 """Create the main menu entries.
280
281 Individual entries are farmed out to the modules.
282
283 menu item template:
284
285 item = menu_emr_edit.Append(-1, _(''), _(''))
286 self.Bind(wx.EVT_MENU, self__on_, item)
287 """
288 global wx
289 self.mainmenu = wx.MenuBar()
290 self.__gb['main.mainmenu'] = self.mainmenu
291
292
293 menu_gnumed = wx.Menu()
294
295 self.menu_plugins = wx.Menu()
296 menu_gnumed.AppendMenu(wx.NewId(), _('&Go to plugin ...'), self.menu_plugins)
297
298 ID = wx.NewId()
299 menu_gnumed.Append(ID, _('Check for updates'), _('Check for new releases of the GNUmed client.'))
300 wx.EVT_MENU(self, ID, self.__on_check_for_updates)
301
302 item = menu_gnumed.Append(-1, _('Announce downtime'), _('Announce database maintenance downtime to all connected clients.'))
303 self.Bind(wx.EVT_MENU, self.__on_announce_maintenance, item)
304
305
306 menu_gnumed.AppendSeparator()
307
308
309 menu_config = wx.Menu()
310
311 item = menu_config.Append(-1, _('All options'), _('List all options as configured in the database.'))
312 self.Bind(wx.EVT_MENU, self.__on_list_configuration, item)
313
314
315 menu_cfg_db = wx.Menu()
316
317 ID = wx.NewId()
318 menu_cfg_db.Append(ID, _('Language'), _('Configure the database language'))
319 wx.EVT_MENU(self, ID, self.__on_configure_db_lang)
320
321 ID = wx.NewId()
322 menu_cfg_db.Append(ID, _('Welcome message'), _('Configure the database welcome message (all users).'))
323 wx.EVT_MENU(self, ID, self.__on_configure_db_welcome)
324
325 menu_config.AppendMenu(wx.NewId(), _('Database ...'), menu_cfg_db)
326
327
328 menu_cfg_client = wx.Menu()
329
330 ID = wx.NewId()
331 menu_cfg_client.Append(ID, _('Export chunk size'), _('Configure the chunk size used when exporting BLOBs from the database.'))
332 wx.EVT_MENU(self, ID, self.__on_configure_export_chunk_size)
333
334 item = menu_cfg_client.Append(-1, _('Email address'), _('The email address of the user for sending bug reports, etc.'))
335 self.Bind(wx.EVT_MENU, self.__on_configure_user_email, item)
336
337 menu_config.AppendMenu(wx.NewId(), _('Client parameters ...'), menu_cfg_client)
338
339
340 menu_cfg_ui = wx.Menu()
341
342 item = menu_cfg_ui.Append(-1, _('Medication measurements'), _('Select the measurements panel to show in the medications plugin.'))
343 self.Bind(wx.EVT_MENU, self.__on_cfg_meds_lab_pnl, item)
344
345
346 menu_cfg_doc = wx.Menu()
347
348 ID = wx.NewId()
349 menu_cfg_doc.Append(ID, _('Review dialog'), _('Configure review dialog after document display.'))
350 wx.EVT_MENU(self, ID, self.__on_configure_doc_review_dialog)
351
352 ID = wx.NewId()
353 menu_cfg_doc.Append(ID, _('UUID display'), _('Configure unique ID dialog on document import.'))
354 wx.EVT_MENU(self, ID, self.__on_configure_doc_uuid_dialog)
355
356 ID = wx.NewId()
357 menu_cfg_doc.Append(ID, _('Empty documents'), _('Whether to allow saving documents without parts.'))
358 wx.EVT_MENU(self, ID, self.__on_configure_partless_docs)
359
360 item = menu_cfg_doc.Append(-1, _('Generate UUID'), _('Whether to generate UUIDs for new documents.'))
361 self.Bind(wx.EVT_MENU, self.__on_configure_generate_doc_uuid, item)
362
363 menu_cfg_ui.AppendMenu(wx.NewId(), _('Document handling ...'), menu_cfg_doc)
364
365
366 menu_cfg_update = wx.Menu()
367
368 ID = wx.NewId()
369 menu_cfg_update.Append(ID, _('Auto-check'), _('Whether to auto-check for updates at startup.'))
370 wx.EVT_MENU(self, ID, self.__on_configure_update_check)
371
372 ID = wx.NewId()
373 menu_cfg_update.Append(ID, _('Check scope'), _('When checking for updates, consider latest branch, too ?'))
374 wx.EVT_MENU(self, ID, self.__on_configure_update_check_scope)
375
376 ID = wx.NewId()
377 menu_cfg_update.Append(ID, _('URL'), _('The URL to retrieve version information from.'))
378 wx.EVT_MENU(self, ID, self.__on_configure_update_url)
379
380 menu_cfg_ui.AppendMenu(wx.NewId(), _('Update handling ...'), menu_cfg_update)
381
382
383 menu_cfg_pat_search = wx.Menu()
384
385 ID = wx.NewId()
386 menu_cfg_pat_search.Append(ID, _('Birthday reminder'), _('Configure birthday reminder proximity interval.'))
387 wx.EVT_MENU(self, ID, self.__on_configure_dob_reminder_proximity)
388
389 ID = wx.NewId()
390 menu_cfg_pat_search.Append(ID, _('Immediate source activation'), _('Configure immediate activation of single external person.'))
391 wx.EVT_MENU(self, ID, self.__on_configure_quick_pat_search)
392
393 ID = wx.NewId()
394 menu_cfg_pat_search.Append(ID, _('Initial plugin'), _('Configure which plugin to show right after person activation.'))
395 wx.EVT_MENU(self, ID, self.__on_configure_initial_pat_plugin)
396
397 item = menu_cfg_pat_search.Append(-1, _('Default region'), _('Configure the default province/region/state for person creation.'))
398 self.Bind(wx.EVT_MENU, self.__on_cfg_default_region, item)
399
400 item = menu_cfg_pat_search.Append(-1, _('Default country'), _('Configure the default country for person creation.'))
401 self.Bind(wx.EVT_MENU, self.__on_cfg_default_country, item)
402
403 menu_cfg_ui.AppendMenu(wx.NewId(), _('Person ...'), menu_cfg_pat_search)
404
405
406 menu_cfg_soap_editing = wx.Menu()
407
408 ID = wx.NewId()
409 menu_cfg_soap_editing.Append(ID, _('Multiple new episodes'), _('Configure opening multiple new episodes on a patient at once.'))
410 wx.EVT_MENU(self, ID, self.__on_allow_multiple_new_episodes)
411
412 item = menu_cfg_soap_editing.Append(-1, _('Auto-open editors'), _('Configure auto-opening editors for recent problems.'))
413 self.Bind(wx.EVT_MENU, self.__on_allow_auto_open_episodes, item)
414
415 menu_cfg_ui.AppendMenu(wx.NewId(), _('Progress notes handling ...'), menu_cfg_soap_editing)
416
417 menu_config.AppendMenu(wx.NewId(), _('User interface ...'), menu_cfg_ui)
418
419
420 menu_cfg_ext_tools = wx.Menu()
421
422
423
424
425
426 item = menu_cfg_ext_tools.Append(-1, _('MI/stroke risk calc cmd'), _('Set the command to start the CV risk calculator.'))
427 self.Bind(wx.EVT_MENU, self.__on_configure_acs_risk_calculator_cmd, item)
428
429 ID = wx.NewId()
430 menu_cfg_ext_tools.Append(ID, _('OOo startup time'), _('Set the time to wait for OpenOffice to settle after startup.'))
431 wx.EVT_MENU(self, ID, self.__on_configure_ooo_settle_time)
432
433 item = menu_cfg_ext_tools.Append(-1, _('Measurements URL'), _('URL for measurements encyclopedia.'))
434 self.Bind(wx.EVT_MENU, self.__on_configure_measurements_url, item)
435
436 item = menu_cfg_ext_tools.Append(-1, _('Drug data source'), _('Select the drug data source.'))
437 self.Bind(wx.EVT_MENU, self.__on_configure_drug_data_source, item)
438
439
440
441
442 item = menu_cfg_ext_tools.Append(-1, _('ADR URL'), _('URL for reporting Adverse Drug Reactions.'))
443 self.Bind(wx.EVT_MENU, self.__on_configure_adr_url, item)
444
445 item = menu_cfg_ext_tools.Append(-1, _('vaccADR URL'), _('URL for reporting Adverse Drug Reactions to *vaccines*.'))
446 self.Bind(wx.EVT_MENU, self.__on_configure_vaccine_adr_url, item)
447
448 item = menu_cfg_ext_tools.Append(-1, _('Vacc plans URL'), _('URL for vaccination plans.'))
449 self.Bind(wx.EVT_MENU, self.__on_configure_vaccination_plans_url, item)
450
451 item = menu_cfg_ext_tools.Append(-1, _('Visual SOAP editor'), _('Set the command for calling the visual progress note editor.'))
452 self.Bind(wx.EVT_MENU, self.__on_configure_visual_soap_cmd, item)
453
454 menu_config.AppendMenu(wx.NewId(), _('External tools ...'), menu_cfg_ext_tools)
455
456
457 menu_cfg_bill = wx.Menu()
458
459 item = menu_cfg_bill.Append(-1, _('Invoice template (no VAT)'), _('Select the template for printing an invoice without VAT.'))
460 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_no_vat, item)
461
462 item = menu_cfg_bill.Append(-1, _('Invoice template (with VAT)'), _('Select the template for printing an invoice with VAT.'))
463 self.Bind(wx.EVT_MENU, self.__on_cfg_invoice_template_with_vat, item)
464
465 item = menu_cfg_bill.Append(-1, _('Catalogs URL'), _('URL for billing catalogs (schedules of fees).'))
466 self.Bind(wx.EVT_MENU, self.__on_configure_billing_catalogs_url, item)
467
468
469 menu_cfg_emr = wx.Menu()
470
471 item = menu_cfg_emr.Append(-1, _('Medication list template'), _('Select the template for printing a medication list.'))
472 self.Bind(wx.EVT_MENU, self.__on_cfg_medication_list_template, item)
473
474 item = menu_cfg_emr.Append(-1, _('Prescription mode'), _('Select the default mode for creating a prescription.'))
475 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_mode, item)
476
477 item = menu_cfg_emr.Append(-1, _('Prescription template'), _('Select the template for printing a prescription.'))
478 self.Bind(wx.EVT_MENU, self.__on_cfg_prescription_template, item)
479
480 item = menu_cfg_emr.Append(-1, _('Default Gnuplot template'), _('Select the default template for plotting test results.'))
481 self.Bind(wx.EVT_MENU, self.__on_cfg_default_gnuplot_template, item)
482
483 item = menu_cfg_emr.Append(-1, _('Fallback provider'), _('Select the doctor to fall back to for patients without a primary provider.'))
484 self.Bind(wx.EVT_MENU, self.__on_cfg_fallback_primary_provider, item)
485
486
487 menu_cfg_encounter = wx.Menu()
488
489 ID = wx.NewId()
490 menu_cfg_encounter.Append(ID, _('Edit before patient change'), _('Edit encounter details before change of patient.'))
491 wx.EVT_MENU(self, ID, self.__on_cfg_enc_pat_change)
492
493 ID = wx.NewId()
494 menu_cfg_encounter.Append(ID, _('Minimum duration'), _('Minimum duration of an encounter.'))
495 wx.EVT_MENU(self, ID, self.__on_cfg_enc_min_ttl)
496
497 ID = wx.NewId()
498 menu_cfg_encounter.Append(ID, _('Maximum duration'), _('Maximum duration of an encounter.'))
499 wx.EVT_MENU(self, ID, self.__on_cfg_enc_max_ttl)
500
501 ID = wx.NewId()
502 menu_cfg_encounter.Append(ID, _('Minimum empty age'), _('Minimum age of an empty encounter before considering for deletion.'))
503 wx.EVT_MENU(self, ID, self.__on_cfg_enc_empty_ttl)
504
505 ID = wx.NewId()
506 menu_cfg_encounter.Append(ID, _('Default type'), _('Default type for new encounters.'))
507 wx.EVT_MENU(self, ID, self.__on_cfg_enc_default_type)
508
509 menu_cfg_emr.AppendMenu(wx.NewId(), _('Encounter ...'), menu_cfg_encounter)
510
511
512 menu_cfg_episode = wx.Menu()
513
514 ID = wx.NewId()
515 menu_cfg_episode.Append(ID, _('Dormancy'), _('Maximum length of dormancy after which an episode will be considered closed.'))
516 wx.EVT_MENU(self, ID, self.__on_cfg_epi_ttl)
517
518 menu_cfg_emr.AppendMenu(wx.NewId(), _('Episode ...'), menu_cfg_episode)
519
520 menu_config.AppendMenu(wx.NewId(), _('EMR ...'), menu_cfg_emr)
521 menu_config.AppendMenu(wx.NewId(), _('Billing ...'), menu_cfg_bill)
522 menu_gnumed.AppendMenu(wx.NewId(), _('Preferences ...'), menu_config)
523
524
525 menu_master_data = wx.Menu()
526
527 item = menu_master_data.Append(-1, _('Manage lists'), _('Manage various lists of master data.'))
528 self.Bind(wx.EVT_MENU, self.__on_manage_master_data, item)
529
530 item = menu_master_data.Append(-1, _('Manage praxis'), _('Manage your praxis branches.'))
531 self.Bind(wx.EVT_MENU, self.__on_manage_praxis, item)
532
533 item = menu_master_data.Append(-1, _('Install data packs'), _('Install reference data from data packs.'))
534 self.Bind(wx.EVT_MENU, self.__on_install_data_packs, item)
535
536 item = menu_master_data.Append(-1, _('Update ATC'), _('Install ATC reference data.'))
537 self.Bind(wx.EVT_MENU, self.__on_update_atc, item)
538
539 item = menu_master_data.Append(-1, _('Update LOINC'), _('Download and install LOINC reference data.'))
540 self.Bind(wx.EVT_MENU, self.__on_update_loinc, item)
541
542 item = menu_master_data.Append(-1, _('Create fake vaccines'), _('Re-create fake generic vaccines.'))
543 self.Bind(wx.EVT_MENU, self.__on_generate_vaccines, item)
544
545 menu_gnumed.AppendMenu(wx.NewId(), _('&Master data ...'), menu_master_data)
546
547
548 menu_users = wx.Menu()
549
550 item = menu_users.Append(-1, _('&Add user'), _('Add a new GNUmed user'))
551 self.Bind(wx.EVT_MENU, self.__on_add_new_staff, item)
552
553 item = menu_users.Append(-1, _('&Edit users'), _('Edit the list of GNUmed users'))
554 self.Bind(wx.EVT_MENU, self.__on_edit_staff_list, item)
555
556 item = menu_users.Append(-1, _('&Change DB owner PWD'), _('Change the password of the GNUmed database owner'))
557 self.Bind(wx.EVT_MENU, self.__on_edit_gmdbowner_password, item)
558
559 menu_gnumed.AppendMenu(wx.NewId(), _('&Users ...'), menu_users)
560
561
562 menu_gnumed.AppendSeparator()
563
564 item = menu_gnumed.Append(wx.ID_EXIT, _('E&xit\tAlt-X'), _('Close this GNUmed client.'))
565 self.Bind(wx.EVT_MENU, self.__on_exit_gnumed, item)
566
567 self.mainmenu.Append(menu_gnumed, '&GNUmed')
568
569
570 menu_person = wx.Menu()
571
572 item = menu_person.Append(-1, _('Search'), _('Search for a person.'))
573 self.Bind(wx.EVT_MENU, self.__on_search_person, item)
574 acc_tab = wx.AcceleratorTable([(wx.ACCEL_NORMAL, wx.WXK_ESCAPE, item.GetId())])
575 self.SetAcceleratorTable(acc_tab)
576
577 ID_CREATE_PATIENT = wx.NewId()
578 menu_person.Append(ID_CREATE_PATIENT, _('&Register person'), _("Register a new person with GNUmed"))
579 wx.EVT_MENU(self, ID_CREATE_PATIENT, self.__on_create_new_patient)
580
581 ID_LOAD_EXT_PAT = wx.NewId()
582 menu_person.Append(ID_LOAD_EXT_PAT, _('&Load external'), _('Load and possibly create person from an external source.'))
583 wx.EVT_MENU(self, ID_LOAD_EXT_PAT, self.__on_load_external_patient)
584
585 item = menu_person.Append(-1, _('Add &tag'), _('Add a text/image tag to this person.'))
586 self.Bind(wx.EVT_MENU, self.__on_add_tag2person, item)
587
588 ID_DEL_PAT = wx.NewId()
589 menu_person.Append(ID_DEL_PAT, _('Deactivate record'), _('Deactivate (exclude from search) person record in database.'))
590 wx.EVT_MENU(self, ID_DEL_PAT, self.__on_delete_patient)
591
592 item = menu_person.Append(-1, _('&Merge persons'), _('Merge two persons into one.'))
593 self.Bind(wx.EVT_MENU, self.__on_merge_patients, item)
594
595 menu_person.AppendSeparator()
596
597 ID_ENLIST_PATIENT_AS_STAFF = wx.NewId()
598 menu_person.Append(ID_ENLIST_PATIENT_AS_STAFF, _('Enlist as user'), _('Enlist current person as GNUmed user'))
599 wx.EVT_MENU(self, ID_ENLIST_PATIENT_AS_STAFF, self.__on_enlist_patient_as_staff)
600
601
602 ID = wx.NewId()
603 menu_person.Append(ID, _('Export to GDT'), _('Export demographics of currently active person into GDT file.'))
604 wx.EVT_MENU(self, ID, self.__on_export_as_gdt)
605
606 menu_person.AppendSeparator()
607
608 self.mainmenu.Append(menu_person, '&Person')
609 self.__gb['main.patientmenu'] = menu_person
610
611
612 menu_emr = wx.Menu()
613
614
615 menu_emr_edit = wx.Menu()
616
617 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'))
618 self.Bind(wx.EVT_MENU, self.__on_add_health_issue, item)
619
620 item = menu_emr_edit.Append(-1, _('&Episode'), _('Add an episode of illness to the EMR of the active patient'))
621 self.Bind(wx.EVT_MENU, self.__on_add_episode, item)
622
623 item = menu_emr_edit.Append(-1, _('&Medication'), _('Add medication / substance use entry.'))
624 self.Bind(wx.EVT_MENU, self.__on_add_medication, item)
625
626 item = menu_emr_edit.Append(-1, _('&Allergies'), _('Manage documentation of allergies for the current patient.'))
627 self.Bind(wx.EVT_MENU, self.__on_manage_allergies, item)
628
629 item = menu_emr_edit.Append(-1, _('&Occupation'), _('Edit occupation details for the current patient.'))
630 self.Bind(wx.EVT_MENU, self.__on_edit_occupation, item)
631
632 item = menu_emr_edit.Append(-1, _('&Hospitalizations'), _('Manage hospitalizations.'))
633 self.Bind(wx.EVT_MENU, self.__on_manage_hospital_stays, item)
634
635 item = menu_emr_edit.Append(-1, _('&Procedures'), _('Manage procedures performed on the patient.'))
636 self.Bind(wx.EVT_MENU, self.__on_manage_performed_procedures, item)
637
638 item = menu_emr_edit.Append(-1, _('&Measurements'), _('Manage measurement results for the current patient.'))
639 self.Bind(wx.EVT_MENU, self.__on_manage_measurements, item)
640
641 item = menu_emr_edit.Append(-1, _('&Vaccinations'), _('Add (a) vaccination(s) for the current patient.'))
642 self.Bind(wx.EVT_MENU, self.__on_add_vaccination, item)
643
644 item = menu_emr_edit.Append(-1, _('&Family history (FHx)'), _('Manage family history.'))
645 self.Bind(wx.EVT_MENU, self.__on_manage_fhx, item)
646
647 item = menu_emr_edit.Append(-1, _('&Encounters'), _('List all encounters including empty ones.'))
648 self.Bind(wx.EVT_MENU, self.__on_list_encounters, item)
649
650 menu_emr.AppendMenu(wx.NewId(), _('&Add / Edit ...'), menu_emr_edit)
651
652
653 item = menu_emr.Append(-1, _('Search this EMR'), _('Search for data in the EMR of the active patient'))
654 self.Bind(wx.EVT_MENU, self.__on_search_emr, item)
655
656 item = menu_emr.Append(-1, _('Start new encounter'), _('Start a new encounter for the active patient right now.'))
657 self.Bind(wx.EVT_MENU, self.__on_start_new_encounter, item)
658
659
660
661
662
663 item = menu_emr.Append(-1, _('Statistics'), _('Show a high-level statistic summary of the EMR.'))
664 self.Bind(wx.EVT_MENU, self.__on_show_emr_summary, item)
665
666
667
668
669 menu_emr.AppendSeparator()
670
671
672 menu_emr_export = wx.Menu()
673
674 ID_EXPORT_EMR_ASCII = wx.NewId()
675 menu_emr_export.Append (
676 ID_EXPORT_EMR_ASCII,
677 _('Text document'),
678 _("Export the EMR of the active patient into a text file")
679 )
680 wx.EVT_MENU(self, ID_EXPORT_EMR_ASCII, self.OnExportEMR)
681
682 ID_EXPORT_EMR_JOURNAL = wx.NewId()
683 menu_emr_export.Append (
684 ID_EXPORT_EMR_JOURNAL,
685 _('Journal'),
686 _("Export the EMR of the active patient as a chronological journal into a text file")
687 )
688 wx.EVT_MENU(self, ID_EXPORT_EMR_JOURNAL, self.__on_export_emr_as_journal)
689
690 ID_EXPORT_MEDISTAR = wx.NewId()
691 menu_emr_export.Append (
692 ID_EXPORT_MEDISTAR,
693 _('MEDISTAR import format'),
694 _("GNUmed -> MEDISTAR. Export progress notes of active patient's active encounter into a text file.")
695 )
696 wx.EVT_MENU(self, ID_EXPORT_MEDISTAR, self.__on_export_for_medistar)
697
698 menu_emr.AppendMenu(wx.NewId(), _('Export as ...'), menu_emr_export)
699
700 menu_emr.AppendSeparator()
701
702 self.mainmenu.Append(menu_emr, _("&EMR"))
703 self.__gb['main.emrmenu'] = menu_emr
704
705
706 menu_paperwork = wx.Menu()
707
708 item = menu_paperwork.Append(-1, _('&Write letter'), _('Write a letter for the current patient.'))
709 self.Bind(wx.EVT_MENU, self.__on_new_letter, item)
710
711 menu_paperwork.AppendSeparator()
712
713 item = menu_paperwork.Append(-1, _('List Placeholders'), _('Show a list of all placeholders.'))
714 self.Bind(wx.EVT_MENU, self.__on_show_placeholders, item)
715
716 self.mainmenu.Append(menu_paperwork, _('&Correspondence'))
717
718
719 self.menu_tools = wx.Menu()
720
721 item = self.menu_tools.Append(-1, _('Search all EMRs'), _('Search for data across the EMRs of all patients'))
722 self.Bind(wx.EVT_MENU, self.__on_search_across_emrs, item)
723
724 ID_DICOM_VIEWER = wx.NewId()
725 viewer = _('no viewer installed')
726 if gmShellAPI.detect_external_binary(binary = 'ginkgocadx')[0]:
727 viewer = u'Ginkgo CADx'
728 elif os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
729 viewer = u'OsiriX'
730 elif gmShellAPI.detect_external_binary(binary = 'aeskulap')[0]:
731 viewer = u'Aeskulap'
732 elif gmShellAPI.detect_external_binary(binary = 'amide')[0]:
733 viewer = u'AMIDE'
734 elif gmShellAPI.detect_external_binary(binary = 'dicomscope')[0]:
735 viewer = u'DicomScope'
736 elif gmShellAPI.detect_external_binary(binary = 'xmedcon')[0]:
737 viewer = u'(x)medcon'
738 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)
739 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_dicom_viewer)
740 if viewer == _('no viewer installed'):
741 _log.info('neither of Ginkgo CADx / OsiriX / Aeskulap / AMIDE / DicomScope / xmedcon found, disabling "DICOM viewer" menu item')
742 self.menu_tools.Enable(id=ID_DICOM_VIEWER, enable=False)
743
744
745
746
747
748 ID = wx.NewId()
749 self.menu_tools.Append(ID, _('Snellen chart'), _('Display fullscreen snellen chart.'))
750 wx.EVT_MENU(self, ID, self.__on_snellen)
751
752 item = self.menu_tools.Append(-1, _('MI/stroke risk'), _('Acute coronary syndrome/stroke risk assessment.'))
753 self.Bind(wx.EVT_MENU, self.__on_acs_risk_assessment, item)
754
755 ID_DICOM_VIEWER = wx.NewId()
756 self.menu_tools.Append(ID_DICOM_VIEWER, u'arriba', _('arriba: cardiovascular risk assessment (%s).') % u'www.arriba-hausarzt.de')
757 wx.EVT_MENU(self, ID_DICOM_VIEWER, self.__on_arriba)
758 if not gmShellAPI.detect_external_binary(binary = 'arriba')[0]:
759 _log.info('<arriba> not found, disabling "arriba" menu item')
760 self.menu_tools.Enable(id = ID_DICOM_VIEWER, enable = False)
761
762
763
764 item = self.menu_tools.Append(-1, u'HL7 (Exelleris)', 'Stage Excelleris HL7')
765 self.Bind(wx.EVT_MENU, self.__on_excelleris, item)
766
767 item = self.menu_tools.Append(-1, u'HL7', 'Stage generic HL7')
768 self.Bind(wx.EVT_MENU, self.__on_hl7, item)
769
770 item = self.menu_tools.Append(-1, u'Incoming', 'Browse incoming data')
771 self.Bind(wx.EVT_MENU, self.__on_incoming, item)
772
773 self.menu_tools.AppendSeparator()
774
775 self.mainmenu.Append(self.menu_tools, _("&Tools"))
776 self.__gb['main.toolsmenu'] = self.menu_tools
777
778
779 menu_knowledge = wx.Menu()
780
781
782 menu_drug_dbs = wx.Menu()
783
784 item = menu_drug_dbs.Append(-1, _('&Database'), _('Jump to the drug database configured as the default.'))
785 self.Bind(wx.EVT_MENU, self.__on_jump_to_drug_db, item)
786
787
788
789
790
791
792 menu_knowledge.AppendMenu(wx.NewId(), _('&Drug Resources'), menu_drug_dbs)
793
794 menu_id = wx.NewId()
795 menu_drug_dbs.Append(menu_id, u'kompendium.ch', _('Show "kompendium.ch" drug database (online, Switzerland)'))
796 wx.EVT_MENU(self, menu_id, self.__on_kompendium_ch)
797
798
799
800
801 ID_MEDICAL_LINKS = wx.NewId()
802 menu_knowledge.Append(ID_MEDICAL_LINKS, _('Medical links (www)'), _('Show a page of links to useful medical content.'))
803 wx.EVT_MENU(self, ID_MEDICAL_LINKS, self.__on_medical_links)
804
805 self.mainmenu.Append(menu_knowledge, _('&Knowledge'))
806 self.__gb['main.knowledgemenu'] = menu_knowledge
807
808
809 self.menu_office = wx.Menu()
810
811 item = self.menu_office.Append(-1, _('&Audit trail'), _('Display database audit trail.'))
812 self.Bind(wx.EVT_MENU, self.__on_display_audit_trail, item)
813
814 self.menu_office.AppendSeparator()
815
816 item = self.menu_office.Append(-1, _('&Bills'), _('List all bills across all patients.'))
817 self.Bind(wx.EVT_MENU, self.__on_show_all_bills, item)
818
819 item = self.menu_office.Append(-1, _('&Organizations'), _('Manage organizations.'))
820 self.Bind(wx.EVT_MENU, self.__on_manage_orgs, item)
821
822 self.mainmenu.Append(self.menu_office, _('&Office'))
823 self.__gb['main.officemenu'] = self.menu_office
824
825
826 help_menu = wx.Menu()
827
828 ID = wx.NewId()
829 help_menu.Append(ID, _('GNUmed wiki'), _('Go to the GNUmed wiki on the web.'))
830 wx.EVT_MENU(self, ID, self.__on_display_wiki)
831
832 ID = wx.NewId()
833 help_menu.Append(ID, _('User manual (www)'), _('Go to the User Manual on the web.'))
834 wx.EVT_MENU(self, ID, self.__on_display_user_manual_online)
835
836 item = help_menu.Append(-1, _('Menu reference (www)'), _('View the reference for menu items on the web.'))
837 self.Bind(wx.EVT_MENU, self.__on_menu_reference, item)
838
839 item = help_menu.Append(-1, _('&Clear status line'), _('Clear out the status line.'))
840 self.Bind(wx.EVT_MENU, self.__on_clear_status_line, item)
841
842 menu_debugging = wx.Menu()
843
844 ID_SCREENSHOT = wx.NewId()
845 menu_debugging.Append(ID_SCREENSHOT, _('Screenshot'), _('Save a screenshot of this GNUmed client.'))
846 wx.EVT_MENU(self, ID_SCREENSHOT, self.__on_save_screenshot)
847
848 item = menu_debugging.Append(-1, _('Show log file'), _('Show the log file in text viewer.'))
849 self.Bind(wx.EVT_MENU, self.__on_show_log_file, item)
850
851 ID = wx.NewId()
852 menu_debugging.Append(ID, _('Backup log file'), _('Backup the content of the log to another file.'))
853 wx.EVT_MENU(self, ID, self.__on_backup_log_file)
854
855 item = menu_debugging.Append(-1, _('Email log file'), _('Send the log file to the authors for help.'))
856 self.Bind(wx.EVT_MENU, self.__on_email_log_file, item)
857
858 ID = wx.NewId()
859 menu_debugging.Append(ID, _('Bug tracker'), _('Go to the GNUmed bug tracker on the web.'))
860 wx.EVT_MENU(self, ID, self.__on_display_bugtracker)
861
862 ID_UNBLOCK = wx.NewId()
863 menu_debugging.Append(ID_UNBLOCK, _('Unlock mouse'), _('Unlock mouse pointer in case it got stuck in hourglass mode.'))
864 wx.EVT_MENU(self, ID_UNBLOCK, self.__on_unblock_cursor)
865
866 item = menu_debugging.Append(-1, _('pgAdmin III'), _('pgAdmin III: Browse GNUmed database(s) in PostgreSQL server.'))
867 self.Bind(wx.EVT_MENU, self.__on_pgadmin3, item)
868
869
870
871
872 if _cfg.get(option = 'debug'):
873 ID_TOGGLE_PAT_LOCK = wx.NewId()
874 menu_debugging.Append(ID_TOGGLE_PAT_LOCK, _('Lock/unlock patient search'), _('Lock/unlock patient search - USE ONLY IF YOU KNOW WHAT YOU ARE DOING !'))
875 wx.EVT_MENU(self, ID_TOGGLE_PAT_LOCK, self.__on_toggle_patient_lock)
876
877 ID_TEST_EXCEPTION = wx.NewId()
878 menu_debugging.Append(ID_TEST_EXCEPTION, _('Test error handling'), _('Throw an exception to test error handling.'))
879 wx.EVT_MENU(self, ID_TEST_EXCEPTION, self.__on_test_exception)
880
881 item = menu_debugging.Append(-1, _('Test access violation exception'), _('Simulate an access violation exception.'))
882 self.Bind(wx.EVT_MENU, self.__on_test_access_violation, item)
883
884 item = menu_debugging.Append(-1, _('Test access checking'), _('Simulate a failing access check.'))
885 self.Bind(wx.EVT_MENU, self.__on_test_access_checking, item)
886
887 ID = wx.NewId()
888 menu_debugging.Append(ID, _('Invoke inspector'), _('Invoke the widget hierarchy inspector (needs wxPython 2.8).'))
889 wx.EVT_MENU(self, ID, self.__on_invoke_inspector)
890 try:
891 import wx.lib.inspection
892 except ImportError:
893 menu_debugging.Enable(id = ID, enable = False)
894
895 help_menu.AppendMenu(wx.NewId(), _('Debugging ...'), menu_debugging)
896
897 help_menu.AppendSeparator()
898
899 help_menu.Append(wx.ID_ABOUT, _('About GNUmed'), "")
900 wx.EVT_MENU (self, wx.ID_ABOUT, self.OnAbout)
901
902 item = help_menu.Append(-1, _('About database'), _('Show information about the current database.'))
903 self.Bind(wx.EVT_MENU, self.__on_about_database, item)
904
905 item = help_menu.Append(-1, _('About contributors'), _('Show GNUmed contributors'))
906 self.Bind(wx.EVT_MENU, self.__on_show_contributors, item)
907
908 help_menu.AppendSeparator()
909
910 self.mainmenu.Append(help_menu, _("&Help"))
911
912 self.__gb['main.helpmenu'] = help_menu
913
914
915 self.SetMenuBar(self.mainmenu)
916
919
920
921
923 """register events we want to react to"""
924
925 wx.EVT_CLOSE(self, self.OnClose)
926 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
927 wx.EVT_END_SESSION(self, self._on_end_session)
928
929 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
930 gmDispatcher.connect(signal = u'dem.names_mod_db', receiver = self._on_pat_name_changed)
931 gmDispatcher.connect(signal = u'dem.identity_mod_db', receiver = self._on_pat_name_changed)
932 gmDispatcher.connect(signal = u'dem.praxis_branch_mod_db', receiver = self._on_pat_name_changed)
933 gmDispatcher.connect(signal = u'statustext', receiver = self._on_set_statustext)
934 gmDispatcher.connect(signal = u'request_user_attention', receiver = self._on_request_user_attention)
935
936 gmDispatcher.connect(signal = u'db_maintenance_warning', receiver = self._on_db_maintenance_warning)
937 gmDispatcher.connect(signal = u'register_pre_exit_callback', receiver = self._register_pre_exit_callback)
938 gmDispatcher.connect(signal = u'plugin_loaded', receiver = self._on_plugin_loaded)
939
940 gmPerson.gmCurrentPatient().register_pre_selection_callback(callback = self._pre_selection_callback)
941
942 - def _on_plugin_loaded(self, plugin_name=None, class_name=None, menu_name=None, menu_item_name=None, menu_help_string=None):
943
944 _log.debug('registering plugin with menu system')
945 _log.debug(' generic name: %s', plugin_name)
946 _log.debug(' class name: %s', class_name)
947 _log.debug(' specific menu: %s', menu_name)
948 _log.debug(' menu item: %s', menu_item_name)
949
950
951 item = self.menu_plugins.Append(-1, plugin_name, _('Raise plugin [%s].') % plugin_name)
952 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
953 self.menu_id2plugin[item.Id] = class_name
954
955
956 if menu_name is not None:
957 menu = self.__gb['main.%smenu' % menu_name]
958 item = menu.Append(-1, menu_item_name, menu_help_string)
959 self.Bind(wx.EVT_MENU, self.__on_raise_a_plugin, item)
960 self.menu_id2plugin[item.Id] = class_name
961
962 return True
963
965 gmDispatcher.send (
966 signal = u'display_widget',
967 name = self.menu_id2plugin[evt.Id]
968 )
969
971 wx.Bell()
972 wx.Bell()
973 wx.Bell()
974 _log.warning('unhandled event detected: QUERY_END_SESSION')
975 _log.info('we should be saving ourselves from here')
976 gmLog2.flush()
977 print "unhandled event detected: QUERY_END_SESSION"
978
980 wx.Bell()
981 wx.Bell()
982 wx.Bell()
983 _log.warning('unhandled event detected: END_SESSION')
984 gmLog2.flush()
985 print "unhandled event detected: END_SESSION"
986
988 if not callable(callback):
989 raise TypeError(u'callback [%s] not callable' % callback)
990
991 self.__pre_exit_callbacks.append(callback)
992
993 - def _on_set_statustext_pubsub(self, context=None):
994 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), context.data['msg'])
995 wx.CallAfter(self.SetStatusText, msg)
996
997 try:
998 if context.data['beep']:
999 wx.Bell()
1000 except KeyError:
1001 pass
1002
1003 - def _on_set_statustext(self, msg=None, loglevel=None, beep=True):
1004
1005 if msg is None:
1006 msg = _('programmer forgot to specify status message')
1007
1008 if loglevel is not None:
1009 _log.log(loglevel, msg.replace('\015', ' ').replace('\012', ' '))
1010
1011 msg = u'%s %s' % (gmDateTime.pydt_now_here().strftime('%H:%M'), msg)
1012 wx.CallAfter(self.SetStatusText, msg)
1013
1014 if beep:
1015 wx.Bell()
1016
1018 wx.CallAfter(self.__on_db_maintenance_warning)
1019
1021
1022 self.SetStatusText(_('The database will be shut down for maintenance in a few minutes.'))
1023 wx.Bell()
1024 if not wx.GetApp().IsActive():
1025 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1026
1027 gmHooks.run_hook_script(hook = u'db_maintenance_warning')
1028
1029 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
1030 None,
1031 -1,
1032 caption = _('Database shutdown warning'),
1033 question = _(
1034 'The database will be shut down for maintenance\n'
1035 'in a few minutes.\n'
1036 '\n'
1037 'In order to not suffer any loss of data you\n'
1038 'will need to save your current work and log\n'
1039 'out of this GNUmed client.\n'
1040 ),
1041 button_defs = [
1042 {
1043 u'label': _('Close now'),
1044 u'tooltip': _('Close this GNUmed client immediately.'),
1045 u'default': False
1046 },
1047 {
1048 u'label': _('Finish work'),
1049 u'tooltip': _('Finish and save current work first, then manually close this GNUmed client.'),
1050 u'default': True
1051 }
1052 ]
1053 )
1054 decision = dlg.ShowModal()
1055 if decision == wx.ID_YES:
1056 top_win = wx.GetApp().GetTopWindow()
1057 wx.CallAfter(top_win.Close)
1058
1060 wx.CallAfter(self.__on_request_user_attention, msg, urgent)
1061
1063
1064 if not wx.GetApp().IsActive():
1065 if urgent:
1066 self.RequestUserAttention(flags = wx.USER_ATTENTION_ERROR)
1067 else:
1068 self.RequestUserAttention(flags = wx.USER_ATTENTION_INFO)
1069
1070 if msg is not None:
1071 self.SetStatusText(msg)
1072
1073 if urgent:
1074 wx.Bell()
1075
1076 gmHooks.run_hook_script(hook = u'request_user_attention')
1077
1079 wx.CallAfter(self.__on_pat_name_changed)
1080
1082 self.__update_window_title()
1083
1084 - def _on_post_patient_selection(self, **kwargs):
1085 wx.CallAfter(self.__on_post_patient_selection, **kwargs)
1086
1088 self.__update_window_title()
1089 gmDispatcher.send(signal = 'statustext', msg = u'')
1090 try:
1091 gmHooks.run_hook_script(hook = u'post_patient_activation')
1092 except:
1093 gmDispatcher.send(signal = 'statustext', msg = _('Cannot run script after patient activation.'))
1094 raise
1095
1097 return self.__sanity_check_encounter()
1098
1100
1101
1102
1103 if _provider['role'] == u'secretary':
1104 return True
1105
1106 dbcfg = gmCfg.cCfgSQL()
1107 check_enc = bool(dbcfg.get2 (
1108 option = 'encounter.show_editor_before_patient_change',
1109 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
1110 bias = 'user',
1111 default = True
1112 ))
1113
1114 if not check_enc:
1115 return True
1116
1117 pat = gmPerson.gmCurrentPatient()
1118 emr = pat.get_emr()
1119 enc = emr.active_encounter
1120
1121
1122 has_narr = enc.has_narrative()
1123 has_docs = enc.has_documents()
1124
1125 if (not has_narr) and (not has_docs):
1126 return True
1127
1128 empty_aoe = (gmTools.coalesce(enc['assessment_of_encounter'], '').strip() == u'')
1129 zero_duration = (enc['last_affirmed'] == enc['started'])
1130
1131
1132 if (not empty_aoe) and (not zero_duration):
1133 return True
1134
1135 if zero_duration:
1136 enc['last_affirmed'] = pyDT.datetime.now(tz=gmDateTime.gmCurrentLocalTimezone)
1137
1138
1139 if not has_narr:
1140 if empty_aoe:
1141 enc['assessment_of_encounter'] = _('only documents added')
1142 enc['pk_type'] = gmEMRStructItems.get_encounter_type(description = 'chart review')[0]['pk']
1143
1144 enc.save_payload()
1145 return True
1146
1147
1148 if empty_aoe:
1149
1150 epis = emr.get_episodes_by_encounter()
1151 if len(epis) > 0:
1152 enc_summary = ''
1153 for epi in epis:
1154 enc_summary += '%s; ' % epi['description']
1155 enc['assessment_of_encounter'] = enc_summary
1156
1157 msg = _('Edit the current encounter of the patient you are ABOUT TO LEAVE:')
1158 gmEncounterWidgets.edit_encounter(parent = self, encounter = enc, msg = msg)
1159
1160 return True
1161
1162
1163
1166
1173
1177
1178
1179
1195
1226
1228 from Gnumed.wxpython import gmAbout
1229 contribs = gmAbout.cContributorsDlg (
1230 parent = self,
1231 id = -1,
1232 title = _('GNUmed contributors'),
1233 size = wx.Size(400,600),
1234 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER
1235 )
1236 contribs.ShowModal()
1237 del contribs
1238 del gmAbout
1239
1240
1241
1243 """Invoked from Menu GNUmed / Exit (which calls this ID_EXIT handler)."""
1244 _log.debug('gmTopLevelFrame._on_exit_gnumed() start')
1245 self.Close(True)
1246 _log.debug('gmTopLevelFrame._on_exit_gnumed() end')
1247
1250
1252 send = gmGuiHelpers.gm_show_question (
1253 _('This will send a notification about database downtime\n'
1254 'to all GNUmed clients connected to your database.\n'
1255 '\n'
1256 'Do you want to send the notification ?\n'
1257 ),
1258 _('Announcing database maintenance downtime')
1259 )
1260 if not send:
1261 return
1262 gmPG2.send_maintenance_notification()
1263
1264
1267
1268
1269
1282
1283 gmCfgWidgets.configure_string_option (
1284 message = _(
1285 'Some network installations cannot cope with loading\n'
1286 'documents of arbitrary size in one piece from the\n'
1287 'database (mainly observed on older Windows versions)\n.'
1288 '\n'
1289 'Under such circumstances documents need to be retrieved\n'
1290 'in chunks and reassembled on the client.\n'
1291 '\n'
1292 'Here you can set the size (in Bytes) above which\n'
1293 'GNUmed will retrieve documents in chunks. Setting this\n'
1294 'value to 0 will disable the chunking protocol.'
1295 ),
1296 option = 'horstspace.blob_export_chunk_size',
1297 bias = 'workplace',
1298 default_value = 1024 * 1024,
1299 validator = is_valid
1300 )
1301
1302
1303
1371
1375
1376
1377
1386
1387 gmCfgWidgets.configure_string_option (
1388 message = _(
1389 'When GNUmed cannot find an OpenOffice server it\n'
1390 'will try to start one. OpenOffice, however, needs\n'
1391 'some time to fully start up.\n'
1392 '\n'
1393 'Here you can set the time for GNUmed to wait for OOo.\n'
1394 ),
1395 option = 'external.ooo.startup_settle_time',
1396 bias = 'workplace',
1397 default_value = 2.0,
1398 validator = is_valid
1399 )
1400
1403
1418
1419 gmCfgWidgets.configure_string_option (
1420 message = _(
1421 'GNUmed will use this URL to access a website which lets\n'
1422 'you report an adverse drug reaction (ADR).\n'
1423 '\n'
1424 'If you leave this empty it will fall back\n'
1425 'to an URL for reporting ADRs in Germany.'
1426 ),
1427 option = 'external.urls.report_ADR',
1428 bias = 'user',
1429 default_value = german_default,
1430 validator = is_valid
1431 )
1432
1446
1447 gmCfgWidgets.configure_string_option (
1448 message = _(
1449 'GNUmed will use this URL to access a website which lets\n'
1450 'you report an adverse vaccination reaction (vADR).\n'
1451 '\n'
1452 'If you set it to a specific address that URL must be\n'
1453 'accessible now. If you leave it empty it will fall back\n'
1454 'to the URL for reporting other adverse drug reactions.'
1455 ),
1456 option = 'external.urls.report_vaccine_ADR',
1457 bias = 'user',
1458 default_value = german_default,
1459 validator = is_valid
1460 )
1461
1475
1476 gmCfgWidgets.configure_string_option (
1477 message = _(
1478 'GNUmed will use this URL to access an encyclopedia of\n'
1479 'measurement/lab methods from within the measurments grid.\n'
1480 '\n'
1481 'You can leave this empty but to set it to a specific\n'
1482 'address the URL must be accessible now.'
1483 ),
1484 option = 'external.urls.measurements_encyclopedia',
1485 bias = 'user',
1486 default_value = german_default,
1487 validator = is_valid
1488 )
1489
1503
1504 gmCfgWidgets.configure_string_option (
1505 message = _(
1506 'GNUmed will use this URL to access a page showing\n'
1507 'vaccination schedules.\n'
1508 '\n'
1509 'You can leave this empty but to set it to a specific\n'
1510 'address the URL must be accessible now.'
1511 ),
1512 option = 'external.urls.vaccination_plans',
1513 bias = 'user',
1514 default_value = german_default,
1515 validator = is_valid
1516 )
1517
1530
1531 gmCfgWidgets.configure_string_option (
1532 message = _(
1533 'Enter the shell command with which to start the\n'
1534 'the ACS risk assessment calculator.\n'
1535 '\n'
1536 'GNUmed will try to verify the path which may,\n'
1537 'however, fail if you are using an emulator such\n'
1538 'as Wine. Nevertheless, starting the calculator\n'
1539 'will work as long as the shell command is correct\n'
1540 'despite the failing test.'
1541 ),
1542 option = 'external.tools.acs_risk_calculator_cmd',
1543 bias = 'user',
1544 validator = is_valid
1545 )
1546
1549
1562
1563 gmCfgWidgets.configure_string_option (
1564 message = _(
1565 'Enter the shell command with which to start\n'
1566 'the FreeDiams drug database frontend.\n'
1567 '\n'
1568 'GNUmed will try to verify that path.'
1569 ),
1570 option = 'external.tools.freediams_cmd',
1571 bias = 'workplace',
1572 default_value = None,
1573 validator = is_valid
1574 )
1575
1588
1589 gmCfgWidgets.configure_string_option (
1590 message = _(
1591 'Enter the shell command with which to start the\n'
1592 'the IFAP drug database.\n'
1593 '\n'
1594 'GNUmed will try to verify the path which may,\n'
1595 'however, fail if you are using an emulator such\n'
1596 'as Wine. Nevertheless, starting IFAP will work\n'
1597 'as long as the shell command is correct despite\n'
1598 'the failing test.'
1599 ),
1600 option = 'external.ifap-win.shell_command',
1601 bias = 'workplace',
1602 default_value = 'C:\Ifapwin\WIAMDB.EXE',
1603 validator = is_valid
1604 )
1605
1606
1607
1656
1657
1658
1675
1678
1681
1686
1687 gmCfgWidgets.configure_string_option (
1688 message = _(
1689 'When a patient is activated GNUmed checks the\n'
1690 "proximity of the patient's birthday.\n"
1691 '\n'
1692 'If the birthday falls within the range of\n'
1693 ' "today %s <the interval you set here>"\n'
1694 'GNUmed will remind you of the recent or\n'
1695 'imminent anniversary.'
1696 ) % u'\u2213',
1697 option = u'patient_search.dob_warn_interval',
1698 bias = 'user',
1699 default_value = '1 week',
1700 validator = is_valid
1701 )
1702
1704
1705 gmCfgWidgets.configure_boolean_option (
1706 parent = self,
1707 question = _(
1708 'When adding progress notes do you want to\n'
1709 'allow opening several unassociated, new\n'
1710 'episodes for a patient at once ?\n'
1711 '\n'
1712 'This can be particularly helpful when entering\n'
1713 'progress notes on entirely new patients presenting\n'
1714 'with a multitude of problems on their first visit.'
1715 ),
1716 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
1717 button_tooltips = [
1718 _('Yes, allow for multiple new episodes concurrently.'),
1719 _('No, only allow editing one new episode at a time.')
1720 ]
1721 )
1722
1724
1725 gmCfgWidgets.configure_boolean_option (
1726 parent = self,
1727 question = _(
1728 'When activating a patient, do you want GNUmed to\n'
1729 'auto-open editors for all active problems that were\n'
1730 'touched upon during the current and the most recent\n'
1731 'encounter ?'
1732 ),
1733 option = u'horstspace.soap_editor.auto_open_latest_episodes',
1734 button_tooltips = [
1735 _('Yes, auto-open editors for all problems of the most recent encounter.'),
1736 _('No, only auto-open one editor for a new, unassociated problem.')
1737 ]
1738 )
1739
1785
1786
1787
1790
1793
1806
1807 gmCfgWidgets.configure_string_option (
1808 message = _(
1809 'GNUmed will use this URL to let you browse\n'
1810 'billing catalogs (schedules of fees).\n'
1811 '\n'
1812 'You can leave this empty but to set it to a specific\n'
1813 'address the URL must be accessible now.'
1814 ),
1815 option = 'external.urls.schedules_of_fees',
1816 bias = 'user',
1817 default_value = german_default,
1818 validator = is_valid
1819 )
1820
1821
1822
1825
1828
1830 gmCfgWidgets.configure_string_from_list_option (
1831 parent = self,
1832 message = _('Select the default prescription mode.\n'),
1833 option = 'horst_space.default_prescription_mode',
1834 bias = 'user',
1835 default_value = u'form',
1836 choices = [ _('Formular'), _('Datenbank') ],
1837 columns = [_('Prescription mode')],
1838 data = [ u'form', u'database' ]
1839 )
1840
1843
1846
1849
1851 enc_types = gmEMRStructItems.get_encounter_types()
1852 msg = _(
1853 'Select the default type for new encounters.\n'
1854 '\n'
1855 'Leaving this unset will make GNUmed apply the most commonly used type.\n'
1856 )
1857 gmCfgWidgets.configure_string_from_list_option (
1858 parent = self,
1859 message = msg,
1860 option = 'encounter.default_type',
1861 bias = 'user',
1862
1863 choices = [ e[0] for e in enc_types ],
1864 columns = [_('Encounter type')],
1865 data = [ e[1] for e in enc_types ]
1866 )
1867
1869 gmCfgWidgets.configure_boolean_option (
1870 parent = self,
1871 question = _(
1872 'Do you want GNUmed to show the encounter\n'
1873 'details editor when changing the active patient ?'
1874 ),
1875 option = 'encounter.show_editor_before_patient_change',
1876 button_tooltips = [
1877 _('Yes, show the encounter editor if it seems appropriate.'),
1878 _('No, never show the encounter editor even if it would seem useful.')
1879 ]
1880 )
1881
1886
1887 gmCfgWidgets.configure_string_option (
1888 message = _(
1889 'When a patient is activated GNUmed checks the\n'
1890 'chart for encounters lacking any entries.\n'
1891 '\n'
1892 'Any such encounters older than what you set\n'
1893 'here will be removed from the medical record.\n'
1894 '\n'
1895 'To effectively disable removal of such encounters\n'
1896 'set this option to an improbable value.\n'
1897 ),
1898 option = 'encounter.ttl_if_empty',
1899 bias = 'user',
1900 default_value = '1 week',
1901 validator = is_valid
1902 )
1903
1908
1909 gmCfgWidgets.configure_string_option (
1910 message = _(
1911 'When a patient is activated GNUmed checks the\n'
1912 'age of the most recent encounter.\n'
1913 '\n'
1914 'If that encounter is younger than this age\n'
1915 'the existing encounter will be continued.\n'
1916 '\n'
1917 '(If it is really old a new encounter is\n'
1918 ' started, or else GNUmed will ask you.)\n'
1919 ),
1920 option = 'encounter.minimum_ttl',
1921 bias = 'user',
1922 default_value = '1 hour 30 minutes',
1923 validator = is_valid
1924 )
1925
1930
1931 gmCfgWidgets.configure_string_option (
1932 message = _(
1933 'When a patient is activated GNUmed checks the\n'
1934 'age of the most recent encounter.\n'
1935 '\n'
1936 'If that encounter is older than this age\n'
1937 'GNUmed will always start a new encounter.\n'
1938 '\n'
1939 '(If it is very recent the existing encounter\n'
1940 ' is continued, or else GNUmed will ask you.)\n'
1941 ),
1942 option = 'encounter.maximum_ttl',
1943 bias = 'user',
1944 default_value = '6 hours',
1945 validator = is_valid
1946 )
1947
1956
1957 gmCfgWidgets.configure_string_option (
1958 message = _(
1959 'At any time there can only be one open (ongoing)\n'
1960 'episode for each health issue.\n'
1961 '\n'
1962 'When you try to open (add data to) an episode on a health\n'
1963 'issue GNUmed will check for an existing open episode on\n'
1964 'that issue. If there is any it will check the age of that\n'
1965 'episode. The episode is closed if it has been dormant (no\n'
1966 'data added, that is) for the period of time (in days) you\n'
1967 'set here.\n'
1968 '\n'
1969 "If the existing episode hasn't been dormant long enough\n"
1970 'GNUmed will consult you what to do.\n'
1971 '\n'
1972 'Enter maximum episode dormancy in DAYS:'
1973 ),
1974 option = 'episode.ttl',
1975 bias = 'user',
1976 default_value = 60,
1977 validator = is_valid
1978 )
1979
2010
2025
2050
2062
2063 gmCfgWidgets.configure_string_option (
2064 message = _(
2065 'GNUmed can check for new releases being available. To do\n'
2066 'so it needs to load version information from an URL.\n'
2067 '\n'
2068 'The default URL is:\n'
2069 '\n'
2070 ' http://www.gnumed.de/downloads/gnumed-versions.txt\n'
2071 '\n'
2072 'but you can configure any other URL locally. Note\n'
2073 'that you must enter the location as a valid URL.\n'
2074 'Depending on the URL the client will need online\n'
2075 'access when checking for updates.'
2076 ),
2077 option = u'horstspace.update.url',
2078 bias = u'workplace',
2079 default_value = u'http://www.gnumed.de/downloads/gnumed-versions.txt',
2080 validator = is_valid
2081 )
2082
2100
2117
2134
2145
2146 gmCfgWidgets.configure_string_option (
2147 message = _(
2148 'GNUmed can show the document review dialog after\n'
2149 'calling the appropriate viewer for that document.\n'
2150 '\n'
2151 'Select the conditions under which you want\n'
2152 'GNUmed to do so:\n'
2153 '\n'
2154 ' 0: never display the review dialog\n'
2155 ' 1: always display the dialog\n'
2156 ' 2: only if there is no previous review by me\n'
2157 ' 3: only if there is no previous review at all\n'
2158 ' 4: only if there is no review by the responsible reviewer\n'
2159 '\n'
2160 'Note that if a viewer is configured to not block\n'
2161 'GNUmed during document display the review dialog\n'
2162 'will actually appear in parallel to the viewer.'
2163 ),
2164 option = u'horstspace.document_viewer.review_after_display',
2165 bias = u'user',
2166 default_value = 3,
2167 validator = is_valid
2168 )
2169
2171
2172
2173 master_data_lists = [
2174 'adr',
2175 'billables',
2176 'drugs',
2177 'hints',
2178 'codes',
2179 'communication_channel_types',
2180 'substances_in_brands',
2181 'substances',
2182 'labs',
2183 'form_templates',
2184 'doc_types',
2185 'enc_types',
2186 'text_expansions',
2187 'meta_test_types',
2188 'orgs',
2189 'patient_tags',
2190 'provinces',
2191 'db_translations',
2192 'ref_data_sources',
2193 'test_types',
2194 'test_panels',
2195 'vacc_indications',
2196 'vaccines',
2197 'workplaces'
2198 ]
2199
2200 master_data_list_names = {
2201 'adr': _('Addresses (likely slow)'),
2202 'drugs': _('Branded drugs (as marketed)'),
2203 'hints': _('Clinical hints'),
2204 'codes': _('Codes and their respective terms'),
2205 'communication_channel_types': _('Communication channel types'),
2206 'substances_in_brands': _('Components of branded drugs (substances in brands)'),
2207 'labs': _('Diagnostic organizations (path labs, ...)'),
2208 'form_templates': _('Document templates (forms, letters, plots, ...)'),
2209 'doc_types': _('Document types'),
2210 'enc_types': _('Encounter types'),
2211 'text_expansions': _('Keyword based text expansion macros'),
2212 'meta_test_types': _('Meta test/measurement types'),
2213 'orgs': _('Organizations with their units, addresses, and comm channels'),
2214 'patient_tags': _('Patient tags'),
2215 'provinces': _('Provinces (counties, territories, states, regions, ...)'),
2216 'db_translations': _('String translations in the database'),
2217 'test_types': _('Test/measurement types'),
2218 'vacc_indications': _('Vaccination targets (conditions known to be preventable by vaccination)'),
2219 'vaccines': _('Vaccines'),
2220 'workplaces': _('Workplace profiles (which plugins to load)'),
2221 'substances': _('Consumable substances'),
2222 'billables': _('Billable items'),
2223 'ref_data_sources': _('Reference data sources'),
2224 'test_panels': _('Test/measurement panels/profiles')
2225 }
2226
2227 map_list2handler = {
2228 'form_templates': gmFormWidgets.manage_form_templates,
2229 'doc_types': gmDocumentWidgets.manage_document_types,
2230 'text_expansions': gmKeywordExpansionWidgets.configure_keyword_text_expansion,
2231 'db_translations': gmI18nWidgets.manage_translations,
2232 'codes': gmCodingWidgets.browse_coded_terms,
2233 'enc_types': gmEncounterWidgets.manage_encounter_types,
2234 'provinces': gmAddressWidgets.manage_provinces,
2235 'workplaces': gmPraxisWidgets.configure_workplace_plugins,
2236 'drugs': gmMedicationWidgets.manage_branded_drugs,
2237 'substances_in_brands': gmMedicationWidgets.manage_drug_components,
2238 'labs': gmMeasurementWidgets.manage_measurement_orgs,
2239 'test_types': gmMeasurementWidgets.manage_measurement_types,
2240 'meta_test_types': gmMeasurementWidgets.manage_meta_test_types,
2241 'vaccines': gmVaccWidgets.manage_vaccines,
2242 'vacc_indications': gmVaccWidgets.manage_vaccination_indications,
2243 'orgs': gmOrganizationWidgets.manage_orgs,
2244 'adr': gmAddressWidgets.manage_addresses,
2245 'substances': gmMedicationWidgets.manage_consumable_substances,
2246 'patient_tags': gmDemographicsWidgets.manage_tag_images,
2247 'communication_channel_types': gmContactWidgets.manage_comm_channel_types,
2248 'billables': gmBillingWidgets.manage_billables,
2249 'ref_data_sources': gmCodingWidgets.browse_data_sources,
2250 'hints': gmProviderInboxWidgets.browse_dynamic_hints,
2251 'test_panels': gmMeasurementWidgets.manage_test_panels
2252 }
2253
2254
2255 def edit(item):
2256 try: map_list2handler[item](parent = self)
2257 except KeyError: pass
2258 return False
2259
2260
2261 gmListWidgets.get_choices_from_list (
2262 parent = self,
2263 caption = _('Master data management'),
2264 choices = [ master_data_list_names[lst] for lst in master_data_lists],
2265 data = master_data_lists,
2266 columns = [_('Select the list you want to manage:')],
2267 edit_callback = edit,
2268 single_selection = True,
2269 ignore_OK_button = True
2270 )
2271
2274
2276
2277 found, cmd = gmShellAPI.detect_external_binary(binary = 'ginkgocadx')
2278 if found:
2279 gmShellAPI.run_command_in_shell(cmd, blocking=False)
2280 return
2281
2282 if os.access('/Applications/OsiriX.app/Contents/MacOS/OsiriX', os.X_OK):
2283 gmShellAPI.run_command_in_shell('/Applications/OsiriX.app/Contents/MacOS/OsiriX', blocking = False)
2284 return
2285
2286 for viewer in ['aeskulap', 'amide', 'dicomscope', 'xmedcon']:
2287 found, cmd = gmShellAPI.detect_external_binary(binary = viewer)
2288 if found:
2289 gmShellAPI.run_command_in_shell(cmd, blocking = False)
2290 return
2291
2292 gmDispatcher.send(signal = 'statustext', msg = _('No DICOM viewer found.'), beep = True)
2293
2295
2296 curr_pat = gmPerson.gmCurrentPatient()
2297
2298 arriba = gmArriba.cArriba()
2299 pat = gmTools.bool2subst(curr_pat.connected, curr_pat, None)
2300 if not arriba.run(patient = pat, debug = _cfg.get(option = 'debug')):
2301 return
2302
2303
2304 if curr_pat is None:
2305 return
2306
2307 if arriba.pdf_result is None:
2308 return
2309
2310 doc = gmDocumentWidgets.save_file_as_new_document (
2311 parent = self,
2312 filename = arriba.pdf_result,
2313 document_type = _('risk assessment')
2314 )
2315
2316 try: os.remove(arriba.pdf_result)
2317 except StandardError: _log.exception('cannot remove [%s]', arriba.pdf_result)
2318
2319 if doc is None:
2320 return
2321
2322 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2323 doc.save()
2324
2325 try:
2326 open(arriba.xml_result).close()
2327 part = doc.add_part(file = arriba.xml_result)
2328 except StandardError:
2329 _log.exception('error accessing [%s]', arriba.xml_result)
2330 gmDispatcher.send(signal = u'statustext', msg = _('[arriba] XML result not found in [%s]') % arriba.xml_result, beep = False)
2331
2332 if part is None:
2333 return
2334
2335 part['obj_comment'] = u'XML-Daten'
2336 part['filename'] = u'arriba-result.xml'
2337 part.save()
2338
2340
2341 dbcfg = gmCfg.cCfgSQL()
2342 cmd = dbcfg.get2 (
2343 option = u'external.tools.acs_risk_calculator_cmd',
2344 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
2345 bias = 'user'
2346 )
2347
2348 if cmd is None:
2349 gmDispatcher.send(signal = u'statustext', msg = _('ACS risk assessment calculator not configured.'), beep = True)
2350 return
2351
2352 cwd = os.path.expanduser(os.path.join('~', '.gnumed'))
2353 try:
2354 subprocess.check_call (
2355 args = (cmd,),
2356 close_fds = True,
2357 cwd = cwd
2358 )
2359 except (OSError, ValueError, subprocess.CalledProcessError):
2360 _log.exception('there was a problem executing [%s]', cmd)
2361 gmDispatcher.send(signal = u'statustext', msg = _('Cannot run [%s] !') % cmd, beep = True)
2362 return
2363
2364 pdfs = glob.glob(os.path.join(cwd, 'arriba-%s-*.pdf' % gmDateTime.pydt_now_here().strftime('%Y-%m-%d')))
2365 for pdf in pdfs:
2366 try:
2367 open(pdf).close()
2368 except:
2369 _log.exception('error accessing [%s]', pdf)
2370 gmDispatcher.send(signal = u'statustext', msg = _('There was a problem accessing the [arriba] result in [%s] !') % pdf, beep = True)
2371 continue
2372
2373 doc = gmDocumentWidgets.save_file_as_new_document (
2374 parent = self,
2375 filename = pdf,
2376 document_type = u'risk assessment'
2377 )
2378
2379 try:
2380 os.remove(pdf)
2381 except StandardError:
2382 _log.exception('cannot remove [%s]', pdf)
2383
2384 if doc is None:
2385 continue
2386 doc['comment'] = u'arriba: %s' % _('cardiovascular risk assessment')
2387 doc.save()
2388
2389 return
2390
2393
2396
2399
2401 dlg = gmSnellen.cSnellenCfgDlg()
2402 if dlg.ShowModal() != wx.ID_OK:
2403 return
2404
2405 frame = gmSnellen.cSnellenChart (
2406 width = dlg.vals[0],
2407 height = dlg.vals[1],
2408 alpha = dlg.vals[2],
2409 mirr = dlg.vals[3],
2410 parent = None
2411 )
2412 frame.CentreOnScreen(wx.BOTH)
2413
2414
2415 frame.Show(True)
2416
2417
2420
2423
2426
2427
2428
2432
2436
2440
2441
2442
2444 wx.CallAfter(self.__save_screenshot)
2445 evt.Skip()
2446
2448
2449 time.sleep(0.5)
2450
2451 rect = self.GetRect()
2452
2453
2454 if sys.platform == 'linux2':
2455 client_x, client_y = self.ClientToScreen((0, 0))
2456 border_width = client_x - rect.x
2457 title_bar_height = client_y - rect.y
2458
2459 if self.GetMenuBar():
2460 title_bar_height /= 2
2461 rect.width += (border_width * 2)
2462 rect.height += title_bar_height + border_width
2463
2464 wdc = wx.ScreenDC()
2465 mdc = wx.MemoryDC()
2466 img = wx.EmptyBitmap(rect.width, rect.height)
2467 mdc.SelectObject(img)
2468 mdc.Blit (
2469 0, 0,
2470 rect.width, rect.height,
2471 wdc,
2472 rect.x, rect.y
2473 )
2474
2475
2476 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'gnumed-screenshot-%s.png')) % pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')
2477 img.SaveFile(fname, wx.BITMAP_TYPE_PNG)
2478 gmDispatcher.send(signal = 'statustext', msg = _('Saved screenshot to file [%s].') % fname)
2479
2481
2482 raise ValueError('raised ValueError to test exception handling')
2483
2485 raise gmExceptions.AccessDenied (
2486 _('[-9999]: <access violation test error>'),
2487 source = u'GNUmed code',
2488 code = -9999,
2489 details = _('This is a deliberate AcessDenied exception thrown to test the handling of access violations by means of a decorator.')
2490 )
2491
2492 @gmAccessPermissionWidgets.verify_minimum_required_role('admin', activity = _('testing access check for non-existant <admin> role'))
2494 raise gmExceptions.AccessDenied (
2495 _('[-9999]: <access violation test error>'),
2496 source = u'GNUmed code',
2497 code = -9999,
2498 details = _('This is a deliberate AcessDenied exception. You should not see this message because the role is checked in a decorator.')
2499 )
2500
2502 import wx.lib.inspection
2503 wx.lib.inspection.InspectionTool().Show()
2504
2507
2510
2513
2516
2523
2527
2530
2533
2540
2545
2547 name = os.path.basename(gmLog2._logfile_name)
2548 name, ext = os.path.splitext(name)
2549 new_name = '%s_%s%s' % (name, pyDT.datetime.now().strftime('%Y-%m-%d_%H-%M-%S'), ext)
2550 new_path = os.path.expanduser(os.path.join('~', 'gnumed'))
2551
2552 dlg = wx.FileDialog (
2553 parent = self,
2554 message = _("Save current log as..."),
2555 defaultDir = new_path,
2556 defaultFile = new_name,
2557 wildcard = "%s (*.log)|*.log" % _("log files"),
2558 style = wx.SAVE
2559 )
2560 choice = dlg.ShowModal()
2561 new_name = dlg.GetPath()
2562 dlg.Destroy()
2563 if choice != wx.ID_OK:
2564 return True
2565
2566 _log.warning('syncing log file for backup to [%s]', new_name)
2567 gmLog2.flush()
2568 shutil.copy2(gmLog2._logfile_name, new_name)
2569 gmDispatcher.send('statustext', msg = _('Log file backed up as [%s].') % new_name)
2570
2573
2574
2575
2577 """This is the wx.EVT_CLOSE handler.
2578
2579 - framework still functional
2580 """
2581 _log.debug('gmTopLevelFrame.OnClose() start')
2582 self._clean_exit()
2583 self.Destroy()
2584 _log.debug('gmTopLevelFrame.OnClose() end')
2585 return True
2586
2592
2597
2605
2612
2619
2626
2636
2644
2652
2660
2668
2669 @gmAccessPermissionWidgets.verify_minimum_required_role('full clinical access', activity = _('manage vaccinations'))
2678
2687
2694
2711
2714
2717
2719
2720 pat = gmPerson.gmCurrentPatient()
2721 if not pat.connected:
2722 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR journal. No active patient.'))
2723 return False
2724
2725 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
2726 aDefDir = os.path.expanduser(os.path.join('~', 'gnumed', pat['dirname']))
2727 gmTools.mkdir(aDefDir)
2728
2729 fname = '%s-%s_%s.txt' % (_('emr-journal'), pat['lastnames'], pat['firstnames'])
2730 dlg = wx.FileDialog (
2731 parent = self,
2732 message = _("Save patient's EMR journal as..."),
2733 defaultDir = aDefDir,
2734 defaultFile = fname,
2735 wildcard = aWildcard,
2736 style = wx.SAVE
2737 )
2738 choice = dlg.ShowModal()
2739 fname = dlg.GetPath()
2740 dlg.Destroy()
2741 if choice != wx.ID_OK:
2742 return True
2743
2744 _log.debug('exporting EMR journal to [%s]' % fname)
2745
2746 exporter = gmPatientExporter.cEMRJournalExporter()
2747
2748 wx.BeginBusyCursor()
2749 try:
2750 fname = exporter.export_to_file(filename = fname)
2751 except:
2752 wx.EndBusyCursor()
2753 gmGuiHelpers.gm_show_error (
2754 _('Error exporting patient EMR as chronological journal.'),
2755 _('EMR journal export')
2756 )
2757 raise
2758 wx.EndBusyCursor()
2759
2760 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported EMR as chronological journal into file [%s].') % fname, beep=False)
2761
2762 return True
2763
2770
2772 curr_pat = gmPerson.gmCurrentPatient()
2773 if not curr_pat.connected:
2774 gmDispatcher.send(signal = 'statustext', msg = _('Cannot add tag to person. No active patient.'))
2775 return
2776
2777 tag = gmDemographicsWidgets.manage_tag_images(parent = self)
2778 if tag is None:
2779 return
2780
2781 tag = curr_pat.add_tag(tag['pk_tag_image'])
2782 msg = _('Edit the comment on tag [%s]') % tag['l10n_description']
2783 comment = wx.GetTextFromUser (
2784 message = msg,
2785 caption = _('Editing tag comment'),
2786 default_value = gmTools.coalesce(tag['comment'], u''),
2787 parent = self
2788 )
2789
2790 if comment == u'':
2791 return
2792
2793 if comment.strip() == tag['comment']:
2794 return
2795
2796 if comment == u' ':
2797 tag['comment'] = None
2798 else:
2799 tag['comment'] = comment.strip()
2800
2801 tag.save()
2802
2812
2814 curr_pat = gmPerson.gmCurrentPatient()
2815 if not curr_pat.connected:
2816 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export patient as GDT. No active patient.'))
2817 return False
2818 enc = 'cp850'
2819 fname = os.path.expanduser(os.path.join('~', 'gnumed', 'current-patient.gdt'))
2820 curr_pat.export_as_gdt(filename = fname, encoding = enc)
2821 gmDispatcher.send(signal = 'statustext', msg = _('Exported demographics to GDT file [%s].') % fname)
2822
2825
2828
2836
2844
2847
2854
2858
2861
2864
2867
2870
2875
2877 """Cleanup helper.
2878
2879 - should ALWAYS be called when this program is
2880 to be terminated
2881 - ANY code that should be executed before a
2882 regular shutdown should go in here
2883 - framework still functional
2884 """
2885 _log.debug('gmTopLevelFrame._clean_exit() start')
2886
2887
2888 listener = gmBackendListener.gmBackendListener()
2889 try:
2890 listener.shutdown()
2891 except:
2892 _log.exception('cannot stop backend notifications listener thread')
2893
2894
2895 if _scripting_listener is not None:
2896 try:
2897 _scripting_listener.shutdown()
2898 except:
2899 _log.exception('cannot stop scripting listener thread')
2900
2901
2902 self.clock_update_timer.Stop()
2903 gmTimer.shutdown()
2904 gmPhraseWheel.shutdown()
2905
2906
2907 for call_back in self.__pre_exit_callbacks:
2908 try:
2909 call_back()
2910 except:
2911 print "*** pre-exit callback failed ***"
2912 print call_back
2913 _log.exception('callback [%s] failed', call_back)
2914
2915
2916 gmDispatcher.send(u'application_closing')
2917
2918
2919 gmDispatcher.disconnect(self._on_set_statustext, 'statustext')
2920
2921
2922 curr_width, curr_height = self.GetClientSizeTuple()
2923 _log.info('GUI size at shutdown: [%s:%s]' % (curr_width, curr_height))
2924 dbcfg = gmCfg.cCfgSQL()
2925 dbcfg.set (
2926 option = 'main.window.width',
2927 value = curr_width,
2928 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace
2929 )
2930 dbcfg.set (
2931 option = 'main.window.height',
2932 value = curr_height,
2933 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace
2934 )
2935
2936 if _cfg.get(option = 'debug'):
2937 print '---=== GNUmed shutdown ===---'
2938 try:
2939 print _('You have to manually close this window to finalize shutting down GNUmed.')
2940 print _('This is so that you can inspect the console output at your leisure.')
2941 except UnicodeEncodeError:
2942 print 'You have to manually close this window to finalize shutting down GNUmed.'
2943 print 'This is so that you can inspect the console output at your leisure.'
2944 print '---=== GNUmed shutdown ===---'
2945
2946
2947 gmExceptionHandlingWidgets.uninstall_wx_exception_handler()
2948
2949
2950 import threading
2951 _log.debug("%s active threads", threading.activeCount())
2952 for t in threading.enumerate():
2953 _log.debug('thread %s', t)
2954
2955 _log.debug('gmTopLevelFrame._clean_exit() end')
2956
2957
2958
2960
2961 if _cfg.get(option = 'slave'):
2962 self.__title_template = u'GMdS: %%(pat)s [%%(prov)s@%%(wp)s in %%(site)s of %%(prax)s] (%s:%s)' % (
2963 _cfg.get(option = 'slave personality'),
2964 _cfg.get(option = 'xml-rpc port')
2965 )
2966 else:
2967 self.__title_template = u'GMd: %(pat)s [%(prov)s@%(wp)s in %(site)s of %(prax)s]'
2968
2970 """Update title of main window based on template.
2971
2972 This gives nice tooltips on iconified GNUmed instances.
2973
2974 User research indicates that in the title bar people want
2975 the date of birth, not the age, so please stick to this
2976 convention.
2977 """
2978 args = {}
2979
2980 pat = gmPerson.gmCurrentPatient()
2981 if pat.connected:
2982 args['pat'] = u'%s %s %s (%s) #%d' % (
2983 gmTools.coalesce(pat['title'], u'', u'%.4s'),
2984 pat['firstnames'],
2985 pat['lastnames'],
2986 pat.get_formatted_dob(format = '%Y %b %d', encoding = gmI18N.get_encoding()),
2987 pat['pk_identity']
2988 )
2989 else:
2990 args['pat'] = _('no patient')
2991
2992 args['prov'] = u'%s%s.%s' % (
2993 gmTools.coalesce(_provider['title'], u'', u'%s '),
2994 _provider['firstnames'][:1],
2995 _provider['lastnames']
2996 )
2997
2998 praxis = gmPraxis.gmCurrentPraxisBranch()
2999 args['wp'] = praxis.active_workplace
3000 args['site'] = praxis['branch']
3001 args['prax'] = praxis['praxis']
3002
3003 self.SetTitle(self.__title_template % args)
3004
3005
3007 sb = self.CreateStatusBar(2, wx.ST_SIZEGRIP)
3008 sb.SetStatusWidths([-1, 225])
3009
3010 self.clock_update_timer = wx.PyTimer(self._cb_update_clock)
3011 self._cb_update_clock()
3012
3013 self.clock_update_timer.Start(milliseconds = 1000)
3014
3016 """Displays date and local time in the second slot of the status bar"""
3017 t = time.localtime(time.time())
3018 st = time.strftime('%Y %b %d %H:%M:%S', t).decode(gmI18N.get_encoding(), 'replace')
3019 self.SetStatusText(st, 1)
3020
3022 """Lock GNUmed client against unauthorized access"""
3023
3024
3025
3026 return
3027
3029 """Unlock the main notebook widgets
3030 As long as we are not logged into the database backend,
3031 all pages but the 'login' page of the main notebook widget
3032 are locked; i.e. not accessible by the user
3033 """
3034
3035
3036
3037
3038
3039 return
3040
3042 wx.LayoutAlgorithm().LayoutWindow (self.LayoutMgr, self.nb)
3043
3045
3047
3048 self.__starting_up = True
3049
3050 gmExceptionHandlingWidgets.install_wx_exception_handler()
3051 gmExceptionHandlingWidgets.set_client_version(_cfg.get(option = 'client_version'))
3052
3053
3054 self.SetAppName(u'gnumed')
3055 self.SetVendorName(u'The GNUmed Development Community.')
3056 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3057 paths.init_paths(wx = wx, app_name = u'gnumed')
3058
3059 if not self.__setup_prefs_file():
3060 return False
3061
3062 gmExceptionHandlingWidgets.set_sender_email(gmPraxis.gmCurrentPraxisBranch().user_email)
3063
3064 self.__guibroker = gmGuiBroker.GuiBroker()
3065 self.__setup_platform()
3066
3067 if not self.__establish_backend_connection():
3068 return False
3069 if not self.__verify_db_account():
3070 return False
3071 if not self.__verify_praxis_branch():
3072 return False
3073
3074 self.__check_db_lang()
3075 self.__update_workplace_list()
3076
3077 if not _cfg.get(option = 'skip-update-check'):
3078 self.__check_for_updates()
3079
3080 if _cfg.get(option = 'slave'):
3081 if not self.__setup_scripting_listener():
3082 return False
3083
3084
3085 frame = gmTopLevelFrame(None, -1, _('GNUmed client'), (640, 440))
3086 frame.CentreOnScreen(wx.BOTH)
3087 self.SetTopWindow(frame)
3088 frame.Show(True)
3089
3090 if _cfg.get(option = 'debug'):
3091 self.RedirectStdio()
3092 self.SetOutputWindowAttributes(title = _('GNUmed stdout/stderr window'))
3093
3094
3095 print '---=== GNUmed startup ===---'
3096 print _('redirecting STDOUT/STDERR to this log window')
3097 print '---=== GNUmed startup ===---'
3098
3099 self.__setup_user_activity_timer()
3100 self.__register_events()
3101
3102 wx.CallAfter(self._do_after_init)
3103
3104 return True
3105
3107 """Called internally by wxPython after EVT_CLOSE has been handled on last frame.
3108
3109 - after destroying all application windows and controls
3110 - before wx.Windows internal cleanup
3111 """
3112 _log.debug('gmApp.OnExit() start')
3113
3114 self.__shutdown_user_activity_timer()
3115
3116 if _cfg.get(option = 'debug'):
3117 self.RestoreStdio()
3118 sys.stdin = sys.__stdin__
3119 sys.stdout = sys.__stdout__
3120 sys.stderr = sys.__stderr__
3121
3122 _log.debug('gmApp.OnExit() end')
3123
3125 wx.Bell()
3126 wx.Bell()
3127 wx.Bell()
3128 _log.warning('unhandled event detected: QUERY_END_SESSION')
3129 _log.info('we should be saving ourselves from here')
3130 gmLog2.flush()
3131 print "unhandled event detected: QUERY_END_SESSION"
3132
3134 wx.Bell()
3135 wx.Bell()
3136 wx.Bell()
3137 _log.warning('unhandled event detected: END_SESSION')
3138 gmLog2.flush()
3139 print "unhandled event detected: END_SESSION"
3140
3151
3153 self.user_activity_detected = True
3154 evt.Skip()
3155
3157
3158 if self.user_activity_detected:
3159 self.elapsed_inactivity_slices = 0
3160 self.user_activity_detected = False
3161 self.elapsed_inactivity_slices += 1
3162 else:
3163 if self.elapsed_inactivity_slices >= self.max_user_inactivity_slices:
3164
3165 pass
3166
3167 self.user_activity_timer.Start(oneShot = True)
3168
3169
3170
3172 try:
3173 kwargs['originated_in_database']
3174 print '==> got notification from database "%s":' % kwargs['signal']
3175 except KeyError:
3176 print '==> received signal from client: "%s"' % kwargs['signal']
3177
3178 del kwargs['signal']
3179 for key in kwargs.keys():
3180 print ' [%s]: %s' % (key, kwargs[key])
3181
3187
3189 self.user_activity_detected = True
3190 self.elapsed_inactivity_slices = 0
3191
3192 self.max_user_inactivity_slices = 15
3193 self.user_activity_timer = gmTimer.cTimer (
3194 callback = self._on_user_activity_timer_expired,
3195 delay = 2000
3196 )
3197 self.user_activity_timer.Start(oneShot=True)
3198
3200 try:
3201 self.user_activity_timer.Stop()
3202 del self.user_activity_timer
3203 except:
3204 pass
3205
3207 wx.EVT_QUERY_END_SESSION(self, self._on_query_end_session)
3208 wx.EVT_END_SESSION(self, self._on_end_session)
3209
3210
3211
3212
3213
3214 self.Bind(wx.EVT_ACTIVATE_APP, self._on_app_activated)
3215
3216 self.Bind(wx.EVT_MOUSE_EVENTS, self._on_user_activity)
3217 self.Bind(wx.EVT_KEY_DOWN, self._on_user_activity)
3218
3219 if _cfg.get(option = 'debug'):
3220 gmDispatcher.connect(receiver = self._signal_debugging_monitor)
3221 _log.debug('connected signal monitor')
3222
3238
3251
3280
3282
3283 if not gmPraxisWidgets.set_active_praxis_branch(no_parent = True):
3284 return False
3285
3286 login = gmPG2.get_default_login()
3287 msg = u'\n'
3288 msg += _('Database <%s> on <%s>') % (
3289 login.database,
3290 gmTools.coalesce(login.host, u'localhost')
3291 )
3292 msg += u'\n\n'
3293
3294 praxis = gmPraxis.gmCurrentPraxisBranch()
3295 msg += _('Branch "%s" of praxis "%s"\n') % (
3296 praxis['branch'],
3297 praxis['praxis']
3298 )
3299 msg += u'\n\n'
3300
3301 banner = praxis.db_logon_banner
3302 if banner.strip() == u'':
3303 return True
3304 msg += banner
3305 msg += u'\n\n'
3306
3307 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3308 None,
3309 -1,
3310 caption = _('Verifying database'),
3311 question = gmTools.wrap(msg, 60, initial_indent = u' ', subsequent_indent = u' '),
3312 button_defs = [
3313 {'label': _('Connect'), 'tooltip': _('Yes, connect to this database.'), 'default': True},
3314 {'label': _('Disconnect'), 'tooltip': _('No, do not connect to this database.'), 'default': False}
3315 ]
3316 )
3317 log_on = dlg.ShowModal()
3318 dlg.Destroy()
3319 if log_on == wx.ID_YES:
3320 return True
3321 _log.info('user decided to not connect to this database')
3322 return False
3323
3337
3339 """Setup access to a config file for storing preferences."""
3340
3341 paths = gmTools.gmPaths(app_name = u'gnumed', wx = wx)
3342
3343 candidates = []
3344 explicit_file = _cfg.get(option = '--conf-file', source_order = [('cli', 'return')])
3345 if explicit_file is not None:
3346 candidates.append(explicit_file)
3347
3348 candidates.append(os.path.join(paths.user_config_dir, 'gnumed.conf'))
3349 candidates.append(os.path.join(paths.local_base_dir, 'gnumed.conf'))
3350 candidates.append(os.path.join(paths.working_dir, 'gnumed.conf'))
3351
3352 prefs_file = None
3353 for candidate in candidates:
3354 try:
3355 open(candidate, 'a+').close()
3356 prefs_file = candidate
3357 break
3358 except IOError:
3359 continue
3360
3361 if prefs_file is None:
3362 msg = _(
3363 'Cannot find configuration file in any of:\n'
3364 '\n'
3365 ' %s\n'
3366 'You may need to use the comand line option\n'
3367 '\n'
3368 ' --conf-file=<FILE>'
3369 ) % '\n '.join(candidates)
3370 gmGuiHelpers.gm_show_error(msg, _('Checking configuration files'))
3371 return False
3372
3373 _cfg.set_option(option = u'user_preferences_file', value = prefs_file)
3374 _log.info('user preferences file: %s', prefs_file)
3375
3376 return True
3377
3379
3380 from socket import error as SocketError
3381 from Gnumed.pycommon import gmScriptingListener
3382 from Gnumed.wxpython import gmMacro
3383
3384 slave_personality = gmTools.coalesce (
3385 _cfg.get (
3386 group = u'workplace',
3387 option = u'slave personality',
3388 source_order = [
3389 ('explicit', 'return'),
3390 ('workbase', 'return'),
3391 ('user', 'return'),
3392 ('system', 'return')
3393 ]
3394 ),
3395 u'gnumed-client'
3396 )
3397 _cfg.set_option(option = 'slave personality', value = slave_personality)
3398
3399
3400 port = int (
3401 gmTools.coalesce (
3402 _cfg.get (
3403 group = u'workplace',
3404 option = u'xml-rpc port',
3405 source_order = [
3406 ('explicit', 'return'),
3407 ('workbase', 'return'),
3408 ('user', 'return'),
3409 ('system', 'return')
3410 ]
3411 ),
3412 9999
3413 )
3414 )
3415 _cfg.set_option(option = 'xml-rpc port', value = port)
3416
3417 macro_executor = gmMacro.cMacroPrimitives(personality = slave_personality)
3418 global _scripting_listener
3419 try:
3420 _scripting_listener = gmScriptingListener.cScriptingListener(port = port, macro_executor = macro_executor)
3421 except SocketError, e:
3422 _log.exception('cannot start GNUmed XML-RPC server')
3423 gmGuiHelpers.gm_show_error (
3424 aMessage = (
3425 'Cannot start the GNUmed server:\n'
3426 '\n'
3427 ' [%s]'
3428 ) % e,
3429 aTitle = _('GNUmed startup')
3430 )
3431 return False
3432
3433 return True
3434
3455
3457 if gmI18N.system_locale is None or gmI18N.system_locale == '':
3458 _log.warning("system locale is undefined (probably meaning 'C')")
3459 return True
3460
3461 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': u"select i18n.get_curr_lang() as lang"}])
3462 curr_db_lang = rows[0]['lang']
3463 _log.debug("current database locale: [%s]" % curr_db_lang)
3464
3465 if curr_db_lang is None:
3466
3467 cmd = u'select i18n.set_curr_lang(%s)'
3468 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]:
3469 if len(lang) == 0:
3470 continue
3471 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [lang]}], return_data = True)
3472 if rows[0][0]:
3473 _log.debug("Successfully set database language to [%s]." % lang)
3474 return True
3475 _log.error('Cannot set database language to [%s].' % lang)
3476
3477 return True
3478
3479 if curr_db_lang == gmI18N.system_locale_level['full']:
3480 _log.debug('Database locale (%s) up to date.' % curr_db_lang)
3481 return True
3482 if curr_db_lang == gmI18N.system_locale_level['country']:
3483 _log.debug('Database locale (%s) matches system locale (%s) at country level.' % (curr_db_lang, gmI18N.system_locale))
3484 return True
3485 if curr_db_lang == gmI18N.system_locale_level['language']:
3486 _log.debug('Database locale (%s) matches system locale (%s) at language level.' % (curr_db_lang, gmI18N.system_locale))
3487 return True
3488
3489 _log.warning('database locale [%s] does not match system locale [%s]' % (curr_db_lang, gmI18N.system_locale))
3490
3491 sys_lang2ignore = _cfg.get (
3492 group = u'backend',
3493 option = u'ignored mismatching system locale',
3494 source_order = [('explicit', 'return'), ('local', 'return'), ('user', 'return'), ('system', 'return')]
3495 )
3496 if gmI18N.system_locale == sys_lang2ignore:
3497 _log.info('configured to ignore system-to-database locale mismatch')
3498 return True
3499
3500
3501 msg = _(
3502 "The currently selected database language ('%s') does\n"
3503 "not match the current system language ('%s').\n"
3504 "\n"
3505 "Do you want to set the database language to '%s' ?\n"
3506 ) % (curr_db_lang, gmI18N.system_locale, gmI18N.system_locale)
3507 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
3508 None,
3509 -1,
3510 caption = _('Checking database language settings'),
3511 question = msg,
3512 button_defs = [
3513 {'label': _('Set'), 'tooltip': _('Set your database language to [%s].') % gmI18N.system_locale, 'default': True},
3514 {'label': _("Don't set"), 'tooltip': _('Do not set your database language now.'), 'default': False}
3515 ],
3516 show_checkbox = True,
3517 checkbox_msg = _('Remember to ignore language mismatch'),
3518 checkbox_tooltip = _(
3519 'Checking this will make GNUmed remember your decision\n'
3520 'until the system language is changed.\n'
3521 '\n'
3522 'You can also reactivate this inquiry by removing the\n'
3523 'corresponding "ignore" option from the configuration file\n'
3524 '\n'
3525 ' [%s]'
3526 ) % _cfg.get(option = 'user_preferences_file')
3527 )
3528 decision = dlg.ShowModal()
3529 remember2ignore_this_mismatch = dlg._CHBOX_dont_ask_again.GetValue()
3530 dlg.Destroy()
3531
3532 if decision == wx.ID_NO:
3533 if not remember2ignore_this_mismatch:
3534 return True
3535 _log.info('User did not want to set database locale. Ignoring mismatch next time.')
3536 gmCfg2.set_option_in_INI_file (
3537 filename = _cfg.get(option = 'user_preferences_file'),
3538 group = 'backend',
3539 option = 'ignored mismatching system locale',
3540 value = gmI18N.system_locale
3541 )
3542 return True
3543
3544
3545 cmd = u'select i18n.set_curr_lang(%s)'
3546 for lang in [gmI18N.system_locale_level['full'], gmI18N.system_locale_level['country'], gmI18N.system_locale_level['language']]:
3547 if len(lang) == 0:
3548 continue
3549 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': [lang]}], return_data = True)
3550 if rows[0][0]:
3551 _log.debug("Successfully set database language to [%s]." % lang)
3552 return True
3553 _log.error('Cannot set database language to [%s].' % lang)
3554
3555
3556 _log.info('forcing database language to [%s]', gmI18N.system_locale_level['country'])
3557 gmPG2.run_rw_queries(queries = [{
3558 'cmd': u'select i18n.force_curr_lang(%s)',
3559 'args': [gmI18N.system_locale_level['country']]
3560 }])
3561
3562 return True
3563
3565 try:
3566 kwargs['originated_in_database']
3567 print '==> got notification from database "%s":' % kwargs['signal']
3568 except KeyError:
3569 print '==> received signal from client: "%s"' % kwargs['signal']
3570
3571 del kwargs['signal']
3572 for key in kwargs.keys():
3573
3574 try: print ' [%s]: %s' % (key, kwargs[key])
3575 except: print 'cannot print signal information'
3576
3580
3591
3593
3594 if _cfg.get(option = 'debug'):
3595 gmDispatcher.connect(receiver = _signal_debugging_monitor)
3596 _log.debug('gmDispatcher signal monitor activated')
3597
3598 setup_safe_wxEndBusyCursor()
3599
3600 wx.InitAllImageHandlers()
3601
3602
3603
3604 app = gmApp(redirect = False, clearSigInt = False)
3605 app.MainLoop()
3606
3607
3608
3609 if __name__ == '__main__':
3610
3611 from GNUmed.pycommon import gmI18N
3612 gmI18N.activate_locale()
3613 gmI18N.install_domain()
3614
3615 _log.info('Starting up as main module.')
3616 main()
3617
3618
3619