| 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 import wx.lib.pubsub as wxps
21
22
23 # GNUmed
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 # performed procedure related widgets/functions
37 #----------------------------------------------------------------
39
40 pat = gmPerson.gmCurrentPatient()
41 emr = pat.get_emr()
42
43 if parent is None:
44 parent = wx.GetApp().GetTopWindow()
45 #-----------------------------------------
46 def edit(procedure=None):
47 return edit_procedure(parent = parent, procedure = procedure)
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 #----------------------------------------------------------------
99 ea = cProcedureEAPnl(parent = parent, id = -1)
100 ea.data = procedure
101 ea.mode = gmTools.coalesce(procedure, 'new', 'edit')
102 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
103 dlg.SetTitle(gmTools.coalesce(procedure, _('Adding a procedure'), _('Editing a procedure')))
104 if dlg.ShowModal() == wx.ID_OK:
105 dlg.Destroy()
106 return True
107 dlg.Destroy()
108 return False
109 #----------------------------------------------------------------
110 from Gnumed.wxGladeWidgets import wxgProcedureEAPnl
111
113
115 wxgProcedureEAPnl.wxgProcedureEAPnl.__init__(self, *args, **kwargs)
116 gmEditArea.cGenericEditAreaMixin.__init__(self)
117
118 self.mode = 'new'
119 self.data = None
120
121 self.__init_ui()
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 # location
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 # procedure
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 # self._PRW_episode.Enable(False)
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 # self._PRW_episode.Enable(True)
198 #----------------------------------------------------------------
200 if not self._DPRW_date.is_valid_timestamp():
201 return
202 end = self._DPRW_end.GetData()
203 if end is None:
204 return
205 end = end.get_pydt()
206 start = self._DPRW_date.GetData().get_pydt()
207 if start < end:
208 return
209 self._DPRW_date.display_as_valid(False)
210 #----------------------------------------------------------------
212 end = self._DPRW_end.GetData()
213 if end is None:
214 self._CHBOX_ongoing.Enable(True)
215 self._DPRW_end.display_as_valid(True)
216 else:
217 self._CHBOX_ongoing.Enable(False)
218 end = end.get_pydt()
219 now = gmDateTime.pydt_now_here()
220 if end > now:
221 self._CHBOX_ongoing.SetValue(True)
222 else:
223 self._CHBOX_ongoing.SetValue(False)
224 start = self._DPRW_date.GetData()
225 if start is None:
226 self._DPRW_end.display_as_valid(True)
227 else:
228 start = start.get_pydt()
229 if end > start:
230 self._DPRW_end.display_as_valid(True)
231 else:
232 self._DPRW_end.display_as_valid(False)
233 #----------------------------------------------------------------
234 # generic Edit Area mixin API
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 #----------------------------------------------------------------
373 self._DPRW_date.SetData(data = self.data['clin_when'])
374 if self.data['clin_end'] is None:
375 self._DPRW_end.SetText()
376 self._CHBOX_ongoing.Enable(True)
377 self._CHBOX_ongoing.SetValue(self.data['is_ongoing'])
378 else:
379 self._DPRW_end.SetData(data = self.data['clin_end'])
380 self._CHBOX_ongoing.Enable(False)
381 now = gmDateTime.pydt_now_here()
382 if self.data['clin_end'] > now:
383 self._CHBOX_ongoing.SetValue(True)
384 else:
385 self._CHBOX_ongoing.SetValue(False)
386 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
387 self._PRW_procedure.SetText(value = self.data['performed_procedure'], data = self.data['performed_procedure'])
388
389 if self.data['pk_hospital_stay'] is None:
390 self._PRW_hospital_stay.SetText()
391 self._LBL_hospital_details.SetLabel(u'')
392 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
393 else:
394 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
395 self._LBL_hospital_details.SetLabel(gmEMRStructItems.cHospitalStay(aPK_obj = self.data['pk_hospital_stay']).format())
396 self._PRW_location.SetText()
397
398 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
399 self._PRW_codes.SetText(val, data)
400
401 self._PRW_procedure.SetFocus()
402 #----------------------------------------------------------------
404 self._refresh_as_new()
405 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
406 if self.data['pk_hospital_stay'] is None:
407 self._PRW_hospital_stay.SetText()
408 self._PRW_location.SetText(value = self.data['clin_where'], data = self.data['clin_where'])
409 else:
410 self._PRW_hospital_stay.SetText(value = self.data['clin_where'], data = self.data['pk_hospital_stay'])
411 self._PRW_location.SetText()
412
413 self._PRW_procedure.SetFocus()
414 #----------------------------------------------------------------
415 # event handlers
416 #----------------------------------------------------------------
421 #----------------------------------------------------------------
423 if self._CHBOX_ongoing.IsChecked():
424 end = self._DPRW_end.GetData()
425 if end is None:
426 self._DPRW_end.display_as_valid(True)
427 else:
428 end = end.get_pydt()
429 now = gmDateTime.pydt_now_here()
430 if end > now:
431 self._DPRW_end.display_as_valid(True)
432 else:
433 self._DPRW_end.display_as_valid(False)
434 else:
435 self._DPRW_end.is_valid_timestamp()
436 event.Skip()
437 #================================================================
438 # hospital stay related widgets/functions
439 #----------------------------------------------------------------
441
442 pat = gmPerson.gmCurrentPatient()
443 emr = pat.get_emr()
444
445 if parent is None:
446 parent = wx.GetApp().GetTopWindow()
447 #-----------------------------------------
448 def edit(stay=None):
449 return edit_hospital_stay(parent = parent, hospital_stay = stay)
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 #----------------------------------------------------------------
488 ea = cHospitalStayEditAreaPnl(parent = parent, id = -1)
489 ea.data = hospital_stay
490 ea.mode = gmTools.coalesce(hospital_stay, 'new', 'edit')
491 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
492 dlg.SetTitle(gmTools.coalesce(hospital_stay, _('Adding a hospital stay'), _('Editing a hospital stay')))
493 if dlg.ShowModal() == wx.ID_OK:
494 dlg.Destroy()
495 return True
496 dlg.Destroy()
497 return False
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
555 wxgHospitalStayEditAreaPnl.wxgHospitalStayEditAreaPnl.__init__(self, *args, **kwargs)
556 gmEditArea.cGenericEditAreaMixin.__init__(self)
557 #----------------------------------------------------------------
558 # generic Edit Area mixin API
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 #----------------------------------------------------------------
592
593 pat = gmPerson.gmCurrentPatient()
594 emr = pat.get_emr()
595 stay = emr.add_hospital_stay(episode = self._PRW_episode.GetData(can_create = True))
596 stay['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
597 stay['admission'] = self._PRW_admission.GetData()
598 stay['discharge'] = self._PRW_discharge.GetData()
599 stay.save_payload()
600
601 self.data = stay
602 return True
603 #----------------------------------------------------------------
605
606 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True)
607 self.data['hospital'] = gmTools.none_if(self._PRW_hospital.GetValue().strip(), u'')
608 self.data['admission'] = self._PRW_admission.GetData()
609 self.data['discharge'] = self._PRW_discharge.GetData()
610 self.data.save_payload()
611
612 return True
613 #----------------------------------------------------------------
615 self._PRW_hospital.SetText(value = u'')
616 self._PRW_episode.SetText(value = u'')
617 self._PRW_admission.SetText(data = gmDateTime.pydt_now_here())
618 self._PRW_discharge.SetText()
619 #----------------------------------------------------------------
621 if self.data['hospital'] is not None:
622 self._PRW_hospital.SetText(value = self.data['hospital'])
623
624 if self.data['pk_episode'] is not None:
625 self._PRW_episode.SetText(value = self.data['episode'], data = self.data['pk_episode'])
626
627 self._PRW_admission.SetText(data = self.data['admission'])
628 self._PRW_discharge.SetText(data = self.data['discharge'])
629 #----------------------------------------------------------------
632 #================================================================
633 # encounter related widgets/functions
634 #----------------------------------------------------------------
636 emr.start_new_encounter()
637 gmDispatcher.send(signal = 'statustext', msg = _('Started a new encounter for the active patient.'), beep = True)
638 time.sleep(0.5)
639 gmGuiHelpers.gm_show_info (
640 _('\nA new encounter was started for the active patient.\n'),
641 _('Start of new encounter')
642 )
643 #----------------------------------------------------------------
644 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaDlg
645
647 if parent is None:
648 parent = wx.GetApp().GetTopWindow()
649
650 # FIXME: use generic dialog 2
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 lctrl.set_string_items(items = items)
691 lctrl.set_data(data = encs)
692 active_pk = emr.active_encounter['pk_encounter']
693 for idx in range(len(encs)):
694 e = encs[idx]
695 if e['pk_encounter'] == active_pk:
696 lctrl.SetItemTextColour(idx, col=wx.NamedColour('RED'))
697 #--------------------
698 def new():
699 cfg_db = gmCfg.cCfgSQL()
700 # FIXME: look for MRU/MCU encounter type config here
701 enc_type = cfg_db.get2 (
702 option = u'encounter.default_type',
703 workplace = gmSurgery.gmCurrentPractice().active_workplace,
704 bias = u'user',
705 default = u'in surgery'
706 )
707 enc = gmEMRStructItems.create_encounter(fk_patient = patient.ID, enc_type = enc_type)
708 return edit_encounter(parent = parent, encounter = enc)
709 #--------------------
710 def edit(enc=None):
711 return edit_encounter(parent = parent, encounter = enc)
712 #--------------------
713 def edit_active(enc=None):
714 return edit_encounter(parent = parent, encounter = emr.active_encounter)
715 #--------------------
716 def start_new(enc=None):
717 start_new_encounter(emr = emr)
718 return True
719 #--------------------
720 return gmListWidgets.get_choices_from_list (
721 parent = parent,
722 msg = _('\nBelow find the relevant encounters of the patient.\n'),
723 caption = _('Encounters ...'),
724 columns = [_('Started'), _('Ended'), _('Type'), _('Reason for Encounter'), _('Assessment of Encounter'), _('Empty'), '#'],
725 can_return_empty = False,
726 single_selection = single_selection,
727 refresh_callback = refresh,
728 edit_callback = edit,
729 new_callback = new,
730 ignore_OK_button = ignore_OK_button,
731 left_extra_button = (_('Edit active'), _('Edit the active encounter'), edit_active),
732 middle_extra_button = (_('Start new'), _('Start new active encounter for the current patient.'), start_new)
733 )
734 #----------------------------------------------------------------
736 """This is used as the callback when the EMR detects that the
737 patient was here rather recently and wants to ask the
738 provider whether to continue the recent encounter.
739 """
740 if parent is None:
741 parent = wx.GetApp().GetTopWindow()
742
743 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
744 parent = None,
745 id = -1,
746 caption = caption,
747 question = msg,
748 button_defs = [
749 {'label': _('Continue'), 'tooltip': _('Continue the existing recent encounter.'), 'default': False},
750 {'label': _('Start new'), 'tooltip': _('Start a new encounter. The existing one will be closed.'), 'default': True}
751 ],
752 show_checkbox = False
753 )
754
755 result = dlg.ShowModal()
756 dlg.Destroy()
757
758 if result == wx.ID_YES:
759 return True
760
761 return False
762 #----------------------------------------------------------------
764
765 if parent is None:
766 parent = wx.GetApp().GetTopWindow()
767
768 #--------------------
769 def edit(enc_type=None):
770 return edit_encounter_type(parent = parent, encounter_type = enc_type)
771 #--------------------
772 def delete(enc_type=None):
773 if gmEMRStructItems.delete_encounter_type(description = enc_type['description']):
774 return True
775 gmDispatcher.send (
776 signal = u'statustext',
777 msg = _('Cannot delete encounter type [%s]. It is in use.') % enc_type['l10n_description'],
778 beep = True
779 )
780 return False
781 #--------------------
782 def refresh(lctrl):
783 enc_types = gmEMRStructItems.get_encounter_types()
784 lctrl.set_string_items(items = enc_types)
785 #--------------------
786 gmListWidgets.get_choices_from_list (
787 parent = parent,
788 msg = _('\nSelect the encounter type you want to edit !\n'),
789 caption = _('Managing encounter types ...'),
790 columns = [_('Local name'), _('Encounter type')],
791 single_selection = True,
792 edit_callback = edit,
793 new_callback = edit,
794 delete_callback = delete,
795 refresh_callback = refresh
796 )
797 #----------------------------------------------------------------
799 ea = cEncounterTypeEditAreaPnl(parent = parent, id = -1)
800 ea.data = encounter_type
801 ea.mode = gmTools.coalesce(encounter_type, 'new', 'edit')
802 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea)
803 dlg.SetTitle(gmTools.coalesce(encounter_type, _('Adding new encounter type'), _('Editing local encounter type name')))
804 if dlg.ShowModal() == wx.ID_OK:
805 return True
806 return False
807 #----------------------------------------------------------------
809 """Phrasewheel to allow selection of encounter type.
810
811 - user input interpreted as encounter type in English or local language
812 - data returned is pk of corresponding encounter type or None
813 """
815
816 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
817
818 mp = gmMatchProvider.cMatchProvider_SQL2 (
819 queries = [
820 u"""
821 SELECT
822 data,
823 field_label,
824 list_label
825 FROM (
826 SELECT DISTINCT ON (data) *
827 FROM (
828 SELECT
829 pk AS data,
830 _(description) AS field_label,
831 case
832 when _(description) = description then _(description)
833 else _(description) || ' (' || description || ')'
834 end AS list_label
835 FROM
836 clin.encounter_type
837 WHERE
838 _(description) %(fragment_condition)s
839 OR
840 description %(fragment_condition)s
841 ) AS q_distinct_pk
842 ) AS q_ordered
843 ORDER BY
844 list_label
845 """ ]
846 )
847 mp.setThresholds(2, 4, 6)
848
849 self.matcher = mp
850 self.selection_only = True
851 self.picklist_delay = 50
852 #----------------------------------------------------------------
853 -class cEncounterTypeEditAreaPnl(wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl, gmEditArea.cGenericEditAreaMixin):
854
856
857 wxgEncounterTypeEditAreaPnl.wxgEncounterTypeEditAreaPnl.__init__(self, *args, **kwargs)
858 gmEditArea.cGenericEditAreaMixin.__init__(self)
859
860 # self.__register_interests()
861 #-------------------------------------------------------
862 # generic edit area API
863 #-------------------------------------------------------
865 if self.mode == 'edit':
866 if self._TCTRL_l10n_name.GetValue().strip() == u'':
867 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
868 return False
869 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
870 return True
871
872 no_errors = True
873
874 if self._TCTRL_l10n_name.GetValue().strip() == u'':
875 if self._TCTRL_name.GetValue().strip() == u'':
876 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = False)
877 no_errors = False
878 else:
879 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
880 else:
881 self.display_tctrl_as_valid(tctrl = self._TCTRL_l10n_name, valid = True)
882
883 if self._TCTRL_name.GetValue().strip() == u'':
884 if self._TCTRL_l10n_name.GetValue().strip() == u'':
885 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = False)
886 no_errors = False
887 else:
888 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
889 else:
890 self.display_tctrl_as_valid(tctrl = self._TCTRL_name, valid = True)
891
892 return no_errors
893 #-------------------------------------------------------
895 enc_type = gmEMRStructItems.create_encounter_type (
896 description = gmTools.none_if(self._TCTRL_name.GetValue().strip(), u''),
897 l10n_description = gmTools.coalesce (
898 gmTools.none_if(self._TCTRL_l10n_name.GetValue().strip(), u''),
899 self._TCTRL_name.GetValue().strip()
900 )
901 )
902 if enc_type is None:
903 return False
904 self.data = enc_type
905 return True
906 #-------------------------------------------------------
908 enc_type = gmEMRStructItems.update_encounter_type (
909 description = self._TCTRL_name.GetValue().strip(),
910 l10n_description = self._TCTRL_l10n_name.GetValue().strip()
911 )
912 if enc_type is None:
913 return False
914 self.data = enc_type
915 return True
916 #-------------------------------------------------------
918 self._TCTRL_l10n_name.SetValue(u'')
919 self._TCTRL_name.SetValue(u'')
920 self._TCTRL_name.Enable(True)
921 #-------------------------------------------------------
923 self._TCTRL_l10n_name.SetValue(self.data['l10n_description'])
924 self._TCTRL_name.SetValue(self.data['description'])
925 # disallow changing type on all encounters by editing system name
926 self._TCTRL_name.Enable(False)
927 #-------------------------------------------------------
932 #-------------------------------------------------------
933 # internal API
934 #-------------------------------------------------------
935 # def __register_interests(self):
936 # return
937 #----------------------------------------------------------------
938 from Gnumed.wxGladeWidgets import wxgEncounterEditAreaPnl
939
941
943 try:
944 self.__encounter = kwargs['encounter']
945 del kwargs['encounter']
946 except KeyError:
947 self.__encounter = None
948
949 try:
950 msg = kwargs['msg']
951 del kwargs['msg']
952 except KeyError:
953 msg = None
954
955 wxgEncounterEditAreaPnl.wxgEncounterEditAreaPnl.__init__(self, *args, **kwargs)
956
957 self.refresh(msg = msg)
958 #--------------------------------------------------------
959 # external API
960 #--------------------------------------------------------
962
963 if msg is not None:
964 self._LBL_instructions.SetLabel(msg)
965
966 if encounter is not None:
967 self.__encounter = encounter
968
969 if self.__encounter is None:
970 return True
971
972 # getting the patient via the encounter allows us to act
973 # on any encounter regardless of the currently active patient
974 pat = gmPerson.cPatient(aPK_obj = self.__encounter['pk_patient'])
975 self._LBL_patient.SetLabel(pat.get_description_gender())
976
977 self._PRW_encounter_type.SetText(self.__encounter['l10n_type'], data=self.__encounter['pk_type'])
978
979 fts = gmDateTime.cFuzzyTimestamp (
980 timestamp = self.__encounter['started'],
981 accuracy = gmDateTime.acc_minutes
982 )
983 self._PRW_start.SetText(fts.format_accurately(), data=fts)
984
985 fts = gmDateTime.cFuzzyTimestamp (
986 timestamp = self.__encounter['last_affirmed'],
987 accuracy = gmDateTime.acc_minutes
988 )
989 self._PRW_end.SetText(fts.format_accurately(), data=fts)
990
991 # RFE
992 self._TCTRL_rfe.SetValue(gmTools.coalesce(self.__encounter['reason_for_encounter'], ''))
993 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_rfe)
994 self._PRW_rfe_codes.SetText(val, data)
995
996 # AOE
997 self._TCTRL_aoe.SetValue(gmTools.coalesce(self.__encounter['assessment_of_encounter'], ''))
998 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(self.__encounter.generic_codes_aoe)
999 self._PRW_aoe_codes.SetText(val, data)
1000
1001 # last affirmed
1002 if self.__encounter['last_affirmed'] == self.__encounter['started']:
1003 self._PRW_end.SetFocus()
1004 else:
1005 self._TCTRL_aoe.SetFocus()
1006
1007 return True
1008 #--------------------------------------------------------
1010
1011 if self._PRW_encounter_type.GetData() is None:
1012 self._PRW_encounter_type.SetBackgroundColour('pink')
1013 self._PRW_encounter_type.Refresh()
1014 self._PRW_encounter_type.SetFocus()
1015 return False
1016 self._PRW_encounter_type.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1017 self._PRW_encounter_type.Refresh()
1018
1019 # start
1020 if self._PRW_start.GetValue().strip() == u'':
1021 self._PRW_start.SetBackgroundColour('pink')
1022 self._PRW_start.Refresh()
1023 self._PRW_start.SetFocus()
1024 return False
1025 if not self._PRW_start.is_valid_timestamp():
1026 self._PRW_start.SetBackgroundColour('pink')
1027 self._PRW_start.Refresh()
1028 self._PRW_start.SetFocus()
1029 return False
1030 self._PRW_start.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1031 self._PRW_start.Refresh()
1032
1033 # last_affirmed
1034 if self._PRW_end.GetValue().strip() == u'':
1035 self._PRW_end.SetBackgroundColour('pink')
1036 self._PRW_end.Refresh()
1037 self._PRW_end.SetFocus()
1038 return False
1039 if not self._PRW_end.is_valid_timestamp():
1040 self._PRW_end.SetBackgroundColour('pink')
1041 self._PRW_end.Refresh()
1042 self._PRW_end.SetFocus()
1043 return False
1044 self._PRW_end.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
1045 self._PRW_end.Refresh()
1046
1047 return True
1048 #--------------------------------------------------------
1050 if not self.__is_valid_for_save():
1051 return False
1052
1053 self.__encounter['pk_type'] = self._PRW_encounter_type.GetData()
1054 self.__encounter['started'] = self._PRW_start.GetData().get_pydt()
1055 self.__encounter['last_affirmed'] = self._PRW_end.GetData().get_pydt()
1056 self.__encounter['reason_for_encounter'] = gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1057 self.__encounter['assessment_of_encounter'] = gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u'')
1058 self.__encounter.save_payload() # FIXME: error checking
1059
1060 self.__encounter.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
1061 self.__encounter.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
1062
1063 return True
1064 #----------------------------------------------------------------
1065 # FIXME: use generic dialog 2
1067
1069 encounter = kwargs['encounter']
1070 del kwargs['encounter']
1071
1072 try:
1073 button_defs = kwargs['button_defs']
1074 del kwargs['button_defs']
1075 except KeyError:
1076 button_defs = None
1077
1078 try:
1079 msg = kwargs['msg']
1080 del kwargs['msg']
1081 except KeyError:
1082 msg = None
1083
1084 wxgEncounterEditAreaDlg.wxgEncounterEditAreaDlg.__init__(self, *args, **kwargs)
1085 self.SetSize((450, 280))
1086 self.SetMinSize((450, 280))
1087
1088 if button_defs is not None:
1089 self._BTN_save.SetLabel(button_defs[0][0])
1090 self._BTN_save.SetToolTipString(button_defs[0][1])
1091 self._BTN_close.SetLabel(button_defs[1][0])
1092 self._BTN_close.SetToolTipString(button_defs[1][1])
1093 self.Refresh()
1094
1095 self._PNL_edit_area.refresh(encounter = encounter, msg = msg)
1096
1097 self.Fit()
1098 #--------------------------------------------------------
1105 #----------------------------------------------------------------
1106 from Gnumed.wxGladeWidgets import wxgActiveEncounterPnl
1107
1109
1111 wxgActiveEncounterPnl.wxgActiveEncounterPnl.__init__(self, *args, **kwargs)
1112 self.__register_events()
1113 self.refresh()
1114 #------------------------------------------------------------
1116 self._TCTRL_encounter.SetValue(u'')
1117 self._TCTRL_encounter.SetToolTipString(u'')
1118 self._BTN_new.Enable(False)
1119 self._BTN_list.Enable(False)
1120 #------------------------------------------------------------
1122 pat = gmPerson.gmCurrentPatient()
1123 if not pat.connected:
1124 self.clear()
1125 return
1126
1127 enc = pat.get_emr().active_encounter
1128 self._TCTRL_encounter.SetValue(enc.format(with_docs = False, with_tests = False, fancy_header = False, with_vaccinations = False, with_family_history = False).strip('\n'))
1129 self._TCTRL_encounter.SetToolTipString (
1130 _('The active encounter of the current patient:\n\n%s') %
1131 enc.format(with_docs = False, with_tests = False, fancy_header = True, with_vaccinations = False, with_rfe_aoe = True, with_family_history = False).strip('\n')
1132 )
1133 self._BTN_new.Enable(True)
1134 self._BTN_list.Enable(True)
1135 #------------------------------------------------------------
1137 self._TCTRL_encounter.Bind(wx.EVT_LEFT_DCLICK, self._on_ldclick)
1138
1139 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._schedule_clear)
1140 # this would throw an exception due to concurrency issues:
1141 #gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._schedule_refresh)
1142 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._schedule_refresh)
1143 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._schedule_refresh)
1144 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._schedule_refresh)
1145 #------------------------------------------------------------
1146 # event handler
1147 #------------------------------------------------------------
1149 wx.CallAfter(self.clear)
1150 #------------------------------------------------------------
1154 #------------------------------------------------------------
1156 pat = gmPerson.gmCurrentPatient()
1157 edit_encounter(encounter = pat.get_emr().active_encounter)
1158 #------------------------------------------------------------
1162 #------------------------------------------------------------
1165 #================================================================
1166 # episode related widgets/functions
1167 #----------------------------------------------------------------
1169 ea = cEpisodeEditAreaPnl(parent = parent, id = -1)
1170 ea.data = episode
1171 ea.mode = gmTools.coalesce(episode, 'new', 'edit')
1172 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = True)
1173 dlg.SetTitle(gmTools.coalesce(episode, _('Adding a new episode'), _('Editing an episode')))
1174 if dlg.ShowModal() == wx.ID_OK:
1175 return True
1176 return False
1177 #----------------------------------------------------------------
1179
1180 created_new_issue = False
1181
1182 try:
1183 issue = gmEMRStructItems.cHealthIssue(name = episode['description'], patient = episode['pk_patient'])
1184 except gmExceptions.NoSuchBusinessObjectError:
1185 issue = None
1186
1187 if issue is None:
1188 issue = emr.add_health_issue(issue_name = episode['description'])
1189 created_new_issue = True
1190 else:
1191 # issue exists already, so ask user
1192 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1193 parent,
1194 -1,
1195 caption = _('Promoting episode to health issue'),
1196 question = _(
1197 'There already is a health issue\n'
1198 '\n'
1199 ' %s\n'
1200 '\n'
1201 'What do you want to do ?'
1202 ) % issue['description'],
1203 button_defs = [
1204 {'label': _('Use existing'), 'tooltip': _('Move episode into existing health issue'), 'default': False},
1205 {'label': _('Create new'), 'tooltip': _('Create a new health issue with another name'), 'default': True}
1206 ]
1207 )
1208 use_existing = dlg.ShowModal()
1209 dlg.Destroy()
1210
1211 if use_existing == wx.ID_CANCEL:
1212 return
1213
1214 # user wants to create new issue with alternate name
1215 if use_existing == wx.ID_NO:
1216 # loop until name modified but non-empty or cancelled
1217 issue_name = episode['description']
1218 while issue_name == episode['description']:
1219 dlg = wx.TextEntryDialog (
1220 parent = parent,
1221 message = _('Enter a short descriptive name for the new health issue:'),
1222 caption = _('Creating a new health issue ...'),
1223 defaultValue = issue_name,
1224 style = wx.OK | wx.CANCEL | wx.CENTRE
1225 )
1226 decision = dlg.ShowModal()
1227 if decision != wx.ID_OK:
1228 dlg.Destroy()
1229 return
1230 issue_name = dlg.GetValue().strip()
1231 dlg.Destroy()
1232 if issue_name == u'':
1233 issue_name = episode['description']
1234
1235 issue = emr.add_health_issue(issue_name = issue_name)
1236 created_new_issue = True
1237
1238 # eventually move the episode to the issue
1239 if not move_episode_to_issue(episode = episode, target_issue = issue, save_to_backend = True):
1240 # user cancelled the move so delete just-created issue
1241 if created_new_issue:
1242 # shouldn't fail as it is completely new
1243 gmEMRStructItems.delete_health_issue(health_issue = issue)
1244 return
1245
1246 return
1247 #----------------------------------------------------------------
1249 """Prepare changing health issue for an episode.
1250
1251 Checks for two-open-episodes conflict. When this
1252 function succeeds, the pk_health_issue has been set
1253 on the episode instance and the episode should - for
1254 all practical purposes - be ready for save_payload().
1255 """
1256 # episode is closed: should always work
1257 if not episode['episode_open']:
1258 episode['pk_health_issue'] = target_issue['pk_health_issue']
1259 if save_to_backend:
1260 episode.save_payload()
1261 return True
1262
1263 # un-associate: should always work, too
1264 if target_issue is None:
1265 episode['pk_health_issue'] = None
1266 if save_to_backend:
1267 episode.save_payload()
1268 return True
1269
1270 # try closing possibly expired episode on target issue if any
1271 db_cfg = gmCfg.cCfgSQL()
1272 epi_ttl = int(db_cfg.get2 (
1273 option = u'episode.ttl',
1274 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1275 bias = 'user',
1276 default = 60 # 2 months
1277 ))
1278 if target_issue.close_expired_episode(ttl=epi_ttl) is True:
1279 gmDispatcher.send(signal='statustext', msg=_('Closed episodes older than %s days on health issue [%s]') % (epi_ttl, target_issue['description']))
1280 existing_epi = target_issue.get_open_episode()
1281
1282 # no more open episode on target issue: should work now
1283 if existing_epi is None:
1284 episode['pk_health_issue'] = target_issue['pk_health_issue']
1285 if save_to_backend:
1286 episode.save_payload()
1287 return True
1288
1289 # don't conflict on SELF ;-)
1290 if existing_epi['pk_episode'] == episode['pk_episode']:
1291 episode['pk_health_issue'] = target_issue['pk_health_issue']
1292 if save_to_backend:
1293 episode.save_payload()
1294 return True
1295
1296 # we got two open episodes at once, ask user
1297 move_range = episode.get_access_range()
1298 exist_range = existing_epi.get_access_range()
1299 question = _(
1300 'You want to associate the running episode:\n\n'
1301 ' "%(new_epi_name)s" (%(new_epi_start)s - %(new_epi_end)s)\n\n'
1302 'with the health issue:\n\n'
1303 ' "%(issue_name)s"\n\n'
1304 'There already is another episode running\n'
1305 'for this health issue:\n\n'
1306 ' "%(old_epi_name)s" (%(old_epi_start)s - %(old_epi_end)s)\n\n'
1307 'However, there can only be one running\n'
1308 'episode per health issue.\n\n'
1309 'Which episode do you want to close ?'
1310 ) % {
1311 'new_epi_name': episode['description'],
1312 'new_epi_start': move_range[0].strftime('%m/%y'),
1313 'new_epi_end': move_range[1].strftime('%m/%y'),
1314 'issue_name': target_issue['description'],
1315 'old_epi_name': existing_epi['description'],
1316 'old_epi_start': exist_range[0].strftime('%m/%y'),
1317 'old_epi_end': exist_range[1].strftime('%m/%y')
1318 }
1319 dlg = gmGuiHelpers.c3ButtonQuestionDlg (
1320 parent = None,
1321 id = -1,
1322 caption = _('Resolving two-running-episodes conflict'),
1323 question = question,
1324 button_defs = [
1325 {'label': _('old episode'), 'default': True, 'tooltip': _('close existing episode "%s"') % existing_epi['description']},
1326 {'label': _('new episode'), 'default': False, 'tooltip': _('close moving (new) episode "%s"') % episode['description']}
1327 ]
1328 )
1329 decision = dlg.ShowModal()
1330
1331 if decision == wx.ID_CANCEL:
1332 # button 3: move cancelled by user
1333 return False
1334
1335 elif decision == wx.ID_YES:
1336 # button 1: close old episode
1337 existing_epi['episode_open'] = False
1338 existing_epi.save_payload()
1339
1340 elif decision == wx.ID_NO:
1341 # button 2: close new episode
1342 episode['episode_open'] = False
1343
1344 else:
1345 raise ValueError('invalid result from c3ButtonQuestionDlg: [%s]' % decision)
1346
1347 episode['pk_health_issue'] = target_issue['pk_health_issue']
1348 if save_to_backend:
1349 episode.save_payload()
1350 return True
1351 #----------------------------------------------------------------
1353
1354 # FIXME: support pre-selection
1355
1357
1358 episodes = kwargs['episodes']
1359 del kwargs['episodes']
1360
1361 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1362
1363 self.SetTitle(_('Select the episodes you are interested in ...'))
1364 self._LCTRL_items.set_columns([_('Episode'), _('Status'), _('Health Issue')])
1365 self._LCTRL_items.set_string_items (
1366 items = [
1367 [ epi['description'],
1368 gmTools.bool2str(epi['episode_open'], _('ongoing'), u''),
1369 gmTools.coalesce(epi['health_issue'], u'')
1370 ]
1371 for epi in episodes ]
1372 )
1373 self._LCTRL_items.set_column_widths()
1374 self._LCTRL_items.set_data(data = episodes)
1375 #----------------------------------------------------------------
1377 """Let user select an episode *description*.
1378
1379 The user can select an episode description from the previously
1380 used descriptions across all episodes across all patients.
1381
1382 Selection is done with a phrasewheel so the user can
1383 type the episode name and matches will be shown. Typing
1384 "*" will show the entire list of episodes.
1385
1386 If the user types a description not existing yet a
1387 new episode description will be returned.
1388 """
1390
1391 mp = gmMatchProvider.cMatchProvider_SQL2 (
1392 queries = [
1393 u"""
1394 SELECT DISTINCT ON (description)
1395 description
1396 AS data,
1397 description
1398 AS field_label,
1399 description || ' ('
1400 || CASE
1401 WHEN is_open IS TRUE THEN _('ongoing')
1402 ELSE _('closed')
1403 END
1404 || ')'
1405 AS list_label
1406 FROM
1407 clin.episode
1408 WHERE
1409 description %(fragment_condition)s
1410 ORDER BY description
1411 LIMIT 30
1412 """
1413 ]
1414 )
1415 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
1416 self.matcher = mp
1417 #----------------------------------------------------------------
1419 """Let user select an episode.
1420
1421 The user can select an episode from the existing episodes of a
1422 patient. Selection is done with a phrasewheel so the user
1423 can type the episode name and matches will be shown. Typing
1424 "*" will show the entire list of episodes. Closed episodes
1425 will be marked as such. If the user types an episode name not
1426 in the list of existing episodes a new episode can be created
1427 from it if the programmer activated that feature.
1428
1429 If keyword <patient_id> is set to None or left out the control
1430 will listen to patient change signals and therefore act on
1431 gmPerson.gmCurrentPatient() changes.
1432 """
1434
1435 ctxt = {'ctxt_pat': {'where_part': u'and pk_patient = %(pat)s', 'placeholder': u'pat'}}
1436
1437 mp = gmMatchProvider.cMatchProvider_SQL2 (
1438 queries = [
1439 u"""(
1440
1441 select
1442 pk_episode
1443 as data,
1444 description
1445 as field_label,
1446 coalesce (
1447 description || ' - ' || health_issue,
1448 description
1449 ) as list_label,
1450 1 as rank
1451 from
1452 clin.v_pat_episodes
1453 where
1454 episode_open is true and
1455 description %(fragment_condition)s
1456 %(ctxt_pat)s
1457
1458 ) union all (
1459
1460 select
1461 pk_episode
1462 as data,
1463 description
1464 as field_label,
1465 coalesce (
1466 description || _(' (closed)') || ' - ' || health_issue,
1467 description || _(' (closed)')
1468 ) as list_label,
1469 2 as rank
1470 from
1471 clin.v_pat_episodes
1472 where
1473 description %(fragment_condition)s and
1474 episode_open is false
1475 %(ctxt_pat)s
1476
1477 )
1478
1479 order by rank, list_label
1480 limit 30"""
1481 ],
1482 context = ctxt
1483 )
1484
1485 try:
1486 kwargs['patient_id']
1487 except KeyError:
1488 kwargs['patient_id'] = None
1489
1490 if kwargs['patient_id'] is None:
1491 self.use_current_patient = True
1492 self.__register_patient_change_signals()
1493 pat = gmPerson.gmCurrentPatient()
1494 if pat.connected:
1495 mp.set_context('pat', pat.ID)
1496 else:
1497 self.use_current_patient = False
1498 self.__patient_id = int(kwargs['patient_id'])
1499 mp.set_context('pat', self.__patient_id)
1500
1501 del kwargs['patient_id']
1502
1503 gmPhraseWheel.cPhraseWheel.__init__ (
1504 self,
1505 *args,
1506 **kwargs
1507 )
1508 self.matcher = mp
1509 #--------------------------------------------------------
1510 # external API
1511 #--------------------------------------------------------
1513 if self.use_current_patient:
1514 return False
1515 self.__patient_id = int(patient_id)
1516 self.set_context('pat', self.__patient_id)
1517 return True
1518 #--------------------------------------------------------
1520 self.__is_open_for_create_data = is_open # used (only) in _create_data()
1521 return gmPhraseWheel.cPhraseWheel.GetData(self, can_create = can_create, as_instance = as_instance)
1522 #--------------------------------------------------------
1524
1525 epi_name = self.GetValue().strip()
1526 if epi_name == u'':
1527 gmDispatcher.send(signal = u'statustext', msg = _('Cannot create episode without name.'), beep = True)
1528 _log.debug('cannot create episode without name')
1529 return
1530
1531 if self.use_current_patient:
1532 pat = gmPerson.gmCurrentPatient()
1533 else:
1534 pat = gmPerson.cPatient(aPK_obj = self.__patient_id)
1535
1536 emr = pat.get_emr()
1537 epi = emr.add_episode(episode_name = epi_name, is_open = self.__is_open_for_create_data)
1538 if epi is None:
1539 self.data = {}
1540 else:
1541 self.SetText (
1542 value = epi_name,
1543 data = epi['pk_episode']
1544 )
1545 #--------------------------------------------------------
1548 #--------------------------------------------------------
1549 # internal API
1550 #--------------------------------------------------------
1552 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1553 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1554 #--------------------------------------------------------
1557 #--------------------------------------------------------
1559 if self.use_current_patient:
1560 patient = gmPerson.gmCurrentPatient()
1561 self.set_context('pat', patient.ID)
1562 return True
1563 #----------------------------------------------------------------
1564 from Gnumed.wxGladeWidgets import wxgEpisodeEditAreaPnl
1565
1566 -class cEpisodeEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl):
1567
1569
1570 try:
1571 episode = kwargs['episode']
1572 del kwargs['episode']
1573 except KeyError:
1574 episode = None
1575
1576 wxgEpisodeEditAreaPnl.wxgEpisodeEditAreaPnl.__init__(self, *args, **kwargs)
1577 gmEditArea.cGenericEditAreaMixin.__init__(self)
1578
1579 self.data = episode
1580 #----------------------------------------------------------------
1581 # generic Edit Area mixin API
1582 #----------------------------------------------------------------
1584
1585 errors = False
1586
1587 if len(self._PRW_description.GetValue().strip()) == 0:
1588 errors = True
1589 self._PRW_description.display_as_valid(False)
1590 self._PRW_description.SetFocus()
1591 else:
1592 self._PRW_description.display_as_valid(True)
1593 self._PRW_description.Refresh()
1594
1595 return not errors
1596 #----------------------------------------------------------------
1598
1599 pat = gmPerson.gmCurrentPatient()
1600 emr = pat.get_emr()
1601
1602 epi = emr.add_episode(episode_name = self._PRW_description.GetValue().strip())
1603 epi['summary'] = self._TCTRL_status.GetValue().strip()
1604 epi['episode_open'] = not self._CHBOX_closed.IsChecked()
1605 epi['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1606
1607 issue_name = self._PRW_issue.GetValue().strip()
1608 if len(issue_name) != 0:
1609 epi['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1610 issue = gmEMRStructItems.cHealthIssue(aPK_obj = epi['pk_health_issue'])
1611
1612 if not move_episode_to_issue(episode = epi, target_issue = issue, save_to_backend = False):
1613 gmDispatcher.send (
1614 signal = 'statustext',
1615 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1616 epi['description'],
1617 issue['description']
1618 )
1619 )
1620 gmEMRStructItems.delete_episode(episode = epi)
1621 return False
1622
1623 epi.save()
1624
1625 epi.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1626
1627 self.data = epi
1628 return True
1629 #----------------------------------------------------------------
1631
1632 self.data['description'] = self._PRW_description.GetValue().strip()
1633 self.data['summary'] = self._TCTRL_status.GetValue().strip()
1634 self.data['episode_open'] = not self._CHBOX_closed.IsChecked()
1635 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1636
1637 issue_name = self._PRW_issue.GetValue().strip()
1638 if len(issue_name) == 0:
1639 self.data['pk_health_issue'] = None
1640 else:
1641 self.data['pk_health_issue'] = self._PRW_issue.GetData(can_create = True)
1642 issue = gmEMRStructItems.cHealthIssue(aPK_obj = self.data['pk_health_issue'])
1643
1644 if not move_episode_to_issue(episode = self.data, target_issue = issue, save_to_backend = False):
1645 gmDispatcher.send (
1646 signal = 'statustext',
1647 msg = _('Cannot attach episode [%s] to health issue [%s] because it already has a running episode.') % (
1648 self.data['description'],
1649 issue['description']
1650 )
1651 )
1652 return False
1653
1654 self.data.save()
1655 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
1656
1657 return True
1658 #----------------------------------------------------------------
1660 if self.data is None:
1661 ident = gmPerson.gmCurrentPatient()
1662 else:
1663 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1664 self._TCTRL_patient.SetValue(ident.get_description_gender())
1665 self._PRW_issue.SetText()
1666 self._PRW_description.SetText()
1667 self._TCTRL_status.SetValue(u'')
1668 self._PRW_certainty.SetText()
1669 self._CHBOX_closed.SetValue(False)
1670 self._PRW_codes.SetText()
1671 #----------------------------------------------------------------
1673 ident = gmPerson.cIdentity(aPK_obj = self.data['pk_patient'])
1674 self._TCTRL_patient.SetValue(ident.get_description_gender())
1675
1676 if self.data['pk_health_issue'] is not None:
1677 self._PRW_issue.SetText(self.data['health_issue'], data=self.data['pk_health_issue'])
1678
1679 self._PRW_description.SetText(self.data['description'], data=self.data['description'])
1680
1681 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
1682
1683 if self.data['diagnostic_certainty_classification'] is not None:
1684 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
1685
1686 self._CHBOX_closed.SetValue(not self.data['episode_open'])
1687
1688 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
1689 self._PRW_codes.SetText(val, data)
1690 #----------------------------------------------------------------
1693 #================================================================
1694 # health issue related widgets/functions
1695 #----------------------------------------------------------------
1697 ea = cHealthIssueEditAreaPnl(parent = parent, id = -1)
1698 ea.data = issue
1699 ea.mode = gmTools.coalesce(issue, 'new', 'edit')
1700 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = (issue is not None))
1701 dlg.SetTitle(gmTools.coalesce(issue, _('Adding a new health issue'), _('Editing a health issue')))
1702 if dlg.ShowModal() == wx.ID_OK:
1703 return True
1704 return False
1705 #----------------------------------------------------------------
1707
1708 # FIXME: support pre-selection
1709
1711
1712 issues = kwargs['issues']
1713 del kwargs['issues']
1714
1715 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
1716
1717 self.SetTitle(_('Select the health issues you are interested in ...'))
1718 self._LCTRL_items.set_columns([u'', _('Health Issue'), u'', u'', u''])
1719
1720 for issue in issues:
1721 if issue['is_confidential']:
1722 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = _('confidential'))
1723 self._LCTRL_items.SetItemTextColour(row_num, col=wx.NamedColour('RED'))
1724 else:
1725 row_num = self._LCTRL_items.InsertStringItem(sys.maxint, label = u'')
1726
1727 self._LCTRL_items.SetStringItem(index = row_num, col = 1, label = issue['description'])
1728 if issue['clinically_relevant']:
1729 self._LCTRL_items.SetStringItem(index = row_num, col = 2, label = _('relevant'))
1730 if issue['is_active']:
1731 self._LCTRL_items.SetStringItem(index = row_num, col = 3, label = _('active'))
1732 if issue['is_cause_of_death']:
1733 self._LCTRL_items.SetStringItem(index = row_num, col = 4, label = _('fatal'))
1734
1735 self._LCTRL_items.set_column_widths()
1736 self._LCTRL_items.set_data(data = issues)
1737 #----------------------------------------------------------------
1739 """Let the user select a health issue.
1740
1741 The user can select a health issue from the existing issues
1742 of a patient. Selection is done with a phrasewheel so the user
1743 can type the issue name and matches will be shown. Typing
1744 "*" will show the entire list of issues. Inactive issues
1745 will be marked as such. If the user types an issue name not
1746 in the list of existing issues a new issue can be created
1747 from it if the programmer activated that feature.
1748
1749 If keyword <patient_id> is set to None or left out the control
1750 will listen to patient change signals and therefore act on
1751 gmPerson.gmCurrentPatient() changes.
1752 """
1754
1755 ctxt = {'ctxt_pat': {'where_part': u'pk_patient=%(pat)s', 'placeholder': u'pat'}}
1756
1757 mp = gmMatchProvider.cMatchProvider_SQL2 (
1758 # FIXME: consider clin.health_issue.clinically_relevant
1759 queries = [
1760 u"""
1761 SELECT
1762 data,
1763 field_label,
1764 list_label
1765 FROM ((
1766 SELECT
1767 pk_health_issue AS data,
1768 description AS field_label,
1769 description AS list_label
1770 FROM clin.v_health_issues
1771 WHERE
1772 is_active IS true
1773 AND
1774 description %(fragment_condition)s
1775 AND
1776 %(ctxt_pat)s
1777
1778 ) UNION (
1779
1780 SELECT
1781 pk_health_issue AS data,
1782 description AS field_label,
1783 description || _(' (inactive)') AS list_label
1784 FROM clin.v_health_issues
1785 WHERE
1786 is_active IS false
1787 AND
1788 description %(fragment_condition)s
1789 AND
1790 %(ctxt_pat)s
1791 )) AS union_query
1792 ORDER BY
1793 list_label"""],
1794 context = ctxt
1795 )
1796
1797 try: kwargs['patient_id']
1798 except KeyError: kwargs['patient_id'] = None
1799
1800 if kwargs['patient_id'] is None:
1801 self.use_current_patient = True
1802 self.__register_patient_change_signals()
1803 pat = gmPerson.gmCurrentPatient()
1804 if pat.connected:
1805 mp.set_context('pat', pat.ID)
1806 else:
1807 self.use_current_patient = False
1808 self.__patient_id = int(kwargs['patient_id'])
1809 mp.set_context('pat', self.__patient_id)
1810
1811 del kwargs['patient_id']
1812
1813 gmPhraseWheel.cPhraseWheel.__init__ (
1814 self,
1815 *args,
1816 **kwargs
1817 )
1818 self.matcher = mp
1819 #--------------------------------------------------------
1820 # external API
1821 #--------------------------------------------------------
1823 if self.use_current_patient:
1824 return False
1825 self.__patient_id = int(patient_id)
1826 self.set_context('pat', self.__patient_id)
1827 return True
1828 #--------------------------------------------------------
1830 if self.data is None:
1831 if can_create:
1832 issue_name = self.GetValue().strip()
1833
1834 if self.use_current_patient:
1835 pat = gmPerson.gmCurrentPatient()
1836 else:
1837 pat = gmPerson.cPatient(aPK_obj=self.__patient_id)
1838 emr = pat.get_emr()
1839
1840 issue = emr.add_health_issue(issue_name = issue_name)
1841 if issue is None:
1842 self.data = None
1843 else:
1844 self.data = issue['pk_health_issue']
1845
1846 return gmPhraseWheel.cPhraseWheel.GetData(self)
1847 #--------------------------------------------------------
1848 # internal API
1849 #--------------------------------------------------------
1851 gmDispatcher.connect(self._pre_patient_selection, u'pre_patient_selection')
1852 gmDispatcher.connect(self._post_patient_selection, u'post_patient_selection')
1853 #--------------------------------------------------------
1856 #--------------------------------------------------------
1858 if self.use_current_patient:
1859 patient = gmPerson.gmCurrentPatient()
1860 self.set_context('pat', patient.ID)
1861 return True
1862 #------------------------------------------------------------
1864
1866 try:
1867 msg = kwargs['message']
1868 except KeyError:
1869 msg = None
1870 del kwargs['message']
1871 wxgIssueSelectionDlg.wxgIssueSelectionDlg.__init__(self, *args, **kwargs)
1872 if msg is not None:
1873 self._lbl_message.SetLabel(label=msg)
1874 #--------------------------------------------------------
1885 #------------------------------------------------------------
1886 from Gnumed.wxGladeWidgets import wxgHealthIssueEditAreaPnl
1887
1888 -class cHealthIssueEditAreaPnl(gmEditArea.cGenericEditAreaMixin, wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl):
1889 """Panel encapsulating health issue edit area functionality."""
1890
1892
1893 try:
1894 issue = kwargs['issue']
1895 except KeyError:
1896 issue = None
1897
1898 wxgHealthIssueEditAreaPnl.wxgHealthIssueEditAreaPnl.__init__(self, *args, **kwargs)
1899
1900 gmEditArea.cGenericEditAreaMixin.__init__(self)
1901
1902 # FIXME: include more sources: coding systems/other database columns
1903 mp = gmMatchProvider.cMatchProvider_SQL2 (
1904 queries = [u"SELECT DISTINCT ON (description) description, description FROM clin.health_issue WHERE description %(fragment_condition)s LIMIT 50"]
1905 )
1906 mp.setThresholds(1, 3, 5)
1907 self._PRW_condition.matcher = mp
1908
1909 mp = gmMatchProvider.cMatchProvider_SQL2 (
1910 queries = [u"""
1911 select distinct on (grouping) grouping, grouping from (
1912
1913 select rank, grouping from ((
1914
1915 select
1916 grouping,
1917 1 as rank
1918 from
1919 clin.health_issue
1920 where
1921 grouping %%(fragment_condition)s
1922 and
1923 (select True from clin.encounter where fk_patient = %s and pk = clin.health_issue.fk_encounter)
1924
1925 ) union (
1926
1927 select
1928 grouping,
1929 2 as rank
1930 from
1931 clin.health_issue
1932 where
1933 grouping %%(fragment_condition)s
1934
1935 )) as union_result
1936
1937 order by rank
1938
1939 ) as order_result
1940
1941 limit 50""" % gmPerson.gmCurrentPatient().ID
1942 ]
1943 )
1944 mp.setThresholds(1, 3, 5)
1945 self._PRW_grouping.matcher = mp
1946
1947 self._PRW_age_noted.add_callback_on_lose_focus(self._on_leave_age_noted)
1948 self._PRW_year_noted.add_callback_on_lose_focus(self._on_leave_year_noted)
1949
1950 self._PRW_age_noted.add_callback_on_modified(self._on_modified_age_noted)
1951 self._PRW_year_noted.add_callback_on_modified(self._on_modified_year_noted)
1952
1953 self._PRW_year_noted.Enable(True)
1954
1955 self._PRW_codes.add_callback_on_lose_focus(self._on_leave_codes)
1956
1957 self.data = issue
1958 #----------------------------------------------------------------
1959 # generic Edit Area mixin API
1960 #----------------------------------------------------------------
1962
1963 if self._PRW_condition.GetValue().strip() == '':
1964 self._PRW_condition.display_as_valid(False)
1965 self._PRW_condition.SetFocus()
1966 return False
1967 self._PRW_condition.display_as_valid(True)
1968 self._PRW_condition.Refresh()
1969
1970 # FIXME: sanity check age/year diagnosed
1971 age_noted = self._PRW_age_noted.GetValue().strip()
1972 if age_noted != '':
1973 if gmDateTime.str2interval(str_interval = age_noted) is None:
1974 self._PRW_age_noted.display_as_valid(False)
1975 self._PRW_age_noted.SetFocus()
1976 return False
1977 self._PRW_age_noted.display_as_valid(True)
1978
1979 return True
1980 #----------------------------------------------------------------
1982 pat = gmPerson.gmCurrentPatient()
1983 emr = pat.get_emr()
1984
1985 issue = emr.add_health_issue(issue_name = self._PRW_condition.GetValue().strip())
1986
1987 side = u''
1988 if self._ChBOX_left.GetValue():
1989 side += u's'
1990 if self._ChBOX_right.GetValue():
1991 side += u'd'
1992 issue['laterality'] = side
1993
1994 issue['summary'] = self._TCTRL_status.GetValue().strip()
1995 issue['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
1996 issue['grouping'] = self._PRW_grouping.GetValue().strip()
1997 issue['is_active'] = self._ChBOX_active.GetValue()
1998 issue['clinically_relevant'] = self._ChBOX_relevant.GetValue()
1999 issue['is_confidential'] = self._ChBOX_confidential.GetValue()
2000 issue['is_cause_of_death'] = self._ChBOX_caused_death.GetValue()
2001
2002 age_noted = self._PRW_age_noted.GetData()
2003 if age_noted is not None:
2004 issue['age_noted'] = age_noted
2005
2006 issue.save()
2007
2008 issue.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2009
2010 self.data = issue
2011 return True
2012 #----------------------------------------------------------------
2014
2015 self.data['description'] = self._PRW_condition.GetValue().strip()
2016
2017 side = u''
2018 if self._ChBOX_left.GetValue():
2019 side += u's'
2020 if self._ChBOX_right.GetValue():
2021 side += u'd'
2022 self.data['laterality'] = side
2023
2024 self.data['summary'] = self._TCTRL_status.GetValue().strip()
2025 self.data['diagnostic_certainty_classification'] = self._PRW_certainty.GetData()
2026 self.data['grouping'] = self._PRW_grouping.GetValue().strip()
2027 self.data['is_active'] = bool(self._ChBOX_active.GetValue())
2028 self.data['clinically_relevant'] = bool(self._ChBOX_relevant.GetValue())
2029 self.data['is_confidential'] = bool(self._ChBOX_confidential.GetValue())
2030 self.data['is_cause_of_death'] = bool(self._ChBOX_caused_death.GetValue())
2031
2032 age_noted = self._PRW_age_noted.GetData()
2033 if age_noted is not None:
2034 self.data['age_noted'] = age_noted
2035
2036 self.data.save()
2037 self.data.generic_codes = [ c['data'] for c in self._PRW_codes.GetData() ]
2038
2039 return True
2040 #----------------------------------------------------------------
2042 self._PRW_condition.SetText()
2043 self._ChBOX_left.SetValue(0)
2044 self._ChBOX_right.SetValue(0)
2045 self._PRW_codes.SetText()
2046 self._on_leave_codes()
2047 self._PRW_certainty.SetText()
2048 self._PRW_grouping.SetText()
2049 self._TCTRL_status.SetValue(u'')
2050 self._PRW_age_noted.SetText()
2051 self._PRW_year_noted.SetText()
2052 self._ChBOX_active.SetValue(0)
2053 self._ChBOX_relevant.SetValue(1)
2054 self._ChBOX_confidential.SetValue(0)
2055 self._ChBOX_caused_death.SetValue(0)
2056
2057 return True
2058 #----------------------------------------------------------------
2060 self._PRW_condition.SetText(self.data['description'])
2061
2062 lat = gmTools.coalesce(self.data['laterality'], '')
2063 if lat.find('s') == -1:
2064 self._ChBOX_left.SetValue(0)
2065 else:
2066 self._ChBOX_left.SetValue(1)
2067 if lat.find('d') == -1:
2068 self._ChBOX_right.SetValue(0)
2069 else:
2070 self._ChBOX_right.SetValue(1)
2071
2072 val, data = self._PRW_codes.generic_linked_codes2item_dict(self.data.generic_codes)
2073 self._PRW_codes.SetText(val, data)
2074 self._on_leave_codes()
2075
2076 if self.data['diagnostic_certainty_classification'] is not None:
2077 self._PRW_certainty.SetData(data = self.data['diagnostic_certainty_classification'])
2078 self._PRW_grouping.SetText(gmTools.coalesce(self.data['grouping'], u''))
2079 self._TCTRL_status.SetValue(gmTools.coalesce(self.data['summary'], u''))
2080
2081 if self.data['age_noted'] is None:
2082 self._PRW_age_noted.SetText()
2083 else:
2084 self._PRW_age_noted.SetText (
2085 value = '%sd' % self.data['age_noted'].days,
2086 data = self.data['age_noted']
2087 )
2088
2089 self._ChBOX_active.SetValue(self.data['is_active'])
2090 self._ChBOX_relevant.SetValue(self.data['clinically_relevant'])
2091 self._ChBOX_confidential.SetValue(self.data['is_confidential'])
2092 self._ChBOX_caused_death.SetValue(self.data['is_cause_of_death'])
2093
2094 # this dance should assure self._PRW_year_noted gets set -- but it doesn't ...
2095 # self._PRW_age_noted.SetFocus()
2096 # self._PRW_condition.SetFocus()
2097
2098 return True
2099 #----------------------------------------------------------------
2102 #--------------------------------------------------------
2103 # internal helpers
2104 #--------------------------------------------------------
2106 if not self._PRW_codes.IsModified():
2107 return True
2108
2109 self._TCTRL_code_details.SetValue(u'- ' + u'\n- '.join([ c['list_label'] for c in self._PRW_codes.GetData() ]))
2110 #--------------------------------------------------------
2112
2113 if not self._PRW_age_noted.IsModified():
2114 return True
2115
2116 str_age = self._PRW_age_noted.GetValue().strip()
2117
2118 if str_age == u'':
2119 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2120 return True
2121
2122 age = gmDateTime.str2interval(str_interval = str_age)
2123
2124 if age is None:
2125 gmDispatcher.send(signal='statustext', msg=_('Cannot parse [%s] into valid interval.') % str_age)
2126 self._PRW_age_noted.SetBackgroundColour('pink')
2127 self._PRW_age_noted.Refresh()
2128 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2129 return True
2130
2131 pat = gmPerson.gmCurrentPatient()
2132 if pat['dob'] is not None:
2133 max_age = pydt.datetime.now(tz=pat['dob'].tzinfo) - pat['dob']
2134
2135 if age >= max_age:
2136 gmDispatcher.send (
2137 signal = 'statustext',
2138 msg = _(
2139 'Health issue cannot have been noted at age %s. Patient is only %s old.'
2140 ) % (age, pat.get_medical_age())
2141 )
2142 self._PRW_age_noted.SetBackgroundColour('pink')
2143 self._PRW_age_noted.Refresh()
2144 wx.CallAfter(self._PRW_year_noted.SetText, u'', None, True)
2145 return True
2146
2147 self._PRW_age_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2148 self._PRW_age_noted.Refresh()
2149 self._PRW_age_noted.SetData(data=age)
2150
2151 if pat['dob'] is not None:
2152 fts = gmDateTime.cFuzzyTimestamp (
2153 timestamp = pat['dob'] + age,
2154 accuracy = gmDateTime.acc_months
2155 )
2156 wx.CallAfter(self._PRW_year_noted.SetText, str(fts), fts)
2157 # if we do this we will *always* navigate there, regardless of TAB vs ALT-TAB
2158 #wx.CallAfter(self._ChBOX_active.SetFocus)
2159 # if we do the following instead it will take us to the save/update button ...
2160 #wx.CallAfter(self.Navigate)
2161
2162 return True
2163 #--------------------------------------------------------
2165
2166 if not self._PRW_year_noted.IsModified():
2167 return True
2168
2169 year_noted = self._PRW_year_noted.GetData()
2170
2171 if year_noted is None:
2172 if self._PRW_year_noted.GetValue().strip() == u'':
2173 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2174 return True
2175 self._PRW_year_noted.SetBackgroundColour('pink')
2176 self._PRW_year_noted.Refresh()
2177 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2178 return True
2179
2180 year_noted = year_noted.get_pydt()
2181
2182 if year_noted >= pydt.datetime.now(tz=year_noted.tzinfo):
2183 gmDispatcher.send(signal='statustext', msg=_('Condition diagnosed in the future.'))
2184 self._PRW_year_noted.SetBackgroundColour('pink')
2185 self._PRW_year_noted.Refresh()
2186 wx.CallAfter(self._PRW_age_noted.SetText, u'', None, True)
2187 return True
2188
2189 self._PRW_year_noted.SetBackgroundColour(wx.SystemSettings_GetColour(wx.SYS_COLOUR_WINDOW))
2190 self._PRW_year_noted.Refresh()
2191
2192 pat = gmPerson.gmCurrentPatient()
2193 if pat['dob'] is not None:
2194 issue_age = year_noted - pat['dob']
2195 str_age = gmDateTime.format_interval_medically(interval = issue_age)
2196 wx.CallAfter(self._PRW_age_noted.SetText, str_age, issue_age)
2197
2198 return True
2199 #--------------------------------------------------------
2203 #--------------------------------------------------------
2207 #================================================================
2208 # diagnostic certainty related widgets/functions
2209 #----------------------------------------------------------------
2211
2213
2214 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
2215
2216 self.selection_only = False # can be NULL, too
2217
2218 mp = gmMatchProvider.cMatchProvider_FixedList (
2219 aSeq = [
2220 {'data': u'A', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'A'), 'weight': 1},
2221 {'data': u'B', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'B'), 'weight': 1},
2222 {'data': u'C', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'C'), 'weight': 1},
2223 {'data': u'D', 'label': gmEMRStructItems.diagnostic_certainty_classification2str(u'D'), 'weight': 1}
2224 ]
2225 )
2226 mp.setThresholds(1, 2, 4)
2227 self.matcher = mp
2228
2229 self.SetToolTipString(_(
2230 "The diagnostic classification or grading of this assessment.\n"
2231 "\n"
2232 "This documents how certain one is about this being a true diagnosis."
2233 ))
2234 #================================================================
2235 # MAIN
2236 #----------------------------------------------------------------
2237 if __name__ == '__main__':
2238
2239 #================================================================
2241 """
2242 Test application for testing EMR struct widgets
2243 """
2244 #--------------------------------------------------------
2246 """
2247 Create test application UI
2248 """
2249 frame = wx.Frame (
2250 None,
2251 -4,
2252 'Testing EMR struct widgets',
2253 size=wx.Size(600, 400),
2254 style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE
2255 )
2256 filemenu= wx.Menu()
2257 filemenu.AppendSeparator()
2258 filemenu.Append(ID_EXIT,"E&xit"," Terminate test application")
2259
2260 # Creating the menubar.
2261 menuBar = wx.MenuBar()
2262 menuBar.Append(filemenu,"&File")
2263
2264 frame.SetMenuBar(menuBar)
2265
2266 txt = wx.StaticText( frame, -1, _("Select desired test option from the 'File' menu"),
2267 wx.DefaultPosition, wx.DefaultSize, 0 )
2268
2269 # event handlers
2270 wx.EVT_MENU(frame, ID_EXIT, self.OnCloseWindow)
2271
2272 # patient EMR
2273 self.__pat = gmPerson.gmCurrentPatient()
2274
2275 frame.Show(1)
2276 return 1
2277 #--------------------------------------------------------
2283 #----------------------------------------------------------------
2285 app = wx.PyWidgetTester(size = (200, 300))
2286 emr = pat.get_emr()
2287 enc = emr.active_encounter
2288 #enc = gmEMRStructItems.cEncounter(1)
2289 pnl = cEncounterEditAreaPnl(app.frame, -1, encounter=enc)
2290 app.frame.Show(True)
2291 app.MainLoop()
2292 return
2293 #----------------------------------------------------------------
2295 app = wx.PyWidgetTester(size = (200, 300))
2296 emr = pat.get_emr()
2297 enc = emr.active_encounter
2298 #enc = gmEMRStructItems.cEncounter(1)
2299
2300 dlg = cEncounterEditAreaDlg(parent=app.frame, id=-1, size = (400,400), encounter=enc)
2301 dlg.ShowModal()
2302
2303 # pnl = cEncounterEditAreaDlg(app.frame, -1, encounter=enc)
2304 # app.frame.Show(True)
2305 # app.MainLoop()
2306 #----------------------------------------------------------------
2308 app = wx.PyWidgetTester(size = (200, 300))
2309 emr = pat.get_emr()
2310 epi = emr.get_episodes()[0]
2311 pnl = cEpisodeEditAreaPnl(app.frame, -1, episode=epi)
2312 app.frame.Show(True)
2313 app.MainLoop()
2314 #----------------------------------------------------------------
2316 app = wx.PyWidgetTester(size = (200, 300))
2317 emr = pat.get_emr()
2318 epi = emr.get_episodes()[0]
2319 edit_episode(parent=app.frame, episode=epi)
2320 #----------------------------------------------------------------
2322 app = wx.PyWidgetTester(size = (400, 40))
2323 app.SetWidget(cHospitalStayPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2324 app.MainLoop()
2325 #----------------------------------------------------------------
2327 app = wx.PyWidgetTester(size = (400, 40))
2328 app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(180,20), pos=(10,20))
2329 # app.SetWidget(cEpisodeSelectionPhraseWheel, id=-1, size=(350,20), pos=(10,20), patient_id=pat.ID)
2330 app.MainLoop()
2331 #----------------------------------------------------------------
2333 app = wx.PyWidgetTester(size = (200, 300))
2334 edit_health_issue(parent=app.frame, issue=None)
2335 #----------------------------------------------------------------
2337 app = wx.PyWidgetTester(size = (200, 300))
2338 app.SetWidget(cHealthIssueEditAreaPnl, id=-1, size = (400,400))
2339 app.MainLoop()
2340 #----------------------------------------------------------------
2344 #================================================================
2345
2346 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'):
2347
2348 gmI18N.activate_locale()
2349 gmI18N.install_domain()
2350 gmDateTime.init()
2351
2352 # obtain patient
2353 pat = gmPersonSearch.ask_for_patient()
2354 if pat is None:
2355 print "No patient. Exiting gracefully..."
2356 sys.exit(0)
2357 gmPatSearchWidgets.set_active_patient(patient=pat)
2358
2359 # try:
2360 # lauch emr dialogs test application
2361 # app = testapp(0)
2362 # app.MainLoop()
2363 # except StandardError:
2364 # _log.exception("unhandled exception caught !")
2365 # but re-raise them
2366 # raise
2367
2368 #test_encounter_edit_area_panel()
2369 #test_encounter_edit_area_dialog()
2370 #test_epsiode_edit_area_pnl()
2371 #test_episode_edit_area_dialog()
2372 #test_health_issue_edit_area_dlg()
2373 #test_episode_selection_prw()
2374 #test_hospital_stay_prw()
2375 test_edit_procedure()
2376
2377 #================================================================
2378
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Dec 5 04:00:10 2011 | http://epydoc.sourceforge.net |