| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed EMR structure editors
2
3 This module contains widgets to create and edit EMR structural
4 elements (issues, enconters, episodes).
5
6 This is based on initial work and ideas by Syan <kittylitter@swiftdsl.com.au>
7 and Karsten <Karsten.Hilbert@gmx.net>.
8 """
9 #================================================================
10 __version__ = "$Revision: 1.114 $"
11 __author__ = "cfmoro1976@yahoo.es, karsten.hilbert@gmx.net"
12 __license__ = "GPL"
13
14 # stdlib
15 import sys, re, datetime as pydt, logging, time
16
17
18 # 3rd party
19 import wx
20
21
22 # GNUmed
23 if __name__ == '__main__':
24 sys.path.insert(0, '../../')
25 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
26 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery, gmPersonSearch
27 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
28 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
29 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
30
31
32 _log = logging.getLogger('gm.ui')
33 _log.info(__version__)
34 #================================================================
35 # performed procedure related widgets/functions
36 #----------------------------------------------------------------
38
39 pat = gmPerson.gmCurrentPatient()
40 emr = pat.get_emr()
41
42 if parent is None:
43 parent = wx.GetApp().GetTopWindow()
44 #-----------------------------------------
45 def edit(procedure=None):
46 return edit_procedure(parent = parent, procedure = procedure)
47 #-----------------------------------------
48 def delete(procedure=None):
49 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
50 return True
51
52 gmDispatcher.send (
53 signal = u'statustext',
54 msg = _('Cannot delete performed procedure.'),
55 beep = True
56 )
57 return False
58 #-----------------------------------------
59 def refresh(lctrl):
60 procs = emr.get_performed_procedures()
61
62 items = [
63 [
64 u'%s%s' % (
65 p['clin_when'].strftime('%Y-%m-%d'),
66 gmTools.bool2subst (
67 p['is_ongoing'],
68 _(' (ongoing)'),
69 gmTools.coalesce (
70 initial = p['clin_end'],
71 instead = u'',
72 template_initial = u' - %s',
73 function_initial = ('strftime', u'%Y-%m-%d')
74 )
75 )
76 ),
77 p['clin_where'],
78 p['episode'],
79 p['performed_procedure']
80 ] for p in procs
81 ]
82 lctrl.set_string_items(items = items)
83 lctrl.set_data(data = procs)
84 #-----------------------------------------
85 gmListWidgets.get_choices_from_list (
86 parent = parent,
87 msg = _('\nSelect the procedure you want to edit !\n'),
88 caption = _('Editing performed procedures ...'),
89 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
90 single_selection = True,
91 edit_callback = edit,
92 new_callback = edit,
93 delete_callback = delete,
94 refresh_callback = refresh
95 )
96 #----------------------------------------------------------------
98 ea = cProcedureEAPnl(parent = parent, id = -1)
99 ea.data = procedure
100 ea.mode = gmTools.coalesce(procedure, 'new', 'edit')
101 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
102 dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure')))
103 if dlg.ShowModal() == wx.ID_OK:
104 dlg.Destroy()
105 return True
106 dlg.Destroy()
107 return False
108 #----------------------------------------------------------------
109 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
110
112
114 wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs)
115 gmEditArea.cGenericEditAreaMixin.__init__(self)
116
117 self.mode = 'new'
118 self.data = None
119
120 self.__init_ui()
121 #----------------------------------------------------------------
123 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
124 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
125 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
126 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
127 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
128
129 # location
130 mp = gmMatchProvider.cMatchProvider_SQL2 (
131 queries = [
132 u"""
133 SELECT DISTINCT ON (data) data, location
134 FROM (
135 SELECT
136 clin_where as data,
137 clin_where as location
138 FROM
139 clin.procedure
140 WHERE
141 clin_where %(fragment_condition)s
142
143 UNION ALL
144
145 SELECT
146 narrative as data,
147 narrative as location
148 FROM
149 clin.hospital_stay
150 WHERE
151 narrative %(fragment_condition)s
152 ) as union_result
153 ORDER BY data
154 LIMIT 25"""
155 ]
156 )
157 mp.setThresholds(2, 4, 6)
158 self._PRW_location.matcher = mp
159
160 # procedure
161 mp = gmMatchProvider.cMatchProvider_SQL2 (
162 queries = [
163 u"""
164 select distinct on (narrative) narrative, narrative
165 from clin.procedure
166 where narrative %(fragment_condition)s
167 order by narrative
168 limit 25
169 """ ]
170 )
171 mp.setThresholds(2, 4, 6)
172 self._PRW_procedure.matcher = mp
173 #----------------------------------------------------------------
175 stay = self._PRW_hospital_stay.GetData()
176 if stay is None:
177 self._PRW_hospital_stay.SetText()
178 self._PRW_location.Enable(True)
179 self._PRW_episode.Enable(True)
180 self._LBL_hospital_details.SetLabel(u'')
181 else:
182 self._PRW_location.SetText()
183 self._PRW_location.Enable(False)
184 self._PRW_episode.SetText()
185 self._PRW_episode.Enable(False)
186 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
187 #----------------------------------------------------------------
189 if self._PRW_location.GetValue().strip() == u'':
190 self._PRW_hospital_stay.Enable(True)
191 # self._PRW_episode.Enable(False)
192 else:
193 self._PRW_hospital_stay.SetText()
194 self._PRW_hospital_stay.Enable(False)
195 self._PRW_hospital_stay.display_as_valid(True)
196 # self._PRW_episode.Enable(True)
197 #----------------------------------------------------------------
199 if not self._DPRW_date.is_valid_timestamp():
200 return
201 end = self._DPRW_end.GetData()
202 if end is None:
203 return
204 end = end.get_pydt()
205 start = self._DPRW_date.GetData().get_pydt()
206 if start < end:
207 return
208 self._DPRW_date.display_as_valid(False)
209 #----------------------------------------------------------------
211 end = self._DPRW_end.GetData()
212 if end is None:
213 self._CHBOX_ongoing.Enable(True)
214 self._DPRW_end.display_as_valid(True)
215 else:
216 self._CHBOX_ongoing.Enable(False)
217 end = end.get_pydt()
218 now = gmDateTime.pydt_now_here()
219 if end > now:
220 self._CHBOX_ongoing.SetValue(True)
221 else:
222 self._CHBOX_ongoing.SetValue(False)
223 start = self._DPRW_date.GetData()
224 if start is None:
225 self._DPRW_end.display_as_valid(True)
226 else:
227 start = start.get_pydt()
228 if end > start:
229 self._DPRW_end.display_as_valid(True)
230 else:
231 self._DPRW_end.display_as_valid(False)
232 #----------------------------------------------------------------
233 # generic Edit Area mixin API
234 #----------------------------------------------------------------
236
237 has_errors = False
238
239 if not self._DPRW_date.is_valid_timestamp():
240 self._DPRW_date.display_as_valid(False)
241 has_errors = True
242 else:
243 self._DPRW_date.display_as_valid(True)
244
245 end = self._DPRW_end.GetData()
246 self._DPRW_end.display_as_valid(True)
247 if end is not None:
248 end = end.get_pydt()
249 start = self._DPRW_end.GetData()
250 if start is not None:
251 start = start.get_pydt()
252 if end < start:
253 has_errors = True
254 self._DPRW_end.display_as_valid(False)
255 if self._CHBOX_ongoing.IsChecked():
256 now = gmDateTime.pydt_now_here()
257 if end < now:
258 has_errors = True
259 self._DPRW_end.display_as_valid(False)
260
261 if self._PRW_hospital_stay.GetData() is None:
262 if self._PRW_episode.GetData() is None:
263 self._PRW_episode.display_as_valid(False)
264 has_errors = True
265 else:
266 self._PRW_episode.display_as_valid(True)
267 else:
268 self._PRW_episode.display_as_valid(True)
269
270 if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''):
271 self._PRW_procedure.display_as_valid(False)
272 has_errors = True
273 else:
274 self._PRW_procedure.display_as_valid(True)
275
276 invalid_location = (
277 (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'')
278 or
279 (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'')
280 )
281 if invalid_location:
282 self._PRW_hospital_stay.display_as_valid(False)
283 self._PRW_location.display_as_valid(False)
284 has_errors = True
285 else:
286 self._PRW_hospital_stay.display_as_valid(True)
287 self._PRW_location.display_as_valid(True)
288
289 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save procedure.'), beep = True)
290
291 return (has_errors is False)
292 #----------------------------------------------------------------
294
295 pat = gmPerson.gmCurrentPatient()
296 emr = pat.get_emr()
297
298 if self._PRW_hospital_stay.GetData() is None:
299 stay = None
300 epi = self._PRW_episode.GetData()
301 loc = self._PRW_location.GetValue().strip()
302 else:
303 stay = self._PRW_hospital_stay.GetData()
304 epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode']
305 loc = None
306
307 proc = emr.add_performed_procedure (
308 episode = epi,
309 location = loc,
310 hospital_stay = stay,
311 procedure = self._PRW_procedure.GetValue().strip()
312 )
313
314 proc['clin_when'] = self._DPRW_date.GetData().get_pydt()
315 if self._DPRW_end.GetData() is None:
316 proc['clin_end'] = None
317 else:
318 proc['clin_end'] = self._DPRW_end.GetData().get_pydt()
319 proc['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
320 proc.save()
321
322 proc.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
323
324 self.data = proc
325
326 return True
327 #----------------------------------------------------------------
329 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
330
331 if self._DPRW_end.GetData() is None:
332 self.data['clin_end'] = None
333 else:
334 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
335
336 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
337
338 if self._PRW_hospital_stay.GetData() is None:
339 self.data['pk_hospital_stay'] = None
340 self.data['clin_where'] = self._PRW_location.GetValue().strip()
341 self.data['pk_episode'] = self._PRW_episode.GetData()
342 else:
343 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
344 self.data['clin_where'] = None
345 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
346 self.data['pk_episode'] = stay['pk_episode']
347
348 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
349
350 self.data.save()
351 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
352
353 return True
354 #----------------------------------------------------------------
356 self._DPRW_date.SetText()
357 self._DPRW_end.SetText()
358 self._CHBOX_ongoing.SetValue(False)
359 self._CHBOX_ongoing.Enable(True)
360 self._PRW_hospital_stay.SetText()
361 self._PRW_location.SetText()
362 self._PRW_episode.SetText()
363 self._PRW_procedure.SetText()
364 self._PRW_codes.SetText()
365
366 self._PRW_procedure.SetFocus()
367 #----------------------------------------------------------------
369 self._DPRW_date.SetData(data = self.data['clin_when'])
370 if self.data['clin_end'] is None:
371 self._DPRW_end.SetText()
372 self._CHBOX_ongoing.Enable(True)
373 self._CHBOX_ongoing.SetValue(self.data['is_ongoing'])
374 else:
375 self._DPRW_end.SetData(data = self.data['clin_end'])
376 self._CHBOX_ongoing.Enable(False)
377 now = gmDateTime.pydt_now_here()
378 if self.data['clin_end'] > now:
379 self._CHBOX_ongoing.SetValue(True)
380 else:
381 self._CHBOX_ongoing.SetValue(False)
382 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
383 self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure'])
384
385 if self.data['pk_hospital_stay'] is None:
386 self._PRW_hospital_stay.SetText()
387 self._LBL_hospital_details.SetLabel(u'')
388 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
389 else:
390 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
391 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = self.data['pk_hospital_stay']).format())
392 self._PRW_location.SetText()
393
394 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
395 self._PRW_codes.SetText(val, data)
396
397 self._PRW_procedure.SetFocus()
398 #----------------------------------------------------------------
400 self._refresh_as_new()
401 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
402 if self.data['pk_hospital_stay'] is None:
403 self._PRW_hospital_stay.SetText()
404 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
405 else:
406 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
407 self._PRW_location.SetText()
408
409 self._PRW_procedure.SetFocus()
410 #----------------------------------------------------------------
411 # event handlers
412 #----------------------------------------------------------------
417 #----------------------------------------------------------------
419 if self._CHBOX_ongoing.IsChecked():
420 end = self._DPRW_end.GetData()
421 if end is None:
422 self._DPRW_end.display_as_valid(True)
423 else:
424 end = end.get_pydt()
425 now = gmDateTime.pydt_now_here()
426 if end > now:
427 self._DPRW_end.display_as_valid(True)
428 else:
429 self._DPRW_end.display_as_valid(False)
430 else:
431 self._DPRW_end.is_valid_timestamp()
432 event.Skip()
433 #================================================================
434 # hospital stay related widgets/functions
435 #----------------------------------------------------------------
437
438 pat = gmPerson.gmCurrentPatient()
439 emr = pat.get_emr()
440
441 if parent is None:
442 parent = wx.GetApp().GetTopWindow()
443 #-----------------------------------------
444 def edit(stay=None):
445 return edit_hospital_stay(parent = parent, hospital_stay = stay)
446 #-----------------------------------------
447 def delete(stay=None):
448 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
449 return True
450 gmDispatcher.send (
451 signal = u'statustext',
452 msg = _('Cannot delete hospital stay.'),
453 beep = True
454 )
455 return False
456 #-----------------------------------------
457 def refresh(lctrl):
458 stays = emr.get_hospital_stays()
459 items = [
460 [
461 s['admission'].strftime('%Y-%m-%d'),
462 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
463 s['episode'],
464 gmTools.coalesce(s['hospital'], u'')
465 ] for s in stays
466 ]
467 lctrl.set_string_items(items = items)
468 lctrl.set_data(data = stays)
469 #-----------------------------------------
470 gmListWidgets.get_choices_from_list (
471 parent = parent,
472 msg = _('\nSelect the hospital stay you want to edit !\n'),
473 caption = _('Editing hospital stays ...'),
474 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
475 single_selection = True,
476 edit_callback = edit,
477 new_callback = edit,
478 delete_callback = delete,
479 refresh_callback = refresh
480 )
481
482 #----------------------------------------------------------------
484 ea = cHospitalStayEditAreaPnl(parent = parent, id = -1)
485 ea.data = hospital_stay
486 ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit')
487 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
488 dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospital stay'), _('Editing a hospital stay')))
489 if dlg.ShowModal() == wx.ID_OK:
490 dlg.Destroy()
491 return True
492 dlg.Destroy()
493 return False
494 #----------------------------------------------------------------
496 """Phrasewheel to allow selection of a hospital stay.
497 """
499
500 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
501
502 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
503
504 mp = gmMatchProvider.cMatchProvider_SQL2 (
505 queries = [
506 u"""
507 select
508 pk_hospital_stay,
509 descr
510 from (
511 select distinct on (pk_hospital_stay)
512 pk_hospital_stay,
513 descr
514 from
515 (select
516 pk_hospital_stay,
517 (
518 to_char(admission, 'YYYY-Mon-DD')
519 || coalesce((' (' || hospital || '):'), ': ')
520 || episode
521 || coalesce((' (' || health_issue || ')'), '')
522 ) as descr
523 from
524 clin.v_pat_hospital_stays
525 where
526 %(ctxt_pat)s
527
528 hospital %(fragment_condition)s
529 or
530 episode %(fragment_condition)s
531 or
532 health_issue %(fragment_condition)s
533 ) as the_stays
534 ) as distinct_stays
535 order by descr
536 limit 25
537 """ ],
538 context = ctxt
539 )
540 mp.setThresholds(3, 4, 6)
541 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
542
543 self.matcher = mp
544 self.selection_only = True
545 #----------------------------------------------------------------
546 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
547
548 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
549
551 wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl.__init__(self, *args, **kwargs)
552 gmEditArea.cGenericEditAreaMixin.__init__(self)
553 #----------------------------------------------------------------
554 # generic Edit Area mixin API
555 #----------------------------------------------------------------
557
558 valid = True
559
560 if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
561 valid = False
562 gmDispatcher.send(signal = 'statustext', msg = _('Missing admission data. Cannot save hospital stay.'), beep = True)
563
564 if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
565 if self._PRW_discharge.date is not None:
566 if not self._PRW_discharge.date > self._PRW_admission.date:
567 valid = False
568 self._PRW_discharge.display_as_valid(False)
569 gmDispatcher.send(signal = 'statustext', msg = _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), beep = True)
570
571 if self._PRW_episode.GetValue().strip() == u'':
572 valid = False
573 self._PRW_episode.display_as_valid(False)
574 gmDispatcher.send(signal = 'statustext', msg = _('Must select an episode or enter a name for a new one. Cannot save hospital stay.'), beep = True)
575
576 return (valid is True)
577 #----------------------------------------------------------------
579
580 pat = gmPerson.gmCurrentPatient()
581 emr = pat.get_emr()
582 stay = emr.add_hospital_stay(episode = self._PRW_episode.GetData(can_create = True))
583 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
584 stay['admission'] = self._PRW_admission.GetData()
585 stay['discharge'] = self._PRW_discharge.GetData()
586 stay.save_payload()
587
588 self.data = stay
589 return True
590 #----------------------------------------------------------------
592
593 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
594 self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
595 self.data['admission'] = self._PRW_admission.GetData()
596 self.data['discharge'] = self._PRW_discharge.GetData()
597 self.data.save_payload()
598
599 return True
600 #----------------------------------------------------------------
602 self._PRW_hospital.SetText(value = u'')
603 self._PRW_episode.SetText(value = u'')
604 self._PRW_admission.SetText(data = gmDateTime.pydt_now_here())
605 self._PRW_discharge.SetText()
606 #----------------------------------------------------------------
608 if self.data['hospital'] is not None:
609 self._PRW_hospital.SetText(value = self.data['hospital'])
610
611 if self.data['pk_episode'] is not None:
612 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
613
614 self._PRW_admission.SetText(data = self.data['admission'])
615 self._PRW_discharge.SetText(data = self.data['discharge'])
616 #----------------------------------------------------------------
619 #================================================================
620 # encounter related widgets/functions
621 #----------------------------------------------------------------
623 emr.start_new_encounter()
624 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True)
625 time.sleep(0.5)
626 gmGuiHelpers.gm_show_info (
627 _('\nA new encounter was started for the active patient.\n'),
628 _('Start of new encounter')
629 )
630 #----------------------------------------------------------------
631 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
632
634 if parent is None:
635 parent = wx.GetApp().GetTopWindow()
636
637 # FIXME: use generic dialog 2
638 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
639 if dlg.ShowModal() == wx.ID_OK:
640 dlg.Destroy()
641 return True
642 dlg.Destroy()
643 return False
644 #----------------------------------------------------------------
646 return select_encounters(**kwargs)
647
648 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
649
650 if patient is None:
651 patient = gmPerson.gmCurrentPatient()
652
653 if not patient.connected:
654 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
655 return False
656
657 if parent is None:
658 parent = wx.GetApp().GetTopWindow()
659
660 emr = patient.get_emr()
661
662 #--------------------
663 def refresh(lctrl):
664 if encounters is None:
665 encs = emr.get_encounters()
666 else:
667 encs = encounters
668
669 items = [
670 [
671 e['started'].strftime('%x %H:%M'),
672 e['last_affirmed'].strftime('%H:%M'),
673 e['l10n_type'],
674 gmTools.coalesce(e['reason_for_encounter'], u''),
675 gmTools.coalesce(e['assessment_of_encounter'], u''),
676 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
677 e['pk_encounter']
678 ] for e in encs
679 ]
680 lctrl.set_string_items(items = items)
681 lctrl.set_data(data = encs)
682 active_pk = emr.active_encounter['pk_encounter']
683 for idx in range(len(encs)):
684 e = encs[idx]
685 if e['pk_encounter'] == active_pk:
686 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
687 #--------------------
688 def new():
689 cfg_db = gmCfg.cCfgSQL()
690 # FIXME: look for MRU/MCU encounter type config here
691 enc_type = cfg_db.get2 (
692 option = u'encounter.default_type',
693 workplace = gmSurgery.gmCurrentPractice().active_workplace,
694 bias = u'user',
695 default = u'in surgery'
696 )
697 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
698 return edit_encounter(parent = parent, encounter = enc)
699 #--------------------
700 def edit(enc=None):
701 return edit_encounter(parent = parent, encounter = enc)
702 #--------------------
703 def edit_active(enc=None):
704 return edit_encounter(parent = parent, encounter = emr.active_encounter)
705 #--------------------
706 def start_new(enc=None):
707 start_new_encounter(emr = emr)
708 return True
709 #--------------------
710 return gmListWidgets.get_choices_from_list (
711 parent = parent,
712 msg = _('\nBelow find the relevant encounters of the patient.\n'),
713 caption = _('Encounters ...'),
714 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
715 can_return_empty = False,
716 single_selection = single_selection,
717 refresh_callback = refresh,
718 edit_callback = edit,
719 new_callback = new,
720 ignore_OK_button = ignore_OK_button,
721 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
722 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
723 )
724 #----------------------------------------------------------------
726 """This is used as the callback when the EMR detects that the
727 patient was here rather recently and wants to ask the
728 provider whether to continue the recent encounter.
729 """
730 if parent is None:
731 parent = wx.GetApp().GetTopWindow()
732
733 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
734 parent = None,
735 id = -1,
736 caption = caption,
737 question = msg,
738 button_defs = [
739 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
740 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
741 ],
742 show_checkbox = False
743 )
744
745 result = dlg.ShowModal()
746 dlg.Destroy()
747
748 if result == wx.ID_YES:
749 return True
750
751 return False
752 #----------------------------------------------------------------
754
755 if parent is None:
756 parent = wx.GetApp().GetTopWindow()
757
758 #--------------------
759 def edit(enc_type=None):
760 return edit_encounter_type(parent = parent, encounter_type = enc_type)
761 #--------------------
762 def delete(enc_type=None):
763 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
764 return True
765 gmDispatcher.send (
766 signal = u'statustext',
767 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
768 beep = True
769 )
770 return False
771 #--------------------
772 def refresh(lctrl):
773 enc_types = gmEMRStructItems.get_encounter_types()
774 lctrl.set_string_items(items = enc_types)
775 #--------------------
776 gmListWidgets.get_choices_from_list (
777 parent = parent,
778 msg = _('\nSelect the encounter type you want to edit !\n'),
779 caption = _('Managing encounter types ...'),
780 columns = [_('Local name'), _('Encounter type')],
781 single_selection = True,
782 edit_callback = edit,
783 new_callback = edit,
784 delete_callback = delete,
785 refresh_callback = refresh
786 )
787 #----------------------------------------------------------------
789 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1)
790 ea.data = encounter_type
791 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit')
792 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
793 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name')))
794 if dlg.ShowModal() == wx.ID_OK:
795 return True
796 return False
797 #----------------------------------------------------------------
799 """Phrasewheel to allow selection of encounter type.
800
801 - user input interpreted as encounter type in English or local language
802 - data returned is pk of corresponding encounter type or None
803 """
805
806 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
807
808 mp = gmMatchProvider.cMatchProvider_SQL2 (
809 queries = [
810 u"""
811 SELECT
812 data,
813 field_label,
814 list_label
815 FROM (
816 SELECT DISTINCT ON (data) *
817 FROM (
818 SELECT
819 pk AS data,
820 _(description) AS field_label,
821 case
822 when _(description) = description then _(description)
823 else _(description) || ' (' || description || ')'
824 end AS list_label
825 FROM
826 clin.encounter_type
827 WHERE
828 _(description) %(fragment_condition)s
829 OR
830 description %(fragment_condition)s
831 ) AS q_distinct_pk
832 ) AS q_ordered
833 ORDER BY
834 list_label
835 """ ]
836 )
837 mp.setThresholds(2, 4, 6)
838
839 self.matcher = mp
840 self.selection_only = True
841 self.picklist_delay = 50
842 #----------------------------------------------------------------
843 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
844
846
847 wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl.__init__(self, *args, **kwargs)
848 gmEditArea.cGenericEditAreaMixin.__init__(self)
849
850 # self.__register_interests()
851 #-------------------------------------------------------
852 # generic edit area API
853 #-------------------------------------------------------
855 if self.mode == 'edit':
856 if self._TCTRL_l10n_name.GetValue().strip() == u'':
857 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
858 return False
859 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
860 return True
861
862 no_errors = True
863
864 if self._TCTRL_l10n_name.GetValue().strip() == u'':
865 if self._TCTRL_name.GetValue().strip() == u'':
866 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
867 no_errors = False
868 else:
869 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
870 else:
871 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
872
873 if self._TCTRL_name.GetValue().strip() == u'':
874 if self._TCTRL_l10n_name.GetValue().strip() == u'':
875 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False)
876 no_errors = False
877 else:
878 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
879 else:
880 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
881
882 return no_errors
883 #-------------------------------------------------------
885 enc_type = gmEMRStructItems.create_encounter_type (
886 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''),
887 l10n_description = gmTools.coalesce (
888 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''),
889 self._TCTRL_name.GetValue().strip()
890 )
891 )
892 if enc_type is None:
893 return False
894 self.data = enc_type
895 return True
896 #-------------------------------------------------------
898 enc_type = gmEMRStructItems.update_encounter_type (
899 description = self._TCTRL_name.GetValue().strip(),
900 l10n_description = self._TCTRL_l10n_name.GetValue().strip()
901 )
902 if enc_type is None:
903 return False
904 self.data = enc_type
905 return True
906 #-------------------------------------------------------
908 self._TCTRL_l10n_name.SetValue(u'')
909 self._TCTRL_name.SetValue(u'')
910 self._TCTRL_name.Enable(True)
911 #-------------------------------------------------------
913 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
914 self._TCTRL_name.SetValue(self.data['description'])
915 # disallow changing type on all encounters by editing system name
916 self._TCTRL_name.Enable(False)
917 #-------------------------------------------------------
922 #-------------------------------------------------------
923 # internal API
924 #-------------------------------------------------------
925 # def __register_interests(self):
926 # return
927 #----------------------------------------------------------------
928 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
929
931
933 try:
934 self.__encounter = kwargs['encounter']
935 del kwargs['encounter']
936 except KeyError:
937 self.__encounter = None
938
939 try:
940 msg = kwargs['msg']
941 del kwargs['msg']
942 except KeyError:
943 msg = None
944
945 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
946
947 self.refresh(msg = msg)
948 #--------------------------------------------------------
949 # external API
950 #--------------------------------------------------------
952
953 if msg is not None:
954 self._LBL_instructions.SetLabel(msg)
955
956 if encounter is not None:
957 self.__encounter = encounter
958
959 if self.__encounter is None:
960 return True
961
962 # getting the patient via the encounter allows us to act
963 # on any encounter regardless of the currently active patient
964 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
965 self._LBL_patient.SetLabel(pat.get_description_gender())
966
967 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
968
969 fts = gmDateTime.cFuzzyTimestamp (
970 timestamp = self.__encounter['started'],
971 accuracy = gmDateTime.acc_minutes
972 )
973 self._PRW_start.SetText(fts.format_accurately(), data=fts)
974
975 fts = gmDateTime.cFuzzyTimestamp (
976 timestamp = self.__encounter['last_affirmed'],
977 accuracy = gmDateTime.acc_minutes
978 )
979 self._PRW_end.SetText(fts.format_accurately(), data=fts)
980
981 # RFE
982 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
983 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
984 self._PRW_rfe_codes.SetText(val, data)
985
986 # AOE
987 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
988 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
989 self._PRW_aoe_codes.SetText(val, data)
990
991 # last affirmed
992 if self.__encounter['last_affirmed'] == self.__encounter['started']:
993 self._PRW_end.SetFocus()
994 else:
995 self._TCTRL_aoe.SetFocus()
996
997 return True
998 #--------------------------------------------------------
1000
1001 if self._PRW_encounter_type.GetData() is None:
1002 self._PRW_encounter_type.SetBackgroundColour('pink')
1003 self._PRW_encounter_type.Refresh()
1004 self._PRW_encounter_type.SetFocus()
1005 return False
1006 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1007 self._PRW_encounter_type.Refresh()
1008
1009 # start
1010 if self._PRW_start.GetValue().strip() == u'':
1011 self._PRW_start.SetBackgroundColour('pink')
1012 self._PRW_start.Refresh()
1013 self._PRW_start.SetFocus()
1014 return False
1015 if not self._PRW_start.is_valid_timestamp():
1016 self._PRW_start.SetBackgroundColour('pink')
1017 self._PRW_start.Refresh()
1018 self._PRW_start.SetFocus()
1019 return False
1020 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1021 self._PRW_start.Refresh()
1022
1023 # last_affirmed
1024 if self._PRW_end.GetValue().strip() == u'':
1025 self._PRW_end.SetBackgroundColour('pink')
1026 self._PRW_end.Refresh()
1027 self._PRW_end.SetFocus()
1028 return False
1029 if not self._PRW_end.is_valid_timestamp():
1030 self._PRW_end.SetBackgroundColour('pink')
1031 self._PRW_end.Refresh()
1032 self._PRW_end.SetFocus()
1033 return False
1034 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1035 self._PRW_end.Refresh()
1036
1037 return True
1038 #--------------------------------------------------------
1040 if not self.__is_valid_for_save():
1041 return False
1042
1043 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1044 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1045 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1046 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1047 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1048 self.__encounter.save_payload() # FIXME: error checking
1049
1050 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1051 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1052
1053 return True
1054 #----------------------------------------------------------------
1055 # FIXME: use generic dialog 2
1057
1059 encounter = kwargs['encounter']
1060 del kwargs['encounter']
1061
1062 try:
1063 button_defs = kwargs['button_defs']
1064 del kwargs['button_defs']
1065 except KeyError:
1066 button_defs = None
1067
1068 try:
1069 msg = kwargs['msg']
1070 del kwargs['msg']
1071 except KeyError:
1072 msg = None
1073
1074 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1075 self.SetSize((450, 280))
1076 self.SetMinSize((450, 280))
1077
1078 if button_defs is not None:
1079 self._BTN_save.SetLabel(button_defs[0][0])
1080 self._BTN_save.SetToolTipString(button_defs[0][1])
1081 self._BTN_close.SetLabel(button_defs[1][0])
1082 self._BTN_close.SetToolTipString(button_defs[1][1])
1083 self.Refresh()
1084
1085 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1086
1087 self.Fit()
1088 #--------------------------------------------------------
1095 #----------------------------------------------------------------
1096 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
1097
1099
1101 wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs)
1102 self.__register_events()
1103 self.refresh()
1104 #------------------------------------------------------------
1106 self._TCTRL_encounter.SetValue(u'')
1107 self._TCTRL_encounter.SetToolTipString(u'')
1108 self._BTN_new.Enable(False)
1109 self._BTN_list.Enable(False)
1110 #------------------------------------------------------------
1112 pat = gmPerson.gmCurrentPatient()
1113 if not pat.connected:
1114 self.clear()
1115 return
1116
1117 enc = pat.get_emr().active_encounter
1118 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
1119 self._TCTRL_encounter.SetToolTipString (
1120 _('The active encounter of the current patient:\n\n%s') %
1121 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
1122 )
1123 self._BTN_new.Enable(True)
1124 self._BTN_list.Enable(True)
1125 #------------------------------------------------------------
1127 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
1128
1129 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
1130 # this would throw an exception due to concurrency issues:
1131 #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_refresh)
1132 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
1133 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
1134 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1135 #------------------------------------------------------------
1136 # event handler
1137 #------------------------------------------------------------
1139 wx.CallAfter(self.clear)
1140 #------------------------------------------------------------
1144 #------------------------------------------------------------
1146 pat = gmPerson.gmCurrentPatient()
1147 edit_encounter(encounter = pat.get_emr().active_encounter)
1148 #------------------------------------------------------------
1152 #------------------------------------------------------------
1155 #================================================================
1156 # episode related widgets/functions
1157 #----------------------------------------------------------------
1159 ea = cEpisodeEditAreaPnl(parent = parent, id = -1)
1160 ea.data = episode
1161 ea.mode = gmTools.coalesce(episode, 'new', 'edit')
1162 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
1163 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode')))
1164 if dlg.ShowModal() == wx.ID_OK:
1165 return True
1166 return False
1167 #----------------------------------------------------------------
1169
1170 created_new_issue = False
1171
1172 try:
1173 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient'])
1174 except gmExceptions.NoSuchBusinessObjectError:
1175 issue = None
1176
1177 if issue is None:
1178 issue = emr.add_health_issue(issue_name = episode['description'])
1179 created_new_issue = True
1180 else:
1181 # issue exists already, so ask user
1182 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1183 parent,
1184 -1,
1185 caption = _('Promoting episode to health issue'),
1186 question = _(
1187 'There already is a health issue\n'
1188 '\n'
1189 ' %s\n'
1190 '\n'
1191 'What do you want to do ?'
1192 ) % issue['description'],
1193 button_defs = [
1194 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False},
1195 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True}
1196 ]
1197 )
1198 use_existing = dlg.ShowModal()
1199 dlg.Destroy()
1200
1201 if use_existing == wx.ID_CANCEL:
1202 return
1203
1204 # user wants to create new issue with alternate name
1205 if use_existing == wx.ID_NO:
1206 # loop until name modified but non-empty or cancelled
1207 issue_name = episode['description']
1208 while issue_name == episode['description']:
1209 dlg = wx.TextEntryDialog (
1210 parent = parent,
1211 message = _('Enter a short descriptive name for the new health issue:'),
1212 caption = _('Creating a new health issue ...'),
1213 defaultValue = issue_name,
1214 style = wx.OK | wx.CANCEL | wx.CENTRE
1215 )
1216 decision = dlg.ShowModal()
1217 if decision != wx.ID_OK:
1218 dlg.Destroy()
1219 return
1220 issue_name = dlg.GetValue().strip()
1221 dlg.Destroy()
1222 if issue_name == u'':
1223 issue_name = episode['description']
1224
1225 issue = emr.add_health_issue(issue_name = issue_name)
1226 created_new_issue = True
1227
1228 # eventually move the episode to the issue
1229 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True):
1230 # user cancelled the move so delete just-created issue
1231 if created_new_issue:
1232 # shouldn't fail as it is completely new
1233 gmEMRStructItems.delete_health_issue(health_issue = issue)
1234 return
1235
1236 return
1237 #----------------------------------------------------------------
1239 """Prepare changing health issue for an episode.
1240
1241 Checks for two-open-episodes conflict. When this
1242 function succeeds, the pk_health_issue has been set
1243 on the episode instance and the episode should - for
1244 all practical purposes - be ready for save_payload().
1245 """
1246 # episode is closed: should always work
1247 if not episode['episode_open']:
1248 episode['pk_health_issue'] = target_issue['pk_health_issue']
1249 if save_to_backend:
1250 episode.save_payload()
1251 return True
1252
1253 # un-associate: should always work, too
1254 if target_issue is None:
1255 episode['pk_health_issue'] = None
1256 if save_to_backend:
1257 episode.save_payload()
1258 return True
1259
1260 # try closing possibly expired episode on target issue if any
1261 db_cfg = gmCfg.cCfgSQL()
1262 epi_ttl = int(db_cfg.get2 (
1263 option = u'episode.ttl',
1264 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1265 bias = 'user',
1266 default = 60 # 2 months
1267 ))
1268 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1269 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1270 existing_epi = target_issue.get_open_episode()
1271
1272 # no more open episode on target issue: should work now
1273 if existing_epi is None:
1274 episode['pk_health_issue'] = target_issue['pk_health_issue']
1275 if save_to_backend:
1276 episode.save_payload()
1277 return True
1278
1279 # don't conflict on SELF ;-)
1280 if existing_epi['pk_episode'] == episode['pk_episode']:
1281 episode['pk_health_issue'] = target_issue['pk_health_issue']
1282 if save_to_backend:
1283 episode.save_payload()
1284 return True
1285
1286 # we got two open episodes at once, ask user
1287 move_range = episode.get_access_range()
1288 exist_range = existing_epi.get_access_range()
1289 question = _(
1290 'You want to associate the running episode:\n\n'
1291 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1292 'with the health issue:\n\n'
1293 ' "%(issue_name)s"\n\n'
1294 'There already is another episode running\n'
1295 'for this health issue:\n\n'
1296 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1297 'However, there can only be one running\n'
1298 'episode per health issue.\n\n'
1299 'Which episode do you want to close ?'
1300 ) % {
1301 'new_epi_name': episode['description'],
1302 'new_epi_start': move_range[0].strftime('%m/%y'),
1303 'new_epi_end': move_range[1].strftime('%m/%y'),
1304 'issue_name': target_issue['description'],
1305 'old_epi_name': existing_epi['description'],
1306 'old_epi_start': exist_range[0].strftime('%m/%y'),
1307 'old_epi_end': exist_range[1].strftime('%m/%y')
1308 }
1309 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1310 parent = None,
1311 id = -1,
1312 caption = _('Resolving two-running-episodes conflict'),
1313 question = question,
1314 button_defs = [
1315 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1316 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1317 ]
1318 )
1319 decision = dlg.ShowModal()
1320
1321 if decision == wx.ID_CANCEL:
1322 # button 3: move cancelled by user
1323 return False
1324
1325 elif decision == wx.ID_YES:
1326 # button 1: close old episode
1327 existing_epi['episode_open'] = False
1328 existing_epi.save_payload()
1329
1330 elif decision == wx.ID_NO:
1331 # button 2: close new episode
1332 episode['episode_open'] = False
1333
1334 else:
1335 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1336
1337 episode['pk_health_issue'] = target_issue['pk_health_issue']
1338 if save_to_backend:
1339 episode.save_payload()
1340 return True
1341 #----------------------------------------------------------------
1343
1344 # FIXME: support pre-selection
1345
1347
1348 episodes = kwargs['episodes']
1349 del kwargs['episodes']
1350
1351 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1352
1353 self.SetTitle(_('Select the episodes you are interested in ...'))
1354 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')])
1355 self._LCTRL_items.set_string_items (
1356 items = [
1357 [ epi['description'],
1358 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''),
1359 gmTools.coalesce(epi['health_issue'], u'')
1360 ]
1361 for epi in episodes ]
1362 )
1363 self._LCTRL_items.set_column_widths()
1364 self._LCTRL_items.set_data(data = episodes)
1365 #----------------------------------------------------------------
1367 """Let user select an episode *description*.
1368
1369 The user can select an episode description from the previously
1370 used descriptions across all episodes across all patients.
1371
1372 Selection is done with a phrasewheel so the user can
1373 type the episode name and matches will be shown. Typing
1374 "*" will show the entire list of episodes.
1375
1376 If the user types a description not existing yet a
1377 new episode description will be returned.
1378 """
1380
1381 mp = gmMatchProvider.cMatchProvider_SQL2 (
1382 queries = [
1383 u"""
1384 SELECT DISTINCT ON (description)
1385 description
1386 AS data,
1387 description
1388 AS field_label,
1389 description || ' ('
1390 || CASE
1391 WHEN is_open IS TRUE THEN _('ongoing')
1392 ELSE _('closed')
1393 END
1394 || ')'
1395 AS list_label
1396 FROM
1397 clin.episode
1398 WHERE
1399 description %(fragment_condition)s
1400 ORDER BY description
1401 LIMIT 30
1402 """
1403 ]
1404 )
1405 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1406 self.matcher = mp
1407 #----------------------------------------------------------------
1409 """Let user select an episode.
1410
1411 The user can select an episode from the existing episodes of a
1412 patient. Selection is done with a phrasewheel so the user
1413 can type the episode name and matches will be shown. Typing
1414 "*" will show the entire list of episodes. Closed episodes
1415 will be marked as such. If the user types an episode name not
1416 in the list of existing episodes a new episode can be created
1417 from it if the programmer activated that feature.
1418
1419 If keyword <patient_id> is set to None or left out the control
1420 will listen to patient change signals and therefore act on
1421 gmPerson.gmCurrentPatient() changes.
1422 """
1424
1425 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1426
1427 mp = gmMatchProvider.cMatchProvider_SQL2 (
1428 queries = [
1429 u"""(
1430
1431 select
1432 pk_episode
1433 as data,
1434 description
1435 as field_label,
1436 coalesce (
1437 description || ' - ' || health_issue,
1438 description
1439 ) as list_label,
1440 1 as rank
1441 from
1442 clin.v_pat_episodes
1443 where
1444 episode_open is true and
1445 description %(fragment_condition)s
1446 %(ctxt_pat)s
1447
1448 ) union all (
1449
1450 select
1451 pk_episode
1452 as data,
1453 description
1454 as field_label,
1455 coalesce (
1456 description || _(' (closed)') || ' - ' || health_issue,
1457 description || _(' (closed)')
1458 ) as list_label,
1459 2 as rank
1460 from
1461 clin.v_pat_episodes
1462 where
1463 description %(fragment_condition)s and
1464 episode_open is false
1465 %(ctxt_pat)s
1466
1467 )
1468
1469 order by rank, list_label
1470 limit 30"""
1471 ],
1472 context = ctxt
1473 )
1474
1475 try:
1476 kwargs['patient_id']
1477 except KeyError:
1478 kwargs['patient_id'] = None
1479
1480 if kwargs['patient_id'] is None:
1481 self.use_current_patient = True
1482 self.__register_patient_change_signals()
1483 pat = gmPerson.gmCurrentPatient()
1484 if pat.connected:
1485 mp.set_context('pat', pat.ID)
1486 else:
1487 self.use_current_patient = False
1488 self.__patient_id = int(kwargs['patient_id'])
1489 mp.set_context('pat', self.__patient_id)
1490
1491 del kwargs['patient_id']
1492
1493 gmPhraseWheel.cPhraseWheel.__init__ (
1494 self,
1495 *args,
1496 **kwargs
1497 )
1498 self.matcher = mp
1499 #--------------------------------------------------------
1500 # external API
1501 #--------------------------------------------------------
1503 if self.use_current_patient:
1504 return False
1505 self.__patient_id = int(patient_id)
1506 self.set_context('pat', self.__patient_id)
1507 return True
1508 #--------------------------------------------------------
1510 self.__is_open_for_create_data = is_open # used (only) in _create_data()
1511 return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1512 #--------------------------------------------------------
1514
1515 epi_name = self.GetValue().strip()
1516 if epi_name == u'':
1517 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1518 _log.debug('cannot create episode without name')
1519 return
1520
1521 if self.use_current_patient:
1522 pat = gmPerson.gmCurrentPatient()
1523 else:
1524 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1525
1526 emr = pat.get_emr()
1527 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1528 if epi is None:
1529 self.data = {}
1530 else:
1531 self.SetText (
1532 value = epi_name,
1533 data = epi['pk_episode']
1534 )
1535 #--------------------------------------------------------
1538 #--------------------------------------------------------
1539 # internal API
1540 #--------------------------------------------------------
1542 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1543 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1544 #--------------------------------------------------------
1547 #--------------------------------------------------------
1549 if self.use_current_patient:
1550 patient = gmPerson.gmCurrentPatient()
1551 self.set_context('pat', patient.ID)
1552 return True
1553 #----------------------------------------------------------------
1554 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1555
1556 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1557
1559
1560 try:
1561 episode = kwargs['episode']
1562 del kwargs['episode']
1563 except KeyError:
1564 episode = None
1565
1566 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
1567 gmEditArea.cGenericEditAreaMixin.__init__(self)
1568
1569 self.data = episode
1570 #----------------------------------------------------------------
1571 # generic Edit Area mixin API
1572 #----------------------------------------------------------------
1574
1575 errors = False
1576
1577 if len(self._PRW_description.GetValue().strip()) == 0:
1578 errors = True
1579 self._PRW_description.display_as_valid(False)
1580 self._PRW_description.SetFocus()
1581 else:
1582 self._PRW_description.display_as_valid(True)
1583 self._PRW_description.Refresh()
1584
1585 return not errors
1586 #----------------------------------------------------------------
1588
1589 pat = gmPerson.gmCurrentPatient()
1590 emr = pat.get_emr()
1591
1592 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1593 epi['summary'] = self._TCTRL_status.GetValue().strip()
1594 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1595 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1596
1597 issue_name = self._PRW_issue.GetValue().strip()
1598 if len(issue_name) != 0:
1599 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1600 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1601
1602 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1603 gmDispatcher.send (
1604 signal = 'statustext',
1605 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1606 epi['description'],
1607 issue['description']
1608 )
1609 )
1610 gmEMRStructItems.delete_episode(episode = epi)
1611 return False
1612
1613 epi.save()
1614
1615 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1616
1617 self.data = epi
1618 return True
1619 #----------------------------------------------------------------
1621
1622 self.data['description'] = self._PRW_description.GetValue().strip()
1623 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1624 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1625 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1626
1627 issue_name = self._PRW_issue.GetValue().strip()
1628 if len(issue_name) == 0:
1629 self.data['pk_health_issue'] = None
1630 else:
1631 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1632 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1633
1634 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1635 gmDispatcher.send (
1636 signal = 'statustext',
1637 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1638 self.data['description'],
1639 issue['description']
1640 )
1641 )
1642 return False
1643
1644 self.data.save()
1645 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1646
1647 return True
1648 #----------------------------------------------------------------
1650 if self.data is None:
1651 ident = gmPerson.gmCurrentPatient()
1652 else:
1653 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1654 self._TCTRL_patient.SetValue(ident.get_description_gender())
1655 self._PRW_issue.SetText()
1656 self._PRW_description.SetText()
1657 self._TCTRL_status.SetValue(u'')
1658 self._PRW_certainty.SetText()
1659 self._CHBOX_closed.SetValue(False)
1660 self._PRW_codes.SetText()
1661 #----------------------------------------------------------------
1663 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1664 self._TCTRL_patient.SetValue(ident.get_description_gender())
1665
1666 if self.data['pk_health_issue'] is not None:
1667 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue'])
1668
1669 self._PRW_description.SetText(self.data['description'], data=self.data['description'])
1670
1671 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
1672
1673 if self.data['diagnostic_certainty_classification'] is not None:
1674 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
1675
1676 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1677
1678 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
1679 self._PRW_codes.SetText(val, data)
1680 #----------------------------------------------------------------
1683 #================================================================
1684 # health issue related widgets/functions
1685 #----------------------------------------------------------------
1687 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1)
1688 ea.data = issue
1689 ea.mode = gmTools.coalesce(issue, 'new', 'edit')
1690 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None))
1691 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue')))
1692 if dlg.ShowModal() == wx.ID_OK:
1693 return True
1694 return False
1695 #----------------------------------------------------------------
1697
1698 # FIXME: support pre-selection
1699
1701
1702 issues = kwargs['issues']
1703 del kwargs['issues']
1704
1705 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1706
1707 self.SetTitle(_('Select the health issues you are interested in ...'))
1708 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1709
1710 for issue in issues:
1711 if issue['is_confidential']:
1712 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1713 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1714 else:
1715 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1716
1717 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1718 if issue['clinically_relevant']:
1719 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1720 if issue['is_active']:
1721 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1722 if issue['is_cause_of_death']:
1723 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1724
1725 self._LCTRL_items.set_column_widths()
1726 self._LCTRL_items.set_data(data = issues)
1727 #----------------------------------------------------------------
1729 """Let the user select a health issue.
1730
1731 The user can select a health issue from the existing issues
1732 of a patient. Selection is done with a phrasewheel so the user
1733 can type the issue name and matches will be shown. Typing
1734 "*" will show the entire list of issues. Inactive issues
1735 will be marked as such. If the user types an issue name not
1736 in the list of existing issues a new issue can be created
1737 from it if the programmer activated that feature.
1738
1739 If keyword <patient_id> is set to None or left out the control
1740 will listen to patient change signals and therefore act on
1741 gmPerson.gmCurrentPatient() changes.
1742 """
1744
1745 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1746
1747 mp = gmMatchProvider.cMatchProvider_SQL2 (
1748 # FIXME: consider clin.health_issue.clinically_relevant
1749 queries = [
1750 u"""
1751 SELECT
1752 data,
1753 field_label,
1754 list_label
1755 FROM ((
1756 SELECT
1757 pk_health_issue AS data,
1758 description AS field_label,
1759 description AS list_label
1760 FROM clin.v_health_issues
1761 WHERE
1762 is_active IS true
1763 AND
1764 description %(fragment_condition)s
1765 AND
1766 %(ctxt_pat)s
1767
1768 ) UNION (
1769
1770 SELECT
1771 pk_health_issue AS data,
1772 description AS field_label,
1773 description || _(' (inactive)') AS list_label
1774 FROM clin.v_health_issues
1775 WHERE
1776 is_active IS false
1777 AND
1778 description %(fragment_condition)s
1779 AND
1780 %(ctxt_pat)s
1781 )) AS union_query
1782 ORDER BY
1783 list_label"""],
1784 context = ctxt
1785 )
1786
1787 try: kwargs['patient_id']
1788 except KeyError: kwargs['patient_id'] = None
1789
1790 if kwargs['patient_id'] is None:
1791 self.use_current_patient = True
1792 self.__register_patient_change_signals()
1793 pat = gmPerson.gmCurrentPatient()
1794 if pat.connected:
1795 mp.set_context('pat', pat.ID)
1796 else:
1797 self.use_current_patient = False
1798 self.__patient_id = int(kwargs['patient_id'])
1799 mp.set_context('pat', self.__patient_id)
1800
1801 del kwargs['patient_id']
1802
1803 gmPhraseWheel.cPhraseWheel.__init__ (
1804 self,
1805 *args,
1806 **kwargs
1807 )
1808 self.matcher = mp
1809 #--------------------------------------------------------
1810 # external API
1811 #--------------------------------------------------------
1813 if self.use_current_patient:
1814 return False
1815 self.__patient_id = int(patient_id)
1816 self.set_context('pat', self.__patient_id)
1817 return True
1818 #--------------------------------------------------------
1820 if self.data is None:
1821 if can_create:
1822 issue_name = self.GetValue().strip()
1823
1824 if self.use_current_patient:
1825 pat = gmPerson.gmCurrentPatient()
1826 else:
1827 pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
1828 emr = pat.get_emr()
1829
1830 issue = emr.add_health_issue(issue_name = issue_name)
1831 if issue is None:
1832 self.data = None
1833 else:
1834 self.data = issue['pk_health_issue']
1835
1836 return gmPhraseWheel.cPhraseWheel.GetData(self)
1837 #--------------------------------------------------------
1838 # internal API
1839 #--------------------------------------------------------
1841 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1842 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1843 #--------------------------------------------------------
1846 #--------------------------------------------------------
1848 if self.use_current_patient:
1849 patient = gmPerson.gmCurrentPatient()
1850 self.set_context('pat', patient.ID)
1851 return True
1852 #------------------------------------------------------------
1854
1856 try:
1857 msg = kwargs['message']
1858 except KeyError:
1859 msg = None
1860 del kwargs['message']
1861 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1862 if msg is not None:
1863 self._lbl_message.SetLabel(label=msg)
1864 #--------------------------------------------------------
1875 #------------------------------------------------------------
1876 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1877
1878 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1879 """Panel encapsulating health issue edit area functionality."""
1880
1882
1883 try:
1884 issue = kwargs['issue']
1885 except KeyError:
1886 issue = None
1887
1888 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1889
1890 gmEditArea.cGenericEditAreaMixin.__init__(self)
1891
1892 # FIXME: include more sources: coding systems/other database columns
1893 mp = gmMatchProvider.cMatchProvider_SQL2 (
1894 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1895 )
1896 mp.setThresholds(1, 3, 5)
1897 self._PRW_condition.matcher = mp
1898
1899 mp = gmMatchProvider.cMatchProvider_SQL2 (
1900 queries = [u"""
1901 select distinct on (grouping) grouping, grouping from (
1902
1903 select rank, grouping from ((
1904
1905 select
1906 grouping,
1907 1 as rank
1908 from
1909 clin.health_issue
1910 where
1911 grouping %%(fragment_condition)s
1912 and
1913 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1914
1915 ) union (
1916
1917 select
1918 grouping,
1919 2 as rank
1920 from
1921 clin.health_issue
1922 where
1923 grouping %%(fragment_condition)s
1924
1925 )) as union_result
1926
1927 order by rank
1928
1929 ) as order_result
1930
1931 limit 50""" % gmPerson.gmCurrentPatient().ID
1932 ]
1933 )
1934 mp.setThresholds(1, 3, 5)
1935 self._PRW_grouping.matcher = mp
1936
1937 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1938 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1939
1940 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1941 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1942
1943 self._PRW_year_noted.Enable(True)
1944
1945 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
1946
1947 self.data = issue
1948 #----------------------------------------------------------------
1949 # generic Edit Area mixin API
1950 #----------------------------------------------------------------
1952
1953 if self._PRW_condition.GetValue().strip() == '':
1954 self._PRW_condition.display_as_valid(False)
1955 self._PRW_condition.SetFocus()
1956 return False
1957 self._PRW_condition.display_as_valid(True)
1958 self._PRW_condition.Refresh()
1959
1960 # FIXME: sanity check age/year diagnosed
1961 age_noted = self._PRW_age_noted.GetValue().strip()
1962 if age_noted != '':
1963 if gmDateTime.str2interval(str_interval = age_noted) is None:
1964 self._PRW_age_noted.display_as_valid(False)
1965 self._PRW_age_noted.SetFocus()
1966 return False
1967 self._PRW_age_noted.display_as_valid(True)
1968
1969 return True
1970 #----------------------------------------------------------------
1972 pat = gmPerson.gmCurrentPatient()
1973 emr = pat.get_emr()
1974
1975 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1976
1977 side = u''
1978 if self._ChBOX_left.GetValue():
1979 side += u's'
1980 if self._ChBOX_right.GetValue():
1981 side += u'd'
1982 issue['laterality'] = side
1983
1984 issue['summary'] = self._TCTRL_status.GetValue().strip()
1985 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1986 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1987 issue['is_active'] = self._ChBOX_active.GetValue()
1988 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1989 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
1990 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
1991
1992 age_noted = self._PRW_age_noted.GetData()
1993 if age_noted is not None:
1994 issue['age_noted'] = age_noted
1995
1996 issue.save()
1997
1998 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1999
2000 self.data = issue
2001 return True
2002 #----------------------------------------------------------------
2004
2005 self.data['description'] = self._PRW_condition.GetValue().strip()
2006
2007 side = u''
2008 if self._ChBOX_left.GetValue():
2009 side += u's'
2010 if self._ChBOX_right.GetValue():
2011 side += u'd'
2012 self.data['laterality'] = side
2013
2014 self.data['summary'] = self._TCTRL_status.GetValue().strip()
2015 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2016 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
2017 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
2018 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
2019 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
2020 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
2021
2022 age_noted = self._PRW_age_noted.GetData()
2023 if age_noted is not None:
2024 self.data['age_noted'] = age_noted
2025
2026 self.data.save()
2027 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2028
2029 return True
2030 #----------------------------------------------------------------
2032 self._PRW_condition.SetText()
2033 self._ChBOX_left.SetValue(0)
2034 self._ChBOX_right.SetValue(0)
2035 self._PRW_codes.SetText()
2036 self._on_leave_codes()
2037 self._PRW_certainty.SetText()
2038 self._PRW_grouping.SetText()
2039 self._TCTRL_status.SetValue(u'')
2040 self._PRW_age_noted.SetText()
2041 self._PRW_year_noted.SetText()
2042 self._ChBOX_active.SetValue(0)
2043 self._ChBOX_relevant.SetValue(1)
2044 self._ChBOX_confidential.SetValue(0)
2045 self._ChBOX_caused_death.SetValue(0)
2046
2047 return True
2048 #----------------------------------------------------------------
2050 self._PRW_condition.SetText(self.data['description'])
2051
2052 lat = gmTools.coalesce(self.data['laterality'], '')
2053 if lat.find('s') == -1:
2054 self._ChBOX_left.SetValue(0)
2055 else:
2056 self._ChBOX_left.SetValue(1)
2057 if lat.find('d') == -1:
2058 self._ChBOX_right.SetValue(0)
2059 else:
2060 self._ChBOX_right.SetValue(1)
2061
2062 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
2063 self._PRW_codes.SetText(val, data)
2064 self._on_leave_codes()
2065
2066 if self.data['diagnostic_certainty_classification'] is not None:
2067 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
2068 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
2069 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
2070
2071 if self.data['age_noted'] is None:
2072 self._PRW_age_noted.SetText()
2073 else:
2074 self._PRW_age_noted.SetText (
2075 value = '%sd' % self.data['age_noted'].days,
2076 data = self.data['age_noted']
2077 )
2078
2079 self._ChBOX_active.SetValue(self.data['is_active'])
2080 self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
2081 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
2082 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
2083
2084 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ...
2085 # self._PRW_age_noted.SetFocus()
2086 # self._PRW_condition.SetFocus()
2087
2088 return True
2089 #----------------------------------------------------------------
2092 #--------------------------------------------------------
2093 # internal helpers
2094 #--------------------------------------------------------
2096 if not self._PRW_codes.IsModified():
2097 return True
2098
2099 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2100 #--------------------------------------------------------
2102
2103 if not self._PRW_age_noted.IsModified():
2104 return True
2105
2106 str_age = self._PRW_age_noted.GetValue().strip()
2107
2108 if str_age == u'':
2109 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2110 return True
2111
2112 age = gmDateTime.str2interval(str_interval = str_age)
2113
2114 if age is None:
2115 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
2116 self._PRW_age_noted.SetBackgroundColour('pink')
2117 self._PRW_age_noted.Refresh()
2118 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2119 return True
2120
2121 pat = gmPerson.gmCurrentPatient()
2122 if pat['dob'] is not None:
2123 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2124
2125 if age >= max_age:
2126 gmDispatcher.send (
2127 signal = 'statustext',
2128 msg = _(
2129 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2130 ) % (age, pat.get_medical_age())
2131 )
2132 self._PRW_age_noted.SetBackgroundColour('pink')
2133 self._PRW_age_noted.Refresh()
2134 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2135 return True
2136
2137 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2138 self._PRW_age_noted.Refresh()
2139 self._PRW_age_noted.SetData(data=age)
2140
2141 if pat['dob'] is not None:
2142 fts = gmDateTime.cFuzzyTimestamp (
2143 timestamp = pat['dob'] + age,
2144 accuracy = gmDateTime.acc_months
2145 )
2146 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2147 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB
2148 #wx.CallAfter(self._ChBOX_active.SetFocus)
2149 # if we do the following instead it will take us to the save/update button ...
2150 #wx.CallAfter(self.Navigate)
2151
2152 return True
2153 #--------------------------------------------------------
2155
2156 if not self._PRW_year_noted.IsModified():
2157 return True
2158
2159 year_noted = self._PRW_year_noted.GetData()
2160
2161 if year_noted is None:
2162 if self._PRW_year_noted.GetValue().strip() == u'':
2163 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2164 return True
2165 self._PRW_year_noted.SetBackgroundColour('pink')
2166 self._PRW_year_noted.Refresh()
2167 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2168 return True
2169
2170 year_noted = year_noted.get_pydt()
2171
2172 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2173 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2174 self._PRW_year_noted.SetBackgroundColour('pink')
2175 self._PRW_year_noted.Refresh()
2176 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2177 return True
2178
2179 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2180 self._PRW_year_noted.Refresh()
2181
2182 pat = gmPerson.gmCurrentPatient()
2183 if pat['dob'] is not None:
2184 issue_age = year_noted - pat['dob']
2185 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2186 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2187
2188 return True
2189 #--------------------------------------------------------
2193 #--------------------------------------------------------
2197 #================================================================
2198 # diagnostic certainty related widgets/functions
2199 #----------------------------------------------------------------
2201
2203
2204 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2205
2206 self.selection_only = False # can be NULL, too
2207
2208 mp = gmMatchProvider.cMatchProvider_FixedList (
2209 aSeq = [
2210 {'data': u'A', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2211 {'data': u'B', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2212 {'data': u'C', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2213 {'data': u'D', 'list_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'field_label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2214 ]
2215 )
2216 mp.setThresholds(1, 2, 4)
2217 self.matcher = mp
2218
2219 self.SetToolTipString(_(
2220 "The diagnostic classification or grading of this assessment.\n"
2221 "\n"
2222 "This documents how certain one is about this being a true diagnosis."
2223 ))
2224 #================================================================
2225 # MAIN
2226 #----------------------------------------------------------------
2227 if __name__ == '__main__':
2228
2229 #================================================================
2231 """
2232 Test application for testing EMR struct widgets
2233 """
2234 #--------------------------------------------------------
2236 """
2237 Create test application UI
2238 """
2239 frame = wx.Frame (
2240 None,
2241 -4,
2242 'Testing EMR struct widgets',
2243 size=wx.Size(600, 400),
2244 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2245 )
2246 filemenu= wx.Menu()
2247 filemenu.AppendSeparator()
2248 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2249
2250 # Creating the menubar.
2251 menuBar = wx.MenuBar()
2252 menuBar.Append(filemenu,"&File")
2253
2254 frame.SetMenuBar(menuBar)
2255
2256 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2257 wx.DefaultPosition, wx.DefaultSize, 0 )
2258
2259 # event handlers
2260 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2261
2262 # patient EMR
2263 self.__pat = gmPerson.gmCurrentPatient()
2264
2265 frame.Show(1)
2266 return 1
2267 #--------------------------------------------------------
2273 #----------------------------------------------------------------
2275 app = wx.PyWidgetTester(size = (200, 300))
2276 emr = pat.get_emr()
2277 enc = emr.active_encounter
2278 #enc = gmEMRStructItems.cEncounter(1)
2279 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
2280 app.frame.Show(True)
2281 app.MainLoop()
2282 return
2283 #----------------------------------------------------------------
2285 app = wx.PyWidgetTester(size = (200, 300))
2286 emr = pat.get_emr()
2287 enc = emr.active_encounter
2288 #enc = gmEMRStructItems.cEncounter(1)
2289
2290 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
2291 dlg.ShowModal()
2292
2293 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc)
2294 # app.frame.Show(True)
2295 # app.MainLoop()
2296 #----------------------------------------------------------------
2298 app = wx.PyWidgetTester(size = (200, 300))
2299 emr = pat.get_emr()
2300 epi = emr.get_episodes()[0]
2301 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
2302 app.frame.Show(True)
2303 app.MainLoop()
2304 #----------------------------------------------------------------
2306 app = wx.PyWidgetTester(size = (200, 300))
2307 emr = pat.get_emr()
2308 epi = emr.get_episodes()[0]
2309 edit_episode(parent=app.frame, episode=epi)
2310 #----------------------------------------------------------------
2312 app = wx.PyWidgetTester(size = (400, 40))
2313 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2314 app.MainLoop()
2315 #----------------------------------------------------------------
2317 app = wx.PyWidgetTester(size = (400, 40))
2318 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2319 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID)
2320 app.MainLoop()
2321 #----------------------------------------------------------------
2323 app = wx.PyWidgetTester(size = (200, 300))
2324 edit_health_issue(parent=app.frame, issue=None)
2325 #----------------------------------------------------------------
2327 app = wx.PyWidgetTester(size = (200, 300))
2328 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2329 app.MainLoop()
2330 #----------------------------------------------------------------
2334 #================================================================
2335
2336 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2337
2338 gmI18N.activate_locale()
2339 gmI18N.install_domain()
2340 gmDateTime.init()
2341
2342 # obtain patient
2343 pat = gmPersonSearch.ask_for_patient()
2344 if pat is None:
2345 print "No patient. Exiting gracefully..."
2346 sys.exit(0)
2347 gmPatSearchWidgets.set_active_patient(patient=pat)
2348
2349 # try:
2350 # lauch emr dialogs test application
2351 # app = testapp(0)
2352 # app.MainLoop()
2353 # except StandardError:
2354 # _log.exception("unhandled exception caught !")
2355 # but re-raise them
2356 # raise
2357
2358 #test_encounter_edit_area_panel()
2359 #test_encounter_edit_area_dialog()
2360 #test_epsiode_edit_area_pnl()
2361 #test_episode_edit_area_dialog()
2362 #test_health_issue_edit_area_dlg()
2363 #test_episode_selection_prw()
2364 #test_hospital_stay_prw()
2365 test_edit_procedure()
2366
2367 #================================================================
2368
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Feb 9 04:01:10 2012 | http://epydoc.sourceforge.net |