| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed narrative handling widgets."""
2 #================================================================
3 __version__ = "$Revision: 1.46 $"
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5
6 import sys, logging, os, os.path, time, re as regex, shutil
7
8
9 import wx
10 import wx.lib.expando as wx_expando
11 import wx.lib.agw.supertooltip as agw_stt
12 import wx.lib.statbmp as wx_genstatbmp
13
14
15 if __name__ == '__main__':
16 sys.path.insert(0, '../../')
17 from Gnumed.pycommon import gmI18N
18 from Gnumed.pycommon import gmDispatcher
19 from Gnumed.pycommon import gmTools
20 from Gnumed.pycommon import gmDateTime
21 from Gnumed.pycommon import gmShellAPI
22 from Gnumed.pycommon import gmPG2
23 from Gnumed.pycommon import gmCfg
24 from Gnumed.pycommon import gmMatchProvider
25
26 from Gnumed.business import gmPerson, gmEMRStructItems, gmClinNarrative, gmSurgery
27 from Gnumed.business import gmForms, gmDocuments, gmPersonSearch
28
29 from Gnumed.wxpython import gmListWidgets
30 from Gnumed.wxpython import gmEMRStructWidgets
31 from Gnumed.wxpython import gmRegetMixin
32 from Gnumed.wxpython import gmPhraseWheel
33 from Gnumed.wxpython import gmGuiHelpers
34 from Gnumed.wxpython import gmPatSearchWidgets
35 from Gnumed.wxpython import gmCfgWidgets
36 from Gnumed.wxpython import gmDocumentWidgets
37
38 from Gnumed.exporters import gmPatientExporter
39
40
41 _log = logging.getLogger('gm.ui')
42 _log.info(__version__)
43 #============================================================
44 # narrative related widgets/functions
45 #------------------------------------------------------------
46 -def move_progress_notes_to_another_encounter(parent=None, encounters=None, episodes=None, patient=None, move_all=False):
47
48 # sanity checks
49 if patient is None:
50 patient = gmPerson.gmCurrentPatient()
51
52 if not patient.connected:
53 gmDispatcher.send(signal = 'statustext', msg = _('Cannot move progress notes. No active patient.'))
54 return False
55
56 if parent is None:
57 parent = wx.GetApp().GetTopWindow()
58
59 emr = patient.get_emr()
60
61 if encounters is None:
62 encs = emr.get_encounters(episodes = episodes)
63 encounters = gmEMRStructWidgets.select_encounters (
64 parent = parent,
65 patient = patient,
66 single_selection = False,
67 encounters = encs
68 )
69
70 notes = emr.get_clin_narrative (
71 encounters = encounters,
72 episodes = episodes
73 )
74
75 # which narrative
76 if move_all:
77 selected_narr = notes
78 else:
79 selected_narr = gmListWidgets.get_choices_from_list (
80 parent = parent,
81 caption = _('Moving progress notes between encounters ...'),
82 single_selection = False,
83 can_return_empty = True,
84 data = notes,
85 msg = _('\n Select the progress notes to move from the list !\n\n'),
86 columns = [_('when'), _('who'), _('type'), _('entry')],
87 choices = [
88 [ narr['date'].strftime('%x %H:%M'),
89 narr['provider'],
90 gmClinNarrative.soap_cat2l10n[narr['soap_cat']],
91 narr['narrative'].replace('\n', '/').replace('\r', '/')
92 ] for narr in notes
93 ]
94 )
95
96 if not selected_narr:
97 return True
98
99 # which encounter to move to
100 enc2move2 = gmEMRStructWidgets.select_encounters (
101 parent = parent,
102 patient = patient,
103 single_selection = True
104 )
105
106 if not enc2move2:
107 return True
108
109 for narr in selected_narr:
110 narr['pk_encounter'] = enc2move2['pk_encounter']
111 narr.save()
112
113 return True
114 #------------------------------------------------------------
116
117 # sanity checks
118 if patient is None:
119 patient = gmPerson.gmCurrentPatient()
120
121 if not patient.connected:
122 gmDispatcher.send(signal = 'statustext', msg = _('Cannot edit progress notes. No active patient.'))
123 return False
124
125 if parent is None:
126 parent = wx.GetApp().GetTopWindow()
127
128 emr = patient.get_emr()
129 #--------------------------
130 def delete(item):
131 if item is None:
132 return False
133 dlg = gmGuiHelpers.c2ButtonQuestionDlg (
134 parent,
135 -1,
136 caption = _('Deleting progress note'),
137 question = _(
138 'Are you positively sure you want to delete this\n'
139 'progress note from the medical record ?\n'
140 '\n'
141 'Note that even if you chose to delete the entry it will\n'
142 'still be (invisibly) kept in the audit trail to protect\n'
143 'you from litigation because physical deletion is known\n'
144 'to be unlawful in some jurisdictions.\n'
145 ),
146 button_defs = (
147 {'label': _('Delete'), 'tooltip': _('Yes, delete the progress note.'), 'default': False},
148 {'label': _('Cancel'), 'tooltip': _('No, do NOT delete the progress note.'), 'default': True}
149 )
150 )
151 decision = dlg.ShowModal()
152
153 if decision != wx.ID_YES:
154 return False
155
156 gmClinNarrative.delete_clin_narrative(narrative = item['pk_narrative'])
157 return True
158 #--------------------------
159 def edit(item):
160 if item is None:
161 return False
162
163 dlg = gmGuiHelpers.cMultilineTextEntryDlg (
164 parent,
165 -1,
166 title = _('Editing progress note'),
167 msg = _('This is the original progress note:'),
168 data = item.format(left_margin = u' ', fancy = True),
169 text = item['narrative']
170 )
171 decision = dlg.ShowModal()
172
173 if decision != wx.ID_SAVE:
174 return False
175
176 val = dlg.value
177 dlg.Destroy()
178 if val.strip() == u'':
179 return False
180
181 item['narrative'] = val
182 item.save_payload()
183
184 return True
185 #--------------------------
186 def refresh(lctrl):
187 notes = emr.get_clin_narrative (
188 encounters = encounters,
189 episodes = episodes,
190 providers = [ gmPerson.gmCurrentProvider()['short_alias'] ]
191 )
192 lctrl.set_string_items(items = [
193 [ narr['date'].strftime('%x %H:%M'),
194 gmClinNarrative.soap_cat2l10n[narr['soap_cat']],
195 narr['narrative'].replace('\n', '/').replace('\r', '/')
196 ] for narr in notes
197 ])
198 lctrl.set_data(data = notes)
199 #--------------------------
200
201 gmListWidgets.get_choices_from_list (
202 parent = parent,
203 caption = _('Managing progress notes'),
204 msg = _(
205 '\n'
206 ' This list shows the progress notes by %s.\n'
207 '\n'
208 ) % gmPerson.gmCurrentProvider()['short_alias'],
209 columns = [_('when'), _('type'), _('entry')],
210 single_selection = True,
211 can_return_empty = False,
212 edit_callback = edit,
213 delete_callback = delete,
214 refresh_callback = refresh,
215 ignore_OK_button = True
216 )
217 #------------------------------------------------------------
219
220 if parent is None:
221 parent = wx.GetApp().GetTopWindow()
222
223 searcher = wx.TextEntryDialog (
224 parent = parent,
225 message = _('Enter (regex) term to search for across all EMRs:'),
226 caption = _('Text search across all EMRs'),
227 style = wx.OK | wx.CANCEL | wx.CENTRE
228 )
229 result = searcher.ShowModal()
230
231 if result != wx.ID_OK:
232 return
233
234 wx.BeginBusyCursor()
235 term = searcher.GetValue()
236 searcher.Destroy()
237 results = gmClinNarrative.search_text_across_emrs(search_term = term)
238 wx.EndBusyCursor()
239
240 if len(results) == 0:
241 gmGuiHelpers.gm_show_info (
242 _(
243 'Nothing found for search term:\n'
244 ' "%s"'
245 ) % term,
246 _('Search results')
247 )
248 return
249
250 items = [ [gmPerson.cIdentity(aPK_obj = r['pk_patient'])['description_gender'], r['narrative'], r['src_table']] for r in results ]
251
252 selected_patient = gmListWidgets.get_choices_from_list (
253 parent = parent,
254 caption = _('Search results for %s') % term,
255 choices = items,
256 columns = [_('Patient'), _('Match'), _('Match location')],
257 data = [ r['pk_patient'] for r in results ],
258 single_selection = True,
259 can_return_empty = False
260 )
261
262 if selected_patient is None:
263 return
264
265 wx.CallAfter(gmPatSearchWidgets.set_active_patient, patient = gmPerson.cIdentity(aPK_obj = selected_patient))
266 #------------------------------------------------------------
268
269 # sanity checks
270 if patient is None:
271 patient = gmPerson.gmCurrentPatient()
272
273 if not patient.connected:
274 gmDispatcher.send(signal = 'statustext', msg = _('Cannot search EMR. No active patient.'))
275 return False
276
277 if parent is None:
278 parent = wx.GetApp().GetTopWindow()
279
280 searcher = wx.TextEntryDialog (
281 parent = parent,
282 message = _('Enter search term:'),
283 caption = _('Text search of entire EMR of active patient'),
284 style = wx.OK | wx.CANCEL | wx.CENTRE
285 )
286 result = searcher.ShowModal()
287
288 if result != wx.ID_OK:
289 searcher.Destroy()
290 return False
291
292 wx.BeginBusyCursor()
293 val = searcher.GetValue()
294 searcher.Destroy()
295 emr = patient.get_emr()
296 rows = emr.search_narrative_simple(val)
297 wx.EndBusyCursor()
298
299 if len(rows) == 0:
300 gmGuiHelpers.gm_show_info (
301 _(
302 'Nothing found for search term:\n'
303 ' "%s"'
304 ) % val,
305 _('Search results')
306 )
307 return True
308
309 txt = u''
310 for row in rows:
311 txt += u'%s: %s\n' % (
312 row['soap_cat'],
313 row['narrative']
314 )
315
316 txt += u' %s: %s - %s %s\n' % (
317 _('Encounter'),
318 row['encounter_started'].strftime('%x %H:%M'),
319 row['encounter_ended'].strftime('%H:%M'),
320 row['encounter_type']
321 )
322 txt += u' %s: %s\n' % (
323 _('Episode'),
324 row['episode']
325 )
326 txt += u' %s: %s\n\n' % (
327 _('Health issue'),
328 row['health_issue']
329 )
330
331 msg = _(
332 'Search term was: "%s"\n'
333 '\n'
334 'Search results:\n\n'
335 '%s\n'
336 ) % (val, txt)
337
338 dlg = wx.MessageDialog (
339 parent = parent,
340 message = msg,
341 caption = _('Search results for %s') % val,
342 style = wx.OK | wx.STAY_ON_TOP
343 )
344 dlg.ShowModal()
345 dlg.Destroy()
346
347 return True
348 #------------------------------------------------------------
350
351 # sanity checks
352 pat = gmPerson.gmCurrentPatient()
353 if not pat.connected:
354 gmDispatcher.send(signal = 'statustext', msg = _('Cannot export EMR for Medistar. No active patient.'))
355 return False
356
357 if encounter is None:
358 encounter = pat.get_emr().active_encounter
359
360 if parent is None:
361 parent = wx.GetApp().GetTopWindow()
362
363 # get file name
364 aWildcard = "%s (*.txt)|*.txt|%s (*)|*" % (_("text files"), _("all files"))
365 # FIXME: make configurable
366 aDefDir = os.path.abspath(os.path.expanduser(os.path.join('~', 'gnumed','export')))
367 # FIXME: make configurable
368 fname = '%s-%s-%s-%s-%s.txt' % (
369 'Medistar-MD',
370 time.strftime('%Y-%m-%d',time.localtime()),
371 pat['lastnames'].replace(' ', '-'),
372 pat['firstnames'].replace(' ', '_'),
373 pat.get_formatted_dob(format = '%Y-%m-%d')
374 )
375 dlg = wx.FileDialog (
376 parent = parent,
377 message = _("Save EMR extract for MEDISTAR import as..."),
378 defaultDir = aDefDir,
379 defaultFile = fname,
380 wildcard = aWildcard,
381 style = wx.SAVE
382 )
383 choice = dlg.ShowModal()
384 fname = dlg.GetPath()
385 dlg.Destroy()
386 if choice != wx.ID_OK:
387 return False
388
389 wx.BeginBusyCursor()
390 _log.debug('exporting encounter for medistar import to [%s]', fname)
391 exporter = gmPatientExporter.cMedistarSOAPExporter()
392 successful, fname = exporter.export_to_file (
393 filename = fname,
394 encounter = encounter,
395 soap_cats = u'soap',
396 export_to_import_file = True
397 )
398 if not successful:
399 gmGuiHelpers.gm_show_error (
400 _('Error exporting progress notes for MEDISTAR import.'),
401 _('MEDISTAR progress notes export')
402 )
403 wx.EndBusyCursor()
404 return False
405
406 gmDispatcher.send(signal = 'statustext', msg = _('Successfully exported progress notes into file [%s] for Medistar import.') % fname, beep=False)
407
408 wx.EndBusyCursor()
409 return True
410 #------------------------------------------------------------
412 """soap_cats needs to be a list"""
413
414 if parent is None:
415 parent = wx.GetApp().GetTopWindow()
416
417 pat = gmPerson.gmCurrentPatient()
418 emr = pat.get_emr()
419
420 selected_soap = {}
421 selected_narrative_pks = []
422
423 #-----------------------------------------------
424 def pick_soap_from_episode(episode):
425
426 narr_for_epi = emr.get_clin_narrative(episodes = [episode['pk_episode']], soap_cats = soap_cats)
427
428 if len(narr_for_epi) == 0:
429 gmDispatcher.send(signal = 'statustext', msg = _('No narrative available for selected episode.'))
430 return True
431
432 dlg = cNarrativeListSelectorDlg (
433 parent = parent,
434 id = -1,
435 narrative = narr_for_epi,
436 msg = _(
437 '\n This is the narrative (type %s) for the chosen episodes.\n'
438 '\n'
439 ' Now, mark the entries you want to include in your report.\n'
440 ) % u'/'.join([ gmClinNarrative.soap_cat2l10n[cat] for cat in gmTools.coalesce(soap_cats, list(u'soap')) ])
441 )
442 # selection_idxs = []
443 # for idx in range(len(narr_for_epi)):
444 # if narr_for_epi[idx]['pk_narrative'] in selected_narrative_pks:
445 # selection_idxs.append(idx)
446 # if len(selection_idxs) != 0:
447 # dlg.set_selections(selections = selection_idxs)
448 btn_pressed = dlg.ShowModal()
449 selected_narr = dlg.get_selected_item_data()
450 dlg.Destroy()
451
452 if btn_pressed == wx.ID_CANCEL:
453 return True
454
455 selected_narrative_pks = [ i['pk_narrative'] for i in selected_narr ]
456 for narr in selected_narr:
457 selected_soap[narr['pk_narrative']] = narr
458
459 print "before returning from picking soap"
460
461 return True
462 #-----------------------------------------------
463 selected_episode_pks = []
464
465 all_epis = [ epi for epi in emr.get_episodes() if epi.has_narrative ]
466
467 if len(all_epis) == 0:
468 gmDispatcher.send(signal = 'statustext', msg = _('No episodes recorded for the health issues selected.'))
469 return []
470
471 dlg = gmEMRStructWidgets.cEpisodeListSelectorDlg (
472 parent = parent,
473 id = -1,
474 episodes = all_epis,
475 msg = _('\n Select the the episode you want to report on.\n')
476 )
477 # selection_idxs = []
478 # for idx in range(len(all_epis)):
479 # if all_epis[idx]['pk_episode'] in selected_episode_pks:
480 # selection_idxs.append(idx)
481 # if len(selection_idxs) != 0:
482 # dlg.set_selections(selections = selection_idxs)
483 dlg.left_extra_button = (
484 _('Pick SOAP'),
485 _('Pick SOAP entries from topmost selected episode'),
486 pick_soap_from_episode
487 )
488 btn_pressed = dlg.ShowModal()
489 dlg.Destroy()
490
491 if btn_pressed == wx.ID_CANCEL:
492 return None
493
494 return selected_soap.values()
495 #------------------------------------------------------------
497 """soap_cats needs to be a list"""
498
499 pat = gmPerson.gmCurrentPatient()
500 emr = pat.get_emr()
501
502 if parent is None:
503 parent = wx.GetApp().GetTopWindow()
504
505 selected_soap = {}
506 selected_issue_pks = []
507 selected_episode_pks = []
508 selected_narrative_pks = []
509
510 while 1:
511 # 1) select health issues to select episodes from
512 all_issues = emr.get_health_issues()
513 all_issues.insert(0, gmEMRStructItems.get_dummy_health_issue())
514 dlg = gmEMRStructWidgets.cIssueListSelectorDlg (
515 parent = parent,
516 id = -1,
517 issues = all_issues,
518 msg = _('\n In the list below mark the health issues you want to report on.\n')
519 )
520 selection_idxs = []
521 for idx in range(len(all_issues)):
522 if all_issues[idx]['pk_health_issue'] in selected_issue_pks:
523 selection_idxs.append(idx)
524 if len(selection_idxs) != 0:
525 dlg.set_selections(selections = selection_idxs)
526 btn_pressed = dlg.ShowModal()
527 selected_issues = dlg.get_selected_item_data()
528 dlg.Destroy()
529
530 if btn_pressed == wx.ID_CANCEL:
531 return selected_soap.values()
532
533 selected_issue_pks = [ i['pk_health_issue'] for i in selected_issues ]
534
535 while 1:
536 # 2) select episodes to select items from
537 all_epis = emr.get_episodes(issues = selected_issue_pks)
538
539 if len(all_epis) == 0:
540 gmDispatcher.send(signal = 'statustext', msg = _('No episodes recorded for the health issues selected.'))
541 break
542
543 dlg = gmEMRStructWidgets.cEpisodeListSelectorDlg (
544 parent = parent,
545 id = -1,
546 episodes = all_epis,
547 msg = _(
548 '\n These are the episodes known for the health issues just selected.\n\n'
549 ' Now, mark the the episodes you want to report on.\n'
550 )
551 )
552 selection_idxs = []
553 for idx in range(len(all_epis)):
554 if all_epis[idx]['pk_episode'] in selected_episode_pks:
555 selection_idxs.append(idx)
556 if len(selection_idxs) != 0:
557 dlg.set_selections(selections = selection_idxs)
558 btn_pressed = dlg.ShowModal()
559 selected_epis = dlg.get_selected_item_data()
560 dlg.Destroy()
561
562 if btn_pressed == wx.ID_CANCEL:
563 break
564
565 selected_episode_pks = [ i['pk_episode'] for i in selected_epis ]
566
567 # 3) select narrative corresponding to the above constraints
568 all_narr = emr.get_clin_narrative(episodes = selected_episode_pks, soap_cats = soap_cats)
569
570 if len(all_narr) == 0:
571 gmDispatcher.send(signal = 'statustext', msg = _('No narrative available for selected episodes.'))
572 continue
573
574 dlg = cNarrativeListSelectorDlg (
575 parent = parent,
576 id = -1,
577 narrative = all_narr,
578 msg = _(
579 '\n This is the narrative (type %s) for the chosen episodes.\n\n'
580 ' Now, mark the entries you want to include in your report.\n'
581 ) % u'/'.join([ gmClinNarrative.soap_cat2l10n[cat] for cat in gmTools.coalesce(soap_cats, list(u'soap')) ])
582 )
583 selection_idxs = []
584 for idx in range(len(all_narr)):
585 if all_narr[idx]['pk_narrative'] in selected_narrative_pks:
586 selection_idxs.append(idx)
587 if len(selection_idxs) != 0:
588 dlg.set_selections(selections = selection_idxs)
589 btn_pressed = dlg.ShowModal()
590 selected_narr = dlg.get_selected_item_data()
591 dlg.Destroy()
592
593 if btn_pressed == wx.ID_CANCEL:
594 continue
595
596 selected_narrative_pks = [ i['pk_narrative'] for i in selected_narr ]
597 for narr in selected_narr:
598 selected_soap[narr['pk_narrative']] = narr
599 #------------------------------------------------------------
601
603
604 narrative = kwargs['narrative']
605 del kwargs['narrative']
606
607 gmListWidgets.cGenericListSelectorDlg.__init__(self, *args, **kwargs)
608
609 self.SetTitle(_('Select the narrative you are interested in ...'))
610 # FIXME: add epi/issue
611 self._LCTRL_items.set_columns([_('when'), _('who'), _('type'), _('entry')]) #, _('Episode'), u'', _('Health Issue')])
612 # FIXME: date used should be date of encounter, not date_modified
613 self._LCTRL_items.set_string_items (
614 items = [ [narr['date'].strftime('%x %H:%M'), narr['provider'], gmClinNarrative.soap_cat2l10n[narr['soap_cat']], narr['narrative'].replace('\n', '/').replace('\r', '/')] for narr in narrative ]
615 )
616 self._LCTRL_items.set_column_widths()
617 self._LCTRL_items.set_data(data = narrative)
618 #------------------------------------------------------------
619 from Gnumed.wxGladeWidgets import wxgMoveNarrativeDlg
620
622
624
625 self.encounter = kwargs['encounter']
626 self.source_episode = kwargs['episode']
627 del kwargs['encounter']
628 del kwargs['episode']
629
630 wxgMoveNarrativeDlg.wxgMoveNarrativeDlg.__init__(self, *args, **kwargs)
631
632 self.LBL_source_episode.SetLabel(u'%s%s' % (self.source_episode['description'], gmTools.coalesce(self.source_episode['health_issue'], u'', u' (%s)')))
633 self.LBL_encounter.SetLabel('%s: %s %s - %s' % (
634 self.encounter['started'].strftime('%x').decode(gmI18N.get_encoding()),
635 self.encounter['l10n_type'],
636 self.encounter['started'].strftime('%H:%M'),
637 self.encounter['last_affirmed'].strftime('%H:%M')
638 ))
639 pat = gmPerson.gmCurrentPatient()
640 emr = pat.get_emr()
641 narr = emr.get_clin_narrative(episodes=[self.source_episode['pk_episode']], encounters=[self.encounter['pk_encounter']])
642 if len(narr) == 0:
643 narr = [{'narrative': _('There is no narrative for this episode in this encounter.')}]
644 self.LBL_narrative.SetLabel(u'\n'.join([n['narrative'] for n in narr]))
645
646 #------------------------------------------------------------
668 #============================================================
669 from Gnumed.wxGladeWidgets import wxgSoapPluginPnl
670
672 """A panel for in-context editing of progress notes.
673
674 Expects to be used as a notebook page.
675
676 Left hand side:
677 - problem list (health issues and active episodes)
678 - previous notes
679
680 Right hand side:
681 - encounter details fields
682 - notebook with progress note editors
683 - visual progress notes
684 - hints
685
686 Listens to patient change signals, thus acts on the current patient.
687 """
689
690 wxgSoapPluginPnl.wxgSoapPluginPnl.__init__(self, *args, **kwargs)
691 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
692
693 self.__pat = gmPerson.gmCurrentPatient()
694 self.__patient_just_changed = False
695 self.__init_ui()
696 self.__reset_ui_content()
697
698 self.__register_interests()
699 #--------------------------------------------------------
700 # public API
701 #--------------------------------------------------------
703
704 if not self.__encounter_valid_for_save():
705 return False
706
707 emr = self.__pat.get_emr()
708 enc = emr.active_encounter
709
710 enc['pk_type'] = self._PRW_encounter_type.GetData()
711 enc['started'] = self._PRW_encounter_start.GetData().get_pydt()
712 enc['last_affirmed'] = self._PRW_encounter_end.GetData().get_pydt()
713 rfe = self._TCTRL_rfe.GetValue().strip()
714 if len(rfe) == 0:
715 enc['reason_for_encounter'] = None
716 else:
717 enc['reason_for_encounter'] = rfe
718 aoe = self._TCTRL_aoe.GetValue().strip()
719 if len(aoe) == 0:
720 enc['assessment_of_encounter'] = None
721 else:
722 enc['assessment_of_encounter'] = aoe
723
724 enc.save_payload()
725
726 return True
727 #--------------------------------------------------------
728 # internal helpers
729 #--------------------------------------------------------
731 self._LCTRL_active_problems.set_columns([_('Last'), _('Problem'), _('Health issue')])
732 self._LCTRL_active_problems.set_string_items()
733
734 self._splitter_main.SetSashGravity(0.5)
735 self._splitter_left.SetSashGravity(0.5)
736 self._splitter_right.SetSashGravity(1.0)
737 # self._splitter_soap.SetSashGravity(0.75)
738
739 splitter_size = self._splitter_main.GetSizeTuple()[0]
740 self._splitter_main.SetSashPosition(splitter_size * 3 / 10, True)
741
742 splitter_size = self._splitter_left.GetSizeTuple()[1]
743 self._splitter_left.SetSashPosition(splitter_size * 6 / 20, True)
744
745 splitter_size = self._splitter_right.GetSizeTuple()[1]
746 self._splitter_right.SetSashPosition(splitter_size * 15 / 20, True)
747
748 # splitter_size = self._splitter_soap.GetSizeTuple()[0]
749 # self._splitter_soap.SetSashPosition(splitter_size * 3 / 4, True)
750
751 self._NB_soap_editors.DeleteAllPages()
752 #--------------------------------------------------------
754 """Clear all information from input panel."""
755
756 self._LCTRL_active_problems.set_string_items()
757
758 self._TCTRL_recent_notes.SetValue(u'')
759
760 self._PRW_encounter_type.SetText(suppress_smarts = True)
761 self._PRW_encounter_start.SetText(suppress_smarts = True)
762 self._PRW_encounter_end.SetText(suppress_smarts = True)
763 self._TCTRL_rfe.SetValue(u'')
764 self._TCTRL_aoe.SetValue(u'')
765
766 self._NB_soap_editors.DeleteAllPages()
767 self._NB_soap_editors.add_editor()
768
769 self._lbl_hints.SetLabel(u'')
770 #--------------------------------------------------------
772 """Update health problems list."""
773
774 self._LCTRL_active_problems.set_string_items()
775
776 emr = self.__pat.get_emr()
777 problems = emr.get_problems (
778 include_closed_episodes = self._CHBOX_show_closed_episodes.IsChecked(),
779 include_irrelevant_issues = self._CHBOX_irrelevant_issues.IsChecked()
780 )
781
782 list_items = []
783 active_problems = []
784 for problem in problems:
785 if not problem['problem_active']:
786 if not problem['is_potential_problem']:
787 continue
788
789 active_problems.append(problem)
790
791 if problem['type'] == 'issue':
792 issue = emr.problem2issue(problem)
793 last_encounter = emr.get_last_encounter(issue_id = issue['pk_health_issue'])
794 if last_encounter is None:
795 last = issue['modified_when'].strftime('%m/%Y')
796 else:
797 last = last_encounter['last_affirmed'].strftime('%m/%Y')
798
799 list_items.append([last, problem['problem'], gmTools.u_left_arrow])
800
801 elif problem['type'] == 'episode':
802 epi = emr.problem2episode(problem)
803 last_encounter = emr.get_last_encounter(episode_id = epi['pk_episode'])
804 if last_encounter is None:
805 last = epi['episode_modified_when'].strftime('%m/%Y')
806 else:
807 last = last_encounter['last_affirmed'].strftime('%m/%Y')
808
809 list_items.append ([
810 last,
811 problem['problem'],
812 gmTools.coalesce(initial = epi['health_issue'], instead = gmTools.u_diameter)
813 ])
814
815 self._LCTRL_active_problems.set_string_items(items = list_items)
816 self._LCTRL_active_problems.set_column_widths()
817 self._LCTRL_active_problems.set_data(data = active_problems)
818
819 showing_potential_problems = (
820 self._CHBOX_show_closed_episodes.IsChecked()
821 or
822 self._CHBOX_irrelevant_issues.IsChecked()
823 )
824 if showing_potential_problems:
825 self._SZR_problem_list_staticbox.SetLabel(_('%s (active+potential) problems') % len(list_items))
826 else:
827 self._SZR_problem_list_staticbox.SetLabel(_('%s active problems') % len(list_items))
828
829 return True
830 #--------------------------------------------------------
832 soap = u''
833 emr = self.__pat.get_emr()
834 prev_enc = emr.get_last_but_one_encounter(issue_id = problem['pk_health_issue'])
835 if prev_enc is not None:
836 soap += prev_enc.format (
837 issues = [ problem['pk_health_issue'] ],
838 with_soap = True,
839 with_docs = False,
840 with_tests = False,
841 patient = self.__pat,
842 fancy_header = False,
843 with_rfe_aoe = True
844 )
845
846 tmp = emr.active_encounter.format_soap (
847 soap_cats = 'soap',
848 emr = emr,
849 issues = [ problem['pk_health_issue'] ],
850 )
851 if len(tmp) > 0:
852 soap += _('Current encounter:') + u'\n'
853 soap += u'\n'.join(tmp) + u'\n'
854
855 if problem['summary'] is not None:
856 soap += u'\n-- %s ----------\n%s' % (
857 _('Cumulative summary'),
858 gmTools.wrap (
859 text = problem['summary'],
860 width = 45,
861 initial_indent = u' ',
862 subsequent_indent = u' '
863 ).strip('\n')
864 )
865
866 return soap
867 #--------------------------------------------------------
869 soap = u''
870 emr = self.__pat.get_emr()
871 prev_enc = emr.get_last_but_one_encounter(episode_id = problem['pk_episode'])
872 if prev_enc is not None:
873 soap += prev_enc.format (
874 episodes = [ problem['pk_episode'] ],
875 with_soap = True,
876 with_docs = False,
877 with_tests = False,
878 patient = self.__pat,
879 fancy_header = False,
880 with_rfe_aoe = True
881 )
882 else:
883 if problem['pk_health_issue'] is not None:
884 prev_enc = emr.get_last_but_one_encounter(episode_id = problem['pk_health_issue'])
885 if prev_enc is not None:
886 soap += prev_enc.format (
887 with_soap = True,
888 with_docs = False,
889 with_tests = False,
890 patient = self.__pat,
891 issues = [ problem['pk_health_issue'] ],
892 fancy_header = False,
893 with_rfe_aoe = True
894 )
895
896 tmp = emr.active_encounter.format_soap (
897 soap_cats = 'soap',
898 emr = emr,
899 issues = [ problem['pk_health_issue'] ],
900 )
901 if len(tmp) > 0:
902 soap += _('Current encounter:') + u'\n'
903 soap += u'\n'.join(tmp) + u'\n'
904
905 if problem['summary'] is not None:
906 soap += u'\n-- %s ----------\n%s' % (
907 _('Cumulative summary'),
908 gmTools.wrap (
909 text = problem['summary'],
910 width = 45,
911 initial_indent = u' ',
912 subsequent_indent = u' '
913 ).strip('\n')
914 )
915
916 return soap
917 #--------------------------------------------------------
919 self._NB_soap_editors.refresh_current_editor()
920 #--------------------------------------------------------
922 if not self.__patient_just_changed:
923 return
924
925 dbcfg = gmCfg.cCfgSQL()
926 auto_open_recent_problems = bool(dbcfg.get2 (
927 option = u'horstspace.soap_editor.auto_open_latest_episodes',
928 workplace = gmSurgery.gmCurrentPractice().active_workplace,
929 bias = u'user',
930 default = True
931 ))
932
933 self.__patient_just_changed = False
934 emr = self.__pat.get_emr()
935 recent_epis = emr.active_encounter.get_episodes()
936 prev_enc = emr.get_last_but_one_encounter()
937 if prev_enc is not None:
938 recent_epis.extend(prev_enc.get_episodes())
939
940 for epi in recent_epis:
941 if not epi['episode_open']:
942 continue
943 self._NB_soap_editors.add_editor(problem = epi, allow_same_problem = False)
944 #--------------------------------------------------------
946 """This refreshes the recent-notes part."""
947
948 soap = u''
949 caption = u'<?>'
950
951 if problem['type'] == u'issue':
952 caption = problem['problem'][:35]
953 soap = self.__get_soap_for_issue_problem(problem = problem)
954
955 elif problem['type'] == u'episode':
956 caption = problem['problem'][:35]
957 soap = self.__get_soap_for_episode_problem(problem = problem)
958
959 self._TCTRL_recent_notes.SetValue(soap)
960 self._TCTRL_recent_notes.ShowPosition(self._TCTRL_recent_notes.GetLastPosition())
961 self._SZR_recent_notes_staticbox.SetLabel(_('Most recent notes on %s%s%s') % (
962 gmTools.u_left_double_angle_quote,
963 caption,
964 gmTools.u_right_double_angle_quote
965 ))
966
967 self._TCTRL_recent_notes.Refresh()
968
969 return True
970 #--------------------------------------------------------
972 """Update encounter fields."""
973
974 emr = self.__pat.get_emr()
975 enc = emr.active_encounter
976 self._PRW_encounter_type.SetText(value = enc['l10n_type'], data = enc['pk_type'])
977
978 fts = gmDateTime.cFuzzyTimestamp (
979 timestamp = enc['started'],
980 accuracy = gmDateTime.acc_minutes
981 )
982 self._PRW_encounter_start.SetText(fts.format_accurately(), data=fts)
983
984 fts = gmDateTime.cFuzzyTimestamp (
985 timestamp = enc['last_affirmed'],
986 accuracy = gmDateTime.acc_minutes
987 )
988 self._PRW_encounter_end.SetText(fts.format_accurately(), data=fts)
989
990 self._TCTRL_rfe.SetValue(gmTools.coalesce(enc['reason_for_encounter'], u''))
991 self._TCTRL_aoe.SetValue(gmTools.coalesce(enc['assessment_of_encounter'], u''))
992
993 self._PRW_encounter_type.Refresh()
994 self._PRW_encounter_start.Refresh()
995 self._PRW_encounter_end.Refresh()
996 self._TCTRL_rfe.Refresh()
997 self._TCTRL_aoe.Refresh()
998 #--------------------------------------------------------
1000 """Assumes that the field data is valid."""
1001
1002 emr = self.__pat.get_emr()
1003 enc = emr.active_encounter
1004
1005 data = {
1006 'pk_type': self._PRW_encounter_type.GetData(),
1007 'reason_for_encounter': gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u''),
1008 'assessment_of_encounter': gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u''),
1009 'pk_location': enc['pk_location'],
1010 'pk_patient': enc['pk_patient']
1011 }
1012
1013 if self._PRW_encounter_start.GetData() is None:
1014 data['started'] = None
1015 else:
1016 data['started'] = self._PRW_encounter_start.GetData().get_pydt()
1017
1018 if self._PRW_encounter_end.GetData() is None:
1019 data['last_affirmed'] = None
1020 else:
1021 data['last_affirmed'] = self._PRW_encounter_end.GetData().get_pydt()
1022
1023 return not enc.same_payload(another_object = data)
1024 #--------------------------------------------------------
1026
1027 found_error = False
1028
1029 if self._PRW_encounter_type.GetData() is None:
1030 found_error = True
1031 msg = _('Cannot save encounter: missing type.')
1032
1033 if self._PRW_encounter_start.GetData() is None:
1034 found_error = True
1035 msg = _('Cannot save encounter: missing start time.')
1036
1037 if self._PRW_encounter_end.GetData() is None:
1038 found_error = True
1039 msg = _('Cannot save encounter: missing end time.')
1040
1041 if found_error:
1042 gmDispatcher.send(signal = 'statustext', msg = msg, beep = True)
1043 return False
1044
1045 return True
1046 #--------------------------------------------------------
1047 # event handling
1048 #--------------------------------------------------------
1050 """Configure enabled event signals."""
1051 # client internal signals
1052 gmDispatcher.connect(signal = u'pre_patient_selection', receiver = self._on_pre_patient_selection)
1053 gmDispatcher.connect(signal = u'post_patient_selection', receiver = self._on_post_patient_selection)
1054 gmDispatcher.connect(signal = u'episode_mod_db', receiver = self._on_episode_issue_mod_db)
1055 gmDispatcher.connect(signal = u'health_issue_mod_db', receiver = self._on_episode_issue_mod_db)
1056 gmDispatcher.connect(signal = u'doc_mod_db', receiver = self._on_doc_mod_db)
1057 gmDispatcher.connect(signal = u'current_encounter_modified', receiver = self._on_current_encounter_modified)
1058 gmDispatcher.connect(signal = u'current_encounter_switched', receiver = self._on_current_encounter_switched)
1059
1060 # synchronous signals
1061 self.__pat.register_pre_selection_callback(callback = self._pre_selection_callback)
1062 gmDispatcher.send(signal = u'register_pre_exit_callback', callback = self._pre_exit_callback)
1063 #--------------------------------------------------------
1065 """Another patient is about to be activated.
1066
1067 Patient change will not proceed before this returns True.
1068 """
1069 # don't worry about the encounter here - it will be offered
1070 # for editing higher up if anything was saved to the EMR
1071 if not self.__pat.connected:
1072 return True
1073 return self._NB_soap_editors.warn_on_unsaved_soap()
1074 #--------------------------------------------------------
1076 """The client is about to be shut down.
1077
1078 Shutdown will not proceed before this returns.
1079 """
1080 if not self.__pat.connected:
1081 return True
1082
1083 # if self.__encounter_modified():
1084 # do_save_enc = gmGuiHelpers.gm_show_question (
1085 # aMessage = _(
1086 # 'You have modified the details\n'
1087 # 'of the current encounter.\n'
1088 # '\n'
1089 # 'Do you want to save those changes ?'
1090 # ),
1091 # aTitle = _('Starting new encounter')
1092 # )
1093 # if do_save_enc:
1094 # if not self.save_encounter():
1095 # gmDispatcher.send(signal = u'statustext', msg = _('Error saving current encounter.'), beep = True)
1096
1097 emr = self.__pat.get_emr()
1098 saved = self._NB_soap_editors.save_all_editors (
1099 emr = emr,
1100 episode_name_candidates = [
1101 gmTools.none_if(self._TCTRL_aoe.GetValue().strip(), u''),
1102 gmTools.none_if(self._TCTRL_rfe.GetValue().strip(), u'')
1103 ]
1104 )
1105 if not saved:
1106 gmDispatcher.send(signal = 'statustext', msg = _('Cannot save all editors. Some were kept open.'), beep = True)
1107 return True
1108 #--------------------------------------------------------
1111 #--------------------------------------------------------
1114 #--------------------------------------------------------
1118 #--------------------------------------------------------
1121 #--------------------------------------------------------
1124 #--------------------------------------------------------
1127 #--------------------------------------------------------
1130 #--------------------------------------------------------
1133 #--------------------------------------------------------
1134 # problem list specific events
1135 #--------------------------------------------------------
1139 #--------------------------------------------------------
1141 """Show related note at the bottom."""
1142 emr = self.__pat.get_emr()
1143 self.__refresh_recent_notes (
1144 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
1145 )
1146 #--------------------------------------------------------
1148 """Open progress note editor for this problem.
1149 """
1150 problem = self._LCTRL_active_problems.get_selected_item_data(only_one = True)
1151 if problem is None:
1152 return True
1153
1154 dbcfg = gmCfg.cCfgSQL()
1155 allow_duplicate_editors = bool(dbcfg.get2 (
1156 option = u'horstspace.soap_editor.allow_same_episode_multiple_times',
1157 workplace = gmSurgery.gmCurrentPractice().active_workplace,
1158 bias = u'user',
1159 default = False
1160 ))
1161 if self._NB_soap_editors.add_editor(problem = problem, allow_same_problem = allow_duplicate_editors):
1162 return True
1163
1164 gmGuiHelpers.gm_show_error (
1165 aMessage = _(
1166 'Cannot open progress note editor for\n\n'
1167 '[%s].\n\n'
1168 ) % problem['problem'],
1169 aTitle = _('opening progress note editor')
1170 )
1171 event.Skip()
1172 return False
1173 #--------------------------------------------------------
1176 #--------------------------------------------------------
1179 #--------------------------------------------------------
1180 # SOAP editor specific buttons
1181 #--------------------------------------------------------
1185 #--------------------------------------------------------
1189 #--------------------------------------------------------
1193 #--------------------------------------------------------
1204 #--------------------------------------------------------
1224 #--------------------------------------------------------
1229 #--------------------------------------------------------
1230 # encounter specific buttons
1231 #--------------------------------------------------------
1235 #--------------------------------------------------------
1259 #--------------------------------------------------------
1260 # other buttons
1261 #--------------------------------------------------------
1270 #--------------------------------------------------------
1282 #--------------------------------------------------------
1283 # reget mixin API
1284 #--------------------------------------------------------
1290 #============================================================
1292 """A notebook holding panels with progress note editors.
1293
1294 There can be one or several progress note editor panel
1295 for each episode being worked on. The editor class in
1296 each panel is configurable.
1297
1298 There will always be one open editor.
1299 """
1301
1302 kwargs['style'] = wx.NB_TOP | wx.NB_MULTILINE | wx.NO_BORDER
1303
1304 wx.Notebook.__init__(self, *args, **kwargs)
1305 #--------------------------------------------------------
1306 # public API
1307 #--------------------------------------------------------
1309 """Add a progress note editor page.
1310
1311 The way <allow_same_problem> is currently used in callers
1312 it only applies to unassociated episodes.
1313 """
1314 problem_to_add = problem
1315
1316 # determine label
1317 if problem_to_add is None:
1318 label = _('new problem')
1319 else:
1320 # normalize problem type
1321 if isinstance(problem_to_add, gmEMRStructItems.cEpisode):
1322 problem_to_add = gmEMRStructItems.episode2problem(episode = problem_to_add)
1323
1324 elif isinstance(problem_to_add, gmEMRStructItems.cHealthIssue):
1325 problem_to_add = gmEMRStructItems.health_issue2problem(episode = problem_to_add)
1326
1327 if not isinstance(problem_to_add, gmEMRStructItems.cProblem):
1328 raise TypeError('cannot open progress note editor for [%s]' % problem_to_add)
1329
1330 label = problem_to_add['problem']
1331 # FIXME: configure maximum length
1332 if len(label) > 23:
1333 label = label[:21] + gmTools.u_ellipsis
1334
1335 # new unassociated problem or dupes allowed
1336 if (problem_to_add is None) or allow_same_problem:
1337 new_page = cSoapNoteExpandoEditAreaPnl(parent = self, id = -1, problem = problem_to_add)
1338 result = self.AddPage (
1339 page = new_page,
1340 text = label,
1341 select = True
1342 )
1343 return result
1344
1345 # real problem, no dupes allowed
1346 # - raise existing editor
1347 for page_idx in range(self.GetPageCount()):
1348 page = self.GetPage(page_idx)
1349
1350 # editor is for unassociated new problem
1351 if page.problem is None:
1352 continue
1353
1354 # editor is for episode
1355 if page.problem['type'] == 'episode':
1356 if page.problem['pk_episode'] == problem_to_add['pk_episode']:
1357 self.SetSelection(page_idx)
1358 gmDispatcher.send(signal = u'statustext', msg = u'Raising existing editor.', beep = True)
1359 return True
1360 continue
1361
1362 # editor is for health issue
1363 if page.problem['type'] == 'issue':
1364 if page.problem['pk_health_issue'] == problem_to_add['pk_health_issue']:
1365 self.SetSelection(page_idx)
1366 gmDispatcher.send(signal = u'statustext', msg = u'Raising existing editor.', beep = True)
1367 return True
1368 continue
1369
1370 # - or add new editor
1371 new_page = cSoapNoteExpandoEditAreaPnl(parent = self, id = -1, problem = problem_to_add)
1372 result = self.AddPage (
1373 page = new_page,
1374 text = label,
1375 select = True
1376 )
1377
1378 return result
1379 #--------------------------------------------------------
1381
1382 page_idx = self.GetSelection()
1383 page = self.GetPage(page_idx)
1384
1385 if not page.empty:
1386 really_discard = gmGuiHelpers.gm_show_question (
1387 _('Are you sure you really want to\n'
1388 'discard this progress note ?\n'
1389 ),
1390 _('Discarding progress note')
1391 )
1392 if really_discard is False:
1393 return
1394
1395 self.DeletePage(page_idx)
1396
1397 # always keep one unassociated editor open
1398 if self.GetPageCount() == 0:
1399 self.add_editor()
1400 #--------------------------------------------------------
1402
1403 page_idx = self.GetSelection()
1404 page = self.GetPage(page_idx)
1405
1406 if not page.save(emr = emr, episode_name_candidates = episode_name_candidates, encounter = encounter):
1407 return
1408
1409 self.DeletePage(page_idx)
1410
1411 # always keep one unassociated editor open
1412 if self.GetPageCount() == 0:
1413 self.add_editor()
1414 #--------------------------------------------------------
1416 for page_idx in range(self.GetPageCount()):
1417 page = self.GetPage(page_idx)
1418 if page.empty:
1419 continue
1420
1421 gmGuiHelpers.gm_show_warning (
1422 _('There are unsaved progress notes !\n'),
1423 _('Unsaved progress notes')
1424 )
1425 return False
1426
1427 return True
1428 #--------------------------------------------------------
1430
1431 _log.debug('saving editors: %s', self.GetPageCount())
1432
1433 all_closed = True
1434 for page_idx in range((self.GetPageCount() - 1), 0, -1):
1435 _log.debug('#%s of %s', page_idx, self.GetPageCount())
1436 try:
1437 self.ChangeSelection(page_idx)
1438 _log.debug('editor raised')
1439 except:
1440 _log.exception('cannot raise editor')
1441 page = self.GetPage(page_idx)
1442 if page.save(emr = emr, episode_name_candidates = episode_name_candidates):
1443 _log.debug('saved, deleting now')
1444 self.DeletePage(page_idx)
1445 else:
1446 _log.debug('not saved, not deleting')
1447 all_closed = False
1448
1449 # always keep one unassociated editor open
1450 if self.GetPageCount() == 0:
1451 self.add_editor()
1452
1453 return (all_closed is True)
1454 #--------------------------------------------------------
1459 #--------------------------------------------------------
1464 #--------------------------------------------------------
1469 #--------------------------------------------------------
1471 page_idx = self.GetSelection()
1472 page = self.GetPage(page_idx)
1473 page.add_visual_progress_note()
1474 #============================================================
1475 from Gnumed.wxGladeWidgets import wxgSoapNoteExpandoEditAreaPnl
1476
1477 -class cSoapNoteExpandoEditAreaPnl(wxgSoapNoteExpandoEditAreaPnl.wxgSoapNoteExpandoEditAreaPnl):
1478 """An Edit Area like panel for entering progress notes.
1479
1480 Subjective:
1481 expando text ctrl
1482 Objective:
1483 expando text ctrl
1484 Assessment:
1485 expando text ctrl
1486 Plan:
1487 expando text ctrl
1488 Problem summary:
1489 text ctrl
1490 visual progress notes
1491 panel with images
1492
1493 - knows the problem this edit area is about
1494 - can deal with issue or episode type problems
1495 """
1496
1498
1499 try:
1500 self.problem = kwargs['problem']
1501 del kwargs['problem']
1502 except KeyError:
1503 self.problem = None
1504
1505 wxgSoapNoteExpandoEditAreaPnl.wxgSoapNoteExpandoEditAreaPnl.__init__(self, *args, **kwargs)
1506
1507 self.fields = [
1508 self._TCTRL_Soap,
1509 self._TCTRL_sOap,
1510 self._TCTRL_soAp,
1511 self._TCTRL_soaP
1512 ]
1513
1514 self.__init_ui()
1515 self.__register_interests()
1516 #--------------------------------------------------------
1518 self.refresh_summary()
1519 if self.problem is not None:
1520 if self.problem['summary'] is None:
1521 self._TCTRL_summary.SetValue(u'')
1522 self.refresh_visual_soap()
1523 #--------------------------------------------------------
1527 #--------------------------------------------------------
1529 if self.problem is None:
1530 return
1531 if self.problem['summary'] is None:
1532 self._TCTRL_summary.SetValue(u'')
1533 else:
1534 self._TCTRL_summary.SetValue(self.problem['summary'])
1535 #--------------------------------------------------------
1537 if self.problem is None:
1538 self._PNL_visual_soap.refresh(document_folder = None)
1539 return
1540
1541 if self.problem['type'] == u'issue':
1542 self._PNL_visual_soap.refresh(document_folder = None)
1543 return
1544
1545 if self.problem['type'] == u'episode':
1546 pat = gmPerson.gmCurrentPatient()
1547 doc_folder = pat.get_document_folder()
1548 emr = pat.get_emr()
1549 self._PNL_visual_soap.refresh (
1550 document_folder = doc_folder,
1551 episodes = [self.problem['pk_episode']],
1552 encounter = emr.active_encounter['pk_encounter']
1553 )
1554 return
1555 #--------------------------------------------------------
1557 for field in self.fields:
1558 field.SetValue(u'')
1559 self._TCTRL_summary.SetValue(u'')
1560 self._PNL_visual_soap.clear()
1561 #--------------------------------------------------------
1563 fname, discard_unmodified = select_visual_progress_note_template(parent = self)
1564 if fname is None:
1565 return False
1566
1567 if self.problem is None:
1568 issue = None
1569 episode = None
1570 elif self.problem['type'] == 'issue':
1571 issue = self.problem['pk_health_issue']
1572 episode = None
1573 else:
1574 issue = self.problem['pk_health_issue']
1575 episode = gmEMRStructItems.problem2episode(self.problem)
1576
1577 wx.CallAfter (
1578 edit_visual_progress_note,
1579 filename = fname,
1580 episode = episode,
1581 discard_unmodified = discard_unmodified,
1582 health_issue = issue
1583 )
1584 #--------------------------------------------------------
1586
1587 if self.empty:
1588 return True
1589
1590 # new episode (standalone=unassociated or new-in-issue)
1591 if (self.problem is None) or (self.problem['type'] == 'issue'):
1592
1593 episode_name_candidates.append(u'')
1594 for candidate in episode_name_candidates:
1595 if candidate is None:
1596 continue
1597 epi_name = candidate.strip().replace('\r', '//').replace('\n', '//')
1598 break
1599
1600 dlg = wx.TextEntryDialog (
1601 parent = self,
1602 message = _('Enter a short working name for this new problem:'),
1603 caption = _('Creating a problem (episode) to save the notelet under ...'),
1604 defaultValue = epi_name,
1605 style = wx.OK | wx.CANCEL | wx.CENTRE
1606 )
1607 decision = dlg.ShowModal()
1608 if decision != wx.ID_OK:
1609 return False
1610
1611 epi_name = dlg.GetValue().strip()
1612 if epi_name == u'':
1613 gmGuiHelpers.gm_show_error(_('Cannot save a new problem without a name.'), _('saving progress note'))
1614 return False
1615
1616 # create episode
1617 new_episode = emr.add_episode(episode_name = epi_name[:45], pk_health_issue = None, is_open = True)
1618 new_episode['summary'] = self._TCTRL_summary.GetValue().strip()
1619 new_episode.save()
1620
1621 if self.problem is not None:
1622 issue = emr.problem2issue(self.problem)
1623 if not gmEMRStructWidgets.move_episode_to_issue(episode = new_episode, target_issue = issue, save_to_backend = True):
1624 gmGuiHelpers.gm_show_warning (
1625 _(
1626 'The new episode:\n'
1627 '\n'
1628 ' "%s"\n'
1629 '\n'
1630 'will remain unassociated despite the editor\n'
1631 'having been invoked from the health issue:\n'
1632 '\n'
1633 ' "%s"'
1634 ) % (
1635 new_episode['description'],
1636 issue['description']
1637 ),
1638 _('saving progress note')
1639 )
1640
1641 epi_id = new_episode['pk_episode']
1642
1643 # existing episode
1644 else:
1645 epi_id = self.problem['pk_episode']
1646
1647 emr.add_notes(notes = self.soap, episode = epi_id, encounter = encounter)
1648
1649 # set summary but only if not already set above for an episode
1650 # newly created either standalone or within a health issue
1651 if self.problem is not None:
1652 if self.problem['type'] == 'episode':
1653 epi = emr.problem2episode(self.problem)
1654 epi['summary'] = self._TCTRL_summary.GetValue().strip()
1655 epi.save()
1656
1657 return True
1658 #--------------------------------------------------------
1659 # event handling
1660 #--------------------------------------------------------
1662 for field in self.fields:
1663 wx_expando.EVT_ETC_LAYOUT_NEEDED(field, field.GetId(), self._on_expando_needs_layout)
1664 wx_expando.EVT_ETC_LAYOUT_NEEDED(self._TCTRL_summary, self._TCTRL_summary.GetId(), self._on_expando_needs_layout)
1665 #--------------------------------------------------------
1667 # need to tell ourselves to re-Layout to refresh scroll bars
1668
1669 # provoke adding scrollbar if needed
1670 self.Fit()
1671
1672 if self.HasScrollbar(wx.VERTICAL):
1673 # scroll panel to show cursor
1674 expando = self.FindWindowById(evt.GetId())
1675 y_expando = expando.GetPositionTuple()[1]
1676 h_expando = expando.GetSizeTuple()[1]
1677 line_cursor = expando.PositionToXY(expando.GetInsertionPoint())[1] + 1
1678 y_cursor = int(round((float(line_cursor) / expando.NumberOfLines) * h_expando))
1679 y_desired_visible = y_expando + y_cursor
1680
1681 y_view = self.ViewStart[1]
1682 h_view = self.GetClientSizeTuple()[1]
1683
1684 # print "expando:", y_expando, "->", h_expando, ", lines:", expando.NumberOfLines
1685 # print "cursor :", y_cursor, "at line", line_cursor, ", insertion point:", expando.GetInsertionPoint()
1686 # print "wanted :", y_desired_visible
1687 # print "view-y :", y_view
1688 # print "scroll2:", h_view
1689
1690 # expando starts before view
1691 if y_desired_visible < y_view:
1692 # print "need to scroll up"
1693 self.Scroll(0, y_desired_visible)
1694
1695 if y_desired_visible > h_view:
1696 # print "need to scroll down"
1697 self.Scroll(0, y_desired_visible)
1698 #--------------------------------------------------------
1699 # properties
1700 #--------------------------------------------------------
1702 note = []
1703
1704 tmp = self._TCTRL_Soap.GetValue().strip()
1705 if tmp != u'':
1706 note.append(['s', tmp])
1707
1708 tmp = self._TCTRL_sOap.GetValue().strip()
1709 if tmp != u'':
1710 note.append(['o', tmp])
1711
1712 tmp = self._TCTRL_soAp.GetValue().strip()
1713 if tmp != u'':
1714 note.append(['a', tmp])
1715
1716 tmp = self._TCTRL_soaP.GetValue().strip()
1717 if tmp != u'':
1718 note.append(['p', tmp])
1719
1720 return note
1721
1722 soap = property(_get_soap, lambda x:x)
1723 #--------------------------------------------------------
1725 for field in self.fields:
1726 if field.GetValue().strip() != u'':
1727 return False
1728
1729 summary = self._TCTRL_summary.GetValue().strip()
1730 if self.problem is None:
1731 if summary != u'':
1732 return False
1733 else:
1734 if self.problem['summary'] is None:
1735 if summary != u'':
1736 return False
1737 else:
1738 if summary != self.problem['summary'].strip():
1739 return False
1740
1741 return True
1742
1743 empty = property(_get_empty, lambda x:x)
1744 #============================================================
1746
1748
1749 wx_expando.ExpandoTextCtrl.__init__(self, *args, **kwargs)
1750
1751 self.__keyword_separators = regex.compile("[!?'\".,:;)}\]\r\n\s\t]+")
1752
1753 self.__register_interests()
1754 #------------------------------------------------
1755 # fixup errors in platform expando.py
1756 #------------------------------------------------
1758
1759 if (wx.MAJOR_VERSION > 1) and (wx.MINOR_VERSION > 8):
1760 return super(cSoapLineTextCtrl, self)._wrapLine(line, dc, width)
1761
1762 # THIS FIX LIFTED FROM TRUNK IN SVN:
1763 # Estimate where the control will wrap the lines and
1764 # return the count of extra lines needed.
1765 pte = dc.GetPartialTextExtents(line)
1766 width -= wx.SystemSettings.GetMetric(wx.SYS_VSCROLL_X)
1767 idx = 0
1768 start = 0
1769 count = 0
1770 spc = -1
1771 while idx < len(pte):
1772 if line[idx] == ' ':
1773 spc = idx
1774 if pte[idx] - start > width:
1775 # we've reached the max width, add a new line
1776 count += 1
1777 # did we see a space? if so restart the count at that pos
1778 if spc != -1:
1779 idx = spc + 1
1780 spc = -1
1781 if idx < len(pte):
1782 start = pte[idx]
1783 else:
1784 idx += 1
1785 return count
1786 #------------------------------------------------
1787 # event handling
1788 #------------------------------------------------
1790 #wx.EVT_KEY_DOWN (self, self.__on_key_down)
1791 #wx.EVT_KEY_UP (self, self.__OnKeyUp)
1792 wx.EVT_CHAR(self, self.__on_char)
1793 wx.EVT_SET_FOCUS(self, self.__on_focus)
1794 #--------------------------------------------------------
1798 #--------------------------------------------------------
1800 evt = wx.PyCommandEvent(wx_expando.wxEVT_ETC_LAYOUT_NEEDED, self.GetId())
1801 evt.SetEventObject(self)
1802 evt.height = None
1803 evt.numLines = None
1804 self.GetEventHandler().ProcessEvent(evt)
1805 #--------------------------------------------------------
1807 char = unichr(evt.GetUnicodeKey())
1808
1809 if self.LastPosition == 1:
1810 evt.Skip()
1811 return
1812
1813 explicit_expansion = False
1814 if evt.GetModifiers() == (wx.MOD_CMD | wx.MOD_ALT): # portable CTRL-ALT-...
1815 if evt.GetKeyCode() != 13:
1816 evt.Skip()
1817 return
1818 explicit_expansion = True
1819
1820 if not explicit_expansion:
1821 if self.__keyword_separators.match(char) is None:
1822 evt.Skip()
1823 return
1824
1825 caret_pos, line_no = self.PositionToXY(self.InsertionPoint)
1826 line = self.GetLineText(line_no)
1827 word = self.__keyword_separators.split(line[:caret_pos])[-1]
1828
1829 if (
1830 (not explicit_expansion)
1831 and
1832 (word != u'$$steffi') # Easter Egg ;-)
1833 and
1834 (word not in [ r[0] for r in gmPG2.get_text_expansion_keywords() ])
1835 ):
1836 evt.Skip()
1837 return
1838
1839 start = self.InsertionPoint - len(word)
1840 wx.CallAfter(self.replace_keyword_with_expansion, word, start, explicit_expansion)
1841
1842 evt.Skip()
1843 return
1844 #------------------------------------------------
1846
1847 if show_list:
1848 candidates = gmPG2.get_keyword_expansion_candidates(keyword = keyword)
1849 if len(candidates) == 0:
1850 return
1851 if len(candidates) == 1:
1852 keyword = candidates[0]
1853 else:
1854 keyword = gmListWidgets.get_choices_from_list (
1855 parent = self,
1856 msg = _(
1857 'Several macros match the keyword [%s].\n'
1858 '\n'
1859 'Please select the expansion you want to happen.'
1860 ) % keyword,
1861 caption = _('Selecting text macro'),
1862 choices = candidates,
1863 columns = [_('Keyword')],
1864 single_selection = True,
1865 can_return_empty = False
1866 )
1867 if keyword is None:
1868 return
1869
1870 expansion = gmPG2.expand_keyword(keyword = keyword)
1871
1872 if expansion is None:
1873 return
1874
1875 if expansion == u'':
1876 return
1877
1878 self.Replace (
1879 position,
1880 position + len(keyword),
1881 expansion
1882 )
1883
1884 self.SetInsertionPoint(position + len(expansion) + 1)
1885 self.ShowPosition(position + len(expansion) + 1)
1886
1887 return
1888 #============================================================
1889 # visual progress notes
1890 #============================================================
1892
1893 def is_valid(value):
1894
1895 if value is None:
1896 gmDispatcher.send (
1897 signal = 'statustext',
1898 msg = _('You need to actually set an editor.'),
1899 beep = True
1900 )
1901 return False, value
1902
1903 if value.strip() == u'':
1904 gmDispatcher.send (
1905 signal = 'statustext',
1906 msg = _('You need to actually set an editor.'),
1907 beep = True
1908 )
1909 return False, value
1910
1911 found, binary = gmShellAPI.detect_external_binary(value)
1912 if not found:
1913 gmDispatcher.send (
1914 signal = 'statustext',
1915 msg = _('The command [%s] is not found.') % value,
1916 beep = True
1917 )
1918 return True, value
1919
1920 return True, binary
1921 #------------------------------------------
1922 gmCfgWidgets.configure_string_option (
1923 message = _(
1924 'Enter the shell command with which to start\n'
1925 'the image editor for visual progress notes.\n'
1926 '\n'
1927 'Any "%(img)s" included with the arguments\n'
1928 'will be replaced by the file name of the\n'
1929 'note template.'
1930 ),
1931 option = u'external.tools.visual_soap_editor_cmd',
1932 bias = 'user',
1933 default_value = None,
1934 validator = is_valid
1935 )
1936 #============================================================
1938 if parent is None:
1939 parent = wx.GetApp().GetTopWindow()
1940
1941 dlg = wx.FileDialog (
1942 parent = parent,
1943 message = _('Choose file to use as template for new visual progress note'),
1944 defaultDir = os.path.expanduser('~'),
1945 defaultFile = '',
1946 #wildcard = "%s (*)|*|%s (*.*)|*.*" % (_('all files'), _('all files (Win)')),
1947 style = wx.OPEN | wx.HIDE_READONLY | wx.FILE_MUST_EXIST
1948 )
1949 result = dlg.ShowModal()
1950
1951 if result == wx.ID_CANCEL:
1952 dlg.Destroy()
1953 return None
1954
1955 full_filename = dlg.GetPath()
1956 dlg.Hide()
1957 dlg.Destroy()
1958 return full_filename
1959 #------------------------------------------------------------
1961
1962 if parent is None:
1963 parent = wx.GetApp().GetTopWindow()
1964
1965 # 1) select from template
1966 from Gnumed.wxpython import gmFormWidgets
1967 template = gmFormWidgets.manage_form_templates (
1968 parent = parent,
1969 template_types = [gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE],
1970 active_only = True
1971 )
1972
1973 # 2) select from disk file
1974 if template is None:
1975 fname = select_file_as_visual_progress_note_template(parent = parent)
1976 if fname is None:
1977 return (None, None)
1978 # create a copy of the picked file -- don't modify the original
1979 ext = os.path.splitext(fname)[1]
1980 tmp_name = gmTools.get_unique_filename(suffix = ext)
1981 _log.debug('visual progress note from file: [%s] -> [%s]', fname, tmp_name)
1982 shutil.copy2(fname, tmp_name)
1983 return (tmp_name, False)
1984
1985 filename = template.export_to_file()
1986 if filename is None:
1987 gmDispatcher.send(signal = u'statustext', msg = _('Cannot export visual progress note template for [%s].') % template['name_long'])
1988 return (None, None)
1989 return (filename, True)
1990
1991 #------------------------------------------------------------
1992 -def edit_visual_progress_note(filename=None, episode=None, discard_unmodified=False, doc_part=None, health_issue=None):
1993 """This assumes <filename> contains an image which can be handled by the configured image editor."""
1994
1995 if doc_part is not None:
1996 filename = doc_part.export_to_file()
1997 if filename is None:
1998 gmDispatcher.send(signal = u'statustext', msg = _('Cannot export visual progress note to file.'))
1999 return None
2000
2001 dbcfg = gmCfg.cCfgSQL()
2002 cmd = dbcfg.get2 (
2003 option = u'external.tools.visual_soap_editor_cmd',
2004 workplace = gmSurgery.gmCurrentPractice().active_workplace,
2005 bias = 'user'
2006 )
2007
2008 if cmd is None:
2009 gmDispatcher.send(signal = u'statustext', msg = _('Editor for visual progress note not configured.'), beep = False)
2010 cmd = configure_visual_progress_note_editor()
2011 if cmd is None:
2012 gmDispatcher.send(signal = u'statustext', msg = _('Editor for visual progress note not configured.'), beep = True)
2013 return None
2014
2015 if u'%(img)s' in cmd:
2016 cmd % {u'img': filename}
2017 else:
2018 cmd = u'%s %s' % (cmd, filename)
2019
2020 if discard_unmodified:
2021 original_stat = os.stat(filename)
2022 original_md5 = gmTools.file2md5(filename)
2023
2024 success = gmShellAPI.run_command_in_shell(cmd, blocking = True)
2025 if not success:
2026 gmGuiHelpers.gm_show_error (
2027 _(
2028 'There was a problem with running the editor\n'
2029 'for visual progress notes.\n'
2030 '\n'
2031 ' [%s]\n'
2032 '\n'
2033 ) % cmd,
2034 _('Editing visual progress note')
2035 )
2036 return None
2037
2038 try:
2039 open(filename, 'r').close()
2040 except StandardError:
2041 _log.exception('problem accessing visual progress note file [%s]', filename)
2042 gmGuiHelpers.gm_show_error (
2043 _(
2044 'There was a problem reading the visual\n'
2045 'progress note from the file:\n'
2046 '\n'
2047 ' [%s]\n'
2048 '\n'
2049 ) % filename,
2050 _('Saving visual progress note')
2051 )
2052 return None
2053
2054 if discard_unmodified:
2055 modified_stat = os.stat(filename)
2056 # same size ?
2057 if original_stat.st_size == modified_stat.st_size:
2058 modified_md5 = gmTools.file2md5(filename)
2059 # same hash ?
2060 if original_md5 == modified_md5:
2061 _log.debug('visual progress note (template) not modified')
2062 # ask user to decide
2063 msg = _(
2064 u'You either created a visual progress note from a template\n'
2065 u'in the database (rather than from a file on disk) or you\n'
2066 u'edited an existing visual progress note.\n'
2067 u'\n'
2068 u'The template/original was not modified at all, however.\n'
2069 u'\n'
2070 u'Do you still want to save the unmodified image as a\n'
2071 u'visual progress note into the EMR of the patient ?\n'
2072 )
2073 save_unmodified = gmGuiHelpers.gm_show_question (
2074 msg,
2075 _('Saving visual progress note')
2076 )
2077 if not save_unmodified:
2078 _log.debug('user discarded unmodified note')
2079 return
2080
2081 if doc_part is not None:
2082 doc_part.update_data_from_file(fname = filename)
2083 doc_part.set_reviewed(technically_abnormal = False, clinically_relevant = True)
2084 return None
2085
2086 if not isinstance(episode, gmEMRStructItems.cEpisode):
2087 if episode is None:
2088 episode = _('visual progress notes')
2089 pat = gmPerson.gmCurrentPatient()
2090 emr = pat.get_emr()
2091 episode = emr.add_episode(episode_name = episode.strip(), pk_health_issue = health_issue, is_open = False)
2092
2093 doc = gmDocumentWidgets.save_file_as_new_document (
2094 filename = filename,
2095 document_type = gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE,
2096 episode = episode,
2097 unlock_patient = True
2098 )
2099 doc.set_reviewed(technically_abnormal = False, clinically_relevant = True)
2100
2101 return doc
2102 #============================================================
2104 """Phrasewheel to allow selection of visual SOAP template."""
2105
2107
2108 gmPhraseWheel.cPhraseWheel.__init__ (self, *args, **kwargs)
2109
2110 query = u"""
2111 SELECT
2112 pk,
2113 name_short
2114 FROM
2115 ref.paperwork_templates
2116 WHERE
2117 fk_template_type = (SELECT pk FROM ref.form_types WHERE name = '%s') AND (
2118 name_long %%(fragment_condition)s
2119 OR
2120 name_short %%(fragment_condition)s
2121 )
2122 ORDER BY name_short
2123 LIMIT 15
2124 """ % gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE
2125
2126 mp = gmMatchProvider.cMatchProvider_SQL2(queries = [query])
2127 mp.setThresholds(2, 3, 5)
2128
2129 self.matcher = mp
2130 self.selection_only = True
2131 #--------------------------------------------------------
2133 if self.data is None:
2134 return None
2135
2136 return gmForms.cFormTemplate(aPK_obj = self.data)
2137 #============================================================
2138 from Gnumed.wxGladeWidgets import wxgVisualSoapPresenterPnl
2139
2141
2143 wxgVisualSoapPresenterPnl.wxgVisualSoapPresenterPnl.__init__(self, *args, **kwargs)
2144 self._SZR_soap = self.GetSizer()
2145 self.__bitmaps = []
2146 #--------------------------------------------------------
2147 # external API
2148 #--------------------------------------------------------
2150
2151 self.clear()
2152 if document_folder is not None:
2153 soap_docs = document_folder.get_visual_progress_notes(episodes = episodes, encounter = encounter)
2154 if len(soap_docs) > 0:
2155 for soap_doc in soap_docs:
2156 parts = soap_doc.parts
2157 if len(parts) == 0:
2158 continue
2159 part = parts[0]
2160 fname = part.export_to_file()
2161 if fname is None:
2162 continue
2163
2164 # create bitmap
2165 img = gmGuiHelpers.file2scaled_image (
2166 filename = fname,
2167 height = 30
2168 )
2169 #bmp = wx.StaticBitmap(self, -1, img, style = wx.NO_BORDER)
2170 bmp = wx_genstatbmp.GenStaticBitmap(self, -1, img, style = wx.NO_BORDER)
2171
2172 # create tooltip
2173 img = gmGuiHelpers.file2scaled_image (
2174 filename = fname,
2175 height = 150
2176 )
2177 tip = agw_stt.SuperToolTip (
2178 u'',
2179 bodyImage = img,
2180 header = _('Created: %s') % part['date_generated'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
2181 footer = gmTools.coalesce(part['doc_comment'], u'').strip()
2182 )
2183 tip.SetTopGradientColor('white')
2184 tip.SetMiddleGradientColor('white')
2185 tip.SetBottomGradientColor('white')
2186 tip.SetTarget(bmp)
2187
2188 bmp.doc_part = part
2189 bmp.Bind(wx.EVT_LEFT_UP, self._on_bitmap_leftclicked)
2190 # FIXME: add context menu for Delete/Clone/Add/Configure
2191 self._SZR_soap.Add(bmp, 0, wx.LEFT | wx.RIGHT | wx.TOP | wx.BOTTOM | wx.EXPAND, 3)
2192 self.__bitmaps.append(bmp)
2193
2194 self.GetParent().Layout()
2195 #--------------------------------------------------------
2197 for child in self._SZR_soap.GetChildren():
2198 self._SZR_soap.Detach(child)
2199 # while self._SZR_soap.Detach(0):
2200 # pass
2201 for bmp in self.__bitmaps:
2202 bmp.Destroy()
2203 self.__bitmaps = []
2204 #--------------------------------------------------------
2206 wx.CallAfter (
2207 edit_visual_progress_note,
2208 doc_part = evt.GetEventObject().doc_part,
2209 discard_unmodified = True
2210 )
2211 #============================================================
2212 from Gnumed.wxGladeWidgets import wxgVisualSoapPnl
2213
2215
2217
2218 wxgVisualSoapPnl.wxgVisualSoapPnl.__init__(self, *args, **kwargs)
2219
2220 # dummy episode to hold images
2221 self.default_episode_name = _('visual progress notes')
2222 #--------------------------------------------------------
2223 # external API
2224 #--------------------------------------------------------
2226 self._PRW_template.SetText(value = u'', data = None)
2227 self._LCTRL_visual_soaps.set_columns([_('Sketches')])
2228 self._LCTRL_visual_soaps.set_string_items()
2229
2230 self.show_image_and_metadata()
2231 #--------------------------------------------------------
2233
2234 self.clear()
2235
2236 if patient is None:
2237 patient = gmPerson.gmCurrentPatient()
2238
2239 if not patient.connected:
2240 return
2241
2242 emr = patient.get_emr()
2243 if encounter is None:
2244 encounter = emr.active_encounter
2245
2246 folder = patient.get_document_folder()
2247 soap_docs = folder.get_documents (
2248 doc_type = gmDocuments.DOCUMENT_TYPE_VISUAL_PROGRESS_NOTE,
2249 encounter = encounter['pk_encounter']
2250 )
2251
2252 if len(soap_docs) == 0:
2253 self._BTN_delete.Enable(False)
2254 return
2255
2256 self._LCTRL_visual_soaps.set_string_items ([
2257 u'%s%s%s' % (
2258 gmTools.coalesce(sd['comment'], u'', u'%s\n'),
2259 gmTools.coalesce(sd['ext_ref'], u'', u'%s\n'),
2260 sd['episode']
2261 ) for sd in soap_docs
2262 ])
2263 self._LCTRL_visual_soaps.set_data(soap_docs)
2264
2265 self._BTN_delete.Enable(True)
2266 #--------------------------------------------------------
2268
2269 if doc is None:
2270 self._IMG_soap.SetBitmap(wx.NullBitmap)
2271 self._PRW_episode.SetText()
2272 #self._PRW_comment.SetText(value = u'', data = None)
2273 self._PRW_comment.SetValue(u'')
2274 return
2275
2276 parts = doc.parts
2277 if len(parts) == 0:
2278 gmDispatcher.send(signal = u'statustext', msg = _('No images in visual progress note.'))
2279 return
2280
2281 fname = parts[0].export_to_file()
2282 if fname is None:
2283 gmDispatcher.send(signal = u'statustext', msg = _('Cannot export visual progress note to file.'))
2284 return
2285
2286 img_data = None
2287 rescaled_width = 300
2288 try:
2289 img_data = wx.Image(fname, wx.BITMAP_TYPE_ANY)
2290 current_width = img_data.GetWidth()
2291 current_height = img_data.GetHeight()
2292 rescaled_height = (rescaled_width * current_height) / current_width
2293 img_data.Rescale(rescaled_width, rescaled_height, quality = wx.IMAGE_QUALITY_HIGH) # w, h
2294 bmp_data = wx.BitmapFromImage(img_data)
2295 except:
2296 _log.exception('cannot load visual progress note from [%s]', fname)
2297 gmDispatcher.send(signal = u'statustext', msg = _('Cannot load visual progress note from [%s].') % fname)
2298 del img_data
2299 return
2300
2301 del img_data
2302 self._IMG_soap.SetBitmap(bmp_data)
2303
2304 self._PRW_episode.SetText(value = doc['episode'], data = doc['pk_episode'])
2305 if doc['comment'] is not None:
2306 self._PRW_comment.SetValue(doc['comment'].strip())
2307 #--------------------------------------------------------
2308 # event handlers
2309 #--------------------------------------------------------
2311
2312 doc = self._LCTRL_visual_soaps.get_selected_item_data(only_one = True)
2313 self.show_image_and_metadata(doc = doc)
2314 if doc is None:
2315 return
2316
2317 self._BTN_delete.Enable(True)
2318 #--------------------------------------------------------
2321 #--------------------------------------------------------
2323
2324 doc = self._LCTRL_visual_soaps.get_selected_item_data(only_one = True)
2325 if doc is None:
2326 self.show_image_and_metadata()
2327 return
2328
2329 parts = doc.parts
2330 if len(parts) == 0:
2331 gmDispatcher.send(signal = u'statustext', msg = _('No images in visual progress note.'))
2332 return
2333
2334 edit_visual_progress_note(doc_part = parts[0], discard_unmodified = True)
2335 self.show_image_and_metadata(doc = doc)
2336
2337 self._BTN_delete.Enable(True)
2338 #--------------------------------------------------------
2368 #--------------------------------------------------------
2431 #--------------------------------------------------------
2449 #============================================================
2450 # main
2451 #------------------------------------------------------------
2452 if __name__ == '__main__':
2453
2454 if len(sys.argv) < 2:
2455 sys.exit()
2456
2457 if sys.argv[1] != 'test':
2458 sys.exit()
2459
2460 gmI18N.activate_locale()
2461 gmI18N.install_domain(domain = 'gnumed')
2462
2463 #----------------------------------------
2465 pat = gmPersonSearch.ask_for_patient()
2466 gmPatSearchWidgets.set_active_patient(patient = pat)
2467 app = wx.PyWidgetTester(size = (200, 200))
2468 sels = select_narrative_from_episodes()
2469 print "selected:"
2470 for sel in sels:
2471 print sel
2472 #----------------------------------------
2474 pat = gmPersonSearch.ask_for_patient()
2475 application = wx.PyWidgetTester(size=(800,500))
2476 soap_input = cSoapNoteExpandoEditAreaPnl(application.frame, -1)
2477 application.frame.Show(True)
2478 application.MainLoop()
2479 #----------------------------------------
2481 patient = gmPersonSearch.ask_for_patient()
2482 if patient is None:
2483 print "No patient. Exiting gracefully..."
2484 return
2485 gmPatSearchWidgets.set_active_patient(patient=patient)
2486
2487 application = wx.PyWidgetTester(size=(800,500))
2488 soap_input = cSoapPluginPnl(application.frame, -1)
2489 application.frame.Show(True)
2490 soap_input._schedule_data_reget()
2491 application.MainLoop()
2492 #----------------------------------------
2493 #test_select_narrative_from_episodes()
2494 test_cSoapNoteExpandoEditAreaPnl()
2495 #test_cSoapPluginPnl()
2496
2497 #============================================================
2498
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Apr 12 03:58:56 2011 | http://epydoc.sourceforge.net |