1 """GNUmed auto/dynamic hints widgets.
2 """
3
4 __author__ = "Karsten Hilbert <Karsten.Hilbert@gmx.net>"
5 __license__ = "GPL v2 or later"
6
7 import sys
8 import logging
9
10
11 import wx
12
13
14 if __name__ == '__main__':
15 sys.path.insert(0, '../../')
16 from Gnumed.pycommon import gmTools
17 from Gnumed.pycommon import gmDispatcher
18 from Gnumed.pycommon import gmDateTime
19 from Gnumed.pycommon import gmNetworkTools
20
21 from Gnumed.business import gmPerson
22 from Gnumed.business import gmAutoHints
23
24 from Gnumed.wxpython import gmListWidgets
25 from Gnumed.wxpython import gmAuthWidgets
26 from Gnumed.wxpython import gmDataPackWidgets
27 from Gnumed.wxpython import gmGuiHelpers
28 from Gnumed.wxpython import gmEditArea
29
30
31 _log = logging.getLogger('gm.auto_hints')
32
33
35
36 pat = gmPerson.gmCurrentPatient()
37 if not pat.connected:
38 return
39
40
41 for msg in pat.overdue_messages:
42 if msg['expiry_date'] is None:
43 exp = ''
44 else:
45 exp = _(' - expires %s') % gmDateTime.pydt_strftime (
46 msg['expiry_date'],
47 '%Y %b %d',
48 accuracy = gmDateTime.acc_days
49 )
50 txt = _(
51 'Due for %s (since %s%s):\n'
52 '%s'
53 '%s'
54 '\n'
55 'Patient: %s\n'
56 'Reminder by: %s'
57 ) % (
58 gmDateTime.format_interval_medically(msg['interval_due']),
59 gmDateTime.pydt_strftime(msg['due_date'], '%Y %b %d', accuracy = gmDateTime.acc_days),
60 exp,
61 gmTools.coalesce(msg['comment'], '', '\n%s\n'),
62 gmTools.coalesce(msg['data'], '', '\n%s\n'),
63 pat['description_gender'],
64 msg['modified_by']
65 )
66 gmGuiHelpers.gm_show_warning (
67 aTitle = _('Clinical reminder'),
68 aMessage = txt
69 )
70
71
72 hints2aggregate = []
73 emr = pat.emr
74 hint_dlg = cDynamicHintDlg(wx.GetApp().GetTopWindow(), -1)
75
76 for hint in emr.dynamic_hints:
77 if hint['popup_type'] == 0:
78 continue
79 if hint['popup_type'] == 2:
80 hints2aggregate.append(hint)
81 continue
82 hint_dlg.hint = hint
83 if hint_dlg.ShowModal() == wx.ID_APPLY:
84 hint.suppress (
85 rationale = hint_dlg.rationale.strip(),
86 pk_encounter = emr.current_encounter['pk_encounter']
87 )
88 hint_dlg.Destroy()
89
90 if len(hints2aggregate) > 0:
91 hints_dlg = cDynamicHintListDlg(wx.GetApp().GetTopWindow(), -1)
92 hints_dlg.pk_encounter = emr.current_encounter['pk_encounter']
93 hints_dlg.hints = hints2aggregate
94 hints_dlg.ShowModal()
95 hints_dlg.Destroy()
96
97 return
98
99 gmDispatcher.connect(signal = 'post_patient_selection', receiver = _display_clinical_reminders)
100
101
117
118
120
121 if parent is None:
122 parent = wx.GetApp().GetTopWindow()
123
124 def get_tooltip(item):
125 if item is None:
126 return None
127 return item.format()
128
129 def manage_data_packs(item):
130 gmDataPackWidgets.manage_data_packs(parent = parent)
131 return True
132
133 def edit_hint(hint=None):
134 return edit_dynamic_hint(parent = parent, hint = hint, single_entry = (hint is not None))
135
136 def del_hint(hint=None):
137 if hint is None:
138 return False
139 really_delete = gmGuiHelpers.gm_show_question (
140 title = _('Deleting automatic dynamic hint'),
141 question = _('Really delete this dynamic hint ?\n\n [%s]') % hint['title']
142 )
143 if not really_delete:
144 return False
145
146 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('deleting a dynamic hint'))
147 if conn is None:
148 return False
149 gmAutoHints.delete_dynamic_hint(link_obj = conn, pk_hint = hint['pk_auto_hint'])
150 conn.commit()
151 conn.close()
152 return True
153
154 def refresh(lctrl):
155 hints = gmAutoHints.get_dynamic_hints(order_by = 'is_active DESC, source, hint')
156 items = [ [
157 gmTools.bool2subst(h['is_active'], gmTools.u_checkmark_thin, ''),
158 h['title'],
159 h['source'][:30],
160 h['hint'][:60],
161 gmTools.coalesce(h['url'], '')[:60],
162 h['lang'],
163 h['pk_auto_hint']
164 ] for h in hints ]
165 lctrl.set_string_items(items)
166 lctrl.set_data(hints)
167
168
169 gmListWidgets.get_choices_from_list (
170 parent = parent,
171 msg = _('\nDynamic hints registered with GNUmed.\n'),
172 caption = _('Showing dynamic hints.'),
173 columns = [ _('Active'), _('Title'), _('Source'), _('Hint'), 'URL', _('Language'), '#' ],
174 single_selection = True,
175 refresh_callback = refresh,
176 edit_callback = edit_hint,
177 new_callback = edit_hint,
178 delete_callback = del_hint,
179
180
181
182
183
184
185 right_extra_button = (
186 _('Data packs'),
187 _('Browse and install automatic dynamic hints data packs'),
188 manage_data_packs
189 ),
190 list_tooltip_callback = get_tooltip
191 )
192
193
194 from Gnumed.wxGladeWidgets import wxgDynamicHintDlg
195
197
207
210
212 self.__hint = hint
213 self.__refresh()
214
215 hint = property(_get_hint, _set_hint)
216
218 return self._TCTRL_rationale.GetValue().strip()
219
220 rationale = property(_get_rationale, lambda x:x)
221
222
223
224
227
228
230 if self.__hint is None:
231 self._TCTRL_title.SetValue('')
232 self._TCTRL_hint.SetValue('')
233 self._URL_info.SetURL('')
234 self._URL_info.Disable()
235 self._TCTRL_source.SetValue('')
236 self._LBL_previous_rationale.Hide()
237 self._TCTRL_previous_rationale.Hide()
238 else:
239 self._TCTRL_title.SetValue(self.__hint['title'])
240 self._TCTRL_hint.SetValue('%s%s' % (
241 self.__hint['hint'],
242 gmTools.coalesce(self.__hint['recommendation'], '', '\n\n%s')
243 ))
244 if self.__hint['url'] is None:
245 self._URL_info.SetURL('')
246 self._URL_info.Disable()
247 else:
248 self._URL_info.SetURL(self.__hint['url'])
249 self._URL_info.Enable()
250 self._TCTRL_source.SetValue(_('By: %s') % self.__hint['source'])
251 if self.__hint['rationale4suppression'] is None:
252 self._LBL_previous_rationale.Hide()
253 self._TCTRL_previous_rationale.Hide()
254 else:
255 self._LBL_previous_rationale.Show()
256 self._TCTRL_previous_rationale.Show()
257 self._TCTRL_previous_rationale.SetValue(self.__hint['rationale4suppression'])
258
259 self._TCTRL_rationale.SetValue('')
260 self._BTN_suppress.Disable()
261 self._TCTRL_rationale.SetFocus()
262
263
264
265
267 if self._TCTRL_rationale.GetValue().strip() == '':
268 self._BTN_suppress.Disable()
269 else:
270 self._BTN_suppress.Enable()
271
272
284
285
289
290
291 from Gnumed.wxGladeWidgets import wxgDynamicHintListDlg
292
294
305
306
309
311 if len(hints) == 0:
312 hints = None
313 self.__hints = hints
314 self.__refresh()
315
316 hints = property(_get_hints, _set_hints)
317
318
321
322 pk_encounter = property(lambda x:x, _set_pk_encounter)
323
324
325
326
334
335
337 if self.__hints is None:
338 self._LCTRL_hints.set_string_items()
339 self._TCTRL_hint.SetValue('')
340 self._URL_info.SetURL('')
341 self._URL_info.Disable()
342 self._TCTRL_source.SetValue('')
343 self._TCTRL_rationale.SetValue('')
344 self._BTN_suppress.Disable()
345 self._LBL_previous_rationale.Hide()
346 self._TCTRL_previous_rationale.Hide()
347 self._LCTRL_hints.SetFocus()
348 return
349
350 priority_hints = []
351 non_priority_hints = []
352 for hint in self.__hints:
353 if hint['highlight_as_priority']:
354 priority_hints.append(hint)
355 else:
356 non_priority_hints.append(hint)
357
358 ordered_hints = []
359 ordered_hints.extend(priority_hints)
360 ordered_hints.extend(non_priority_hints)
361
362 self._LCTRL_hints.set_string_items([ [h['title'], h['source']] for h in ordered_hints ])
363 self._LCTRL_hints.set_data(ordered_hints)
364 for idx in range(len(priority_hints)):
365 self._LCTRL_hints.SetItemTextColour(idx, wx.Colour('YELLOW'))
366 self._LCTRL_hints.Select(0)
367
368
370 hint = self._LCTRL_hints.get_selected_item_data(only_one = True)
371
372 self._TCTRL_hint.SetValue('%s%s' % (
373 hint['hint'],
374 gmTools.coalesce(hint['recommendation'], '', '\n\n%s')
375 ))
376 if hint['url'] is None:
377 self._URL_info.SetURL('')
378 self._URL_info.Disable()
379 else:
380 self._URL_info.SetURL(hint['url'])
381 self._URL_info.Enable()
382 self._TCTRL_source.SetValue(_('By: %s') % hint['source'])
383 self._TCTRL_rationale.SetValue('')
384 self._BTN_suppress.Disable()
385
386 if hint['rationale4suppression'] is None:
387 self._LBL_previous_rationale.Hide()
388 self._TCTRL_previous_rationale.Hide()
389 self._TCTRL_previous_rationale.SetValue('')
390 else:
391 self._LBL_previous_rationale.Show()
392 self._TCTRL_previous_rationale.SetValue(hint['rationale4suppression'])
393 self._TCTRL_previous_rationale.Show()
394
395 self._TCTRL_rationale.SetFocus()
396
397
399 self._TCTRL_hint.SetValue('')
400 self._URL_info.SetURL('')
401 self._URL_info.Disable()
402 self._TCTRL_source.SetValue('')
403 self._TCTRL_rationale.SetValue('')
404 self._LBL_previous_rationale.Hide()
405 self._TCTRL_previous_rationale.Hide()
406 self._TCTRL_previous_rationale.SetValue('')
407 self._BTN_suppress.Disable()
408
409
410
411
413 if self._TCTRL_rationale.GetValue().strip() == '':
414 self._BTN_suppress.Disable()
415 else:
416 self._BTN_suppress.Enable()
417
418
442
443
447
448
449 from Gnumed.wxGladeWidgets import wxgAutoHintEAPnl
450
451 -class cAutoHintEAPnl(wxgAutoHintEAPnl.wxgAutoHintEAPnl, gmEditArea.cGenericEditAreaMixin):
452
470
471
472
473
474
475
476
477
516
517
519 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('creating a new dynamic hint'))
520 if conn is None:
521 return False
522
523 curs = conn.cursor()
524 data = gmAutoHints.create_dynamic_hint (
525 link_obj = curs,
526 query = self._TCTRL_query.GetValue().strip(),
527 title = self._TCTRL_title.GetValue().strip(),
528 hint = self._TCTRL_hint.GetValue().strip(),
529 source = self._TCTRL_source.GetValue().strip()
530 )
531 curs.close()
532 data['url'] = self._TCTRL_url.GetValue().strip()
533 data['recommendation_query'] = self._TCTRL_recommendation_query.GetValue().strip()
534 data['is_active'] = self._CHBOX_is_active.GetValue()
535 data['highlight_as_priority'] = self._CHBOX_highlight.GetValue()
536 if self._RBTN_popup_none.GetValue() is True:
537 data['popup_type'] = 0
538 elif self._RBTN_popup_single.GetValue() is True:
539 data['popup_type'] = 1
540 elif self._RBTN_popup_multiple.GetValue() is True:
541 data['popup_type'] = 2
542 else:
543 raise ValueError('no popup type radio button selected - should be impossible')
544 data.save(conn = conn)
545 conn.commit()
546 conn.close()
547
548 self.data = data
549
550 return True
551
552
554 conn = gmAuthWidgets.get_dbowner_connection(procedure = _('updating an existing dynamic hint'))
555 if conn is None:
556 return False
557
558 self.data['title'] = self._TCTRL_title.GetValue().strip()
559 self.data['hint'] = self._TCTRL_hint.GetValue().strip()
560 self.data['query'] = self._TCTRL_query.GetValue().strip()
561 self.data['source'] = self._TCTRL_source.GetValue().strip()
562 self.data['url'] = self._TCTRL_url.GetValue().strip()
563 self.data['recommendation_query'] = self._TCTRL_recommendation_query.GetValue().strip()
564 self.data['is_active'] = self._CHBOX_is_active.GetValue()
565 self.data['highlight_as_priority'] = self._CHBOX_highlight.GetValue()
566 if self._RBTN_popup_none.GetValue() is True:
567 self.data['popup_type'] = 0
568 elif self._RBTN_popup_single.GetValue() is True:
569 self.data['popup_type'] = 1
570 elif self._RBTN_popup_multiple.GetValue() is True:
571 self.data['popup_type'] = 2
572 else:
573 raise ValueError('no popup type radio button selected - should be impossible')
574 self.data.save(conn = conn)
575 conn.commit()
576 conn.close()
577
578 return True
579
580
593
594
596 self._refresh_as_new()
597 self._TCTRL_source.SetValue(self.data['source'])
598
599 self._TCTRL_title.SetFocus()
600
601
603 self._TCTRL_title.SetValue(self.data['title'])
604 self._TCTRL_hint.SetValue(self.data['hint'])
605 self._TCTRL_query.SetValue(self.data['query'])
606 self._TCTRL_recommendation_query.SetValue(gmTools.coalesce(self.data['recommendation_query'], ''))
607 self._TCTRL_source.SetValue(self.data['source'])
608 self._TCTRL_url.SetValue(gmTools.coalesce(self.data['url'], ''))
609 self._CHBOX_is_active.SetValue(self.data['is_active'])
610 self._CHBOX_highlight.SetValue(self.data['highlight_as_priority'])
611 if self.data['popup_type'] == 0:
612 self._RBTN_popup_none.SetValue(True)
613 elif self.data['popup_type'] == 1:
614 self._RBTN_popup_single.SetValue(True)
615 elif self.data['popup_type'] == 2:
616 self._RBTN_popup_multiple.SetValue(True)
617 else:
618 raise ValueError('invalid popup type value [%s] - should be impossible' % self.data['popup_type'])
619
620 self._TCTRL_query.SetFocus()
621
622
623
624
633
634
636
637 if parent is None:
638 parent = wx.GetApp().GetTopWindow()
639
640 def get_tooltip(item):
641 if item is None:
642 return None
643 return item.format()
644
645 def manage_hints(item):
646 manage_dynamic_hints(parent = parent)
647 return True
648
649 def del_hint(hint=None):
650 if hint is None:
651 return False
652 really_delete = gmGuiHelpers.gm_show_question (
653 title = _('Deleting suppressed dynamic hint'),
654 question = _('Really delete the suppression of this dynamic hint ?\n\n [%s]') % hint['title']
655 )
656 if not really_delete:
657 return False
658 gmAutoHints.delete_suppressed_hint(pk_suppressed_hint = hint['pk_suppressed_hint'])
659 return True
660
661 def refresh(lctrl):
662 hints = gmAutoHints.get_suppressed_hints(pk_identity = pk_identity, order_by = 'title')
663 items = [ [
664 h['title'],
665 gmDateTime.pydt_strftime(h['suppressed_when'], '%Y %b %d'),
666 h['suppressed_by'],
667 h['rationale'],
668 gmTools.bool2subst(h['is_active'], gmTools.u_checkmark_thin, ''),
669 h['source'][:30],
670 gmTools.coalesce(h['url'], '')[:60],
671 h['pk_hint']
672 ] for h in hints ]
673 lctrl.set_string_items(items)
674 lctrl.set_data(hints)
675
676 gmListWidgets.get_choices_from_list (
677 parent = parent,
678 msg = _('\nDynamic hints suppressed in this patient.\n'),
679 caption = _('Showing suppressed dynamic hints.'),
680 columns = [ _('Title'), _('When'), _('By'), _('Rationale'), _('Active'), _('Source'), 'URL', 'Hint #' ],
681 single_selection = True,
682 ignore_OK_button = True,
683 refresh_callback = refresh,
684 delete_callback = del_hint,
685 right_extra_button = (
686 _('Manage hints'),
687 _('Manage automatic dynamic hints'),
688 manage_hints
689 ),
690 list_tooltip_callback = get_tooltip
691 )
692
693
694
695
696 if __name__ == '__main__':
697
698 if len(sys.argv) < 2:
699 sys.exit()
700
701 if sys.argv[1] != 'test':
702 sys.exit()
703
704 gmI18N.activate_locale()
705 gmI18N.install_domain(domain = 'gnumed')
706
707
708
709
710
711
712
713
714
715
716
717
718
719