| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed narrative handling widgets."""
2 #================================================================
3 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
4 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
5
6 import sys
7 import logging
8 import os.path
9 import time
10
11
12 import wx
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17
18 from Gnumed.pycommon import gmI18N
19
20 if __name__ == '__main__':
21 gmI18N.activate_locale()
22 gmI18N.install_domain()
23
24 from Gnumed.pycommon import gmDispatcher
25 from Gnumed.pycommon import gmTools
26 from Gnumed.pycommon import gmDateTime
27 from Gnumed.pycommon import gmCfg
28
29 from Gnumed.business import gmPerson
30 from Gnumed.business import gmStaff
31 from Gnumed.business import gmEMRStructItems
32 from Gnumed.business import gmSoapDefs
33 from Gnumed.business import gmPraxis
34 from Gnumed.business import gmPersonSearch
35
36 from Gnumed.wxpython import gmListWidgets
37 from Gnumed.wxpython import gmEMRStructWidgets
38 from Gnumed.wxpython import gmEncounterWidgets
39 from Gnumed.wxpython import gmRegetMixin
40 from Gnumed.wxpython import gmGuiHelpers
41 from Gnumed.wxpython import gmVisualProgressNoteWidgets
42 from Gnumed.wxpython import gmProgressNotesEAWidgets
43 from Gnumed.wxpython.gmPatSearchWidgets import set_active_patient
44
45 from Gnumed.exporters import gmPatientExporter
46
47
48 _log = logging.getLogger('gm.ui')
49 #============================================================
50 # narrative related widgets
51 #------------------------------------------------------------
53
55
56 narrative = kwargs['narrative']
57 del kwargs['narrative']
58
59 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
60
61 self.SetTitle(_('Select the narrative you are interested in ...'))
62 # FIXME: add epi/issue
63 self._LCTRL_items.set_columns([_('when'), _('who'), _('type'), _('entry')]) #, _('Episode'), u'', _('Health Issue')])
64 # FIXME: date used should be date of encounter, not date_modified
65 self._LCTRL_items.set_string_items (
66 items = [ [narr['date'].strftime('%x %H:%M'), narr['modified_by'], gmSoapDefs.soap_cat2l10n[narr['soap_cat']], narr['narrative'].replace('\n', '/').replace('\r', '/')] for narr in narrative ]
67 )
68 self._LCTRL_items.set_column_widths()
69 self._LCTRL_items.set_data(data = narrative)
70
71 #------------------------------------------------------------
72 from Gnumed.wxGladeWidgets import wxgMoveNarrativeDlg
73
75
77
78 self.encounter = kwargs['encounter']
79 self.source_episode = kwargs['episode']
80 del kwargs['encounter']
81 del kwargs['episode']
82
83 wxgMoveNarrativeDlg.wxgMoveNarrativeDlg.__init__(self, *args, **kwargs)
84
85 self.LBL_source_episode.SetLabel('%s%s' % (self.source_episode['description'], gmTools.coalesce(self.source_episode['health_issue'], '', ' (%s)')))
86 self.LBL_encounter.SetLabel('%s: %s %s - %s' % (
87 gmDateTime.pydt_strftime(self.encounter['started'], '%Y %b %d'),
88 self.encounter['l10n_type'],
89 gmDateTime.pydt_strftime(self.encounter['started'], '%H:%M'),
90 gmDateTime.pydt_strftime(self.encounter['last_affirmed'], '%H:%M')
91 ))
92 pat = gmPerson.gmCurrentPatient()
93 emr = pat.emr
94 narr = emr.get_clin_narrative(episodes=[self.source_episode['pk_episode']], encounters=[self.encounter['pk_encounter']])
95 if len(narr) == 0:
96 narr = [{'narrative': _('There is no narrative for this episode in this encounter.')}]
97 self.LBL_narrative.SetLabel('\n'.join([n['narrative'] for n in narr]))
98
99 #------------------------------------------------------------
121
122 #============================================================
123 #============================================================
124 from Gnumed.wxGladeWidgets import wxgSoapPluginPnl
125
127 """A panel for in-context editing of progress notes.
128
129 Expects to be used as a notebook page.
130
131 Left hand side:
132 - problem list (health issues and active episodes)
133 - previous notes
134
135 Right hand side:
136 - panel handling
137 - encounter details fields
138 - notebook with progress note editors
139 - visual progress notes
140
141 Listens to patient change signals, thus acts on the current patient.
142 """
144
145 wxgSoapPluginPnl.wxgSoapPluginPnl.__init__(self, *args, **kwargs)
146 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
147
148 self.__pat = gmPerson.gmCurrentPatient()
149 self.__init_ui()
150 self.__reset_ui_content()
151 self.__register_interests()
152 #--------------------------------------------------------
153 # internal helpers
154 #--------------------------------------------------------
156 self._LCTRL_active_problems.set_columns([_('Last'), _('Problem'), _('In health issue')])
157 self._LCTRL_active_problems.set_string_items()
158 self._LCTRL_active_problems.extend_popup_menu_callback = self._extend_popup_menu
159
160 self._splitter_main.SetSashGravity(0.5)
161 self._splitter_left.SetSashGravity(0.5)
162
163 splitter_size = self._splitter_main.GetSize()[0]
164 self._splitter_main.SetSashPosition(splitter_size * 3 // 10, True)
165
166 splitter_size = self._splitter_left.GetSize()[1]
167 self._splitter_left.SetSashPosition(splitter_size * 6 // 20, True)
168
169 #--------------------------------------------------------
181
182 #--------------------------------------------------------
184 """Clear all information from input panel."""
185
186 self._LCTRL_active_problems.set_string_items()
187
188 self._TCTRL_recent_notes.SetValue('')
189 self._SZR_recent_notes.StaticBox.SetLabel(_('Most recent notes on selected problem'))
190
191 self._PNL_editors.patient = None
192 #--------------------------------------------------------
194 """Update health problems list."""
195
196 self._LCTRL_active_problems.set_string_items()
197
198 emr = self.__pat.emr
199 problems = emr.get_problems (
200 include_closed_episodes = self._CHBOX_show_closed_episodes.IsChecked(),
201 include_irrelevant_issues = self._CHBOX_irrelevant_issues.IsChecked()
202 )
203
204 list_items = []
205 active_problems = []
206 for problem in problems:
207 if not problem['problem_active']:
208 if not problem['is_potential_problem']:
209 continue
210
211 active_problems.append(problem)
212
213 if problem['type'] == 'issue':
214 issue = emr.problem2issue(problem)
215 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
216 if last_encounter is None:
217 last = issue['modified_when'].strftime('%m/%Y')
218 else:
219 last = last_encounter['last_affirmed'].strftime('%m/%Y')
220
221 list_items.append([last, problem['problem'], gmTools.u_left_arrow_with_tail])
222
223 elif problem['type'] == 'episode':
224 epi = emr.problem2episode(problem)
225 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
226 if last_encounter is None:
227 last = epi['episode_modified_when'].strftime('%m/%Y')
228 else:
229 last = last_encounter['last_affirmed'].strftime('%m/%Y')
230
231 list_items.append ([
232 last,
233 problem['problem'],
234 gmTools.coalesce(value2test = epi['health_issue'], return_instead = '?') #gmTools.u_diameter
235 ])
236
237 self._LCTRL_active_problems.set_string_items(items = list_items)
238 self._LCTRL_active_problems.set_column_widths()
239 self._LCTRL_active_problems.set_data(data = active_problems)
240
241 showing_potential_problems = (
242 self._CHBOX_show_closed_episodes.IsChecked()
243 or
244 self._CHBOX_irrelevant_issues.IsChecked()
245 )
246 if showing_potential_problems:
247 self._SZR_problem_list.StaticBox.SetLabel(_('%s (active+potential) problems') % len(list_items))
248 else:
249 self._SZR_problem_list.StaticBox.SetLabel(_('%s active problems') % len(list_items))
250
251 return True
252 #--------------------------------------------------------
254 soap = ''
255 emr = self.__pat.emr
256 prev_enc = emr.get_last_but_one_encounter(issue_id = problem['pk_health_issue'])
257 if prev_enc is not None:
258 soap += prev_enc.format (
259 issues = [ problem['pk_health_issue'] ],
260 with_soap = True,
261 with_docs = fancy,
262 with_tests = fancy,
263 patient = self.__pat,
264 fancy_header = False,
265 with_rfe_aoe = True
266 )
267
268 tmp = emr.active_encounter.format_soap (
269 soap_cats = 'soapu',
270 emr = emr,
271 issues = [ problem['pk_health_issue'] ],
272 )
273 if len(tmp) > 0:
274 soap += _('Current encounter:') + '\n'
275 soap += '\n'.join(tmp) + '\n'
276
277 if problem['summary'] is not None:
278 soap += '\n-- %s ----------\n%s' % (
279 _('Cumulative summary'),
280 gmTools.wrap (
281 text = problem['summary'],
282 width = 45,
283 initial_indent = ' ',
284 subsequent_indent = ' '
285 ).strip('\n')
286 )
287
288 return soap
289 #--------------------------------------------------------
291 soap = ''
292 emr = self.__pat.emr
293 prev_enc = emr.get_last_but_one_encounter(episode_id = problem['pk_episode'])
294 if prev_enc is not None:
295 soap += prev_enc.format (
296 episodes = [ problem['pk_episode'] ],
297 with_soap = True,
298 with_docs = fancy,
299 with_tests = fancy,
300 patient = self.__pat,
301 fancy_header = False,
302 with_rfe_aoe = True
303 )
304 else:
305 if problem['pk_health_issue'] is not None:
306 prev_enc = emr.get_last_but_one_encounter(episode_id = problem['pk_health_issue'])
307 if prev_enc is not None:
308 soap += prev_enc.format (
309 with_soap = True,
310 with_docs = fancy,
311 with_tests = fancy,
312 patient = self.__pat,
313 issues = [ problem['pk_health_issue'] ],
314 fancy_header = False,
315 with_rfe_aoe = True
316 )
317
318 if problem['pk_health_issue'] is None:
319 tmp = emr.active_encounter.format_soap(soap_cats = 'soapu', emr = emr)
320 else:
321 tmp = emr.active_encounter.format_soap(soap_cats = 'soapu', emr = emr, issues = [problem['pk_health_issue']])
322 if len(tmp) > 0:
323 soap += _('Current encounter:') + '\n'
324 soap += '\n'.join(tmp) + '\n'
325
326 if problem['summary'] is not None:
327 soap += '\n-- %s ----------\n%s' % (
328 _('Cumulative summary'),
329 gmTools.wrap (
330 text = problem['summary'],
331 width = 45,
332 initial_indent = ' ',
333 subsequent_indent = ' '
334 ).strip('\n')
335 )
336
337 return soap
338 #--------------------------------------------------------
340 """This refreshes the recent-notes part."""
341
342 if problem is None:
343 caption = '<?>'
344 txt = ''
345 elif problem['type'] == 'issue':
346 caption = problem['problem'][:35]
347 txt = self.__get_info_for_issue_problem(problem = problem, fancy = not self._RBTN_notes_only.GetValue())
348 elif problem['type'] == 'episode':
349 caption = problem['problem'][:35]
350 txt = self.__get_info_for_episode_problem(problem = problem, fancy = not self._RBTN_notes_only.GetValue())
351
352 self._TCTRL_recent_notes.SetValue(txt)
353 self._TCTRL_recent_notes.ShowPosition(self._TCTRL_recent_notes.GetLastPosition())
354 self._SZR_recent_notes.StaticBox.SetLabel(_('Most recent info on %s%s%s') % (
355 gmTools.u_left_double_angle_quote,
356 caption,
357 gmTools.u_right_double_angle_quote
358 ))
359
360 self._TCTRL_recent_notes.Refresh()
361
362 return True
363 #--------------------------------------------------------
364 # event handling
365 #--------------------------------------------------------
367 """Configure enabled event signals."""
368 # client internal signals
369 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
370 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
371 gmDispatcher.connect(signal = 'clin.episode_mod_db', receiver = self._on_episode_issue_mod_db)
372 gmDispatcher.connect(signal = 'clin.health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
373 gmDispatcher.connect(signal = 'clin.episode_code_mod_db', receiver = self._on_episode_issue_mod_db)
374 #--------------------------------------------------------
377 #--------------------------------------------------------
381 #--------------------------------------------------------
384 #--------------------------------------------------------
385 # problem list specific events
386 #--------------------------------------------------------
390 #--------------------------------------------------------
392 gmEMRStructWidgets.edit_health_issue(parent = self, issue = self.__focussed_problem.get_as_health_issue())
393
394 #--------------------------------------------------------
396 gmEMRStructWidgets.edit_episode(parent = self, episode = self.__focussed_problem.get_as_episode())
397
398 #--------------------------------------------------------
400 """Show related note at the bottom."""
401 self.__refresh_recent_notes (
402 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
403 )
404 #--------------------------------------------------------
406 """Open progress note editor for this problem.
407 """
408 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
409 if problem is None:
410 return True
411
412 dbcfg = gmCfg.cCfgSQL()
413 allow_duplicate_editors = bool(dbcfg.get2 (
414 option = 'horstspace.soap_editor.allow_same_episode_multiple_times',
415 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
416 bias = 'user',
417 default = False
418 ))
419 if self._PNL_editors.add_editor(problem = problem, allow_same_problem = allow_duplicate_editors):
420 return True
421
422 gmGuiHelpers.gm_show_error (
423 aMessage = _(
424 'Cannot open progress note editor for\n\n'
425 '[%s].\n\n'
426 ) % problem['problem'],
427 aTitle = _('opening progress note editor')
428 )
429 return False
430 #--------------------------------------------------------
433 #--------------------------------------------------------
436 #--------------------------------------------------------
437 # recent-notes specific events
438 #--------------------------------------------------------
440 self.__refresh_recent_notes (
441 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
442 )
443 #--------------------------------------------------------
445 self.__refresh_recent_notes (
446 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
447 )
448 #--------------------------------------------------------
449 # reget mixin API
450 #--------------------------------------------------------
451 # only needed for debugging:
452 #def _schedule_data_reget(self):
453 # gmRegetMixin.cRegetOnPaintMixin._schedule_data_reget(self)
454 #--------------------------------------------------------
458
459 #============================================================
460 from Gnumed.wxGladeWidgets import wxgFancySoapEditorPnl
461
463 """A panel holding everything needed to edit in context:
464
465 - encounter metadata
466 - progress notes
467 - textual
468 - visual
469 - episode summary
470
471 Does NOT act on the current patient.
472 """
474
475 wxgFancySoapEditorPnl.wxgFancySoapEditorPnl.__init__(self, *args, **kwargs)
476
477 self.__init_ui()
478 self.patient = None
479 self.__register_interests()
480 #--------------------------------------------------------
481 # public API
482 #--------------------------------------------------------
484 return self._NB_soap_editors.add_editor(problem = problem, allow_same_problem = allow_same_problem)
485 #--------------------------------------------------------
488
490 #self.__pat.register_before_switching_from_patient_callback(callback = self._before_switching_from_patient_callback)
491 self.__pat = patient
492 self.__refresh_encounter()
493 self.__refresh_soap_notebook()
494
495 patient = property(_get_patient, _set_patient)
496 #--------------------------------------------------------
498
499 if self.__pat is None:
500 return True
501
502 if not self.__encounter_valid_for_save():
503 return False
504
505 enc = self.__pat.emr.active_encounter
506
507 rfe = self._TCTRL_rfe.GetValue().strip()
508 if len(rfe) == 0:
509 enc['reason_for_encounter'] = None
510 else:
511 enc['reason_for_encounter'] = rfe
512 aoe = self._TCTRL_aoe.GetValue().strip()
513 if len(aoe) == 0:
514 enc['assessment_of_encounter'] = None
515 else:
516 enc['assessment_of_encounter'] = aoe
517
518 enc.save_payload()
519
520 enc.generic_codes_rfe = [ c['data'] for c in self._PRW_rfe_codes.GetData() ]
521 enc.generic_codes_aoe = [ c['data'] for c in self._PRW_aoe_codes.GetData() ]
522
523 return True
524 #--------------------------------------------------------
525 # internal helpers
526 #--------------------------------------------------------
529 #--------------------------------------------------------
533 #--------------------------------------------------------
535 self.__reset_soap_notebook()
536
537 if self.__pat is None:
538 return
539
540 dbcfg = gmCfg.cCfgSQL()
541 auto_open_recent_problems = bool(dbcfg.get2 (
542 option = 'horstspace.soap_editor.auto_open_latest_episodes',
543 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
544 bias = 'user',
545 default = True
546 ))
547
548 emr = self.__pat.emr
549 recent_epis = emr.active_encounter.get_episodes()
550 prev_enc = emr.get_last_but_one_encounter()
551 if prev_enc is not None:
552 recent_epis.extend(prev_enc.get_episodes())
553
554 for epi in recent_epis:
555 if not epi['episode_open']:
556 continue
557 self._NB_soap_editors.add_editor(problem = epi)
558
559 #--------------------------------------------------------
561 self._TCTRL_rfe.SetValue('')
562 self._PRW_rfe_codes.SetText(suppress_smarts = True)
563 self._TCTRL_aoe.SetValue('')
564 self._PRW_aoe_codes.SetText(suppress_smarts = True)
565
566 #--------------------------------------------------------
568 """Update encounter fields."""
569
570 self.__reset_encounter_fields()
571
572 if self.__pat is None:
573 return
574
575 enc = self.__pat.emr.active_encounter
576
577 self._TCTRL_rfe.SetValue(gmTools.coalesce(enc['reason_for_encounter'], ''))
578 val, data = self._PRW_rfe_codes.generic_linked_codes2item_dict(enc.generic_codes_rfe)
579 self._PRW_rfe_codes.SetText(val, data)
580
581 self._TCTRL_aoe.SetValue(gmTools.coalesce(enc['assessment_of_encounter'], ''))
582 val, data = self._PRW_aoe_codes.generic_linked_codes2item_dict(enc.generic_codes_aoe)
583 self._PRW_aoe_codes.SetText(val, data)
584
585 self._TCTRL_rfe.Refresh()
586 self._PRW_rfe_codes.Refresh()
587 self._TCTRL_aoe.Refresh()
588 self._PRW_aoe_codes.Refresh()
589
590 #--------------------------------------------------------
592 self._NB_soap_editors.refresh_current_editor()
593
594 # #--------------------------------------------------------
595 # def __encounter_modified(self):
596 # """Assumes that the field data is valid."""
597 #
598 # emr = self.__pat.emr
599 # enc = emr.active_encounter
600 #
601 # data = {
602 # 'pk_type': enc['pk_type'],
603 # 'reason_for_encounter': gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u''),
604 # 'assessment_of_encounter': gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u''),
605 # 'pk_location': enc['pk_org_unit'],
606 # 'pk_patient': enc['pk_patient'],
607 # 'pk_generic_codes_rfe': self._PRW_rfe_codes.GetData(),
608 # 'pk_generic_codes_aoe': self._PRW_aoe_codes.GetData(),
609 # 'started': enc['started'],
610 # 'last_affirmed': enc['last_affirmed']
611 # }
612 #
613 # return not enc.same_payload(another_object = data)
614 #--------------------------------------------------------
617 #--------------------------------------------------------
618 # event handling
619 #--------------------------------------------------------
621 """Configure enabled event signals."""
622 # synchronous signals
623 gmDispatcher.send(signal = 'register_pre_exit_callback', callback = self._pre_exit_callback)
624
625 # client internal signals
626 gmDispatcher.connect(signal = 'blobs.doc_med_mod_db', receiver = self._on_doc_mod_db) # visual progress notes
627 gmDispatcher.connect(signal = 'current_encounter_modified', receiver = self._on_current_encounter_modified)
628 gmDispatcher.connect(signal = 'current_encounter_switched', receiver = self._on_current_encounter_switched)
629 gmDispatcher.connect(signal = 'clin.rfe_code_mod_db', receiver = self._on_encounter_code_modified)
630 gmDispatcher.connect(signal = 'clin.aoe_code_mod_db', receiver = self._on_encounter_code_modified)
631 #--------------------------------------------------------
633 """Another patient is about to be activated.
634
635 Patient change will not proceed before this returns True.
636 """
637 # don't worry about the encounter here - it will be offered
638 # for editing higher up if anything was saved to the EMR
639 if self.__pat is None:
640 return True
641 return self._NB_soap_editors.warn_on_unsaved_soap()
642 #--------------------------------------------------------
644 """The client is about to (be) shut down.
645
646 Shutdown will not proceed before this returns.
647 """
648 if self.__pat is None:
649 return True
650
651 # if self.__encounter_modified():
652 # do_save_enc = gmGuiHelpers.gm_show_question (
653 # aMessage = _(
654 # 'You have modified the details\n'
655 # 'of the current encounter.\n'
656 # '\n'
657 # 'Do you want to save those changes ?'
658 # ),
659 # aTitle = _('Starting new encounter')
660 # )
661 # if do_save_enc:
662 # if not self.save_encounter():
663 # gmDispatcher.send(signal = u'statustext', msg = _('Error saving current encounter.'), beep = True)
664
665 saved = self._NB_soap_editors.save_all_editors (
666 emr = self.__pat.emr,
667 episode_name_candidates = [
668 gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), ''),
669 gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), '')
670 ]
671 )
672 if not saved:
673 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save all editors. Some were kept open.'), beep = True)
674 return True
675 #--------------------------------------------------------
678 #--------------------------------------------------------
682 #--------------------------------------------------------
685 #--------------------------------------------------------
688 #--------------------------------------------------------
689 # SOAP editor specific buttons
690 #--------------------------------------------------------
694 #--------------------------------------------------------
698 #--------------------------------------------------------
702 #--------------------------------------------------------
712 #--------------------------------------------------------
732 #--------------------------------------------------------
736 #--------------------------------------------------------
737 # encounter specific buttons
738 #--------------------------------------------------------
742 #--------------------------------------------------------
743 # other buttons
744 #--------------------------------------------------------
753 #--------------------------------------------------------
765
766 #============================================================
768 """A notebook holding panels with progress note editors.
769
770 There can be one or several progress note editor panels
771 for each episode being worked on. The editor class in
772 each panel is configurable.
773
774 There will always be one open editor.
775 """
777
778 kwargs['style'] = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER
779
780 wx.Notebook.__init__(self, *args, **kwargs)
781
782 _log.debug('created wx.Notebook: %s with ID %s', self.__class__.__name__, self.Id)
783 #--------------------------------------------------------
784 # public API
785 #--------------------------------------------------------
787 """Add a progress note editor page.
788
789 The way <allow_same_problem> is currently used in callers
790 it only applies to unassociated episodes.
791 """
792 problem_to_add = problem
793
794 # determine label
795 if problem_to_add is None:
796 label = _('new problem')
797 else:
798 # normalize problem type
799 if isinstance(problem_to_add, gmEMRStructItems.cEpisode):
800 problem_to_add = gmEMRStructItems.episode2problem(episode = problem_to_add, allow_closed = True)
801
802 elif isinstance(problem_to_add, gmEMRStructItems.cHealthIssue):
803 problem_to_add = gmEMRStructItems.health_issue2problem(health_issue = problem_to_add, allow_irrelevant = True)
804
805 if not isinstance(problem_to_add, gmEMRStructItems.cProblem):
806 raise TypeError('cannot open progress note editor for [%s]' % problem_to_add)
807
808 label = problem_to_add['problem']
809 # FIXME: configure maximum length
810 if len(label) > 23:
811 label = label[:21] + gmTools.u_ellipsis
812
813 # new unassociated problem or dupes allowed
814 if allow_same_problem:
815 new_page = gmProgressNotesEAWidgets.cProgressNotesEAPnl(self, -1, problem = problem_to_add)
816 result = self.AddPage (
817 page = new_page,
818 text = label,
819 select = True
820 )
821 return result
822
823 # real problem, no dupes allowed
824 # - raise existing editor
825 for page_idx in range(self.GetPageCount()):
826 page = self.GetPage(page_idx)
827
828 if problem_to_add is None:
829 if page.problem is None:
830 self.SetSelection(page_idx)
831 gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True)
832 return True
833 continue
834
835 # editor is for unassociated new problem
836 if page.problem is None:
837 continue
838
839 # editor is for episode
840 if page.problem['type'] == 'episode':
841 if page.problem['pk_episode'] == problem_to_add['pk_episode']:
842 self.SetSelection(page_idx)
843 gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True)
844 return True
845 continue
846
847 # editor is for health issue
848 if page.problem['type'] == 'issue':
849 if page.problem['pk_health_issue'] == problem_to_add['pk_health_issue']:
850 self.SetSelection(page_idx)
851 gmDispatcher.send(signal = 'statustext', msg = 'Raising existing editor.', beep = True)
852 return True
853 continue
854
855 # - or add new editor
856 new_page = gmProgressNotesEAWidgets.cProgressNotesEAPnl(parent = self, problem = problem_to_add)
857 result = self.AddPage (
858 page = new_page,
859 text = label,
860 select = True
861 )
862
863 return result
864 #--------------------------------------------------------
866
867 page_idx = self.GetSelection()
868 page = self.GetPage(page_idx)
869
870 if not page.empty:
871 really_discard = gmGuiHelpers.gm_show_question (
872 _('Are you sure you really want to\n'
873 'discard this progress note ?\n'
874 ),
875 _('Discarding progress note')
876 )
877 if really_discard is False:
878 return
879
880 self.DeletePage(page_idx)
881
882 # always keep one unassociated editor open
883 if self.GetPageCount() == 0:
884 self.add_editor()
885 #--------------------------------------------------------
887 page_idx = self.GetSelection()
888 _log.debug('saving editor on current page (#%s)', page_idx)
889 page = self.GetPage(page_idx)
890 if not page.save(emr = emr, episode_name_candidates = episode_name_candidates, encounter = encounter):
891 _log.debug('not saved, not deleting')
892 return False
893
894 _log.debug('deleting')
895 self.DeletePage(page_idx)
896
897 # always keep one unassociated editor open
898 if self.GetPageCount() == 0:
899 self.add_editor()
900 return True
901 #--------------------------------------------------------
903 for page_idx in range(self.GetPageCount()):
904 page = self.GetPage(page_idx)
905 if page.empty:
906 continue
907
908 gmGuiHelpers.gm_show_warning (
909 _('There are unsaved progress notes !\n'),
910 _('Unsaved progress notes')
911 )
912 return False
913
914 return True
915 #--------------------------------------------------------
917
918 _log.debug('saving editors: %s', self.GetPageCount())
919
920 # always keep one unassociated editor open
921 if self.GetPageCount() == 0:
922 self.add_editor()
923 return True
924
925 # first of all save the current editor such
926 # as not to confuse the user by switching away
927 # from the page she invoked [save all] from
928 idx_of_current_page = self.GetSelection()
929 _log.debug('saving editor on current page (#%s)', idx_of_current_page)
930 all_closed = self.GetPage(idx_of_current_page).save(emr = emr, episode_name_candidates = episode_name_candidates)
931 if all_closed:
932 _log.debug('deleting')
933 self.DeletePage(idx_of_current_page)
934 idx_of_current_page = None
935 else:
936 _log.debug('not saved, not deleting')
937
938 # now save remaining editors from right to left
939 for page_idx in range((self.GetPageCount() - 1), -1, -1):
940 # skip current ?
941 if page_idx == idx_of_current_page:
942 # we tried and failed, no need to retry
943 continue
944 _log.debug('saving editor on page %s of %s', page_idx, self.GetPageCount())
945 try:
946 self.ChangeSelection(page_idx)
947 _log.debug('editor raised')
948 except Exception:
949 _log.exception('cannot raise editor')
950 page = self.GetPage(page_idx)
951 if page.save(emr = emr, episode_name_candidates = episode_name_candidates):
952 _log.debug('saved, deleting')
953 self.DeletePage(page_idx)
954 else:
955 _log.debug('not saved, not deleting')
956 all_closed = False
957
958 # always keep one unassociated editor open
959 if self.GetPageCount() == 0:
960 self.add_editor()
961
962 return (all_closed is True)
963 #--------------------------------------------------------
965 self.GetCurrentPage().clear()
966 #--------------------------------------------------------
969 #--------------------------------------------------------
971 self.GetCurrentPage().refresh()
972 #--------------------------------------------------------
974 self.GetCurrentPage().add_visual_progress_note()
975
976 #============================================================
977 #============================================================
978 from Gnumed.wxGladeWidgets import wxgSimpleSoapPluginPnl
979
980 -class cSimpleSoapPluginPnl(wxgSimpleSoapPluginPnl.wxgSimpleSoapPluginPnl, gmRegetMixin.cRegetOnPaintMixin):
982
983 wxgSimpleSoapPluginPnl.wxgSimpleSoapPluginPnl.__init__(self, *args, **kwargs)
984 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
985
986 self.__curr_pat = gmPerson.gmCurrentPatient()
987 self.__problem = None
988 self.__init_ui()
989 self.__register_interests()
990 #-----------------------------------------------------
991 # internal API
992 #-----------------------------------------------------
994 self._LCTRL_problems.set_columns(columns = [_('Problem list')])
995 self._LCTRL_problems.activate_callback = self._on_problem_activated
996 self._LCTRL_problems.item_tooltip_callback = self._on_get_problem_tooltip
997
998 self._splitter_main.SetSashGravity(0.5)
999 splitter_width = self._splitter_main.GetSize()[0]
1000 self._splitter_main.SetSashPosition(splitter_width // 2, True)
1001
1002 self._TCTRL_soap.Disable()
1003 self._BTN_save_soap.Disable()
1004 self._BTN_clear_soap.Disable()
1005 #-----------------------------------------------------
1007 self._LCTRL_problems.set_string_items()
1008 self._TCTRL_soap_problem.SetValue(_('<above, double-click problem to start entering SOAP note>'))
1009 self._TCTRL_soap.SetValue('')
1010 self._CHBOX_filter_by_problem.SetLabel(_('&Filter by problem'))
1011 self._TCTRL_journal.SetValue('')
1012
1013 self._TCTRL_soap.Disable()
1014 self._BTN_save_soap.Disable()
1015 self._BTN_clear_soap.Disable()
1016 #-----------------------------------------------------
1018 if not self.__curr_pat.connected:
1019 return None
1020
1021 if self.__problem is None:
1022 return None
1023
1024 saved = self.__curr_pat.emr.add_clin_narrative (
1025 note = self._TCTRL_soap.GetValue().strip(),
1026 soap_cat = 'u',
1027 episode = self.__problem
1028 )
1029
1030 if saved is None:
1031 return False
1032
1033 self._TCTRL_soap.SetValue('')
1034 self.__refresh_journal()
1035 return True
1036 #-----------------------------------------------------
1038 if self._TCTRL_soap.GetValue().strip() == '':
1039 return True
1040 if self.__problem is None:
1041 # FIXME: this could potentially lose input
1042 self._TCTRL_soap.SetValue('')
1043 return None
1044 save_it = gmGuiHelpers.gm_show_question (
1045 title = _('Saving SOAP note'),
1046 question = _('Do you want to save the SOAP note ?')
1047 )
1048 if save_it:
1049 return self.__save_soap()
1050 return False
1051 #-----------------------------------------------------
1053 self._LCTRL_problems.set_string_items()
1054 emr = self.__curr_pat.emr
1055 epis = emr.get_episodes(open_status = True)
1056 if len(epis) > 0:
1057 self._LCTRL_problems.set_string_items(items = [ '%s%s' % (
1058 e['description'],
1059 gmTools.coalesce(e['health_issue'], '', ' (%s)')
1060 ) for e in epis ])
1061 self._LCTRL_problems.set_data(epis)
1062 #-----------------------------------------------------
1064 self._TCTRL_journal.SetValue('')
1065 epi = self._LCTRL_problems.get_selected_item_data(only_one = True)
1066
1067 if epi is not None:
1068 self._CHBOX_filter_by_problem.SetLabel(_('&Filter by problem %s%s%s') % (
1069 gmTools.u_left_double_angle_quote,
1070 epi['description'],
1071 gmTools.u_right_double_angle_quote
1072 ))
1073 self._CHBOX_filter_by_problem.Refresh()
1074
1075 if not self._CHBOX_filter_by_problem.IsChecked():
1076 self._TCTRL_journal.SetValue(self.__curr_pat.emr.format_summary())
1077 return
1078
1079 if epi is None:
1080 return
1081
1082 self._TCTRL_journal.SetValue(epi.format_as_journal())
1083 #-----------------------------------------------------
1084 # event handling
1085 #-----------------------------------------------------
1087 """Configure enabled event signals."""
1088 # client internal signals
1089 gmDispatcher.connect(signal = 'pre_patient_unselection', receiver = self._on_pre_patient_unselection)
1090 gmDispatcher.connect(signal = 'post_patient_selection', receiver = self._on_post_patient_selection)
1091 gmDispatcher.connect(signal = 'clin.episode_mod_db', receiver = self._on_episode_issue_mod_db)
1092 gmDispatcher.connect(signal = 'clin.health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
1093
1094 # synchronous signals
1095 self.__curr_pat.register_before_switching_from_patient_callback(callback = self._before_switching_from_patient_callback)
1096 gmDispatcher.send(signal = 'register_pre_exit_callback', callback = self._pre_exit_callback)
1097 #-----------------------------------------------------
1099 """Another patient is about to be activated.
1100
1101 Patient change will not proceed before this returns True.
1102 """
1103 if not self.__curr_pat.connected:
1104 return True
1105 self.__perhaps_save_soap()
1106 self.__problem = None
1107 return True
1108 #-----------------------------------------------------
1110 """The client is about to be shut down.
1111
1112 Shutdown will not proceed before this returns.
1113 """
1114 if not self.__curr_pat.connected:
1115 return
1116 if not self.__save_soap():
1117 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save SimpleNotes SOAP note.'), beep = True)
1118 return
1119 #-----------------------------------------------------
1122 #-----------------------------------------------------
1125 #-----------------------------------------------------
1128 #-----------------------------------------------------
1130 self.__perhaps_save_soap()
1131 epi = self._LCTRL_problems.get_selected_item_data(only_one = True)
1132 self._TCTRL_soap_problem.SetValue(_('Progress note: %s%s') % (
1133 epi['description'],
1134 gmTools.coalesce(epi['health_issue'], '', ' (%s)')
1135 ))
1136 self.__problem = epi
1137 self._TCTRL_soap.SetValue('')
1138
1139 self._TCTRL_soap.Enable()
1140 self._BTN_save_soap.Enable()
1141 self._BTN_clear_soap.Enable()
1142 #-----------------------------------------------------
1144 return episode.format (
1145 patient = self.__curr_pat,
1146 with_summary = False,
1147 with_codes = True,
1148 with_encounters = False,
1149 with_documents = False,
1150 with_hospital_stays = False,
1151 with_procedures = False,
1152 with_family_history = False,
1153 with_tests = False,
1154 with_vaccinations = False,
1155 with_health_issue = True
1156 )
1157 #-----------------------------------------------------
1161 #-----------------------------------------------------
1165 #-----------------------------------------------------
1180 #-----------------------------------------------------
1187 #-----------------------------------------------------
1195 #-----------------------------------------------------
1199 #-----------------------------------------------------
1203 #-----------------------------------------------------
1204 # reget-on-paint mixin API
1205 #-----------------------------------------------------
1207 self.__refresh_problem_list()
1208 self.__refresh_journal()
1209 self._TCTRL_soap.SetValue('')
1210 return True
1211
1212 #============================================================
1213 # main
1214 #------------------------------------------------------------
1215 if __name__ == '__main__':
1216
1217 if len(sys.argv) < 2:
1218 sys.exit()
1219
1220 if sys.argv[1] != 'test':
1221 sys.exit()
1222
1223 gmI18N.activate_locale()
1224 gmI18N.install_domain(domain = 'gnumed')
1225
1226 #----------------------------------------
1228 patient = gmPersonSearch.ask_for_patient()
1229 if patient is None:
1230 print("No patient. Exiting gracefully...")
1231 return
1232 set_active_patient(patient=patient)
1233
1234 application = wx.PyWidgetTester(size=(800,500))
1235 soap_input = cSoapPluginPnl(application.frame, -1)
1236 application.frame.Show(True)
1237 soap_input._schedule_data_reget()
1238 application.MainLoop()
1239 #----------------------------------------
1240 #test_cSoapPluginPnl()
1241
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Feb 29 02:55:27 2020 | http://epydoc.sourceforge.net |