1
2
3
4 __author__ = "R.Terry <rterry@gnumed.net>, I.Haywood <i.haywood@ugrad.unimelb.edu.au>, K.Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7
8 import sys
9 import os.path
10 import datetime as pyDT
11 import logging
12 import decimal
13
14
15 import wx
16
17
18 from Gnumed.pycommon import gmGuiBroker
19 from Gnumed.pycommon import gmDispatcher
20 from Gnumed.pycommon import gmTools
21 from Gnumed.pycommon import gmCfg
22 from Gnumed.pycommon import gmCfg2
23 from Gnumed.pycommon import gmDateTime
24 from Gnumed.pycommon import gmI18N
25
26 from Gnumed.business import gmPerson
27 from Gnumed.business import gmEMRStructItems
28 from Gnumed.business import gmAllergy
29 from Gnumed.business import gmLOINC
30 from Gnumed.business import gmClinicalCalculator
31 from Gnumed.business import gmPathLab
32 from Gnumed.business import gmPraxis
33
34 from Gnumed.wxpython import gmGuiHelpers
35 from Gnumed.wxpython import gmDemographicsWidgets
36 from Gnumed.wxpython import gmAllergyWidgets
37 from Gnumed.wxpython import gmPatSearchWidgets
38 from Gnumed.wxpython import gmEMRStructWidgets
39 from Gnumed.wxpython import gmPatPicWidgets
40
41
42 _log = logging.getLogger('gm.ui')
43
44
45 from Gnumed.wxGladeWidgets import wxgTopPnl
46
48
59
60
62 cfg = gmCfg2.gmCfgData()
63 if cfg.get(option = 'slave'):
64 self._TCTRL_patient_selector.SetEditable(0)
65 self._TCTRL_patient_selector.SetToolTip(None)
66
67 if sys.platform == 'darwin':
68 _log.debug('adjusting font size on Mac for top panel parts')
69 for ctrl in [self._TCTRL_patient_selector, self._LBL_age, self._LBL_allergies, self._TCTRL_allergies]:
70 curr_font = ctrl.GetFont()
71 mac_font = wx.Font(curr_font.GetNativeFontInfo())
72 mac_font.SetPointSize(pointSize = int(curr_font.GetPointSize() / 0.8))
73 ctrl.SetFont(mac_font)
74
75
76 dbcfg = gmCfg.cCfgSQL()
77 pk_panel = dbcfg.get2 (
78 option = 'horstspace.top_panel.lab_panel',
79 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
80 bias = 'user'
81 )
82 if pk_panel is None:
83 self.__lab_panel = None
84 else:
85 self.__lab_panel = gmPathLab.cTestPanel(aPK_obj = pk_panel)
86
87
89
90 self._TCTRL_allergies.Bind(wx.EVT_LEFT_DCLICK, self._on_allergies_dclicked)
91
92
93 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
94 gmDispatcher.connect(signal = 'focus_patient_search', receiver = self._on_focus_patient_search)
95
96 gmDispatcher.connect(signal = 'gm_table_mod', receiver = self._on_database_signal)
97
98
99
100
108
109
111
112 if kwds['table'] not in ['dem.identity', 'dem.names', 'dem.identity_tag', 'clin.allergy', 'clin.allergy_state', 'clin.test_result', 'clin.patient']:
113 return True
114
115 if self.curr_pat.connected:
116
117 if kwds['pk_identity'] != self.curr_pat.ID:
118 return True
119
120 if kwds['table'] == 'dem.identity':
121
122 if kwds['operation'] != 'UPDATE':
123 return True
124 self.__update_age_label()
125 return True
126
127 if kwds['table'] == 'dem.names':
128 self.__update_age_label()
129 return True
130
131 if kwds['table'] == 'dem.identity_tag':
132 self.__update_tags()
133 return True
134
135 if kwds['table'] in ['clin.allergy', 'clin.allergy_state']:
136 self.__update_allergies()
137 return True
138
139 if kwds['table'] in ['clin.test_result', 'clin.patient']:
140 self.__update_lab()
141 return True
142
143 return True
144
145
147 wx.CallAfter(self.__on_post_patient_selection)
148
149
151 self.__update_age_label()
152 self.__update_allergies()
153 self.__update_tags()
154 self.__update_lab()
155 self.Layout()
156
157
159 wx.CallAfter(self._TCTRL_patient_selector.SetFocus)
160
161
162
163
166
167
169
170 if not self.curr_pat.connected:
171 self._LBL_lab.SetLabel('')
172 return
173
174 tests2show = []
175
176 rrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_rr_quantity, max_no_of_results = 1)
177 if len(rrs) == 0:
178 tests2show.append(_('RR ?'))
179 else:
180 tests2show.append(rrs[0]['unified_val'])
181
182 hrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_heart_rate_quantity, max_no_of_results = 1)
183 if len(hrs) > 0:
184 tests2show.append('%s %s' % (hrs[0]['abbrev_tt'], hrs[0]['unified_val']))
185
186 bmi = self.curr_pat.emr.bmi
187 if bmi.numeric_value is not None:
188 tests2show.append(_('BMI %s') % bmi.numeric_value.quantize(decimal.Decimal('1.')))
189 else:
190 weights = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_weight, max_no_of_results = 1)
191 if len(weights) == 0:
192 tests2show.append(_('BMI ?'))
193 else:
194 tests2show.append('%s%s' % (weights[0]['unified_val'], weights[0]['val_unit']))
195
196 gfr_or_crea = self.curr_pat.emr.best_gfr_or_crea
197 if gfr_or_crea is None:
198 tests2show.append(_('GFR ?'))
199 else:
200 try:
201 tests2show.append(_('GFR %s') % gfr_or_crea.numeric_value.quantize(decimal.Decimal('1.')))
202 except AttributeError:
203 tests2show.append('%s %s' % (gfr_or_crea['abbrev_tt'], gfr_or_crea['unified_val']))
204
205 edc = self.curr_pat.emr.EDC
206 if edc is not None:
207 if self.curr_pat.emr.EDC_is_fishy:
208 tests2show.append(_('?EDC %s') % gmDateTime.pydt_strftime(edc, '%Y-%b-%d', accuracy = gmDateTime.acc_days))
209 else:
210 tests2show.append(_('EDC %s') % gmDateTime.pydt_strftime(edc, '%Y-%b-%d', accuracy = gmDateTime.acc_days))
211
212 inrs = self.curr_pat.emr.get_most_recent_results_in_loinc_group(loincs = gmLOINC.LOINC_inr_quantity, max_no_of_results = 1)
213 if len(inrs) > 0:
214 tests2show.append('%s %s' % (inrs[0]['abbrev_tt'], inrs[0]['unified_val']))
215
216
217 if self.__lab_panel is not None:
218 for result in self.__lab_panel.get_most_recent_results(pk_patient = self.curr_pat.ID, order_by = 'unified_abbrev', group_by_meta_type = True):
219 tests2show.append('%s %s' % (result['abbrev_tt'], result['unified_val']))
220
221 self._LBL_lab.SetLabel('; '.join(tests2show))
222
223
225
226
227 if not self.curr_pat.connected:
228 self._LBL_age.SetLabel(_('<Age>'))
229 self._LBL_age.SetToolTip(_('no patient selected'))
230 return
231
232
233 tt = _('Gender: %s (%s) - %s\n') % (
234 self.curr_pat.gender_symbol,
235 gmTools.coalesce(self.curr_pat['gender'], '?'),
236 self.curr_pat.gender_string
237 )
238
239
240 if self.curr_pat['dob'] is None:
241 age = '%s %s' % (
242 self.curr_pat.gender_symbol,
243 self.curr_pat.get_formatted_dob()
244 )
245 self._LBL_age.SetLabel(age)
246 self._LBL_age.SetToolTip(tt)
247 return
248
249 tt += _('Born: %s\n') % self.curr_pat.get_formatted_dob(format = '%d %b %Y')
250
251
252 if self.curr_pat['deceased'] is not None:
253 tt += _('Died: %s\n') % gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y')
254 tt += _('At age: %s\n') % self.curr_pat['medical_age']
255 age = '%s %s - %s (%s)' % (
256 self.curr_pat.gender_symbol,
257 self.curr_pat.get_formatted_dob(format = '%d %b %Y'),
258 gmDateTime.pydt_strftime(self.curr_pat['deceased'], '%d %b %Y'),
259 self.curr_pat['medical_age']
260 )
261 if self.curr_pat['dob_is_estimated']:
262 tt += _(' (date of birth and age are estimated)\n')
263 self._LBL_age.SetLabel(age)
264 self._LBL_age.SetToolTip(tt)
265 return
266
267
268 now = gmDateTime.pydt_now_here()
269
270
271 if self.curr_pat.get_formatted_dob(format = '%m-%d', honor_estimation = False) == now.strftime('%m-%d'):
272 template = _('%(sex)s %(dob)s (%(age)s today !)')
273 tt += _("\nToday is the patient's birthday !\n\n")
274 tt += _('Age: %s\n') % self.curr_pat['medical_age']
275 else:
276 tt += _('Age: %s, birthday:\n') % self.curr_pat['medical_age']
277 if self.curr_pat.current_birthday_passed is True:
278 template = '%(sex)s %(dob)s%(l_arr)s (%(age)s)'
279 tt += ' ' + _('last: %s ago (this year)') % gmDateTime.format_apparent_age_medically (
280 age = gmDateTime.calculate_apparent_age(start = self.curr_pat.birthday_this_year, end = now)
281 ) + '\n'
282 tt += ' ' + _('next: in %s (next year)') % gmDateTime.format_apparent_age_medically (
283 age = gmDateTime.calculate_apparent_age(start = now, end = self.curr_pat.birthday_next_year)
284 ) + '\n'
285 elif self.curr_pat.current_birthday_passed is False:
286 template = '%(sex)s %(r_arr)s%(dob)s (%(age)s)'
287 tt += ' ' + _('next: in %s (this year)') % gmDateTime.format_apparent_age_medically (
288 age = gmDateTime.calculate_apparent_age(start = now, end = self.curr_pat.birthday_this_year)
289 ) + '\n'
290 tt += ' ' + _('last: %s ago (last year)') % gmDateTime.format_apparent_age_medically (
291 age = gmDateTime.calculate_apparent_age(start = self.curr_pat.birthday_last_year, end = now)
292 ) + '\n'
293 else:
294 template = '%(sex)s %(dob)s (%(age)s)'
295
296
297
298 age = template % {
299 'sex': self.curr_pat.gender_symbol,
300 'dob': self.curr_pat.get_formatted_dob(format = '%d %b %Y'),
301 'age': self.curr_pat['medical_age'],
302 'r_arr': gmTools.u_arrow2right,
303 'l_arr': gmTools.u_left_arrow
304 }
305
306
307 if self.curr_pat['lastnames'] == 'Leibner':
308 if self.curr_pat['firstnames'] == 'Steffi':
309 if self.curr_pat['preferred'] == 'Wildfang':
310 age = '%s %s' % (gmTools.u_black_heart, age)
311
312 if self.curr_pat['dob_is_estimated']:
313 tt += _(' (date of birth and age are estimated)\n')
314
315 self._LBL_age.SetLabel(age)
316 self._LBL_age.SetToolTip(tt)
317
318
320
321 if not self.curr_pat.connected:
322 self._LBL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
323 self._TCTRL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
324 self._TCTRL_allergies.SetValue('')
325 self._TCTRL_allergies.SetToolTip('')
326 return
327
328 show_red = True
329
330 emr = self.curr_pat.emr
331 state = emr.allergy_state
332
333
334 if state['last_confirmed'] is None:
335 confirmed = _('never')
336 else:
337 confirmed = gmDateTime.pydt_strftime(state['last_confirmed'], '%Y %b %d')
338 tt = (state.state_string + (90 * ' '))[:90] + '\n'
339 tt += _('last confirmed %s\n') % confirmed
340 tt += gmTools.coalesce(state['comment'], '', _('Comment (%s): %%s') % state['modified_by'])
341 tt += '\n'
342
343
344 display = []
345 for allergy in emr.get_allergies():
346
347 if allergy['type'] == 'allergy':
348 display.append(allergy['descriptor'][:10].strip() + gmTools.u_ellipsis)
349
350 if allergy['definite']:
351 certainty = _('definite')
352 else:
353 certainty = _('suspected')
354 reaction = gmTools.coalesce(allergy['reaction'], _('reaction not recorded'))
355 if len(reaction) > 50:
356 reaction = reaction[:50] + gmTools.u_ellipsis
357 tt += '%s (%s, %s): %s\n' % (
358 allergy['descriptor'],
359 allergy['l10n_type'],
360 certainty,
361 reaction
362 )
363
364 if len(display) == 0:
365 display = state.state_symbol
366 if display == gmTools.u_diameter:
367 show_red = False
368 else:
369 display = ','.join(display)
370
371 if state['last_confirmed'] is not None:
372 display += gmDateTime.pydt_strftime(state['last_confirmed'], ' (%Y %b)')
373
374 if show_red:
375 self._LBL_allergies.SetForegroundColour('red')
376 self._TCTRL_allergies.SetForegroundColour('red')
377 else:
378 self._LBL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
379 self._TCTRL_allergies.SetForegroundColour(wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT))
380
381 self._TCTRL_allergies.SetValue(display)
382 self._TCTRL_allergies.SetToolTip(tt)
383
384
385 if __name__ == "__main__":
386 app = wxPyWidgetTester(size = (400, 200))
387 app.SetWidget(cMainTopPanel, -1)
388 app.SetWidget(cTopPanel, -1)
389 app.MainLoop()
390
391