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 import wx.lib.pubsub as wxps
21
22
23
24 if __name__ == '__main__':
25 sys.path.insert(0, '../../')
26 from Gnumed.pycommon import gmI18N, gmMatchProvider, gmDispatcher, gmTools, gmDateTime, gmCfg, gmExceptions
27 from Gnumed.business import gmEMRStructItems, gmPerson, gmSOAPimporter, gmSurgery, gmPersonSearch
28 from Gnumed.wxpython import gmPhraseWheel, gmGuiHelpers, gmListWidgets, gmEditArea, gmPatSearchWidgets
29 from Gnumed.wxGladeWidgets import wxgIssueSelectionDlg, wxgMoveNarrativeDlg
30 from Gnumed.wxGladeWidgets import wxgEncounterTypeEditAreaPnl
31
32
33 _log = logging.getLogger('gm.ui')
34 _log.info(__version__)
35
36
37
48
49 def delete(procedure=None):
50 if gmEMRStructItems.delete_performed_procedure(procedure = procedure['pk_procedure']):
51 return True
52
53 gmDispatcher.send (
54 signal = u'statustext',
55 msg = _('Cannot delete performed procedure.'),
56 beep = True
57 )
58 return False
59
60 def refresh(lctrl):
61 procs = emr.get_performed_procedures()
62
63 items = [
64 [
65 u'%s%s' % (
66 p['clin_when'].strftime('%Y-%m-%d'),
67 gmTools.bool2subst (
68 p['is_ongoing'],
69 _(' (ongoing)'),
70 gmTools.coalesce (
71 initial = p['clin_end'],
72 instead = u'',
73 template_initial = u' - %s',
74 function_initial = ('strftime', u'%Y-%m-%d')
75 )
76 )
77 ),
78 p['clin_where'],
79 p['episode'],
80 p['performed_procedure']
81 ] for p in procs
82 ]
83 lctrl.set_string_items(items = items)
84 lctrl.set_data(data = procs)
85
86 gmListWidgets.get_choices_from_list (
87 parent = parent,
88 msg = _('\nSelect the procedure you want to edit !\n'),
89 caption = _('Editing performed procedures ...'),
90 columns = [_('When'), _('Where'), _('Episode'), _('Procedure')],
91 single_selection = True,
92 edit_callback = edit,
93 new_callback = edit,
94 delete_callback = delete,
95 refresh_callback = refresh
96 )
97
109
110 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
111
112 -class cProcedureEAPnl(wxgProcedureEAPnl.wxgProcedureEAPnl, gmEditArea.cGenericEditAreaMixin):
113
122
124 self._PRW_hospital_stay.add_callback_on_lose_focus(callback = self._on_hospital_stay_lost_focus)
125 self._PRW_hospital_stay.set_context(context = 'pat', val = gmPerson.gmCurrentPatient().ID)
126 self._PRW_location.add_callback_on_lose_focus(callback = self._on_location_lost_focus)
127 self._DPRW_date.add_callback_on_lose_focus(callback = self._on_start_lost_focus)
128 self._DPRW_end.add_callback_on_lose_focus(callback = self._on_end_lost_focus)
129
130
131 mp = gmMatchProvider.cMatchProvider_SQL2 (
132 queries = [
133 u"""
134 SELECT DISTINCT ON (data) data, location
135 FROM (
136 SELECT
137 clin_where as data,
138 clin_where as location
139 FROM
140 clin.procedure
141 WHERE
142 clin_where %(fragment_condition)s
143
144 UNION ALL
145
146 SELECT
147 narrative as data,
148 narrative as location
149 FROM
150 clin.hospital_stay
151 WHERE
152 narrative %(fragment_condition)s
153 ) as union_result
154 ORDER BY data
155 LIMIT 25"""
156 ]
157 )
158 mp.setThresholds(2, 4, 6)
159 self._PRW_location.matcher = mp
160
161
162 mp = gmMatchProvider.cMatchProvider_SQL2 (
163 queries = [
164 u"""
165 select distinct on (narrative) narrative, narrative
166 from clin.procedure
167 where narrative %(fragment_condition)s
168 order by narrative
169 limit 25
170 """ ]
171 )
172 mp.setThresholds(2, 4, 6)
173 self._PRW_procedure.matcher = mp
174
176 stay = self._PRW_hospital_stay.GetData()
177 if stay is None:
178 self._PRW_hospital_stay.SetText()
179 self._PRW_location.Enable(True)
180 self._PRW_episode.Enable(True)
181 self._LBL_hospital_details.SetLabel(u'')
182 else:
183 self._PRW_location.SetText()
184 self._PRW_location.Enable(False)
185 self._PRW_episode.SetText()
186 self._PRW_episode.Enable(False)
187 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = stay).format())
188
190 if self._PRW_location.GetValue().strip() == u'':
191 self._PRW_hospital_stay.Enable(True)
192
193 else:
194 self._PRW_hospital_stay.SetText()
195 self._PRW_hospital_stay.Enable(False)
196 self._PRW_hospital_stay.display_as_valid(True)
197
198
210
233
234
235
237
238 has_errors = False
239
240 if not self._DPRW_date.is_valid_timestamp():
241 self._DPRW_date.display_as_valid(False)
242 has_errors = True
243 else:
244 self._DPRW_date.display_as_valid(True)
245
246 end = self._DPRW_end.GetData()
247 self._DPRW_end.display_as_valid(True)
248 if end is not None:
249 end = end.get_pydt()
250 start = self._DPRW_end.GetData()
251 if start is not None:
252 start = start.get_pydt()
253 if end < start:
254 has_errors = True
255 self._DPRW_end.display_as_valid(False)
256 if self._CHBOX_ongoing.IsChecked():
257 now = gmDateTime.pydt_now_here()
258 if end < now:
259 has_errors = True
260 self._DPRW_end.display_as_valid(False)
261
262 if self._PRW_hospital_stay.GetData() is None:
263 if self._PRW_episode.GetData() is None:
264 self._PRW_episode.display_as_valid(False)
265 has_errors = True
266 else:
267 self._PRW_episode.display_as_valid(True)
268 else:
269 self._PRW_episode.display_as_valid(True)
270
271 if (self._PRW_procedure.GetValue() is None) or (self._PRW_procedure.GetValue().strip() == u''):
272 self._PRW_procedure.display_as_valid(False)
273 has_errors = True
274 else:
275 self._PRW_procedure.display_as_valid(True)
276
277 invalid_location = (
278 (self._PRW_hospital_stay.GetData() is None) and (self._PRW_location.GetValue().strip() == u'')
279 or
280 (self._PRW_hospital_stay.GetData() is not None) and (self._PRW_location.GetValue().strip() != u'')
281 )
282 if invalid_location:
283 self._PRW_hospital_stay.display_as_valid(False)
284 self._PRW_location.display_as_valid(False)
285 has_errors = True
286 else:
287 self._PRW_hospital_stay.display_as_valid(True)
288 self._PRW_location.display_as_valid(True)
289
290 wxps.Publisher().sendMessage (
291 topic = 'statustext',
292 data = {'msg': _('Cannot save procedure.'), 'beep': True}
293 )
294
295 return (has_errors is False)
296
298
299 pat = gmPerson.gmCurrentPatient()
300 emr = pat.get_emr()
301
302 if self._PRW_hospital_stay.GetData() is None:
303 stay = None
304 epi = self._PRW_episode.GetData()
305 loc = self._PRW_location.GetValue().strip()
306 else:
307 stay = self._PRW_hospital_stay.GetData()
308 epi = gmEMRStructItems.cHospitalStay(aPK_obj = stay)['pk_episode']
309 loc = None
310
311 proc = emr.add_performed_procedure (
312 episode = epi,
313 location = loc,
314 hospital_stay = stay,
315 procedure = self._PRW_procedure.GetValue().strip()
316 )
317
318 proc['clin_when'] = self._DPRW_date.GetData().get_pydt()
319 if self._DPRW_end.GetData() is None:
320 proc['clin_end'] = None
321 else:
322 proc['clin_end'] = self._DPRW_end.GetData().get_pydt()
323 proc['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
324 proc.save()
325
326 proc.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
327
328 self.data = proc
329
330 return True
331
333 self.data['clin_when'] = self._DPRW_date.GetData().get_pydt()
334
335 if self._DPRW_end.GetData() is None:
336 self.data['clin_end'] = None
337 else:
338 self.data['clin_end'] = self._DPRW_end.GetData().get_pydt()
339
340 self.data['is_ongoing'] = self._CHBOX_ongoing.IsChecked()
341
342 if self._PRW_hospital_stay.GetData() is None:
343 self.data['pk_hospital_stay'] = None
344 self.data['clin_where'] = self._PRW_location.GetValue().strip()
345 self.data['pk_episode'] = self._PRW_episode.GetData()
346 else:
347 self.data['pk_hospital_stay'] = self._PRW_hospital_stay.GetData()
348 self.data['clin_where'] = None
349 stay = gmEMRStructItems.cHospitalStay(aPK_obj = self._PRW_hospital_stay.GetData())
350 self.data['pk_episode'] = stay['pk_episode']
351
352 self.data['performed_procedure'] = self._PRW_procedure.GetValue().strip()
353
354 self.data.save()
355 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
356
357 return True
358
360 self._DPRW_date.SetText()
361 self._DPRW_end.SetText()
362 self._CHBOX_ongoing.SetValue(False)
363 self._CHBOX_ongoing.Enable(True)
364 self._PRW_hospital_stay.SetText()
365 self._PRW_location.SetText()
366 self._PRW_episode.SetText()
367 self._PRW_procedure.SetText()
368 self._PRW_codes.SetText()
369
370 self._PRW_procedure.SetFocus()
371
402
414
415
416
421
437
438
439
450
451 def delete(stay=None):
452 if gmEMRStructItems.delete_hospital_stay(stay = stay['pk_hospital_stay']):
453 return True
454 gmDispatcher.send (
455 signal = u'statustext',
456 msg = _('Cannot delete hospital stay.'),
457 beep = True
458 )
459 return False
460
461 def refresh(lctrl):
462 stays = emr.get_hospital_stays()
463 items = [
464 [
465 s['admission'].strftime('%Y-%m-%d'),
466 gmTools.coalesce(s['discharge'], u'', function_initial = ('strftime', '%Y-%m-%d')),
467 s['episode'],
468 gmTools.coalesce(s['hospital'], u'')
469 ] for s in stays
470 ]
471 lctrl.set_string_items(items = items)
472 lctrl.set_data(data = stays)
473
474 gmListWidgets.get_choices_from_list (
475 parent = parent,
476 msg = _('\nSelect the hospital stay you want to edit !\n'),
477 caption = _('Editing hospital stays ...'),
478 columns = [_('Admission'), _('Discharge'), _('Reason'), _('Hospital')],
479 single_selection = True,
480 edit_callback = edit,
481 new_callback = edit,
482 delete_callback = delete,
483 refresh_callback = refresh
484 )
485
486
498
500 """Phrasewheel to allow selection of a hospital stay.
501 """
503
504 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
505
506 ctxt = {'ctxt_pat': {'where_part': u'pk_patient = %(pat)s and', 'placeholder': u'pat'}}
507
508 mp = gmMatchProvider.cMatchProvider_SQL2 (
509 queries = [
510 u"""
511 select
512 pk_hospital_stay,
513 descr
514 from (
515 select distinct on (pk_hospital_stay)
516 pk_hospital_stay,
517 descr
518 from
519 (select
520 pk_hospital_stay,
521 (
522 to_char(admission, 'YYYY-Mon-DD')
523 || coalesce((' (' || hospital || '):'), ': ')
524 || episode
525 || coalesce((' (' || health_issue || ')'), '')
526 ) as descr
527 from
528 clin.v_pat_hospital_stays
529 where
530 %(ctxt_pat)s
531
532 hospital %(fragment_condition)s
533 or
534 episode %(fragment_condition)s
535 or
536 health_issue %(fragment_condition)s
537 ) as the_stays
538 ) as distinct_stays
539 order by descr
540 limit 25
541 """ ],
542 context = ctxt
543 )
544 mp.setThresholds(3, 4, 6)
545 mp.set_context('pat', gmPerson.gmCurrentPatient().ID)
546
547 self.matcher = mp
548 self.selection_only = True
549
550 from Gnumed.wxGladeWidgets import wxgHospitalStayEditAreaPnl
551
552 -class cHospitalStayEditAreaPnl(wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
553
557
558
559
561
562 valid = True
563
564 if not self._PRW_admission.is_valid_timestamp(allow_empty = False):
565 valid = False
566 wxps.Publisher().sendMessage (
567 topic = 'statustext',
568 data = {'msg': _('Missing admission data. Cannot save hospital stay.'), 'beep': True}
569 )
570
571 if self._PRW_discharge.is_valid_timestamp(allow_empty = True):
572 if self._PRW_discharge.date is not None:
573 if not self._PRW_discharge.date > self._PRW_admission.date:
574 valid = False
575 self._PRW_discharge.display_as_valid(False)
576 wxps.Publisher().sendMessage (
577 topic = 'statustext',
578 data = {'msg': _('Discharge date must be empty or later than admission. Cannot save hospital stay.'), 'beep': True}
579 )
580
581 if self._PRW_episode.GetValue().strip() == u'':
582 valid = False
583 self._PRW_episode.display_as_valid(False)
584 wxps.Publisher().sendMessage (
585 topic = 'statustext',
586 data = {'msg': _('Must select an episode or enter a name for a new one. Cannot save hospital stay.'), 'beep': True}
587 )
588
589 return (valid is True)
590
603
613
619
629
631 print "this was not expected to be used in this edit area"
632
633
634
643
644 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
645
647 if parent is None:
648 parent = wx.GetApp().GetTopWindow()
649
650
651 dlg = cEncounterEditAreaDlg(parent = parent, encounter = encounter)
652 if dlg.ShowModal() == wx.ID_OK:
653 dlg.Destroy()
654 return True
655 dlg.Destroy()
656 return False
657
658 -def select_encounters(parent=None, patient=None, single_selection=True, encounters=None, ignore_OK_button=False):
659
660 if patient is None:
661 patient = gmPerson.gmCurrentPatient()
662
663 if not patient.connected:
664 gmDispatcher.send(signal = 'statustext', msg = _('Cannot list encounters. No active patient.'))
665 return False
666
667 if parent is None:
668 parent = wx.GetApp().GetTopWindow()
669
670 emr = patient.get_emr()
671
672
673 def refresh(lctrl):
674 if encounters is None:
675 encs = emr.get_encounters()
676 else:
677 encs = encounters
678
679 items = [
680 [
681 e['started'].strftime('%x %H:%M'),
682 e['last_affirmed'].strftime('%H:%M'),
683 e['l10n_type'],
684 gmTools.coalesce(e['reason_for_encounter'], u''),
685 gmTools.coalesce(e['assessment_of_encounter'], u''),
686 gmTools.bool2subst(e.has_clinical_data(), u'', gmTools.u_checkmark_thin),
687 e['pk_encounter']
688 ] for e in encs
689 ]
690
691 lctrl.set_string_items(items = items)
692 lctrl.set_data(data = encs)
693
694 def new():
695 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID)
696 return edit_encounter(parent = parent, encounter = enc)
697
698 def edit(enc=None):
699 return edit_encounter(parent = parent, encounter = enc)
700
701 return gmListWidgets.get_choices_from_list (
702 parent = parent,
703 msg = _('\nBelow find the relevant encounters of the patient.\n'),
704 caption = _('Encounters ...'),
705 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
706 can_return_empty = False,
707 single_selection = single_selection,
708 refresh_callback = refresh,
709 edit_callback = edit,
710 new_callback = new,
711 ignore_OK_button = ignore_OK_button
712 )
713
715 """This is used as the callback when the EMR detects that the
716 patient was here rather recently and wants to ask the
717 provider whether to continue the recent encounter.
718 """
719 if parent is None:
720 parent = wx.GetApp().GetTopWindow()
721
722 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
723 parent = None,
724 id = -1,
725 caption = caption,
726 question = msg,
727 button_defs = [
728 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
729 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
730 ],
731 show_checkbox = False
732 )
733
734 result = dlg.ShowModal()
735 dlg.Destroy()
736
737 if result == wx.ID_YES:
738 return True
739
740 return False
741
743
744 if parent is None:
745 parent = wx.GetApp().GetTopWindow()
746
747
748 def edit(enc_type=None):
749 return edit_encounter_type(parent = parent, encounter_type = enc_type)
750
751 def delete(enc_type=None):
752 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
753 return True
754 gmDispatcher.send (
755 signal = u'statustext',
756 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
757 beep = True
758 )
759 return False
760
761 def refresh(lctrl):
762 enc_types = gmEMRStructItems.get_encounter_types()
763 lctrl.set_string_items(items = enc_types)
764
765 gmListWidgets.get_choices_from_list (
766 parent = parent,
767 msg = _('\nSelect the encounter type you want to edit !\n'),
768 caption = _('Managing encounter types ...'),
769 columns = [_('Local name'), _('Encounter type')],
770 single_selection = True,
771 edit_callback = edit,
772 new_callback = edit,
773 delete_callback = delete,
774 refresh_callback = refresh
775 )
776
786
788 """Phrasewheel to allow selection of encounter type.
789
790 - user input interpreted as encounter type in English or local language
791 - data returned is pk of corresponding encounter type or None
792 """
794
795 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
796
797 mp = gmMatchProvider.cMatchProvider_SQL2 (
798 queries = [
799 u"""
800 select pk, l10n_description from (
801 select distinct on (pk) * from (
802 (select
803 pk,
804 _(description) as l10n_description,
805 1 as rank
806 from
807 clin.encounter_type
808 where
809 _(description) %(fragment_condition)s
810
811 ) union all (
812
813 select
814 pk,
815 _(description) as l10n_description,
816 2 as rank
817 from
818 clin.encounter_type
819 where
820 description %(fragment_condition)s
821 )
822 ) as q_distinct_pk
823 ) as q_ordered order by rank, l10n_description
824 """ ]
825 )
826 mp.setThresholds(2, 4, 6)
827
828 self.matcher = mp
829 self.selection_only = True
830 self.picklist_delay = 50
831
833
838
839
840
841
842
872
885
895
897 self._TCTRL_l10n_name.SetValue(u'')
898 self._TCTRL_name.SetValue(u'')
899 self._TCTRL_name.Enable(True)
900
902 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
903 self._TCTRL_name.SetValue(self.data['description'])
904
905 self._TCTRL_name.Enable(False)
906
908 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
909 self._TCTRL_name.SetValue(self.data['description'])
910 self._TCTRL_name.Enable(True)
911
912
913
914
915
916
917 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
918
920
922 try:
923 self.__encounter = kwargs['encounter']
924 del kwargs['encounter']
925 except KeyError:
926 self.__encounter = None
927
928 try:
929 msg = kwargs['msg']
930 del kwargs['msg']
931 except KeyError:
932 msg = None
933
934 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
935
936 self.refresh(msg = msg)
937
938
939
940 - def refresh(self, encounter=None, msg=None):
941
942 if msg is not None:
943 self._LBL_instructions.SetLabel(msg)
944
945 if encounter is not None:
946 self.__encounter = encounter
947
948 if self.__encounter is None:
949 return True
950
951
952
953 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
954 self._LBL_patient.SetLabel(pat.get_description_gender())
955
956 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
957
958 fts = gmDateTime.cFuzzyTimestamp (
959 timestamp = self.__encounter['started'],
960 accuracy = gmDateTime.acc_minutes
961 )
962 self._PRW_start.SetText(fts.format_accurately(), data=fts)
963
964 fts = gmDateTime.cFuzzyTimestamp (
965 timestamp = self.__encounter['last_affirmed'],
966 accuracy = gmDateTime.acc_minutes
967 )
968 self._PRW_end.SetText(fts.format_accurately(), data=fts)
969
970
971 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
972 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
973 self._PRW_rfe_codes.SetText(val, data)
974
975
976 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
977 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
978 self._PRW_aoe_codes.SetText(val, data)
979
980
981 if self.__encounter['last_affirmed'] == self.__encounter['started']:
982 self._PRW_end.SetFocus()
983 else:
984 self._TCTRL_aoe.SetFocus()
985
986 return True
987
989
990 if self._PRW_encounter_type.GetData() is None:
991 self._PRW_encounter_type.SetBackgroundColour('pink')
992 self._PRW_encounter_type.Refresh()
993 self._PRW_encounter_type.SetFocus()
994 return False
995 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
996 self._PRW_encounter_type.Refresh()
997
998 if (not self._PRW_start.is_valid_timestamp()) or (self._PRW_start.GetValue().strip() == u''):
999 self._PRW_start.SetFocus()
1000 return False
1001
1002 if (not self._PRW_end.is_valid_timestamp()) or (self._PRW_end.GetValue().strip() == u''):
1003 self._PRW_end.SetFocus()
1004 return False
1005
1006 return True
1007
1009 if not self.__is_valid_for_save():
1010 return False
1011
1012 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1013 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1014 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1015 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1016 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1017 self.__encounter.save_payload()
1018
1019 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1020 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1021
1022 return True
1023
1024
1026
1028 encounter = kwargs['encounter']
1029 del kwargs['encounter']
1030
1031 try:
1032 button_defs = kwargs['button_defs']
1033 del kwargs['button_defs']
1034 except KeyError:
1035 button_defs = None
1036
1037 try:
1038 msg = kwargs['msg']
1039 del kwargs['msg']
1040 except KeyError:
1041 msg = None
1042
1043 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1044 self.SetSize((450, 280))
1045 self.SetMinSize((450, 280))
1046
1047 if button_defs is not None:
1048 self._BTN_save.SetLabel(button_defs[0][0])
1049 self._BTN_save.SetToolTipString(button_defs[0][1])
1050 self._BTN_close.SetLabel(button_defs[1][0])
1051 self._BTN_close.SetToolTipString(button_defs[1][1])
1052 self.Refresh()
1053
1054 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1055
1056 self.Fit()
1057
1064
1065
1066
1076
1146
1148 """Prepare changing health issue for an episode.
1149
1150 Checks for two-open-episodes conflict. When this
1151 function succeeds, the pk_health_issue has been set
1152 on the episode instance and the episode should - for
1153 all practical purposes - be ready for save_payload().
1154 """
1155
1156 if not episode['episode_open']:
1157 episode['pk_health_issue'] = target_issue['pk_health_issue']
1158 if save_to_backend:
1159 episode.save_payload()
1160 return True
1161
1162
1163 if target_issue is None:
1164 episode['pk_health_issue'] = None
1165 if save_to_backend:
1166 episode.save_payload()
1167 return True
1168
1169
1170 db_cfg = gmCfg.cCfgSQL()
1171 epi_ttl = int(db_cfg.get2 (
1172 option = u'episode.ttl',
1173 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1174 bias = 'user',
1175 default = 60
1176 ))
1177 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1178 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1179 existing_epi = target_issue.get_open_episode()
1180
1181
1182 if existing_epi is None:
1183 episode['pk_health_issue'] = target_issue['pk_health_issue']
1184 if save_to_backend:
1185 episode.save_payload()
1186 return True
1187
1188
1189 if existing_epi['pk_episode'] == episode['pk_episode']:
1190 episode['pk_health_issue'] = target_issue['pk_health_issue']
1191 if save_to_backend:
1192 episode.save_payload()
1193 return True
1194
1195
1196 move_range = episode.get_access_range()
1197 exist_range = existing_epi.get_access_range()
1198 question = _(
1199 'You want to associate the running episode:\n\n'
1200 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1201 'with the health issue:\n\n'
1202 ' "%(issue_name)s"\n\n'
1203 'There already is another episode running\n'
1204 'for this health issue:\n\n'
1205 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1206 'However, there can only be one running\n'
1207 'episode per health issue.\n\n'
1208 'Which episode do you want to close ?'
1209 ) % {
1210 'new_epi_name': episode['description'],
1211 'new_epi_start': move_range[0].strftime('%m/%y'),
1212 'new_epi_end': move_range[1].strftime('%m/%y'),
1213 'issue_name': target_issue['description'],
1214 'old_epi_name': existing_epi['description'],
1215 'old_epi_start': exist_range[0].strftime('%m/%y'),
1216 'old_epi_end': exist_range[1].strftime('%m/%y')
1217 }
1218 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1219 parent = None,
1220 id = -1,
1221 caption = _('Resolving two-running-episodes conflict'),
1222 question = question,
1223 button_defs = [
1224 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1225 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1226 ]
1227 )
1228 decision = dlg.ShowModal()
1229
1230 if decision == wx.ID_CANCEL:
1231
1232 return False
1233
1234 elif decision == wx.ID_YES:
1235
1236 existing_epi['episode_open'] = False
1237 existing_epi.save_payload()
1238
1239 elif decision == wx.ID_NO:
1240
1241 episode['episode_open'] = False
1242
1243 else:
1244 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1245
1246 episode['pk_health_issue'] = target_issue['pk_health_issue']
1247 if save_to_backend:
1248 episode.save_payload()
1249 return True
1250
1274
1276 """Let user select an episode *description*.
1277
1278 The user can select an episode description from the previously
1279 used descriptions across all episodes across all patients.
1280
1281 Selection is done with a phrasewheel so the user can
1282 type the episode name and matches will be shown. Typing
1283 "*" will show the entire list of episodes.
1284
1285 If the user types a description not existing yet a
1286 new episode description will be returned.
1287 """
1289
1290 mp = gmMatchProvider.cMatchProvider_SQL2 (
1291 queries = [u"""
1292 select distinct on (description) description, description, 1
1293 from clin.episode
1294 where description %(fragment_condition)s
1295 order by description
1296 limit 30"""
1297 ]
1298 )
1299 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1300 self.matcher = mp
1301
1303 """Let user select an episode.
1304
1305 The user can select an episode from the existing episodes of a
1306 patient. Selection is done with a phrasewheel so the user
1307 can type the episode name and matches will be shown. Typing
1308 "*" will show the entire list of episodes. Closed episodes
1309 will be marked as such. If the user types an episode name not
1310 in the list of existing episodes a new episode can be created
1311 from it if the programmer activated that feature.
1312
1313 If keyword <patient_id> is set to None or left out the control
1314 will listen to patient change signals and therefore act on
1315 gmPerson.gmCurrentPatient() changes.
1316 """
1318
1319 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1320
1321 mp = gmMatchProvider.cMatchProvider_SQL2 (
1322 queries = [
1323 u"""(
1324
1325 select
1326 pk_episode
1327 as data,
1328 description
1329 as field_label,
1330 coalesce (
1331 description || ' - ' || health_issue,
1332 description
1333 ) as list_label,
1334 1 as rank
1335 from
1336 clin.v_pat_episodes
1337 where
1338 episode_open is true and
1339 description %(fragment_condition)s
1340 %(ctxt_pat)s
1341
1342 ) union all (
1343
1344 select
1345 pk_episode
1346 as data,
1347 description
1348 as field_label,
1349 coalesce (
1350 description || _(' (closed)') || ' - ' || health_issue,
1351 description || _(' (closed)')
1352 ) as list_label,
1353 2 as rank
1354 from
1355 clin.v_pat_episodes
1356 where
1357 description %(fragment_condition)s and
1358 episode_open is false
1359 %(ctxt_pat)s
1360
1361 )
1362
1363 order by rank, list_label
1364 limit 30"""
1365 ],
1366 context = ctxt
1367 )
1368
1369 try:
1370 kwargs['patient_id']
1371 except KeyError:
1372 kwargs['patient_id'] = None
1373
1374 if kwargs['patient_id'] is None:
1375 self.use_current_patient = True
1376 self.__register_patient_change_signals()
1377 pat = gmPerson.gmCurrentPatient()
1378 if pat.connected:
1379 mp.set_context('pat', pat.ID)
1380 else:
1381 self.use_current_patient = False
1382 self.__patient_id = int(kwargs['patient_id'])
1383 mp.set_context('pat', self.__patient_id)
1384
1385 del kwargs['patient_id']
1386
1387 gmPhraseWheel.cPhraseWheel.__init__ (
1388 self,
1389 *args,
1390 **kwargs
1391 )
1392 self.matcher = mp
1393
1394
1395
1397 if self.use_current_patient:
1398 return False
1399 self.__patient_id = int(patient_id)
1400 self.set_context('pat', self.__patient_id)
1401 return True
1402
1403 - def GetData(self, can_create=False, as_instance=False, is_open=False):
1406
1408
1409 epi_name = self.GetValue().strip()
1410 if epi_name == u'':
1411 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1412 _log.debug('cannot create episode without name')
1413 return
1414
1415 if self.use_current_patient:
1416 pat = gmPerson.gmCurrentPatient()
1417 else:
1418 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1419
1420 emr = pat.get_emr()
1421 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1422 if epi is None:
1423 self.data = {}
1424 else:
1425 self.SetText (
1426 value = epi_name,
1427 data = epi['pk_episode']
1428 )
1429
1432
1433
1434
1438
1441
1443 if self.use_current_patient:
1444 patient = gmPerson.gmCurrentPatient()
1445 self.set_context('pat', patient.ID)
1446 return True
1447
1448 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1449
1450 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1451
1464
1465
1466
1468
1469 errors = False
1470
1471 if len(self._PRW_description.GetValue().strip()) == 0:
1472 errors = True
1473 self._PRW_description.display_as_valid(False)
1474 self._PRW_description.SetFocus()
1475 else:
1476 self._PRW_description.display_as_valid(True)
1477 self._PRW_description.Refresh()
1478
1479 return not errors
1480
1482
1483 pat = gmPerson.gmCurrentPatient()
1484 emr = pat.get_emr()
1485
1486 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1487 epi['summary'] = self._TCTRL_status.GetValue().strip()
1488 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1489 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1490
1491 issue_name = self._PRW_issue.GetValue().strip()
1492 if len(issue_name) != 0:
1493 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1494 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1495
1496 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1497 gmDispatcher.send (
1498 signal = 'statustext',
1499 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1500 epi['description'],
1501 issue['description']
1502 )
1503 )
1504 gmEMRStructItems.delete_episode(episode = epi)
1505 return False
1506
1507 epi.save()
1508
1509 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1510
1511 self.data = epi
1512 return True
1513
1515
1516 self.data['description'] = self._PRW_description.GetValue().strip()
1517 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1518 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1519 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1520
1521 issue_name = self._PRW_issue.GetValue().strip()
1522 if len(issue_name) == 0:
1523 self.data['pk_health_issue'] = None
1524 else:
1525 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1526 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1527
1528 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1529 gmDispatcher.send (
1530 signal = 'statustext',
1531 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1532 self.data['description'],
1533 issue['description']
1534 )
1535 )
1536 return False
1537
1538 self.data.save()
1539 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1540
1541 return True
1542
1555
1574
1576 self._refresh_as_new()
1577
1578
1579
1589
1591
1592
1593
1595
1596 issues = kwargs['issues']
1597 del kwargs['issues']
1598
1599 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1600
1601 self.SetTitle(_('Select the health issues you are interested in ...'))
1602 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1603
1604 for issue in issues:
1605 if issue['is_confidential']:
1606 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1607 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1608 else:
1609 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1610
1611 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1612 if issue['clinically_relevant']:
1613 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1614 if issue['is_active']:
1615 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1616 if issue['is_cause_of_death']:
1617 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1618
1619 self._LCTRL_items.set_column_widths()
1620 self._LCTRL_items.set_data(data = issues)
1621
1623 """Let the user select a health issue.
1624
1625 The user can select a health issue from the existing issues
1626 of a patient. Selection is done with a phrasewheel so the user
1627 can type the issue name and matches will be shown. Typing
1628 "*" will show the entire list of issues. Inactive issues
1629 will be marked as such. If the user types an issue name not
1630 in the list of existing issues a new issue can be created
1631 from it if the programmer activated that feature.
1632
1633 If keyword <patient_id> is set to None or left out the control
1634 will listen to patient change signals and therefore act on
1635 gmPerson.gmCurrentPatient() changes.
1636 """
1638
1639 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1640
1641 mp = gmMatchProvider.cMatchProvider_SQL2 (
1642
1643 queries = [u"""
1644 (select pk_health_issue, description, 1
1645 from clin.v_health_issues where
1646 is_active is true and
1647 description %(fragment_condition)s and
1648 %(ctxt_pat)s
1649 order by description)
1650
1651 union
1652
1653 (select pk_health_issue, description || _(' (inactive)'), 2
1654 from clin.v_health_issues where
1655 is_active is false and
1656 description %(fragment_condition)s and
1657 %(ctxt_pat)s
1658 order by description)"""
1659 ],
1660 context = ctxt
1661 )
1662
1663 try: kwargs['patient_id']
1664 except KeyError: kwargs['patient_id'] = None
1665
1666 if kwargs['patient_id'] is None:
1667 self.use_current_patient = True
1668 self.__register_patient_change_signals()
1669 pat = gmPerson.gmCurrentPatient()
1670 if pat.connected:
1671 mp.set_context('pat', pat.ID)
1672 else:
1673 self.use_current_patient = False
1674 self.__patient_id = int(kwargs['patient_id'])
1675 mp.set_context('pat', self.__patient_id)
1676
1677 del kwargs['patient_id']
1678
1679 gmPhraseWheel.cPhraseWheel.__init__ (
1680 self,
1681 *args,
1682 **kwargs
1683 )
1684 self.matcher = mp
1685
1686
1687
1689 if self.use_current_patient:
1690 return False
1691 self.__patient_id = int(patient_id)
1692 self.set_context('pat', self.__patient_id)
1693 return True
1694
1695 - def GetData(self, can_create=False, is_open=False):
1713
1714
1715
1719
1722
1724 if self.use_current_patient:
1725 patient = gmPerson.gmCurrentPatient()
1726 self.set_context('pat', patient.ID)
1727 return True
1728
1730
1732 try:
1733 msg = kwargs['message']
1734 except KeyError:
1735 msg = None
1736 del kwargs['message']
1737 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1738 if msg is not None:
1739 self._lbl_message.SetLabel(label=msg)
1740
1751
1752 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1753
1754 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1755 """Panel encapsulating health issue edit area functionality."""
1756
1758
1759 try:
1760 issue = kwargs['issue']
1761 except KeyError:
1762 issue = None
1763
1764 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1765
1766 gmEditArea.cGenericEditAreaMixin.__init__(self)
1767
1768
1769 mp = gmMatchProvider.cMatchProvider_SQL2 (
1770 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1771 )
1772 mp.setThresholds(1, 3, 5)
1773 self._PRW_condition.matcher = mp
1774
1775 mp = gmMatchProvider.cMatchProvider_SQL2 (
1776 queries = [u"""
1777 select distinct on (grouping) grouping, grouping from (
1778
1779 select rank, grouping from ((
1780
1781 select
1782 grouping,
1783 1 as rank
1784 from
1785 clin.health_issue
1786 where
1787 grouping %%(fragment_condition)s
1788 and
1789 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1790
1791 ) union (
1792
1793 select
1794 grouping,
1795 2 as rank
1796 from
1797 clin.health_issue
1798 where
1799 grouping %%(fragment_condition)s
1800
1801 )) as union_result
1802
1803 order by rank
1804
1805 ) as order_result
1806
1807 limit 50""" % gmPerson.gmCurrentPatient().ID
1808 ]
1809 )
1810 mp.setThresholds(1, 3, 5)
1811 self._PRW_grouping.matcher = mp
1812
1813 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1814 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1815
1816 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1817 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1818
1819 self._PRW_year_noted.Enable(True)
1820
1821 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
1822
1823 self.data = issue
1824
1825
1826
1846
1848 pat = gmPerson.gmCurrentPatient()
1849 emr = pat.get_emr()
1850
1851 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1852
1853 side = u''
1854 if self._ChBOX_left.GetValue():
1855 side += u's'
1856 if self._ChBOX_right.GetValue():
1857 side += u'd'
1858 issue['laterality'] = side
1859
1860 issue['summary'] = self._TCTRL_status.GetValue().strip()
1861 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1862 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1863 issue['is_active'] = self._ChBOX_active.GetValue()
1864 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1865 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
1866 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
1867
1868 age_noted = self._PRW_age_noted.GetData()
1869 if age_noted is not None:
1870 issue['age_noted'] = age_noted
1871
1872 issue.save()
1873
1874 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1875
1876 self.data = issue
1877 return True
1878
1880
1881 self.data['description'] = self._PRW_condition.GetValue().strip()
1882
1883 side = u''
1884 if self._ChBOX_left.GetValue():
1885 side += u's'
1886 if self._ChBOX_right.GetValue():
1887 side += u'd'
1888 self.data['laterality'] = side
1889
1890 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1891 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1892 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
1893 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
1894 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
1895 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
1896 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
1897
1898 age_noted = self._PRW_age_noted.GetData()
1899 if age_noted is not None:
1900 self.data['age_noted'] = age_noted
1901
1902 self.data.save()
1903 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1904
1905 return True
1906
1908 self._PRW_condition.SetText()
1909 self._ChBOX_left.SetValue(0)
1910 self._ChBOX_right.SetValue(0)
1911 self._PRW_codes.SetText()
1912 self._on_leave_codes()
1913 self._PRW_certainty.SetText()
1914 self._PRW_grouping.SetText()
1915 self._TCTRL_status.SetValue(u'')
1916 self._PRW_age_noted.SetText()
1917 self._PRW_year_noted.SetText()
1918 self._ChBOX_active.SetValue(0)
1919 self._ChBOX_relevant.SetValue(1)
1920 self._ChBOX_confidential.SetValue(0)
1921 self._ChBOX_caused_death.SetValue(0)
1922
1923 return True
1924
1965
1967 return self._refresh_as_new()
1968
1969
1970
1972 if not self._PRW_codes.IsModified():
1973 return True
1974
1975 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
1976
1978
1979 if not self._PRW_age_noted.IsModified():
1980 return True
1981
1982 str_age = self._PRW_age_noted.GetValue().strip()
1983
1984 if str_age == u'':
1985 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1986 return True
1987
1988 age = gmDateTime.str2interval(str_interval = str_age)
1989
1990 if age is None:
1991 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
1992 self._PRW_age_noted.SetBackgroundColour('pink')
1993 self._PRW_age_noted.Refresh()
1994 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
1995 return True
1996
1997 pat = gmPerson.gmCurrentPatient()
1998 if pat['dob'] is not None:
1999 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2000
2001 if age >= max_age:
2002 gmDispatcher.send (
2003 signal = 'statustext',
2004 msg = _(
2005 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2006 ) % (age, pat.get_medical_age())
2007 )
2008 self._PRW_age_noted.SetBackgroundColour('pink')
2009 self._PRW_age_noted.Refresh()
2010 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2011 return True
2012
2013 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2014 self._PRW_age_noted.Refresh()
2015 self._PRW_age_noted.SetData(data=age)
2016
2017 if pat['dob'] is not None:
2018 fts = gmDateTime.cFuzzyTimestamp (
2019 timestamp = pat['dob'] + age,
2020 accuracy = gmDateTime.acc_months
2021 )
2022 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2023
2024
2025
2026
2027
2028 return True
2029
2031
2032 if not self._PRW_year_noted.IsModified():
2033 return True
2034
2035 year_noted = self._PRW_year_noted.GetData()
2036
2037 if year_noted is None:
2038 if self._PRW_year_noted.GetValue().strip() == u'':
2039 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2040 return True
2041 self._PRW_year_noted.SetBackgroundColour('pink')
2042 self._PRW_year_noted.Refresh()
2043 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2044 return True
2045
2046 year_noted = year_noted.get_pydt()
2047
2048 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2049 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2050 self._PRW_year_noted.SetBackgroundColour('pink')
2051 self._PRW_year_noted.Refresh()
2052 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2053 return True
2054
2055 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2056 self._PRW_year_noted.Refresh()
2057
2058 pat = gmPerson.gmCurrentPatient()
2059 if pat['dob'] is not None:
2060 issue_age = year_noted - pat['dob']
2061 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2062 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2063
2064 return True
2065
2067 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2068 return True
2069
2071 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2072 return True
2073
2074
2075
2077
2079
2080 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2081
2082 self.selection_only = False
2083
2084 mp = gmMatchProvider.cMatchProvider_FixedList (
2085 aSeq = [
2086 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2087 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2088 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2089 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2090 ]
2091 )
2092 mp.setThresholds(1, 2, 4)
2093 self.matcher = mp
2094
2095 self.SetToolTipString(_(
2096 "The diagnostic classification or grading of this assessment.\n"
2097 "\n"
2098 "This documents how certain one is about this being a true diagnosis."
2099 ))
2100
2101
2102
2103 if __name__ == '__main__':
2104
2105
2107 """
2108 Test application for testing EMR struct widgets
2109 """
2110
2112 """
2113 Create test application UI
2114 """
2115 frame = wx.Frame (
2116 None,
2117 -4,
2118 'Testing EMR struct widgets',
2119 size=wx.Size(600, 400),
2120 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2121 )
2122 filemenu= wx.Menu()
2123 filemenu.AppendSeparator()
2124 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2125
2126
2127 menuBar = wx.MenuBar()
2128 menuBar.Append(filemenu,"&File")
2129
2130 frame.SetMenuBar(menuBar)
2131
2132 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2133 wx.DefaultPosition, wx.DefaultSize, 0 )
2134
2135
2136 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2137
2138
2139 self.__pat = gmPerson.gmCurrentPatient()
2140
2141 frame.Show(1)
2142 return 1
2143
2145 """
2146 Close test aplication
2147 """
2148 self.ExitMainLoop ()
2149
2151 app = wx.PyWidgetTester(size = (200, 300))
2152 emr = pat.get_emr()
2153 enc = emr.active_encounter
2154
2155 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
2156 app.frame.Show(True)
2157 app.MainLoop()
2158 return
2159
2161 app = wx.PyWidgetTester(size = (200, 300))
2162 emr = pat.get_emr()
2163 enc = emr.active_encounter
2164
2165
2166 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
2167 dlg.ShowModal()
2168
2169
2170
2171
2172
2174 app = wx.PyWidgetTester(size = (200, 300))
2175 emr = pat.get_emr()
2176 epi = emr.get_episodes()[0]
2177 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
2178 app.frame.Show(True)
2179 app.MainLoop()
2180
2186
2188 app = wx.PyWidgetTester(size = (400, 40))
2189 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2190 app.MainLoop()
2191
2193 app = wx.PyWidgetTester(size = (400, 40))
2194 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2195
2196 app.MainLoop()
2197
2199 app = wx.PyWidgetTester(size = (200, 300))
2200 edit_health_issue(parent=app.frame, issue=None)
2201
2203 app = wx.PyWidgetTester(size = (200, 300))
2204 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2205 app.MainLoop()
2206
2208 app = wx.PyWidgetTester(size = (200, 300))
2209 edit_procedure(parent=app.frame)
2210
2211
2212 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2213
2214 gmI18N.activate_locale()
2215 gmI18N.install_domain()
2216 gmDateTime.init()
2217
2218
2219 pat = gmPersonSearch.ask_for_patient()
2220 if pat is None:
2221 print "No patient. Exiting gracefully..."
2222 sys.exit(0)
2223 gmPatSearchWidgets.set_active_patient(patient=pat)
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241 test_edit_procedure()
2242
2243
2244