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