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
15 import sys, re, datetime as pydt, logging, time
16
17
18
19 import wx
20
21
22
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
36
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
108
109 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
110
111 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
112
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
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
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
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
197
209
232
233
234
292
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
398
410
411
412
417
433
434
435
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
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
553
554
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
590
600
606
616
618 print "this was not expected to be used in this edit area"
619
620
621
630
631 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
632
634 if parent is None:
635 parent = wx.GetApp().GetTopWindow()
636
637
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
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
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
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
844
849
850
851
852
853
883
896
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
916 self._TCTRL_name.Enable(False)
917
919 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
920 self._TCTRL_name.SetValue(self.data['description'])
921 self._TCTRL_name.Enable(True)
922
923
924
925
926
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
950
951 - def refresh(self, encounter=None, msg=None):
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
963
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
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
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
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
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
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()
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
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
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
1131
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
1137
1139 wx.CallAfter(self.clear)
1140
1142 wx.CallAfter(self.refresh)
1143 return True
1144
1148
1152
1155
1156
1157
1167
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
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
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
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
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
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
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
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
1323 return False
1324
1325 elif decision == wx.ID_YES:
1326
1327 existing_epi['episode_open'] = False
1328 existing_epi.save_payload()
1329
1330 elif decision == wx.ID_NO:
1331
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
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
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
1509 - def GetData(self, can_create=False, as_instance=False, is_open=False):
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
1540
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
1570
1571
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
1661
1680
1682 self._refresh_as_new()
1683
1684
1685
1695
1697
1698
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
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
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
1819 - def GetData(self, can_create=False, is_open=False):
1837
1838
1839
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
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
1950
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
2089
2091 return self._refresh_as_new()
2092
2093
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
2148
2149
2150
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
2191 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2192 return True
2193
2195 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2196 return True
2197
2198
2199
2201
2203
2204 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2205
2206 self.selection_only = False
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
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
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
2260 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2261
2262
2263 self.__pat = gmPerson.gmCurrentPatient()
2264
2265 frame.Show(1)
2266 return 1
2267
2269 """
2270 Close test aplication
2271 """
2272 self.ExitMainLoop ()
2273
2283
2292
2293
2294
2295
2296
2304
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
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
2332 app = wx.PyWidgetTester(size = (200, 300))
2333 edit_procedure(parent=app.frame)
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
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
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365 test_edit_procedure()
2366
2367
2368