| Home | Trees | Indices | Help |
|
|---|
|
|
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 # reminders
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 # dynamic hints
72 hints2aggregate = []
73 emr = pat.emr
74 hint_dlg = cDynamicHintDlg(wx.GetApp().GetTopWindow(), -1)
75 # single-hint popups
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 # aggregate popup
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 #================================================================
103
104 if parent is None:
105 parent = wx.GetApp().GetTopWindow()
106
107 ea = cAutoHintEAPnl(parent, -1)
108 ea.data = hint
109 ea.mode = gmTools.coalesce(hint, 'new', 'edit')
110 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
111 dlg.SetTitle(gmTools.coalesce(hint, _('Adding automatic dynamic hint'), _('Editing automatic dynamic hint')))
112 if dlg.ShowModal() == wx.ID_OK:
113 dlg.Destroy()
114 return True
115 dlg.Destroy()
116 return False
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 # left_extra_button = (
180 # _('(De)-Activate'),
181 # _('Toggle activation of the selected hint'),
182 # toggle_activation
183 # ),
184 # button to show DB schema
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
199
200 try:
201 self.__hint = kwargs['hint']
202 del kwargs['hint']
203 except KeyError:
204 self.__hint = None
205 wxgDynamicHintDlg.wxgDynamicHintDlg.__init__(self, *args, **kwargs)
206 self.__init_ui()
207 #------------------------------------------------------------
210
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 # internal helpers
224 #------------------------------------------------------------
226 self._TCTRL_rationale.add_callback_on_modified(callback = self._on_rationale_modified)
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 # event handlers
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
296
297 try:
298 self.__hints = kwargs['hints']
299 del kwargs['hints']
300 except KeyError:
301 self.__hints = None
302 wxgDynamicHintListDlg.wxgDynamicHintListDlg.__init__(self, *args, **kwargs)
303 self.__pk_encounter = None
304 self.__init_ui()
305
306 #------------------------------------------------------------
309
315
316 hints = property(_get_hints, _set_hints)
317
318 #------------------------------------------------------------
320 self.__pk_encounter = pk_encounter
321
322 pk_encounter = property(lambda x:x, _set_pk_encounter)
323
324 #------------------------------------------------------------
325 # internal helpers
326 #------------------------------------------------------------
328 self._LCTRL_hints.set_columns([_('Hint'), _('Source')])
329 self._LCTRL_hints.set_column_widths([wx.LIST_AUTOSIZE, wx.LIST_AUTOSIZE])
330 self._LCTRL_hints.set_resize_column(column = 0)
331 self._LCTRL_hints.select_callback = self._on_hint_selected
332 self._LCTRL_hints.deselect_callback = self._on_hint_deselected
333 self._TCTRL_rationale.add_callback_on_modified(callback = self._on_rationale_modified)
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 # event handlers
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
452
454
455 try:
456 data = kwargs['hint']
457 del kwargs['hint']
458 except KeyError:
459 data = None
460
461 wxgAutoHintEAPnl.wxgAutoHintEAPnl.__init__(self, *args, **kwargs)
462 gmEditArea.cGenericEditAreaMixin.__init__(self)
463
464 # Code using this mixin should set mode and data
465 # after instantiating the class:
466 self.mode = 'new'
467 self.data = data
468 if data is not None:
469 self.mode = 'edit'
470
471 #self.__init_ui()
472 #----------------------------------------------------------------
473 # def __init_ui(self):
474 # # adjust phrasewheels etc
475 #----------------------------------------------------------------
476 # generic Edit Area mixin API
477 #----------------------------------------------------------------
479
480 validity = True
481
482 if self._TCTRL_source.GetValue().strip() == '':
483 validity = False
484 self.display_tctrl_as_valid(tctrl = self._TCTRL_source, valid = False)
485 self.status_message = _('No entry in field <Source>.')
486 self._TCTRL_source.SetFocus()
487 else:
488 self.display_tctrl_as_valid(tctrl = self._TCTRL_source, valid = True)
489
490 if self._TCTRL_query.GetValue().strip() == '':
491 validity = False
492 self.display_tctrl_as_valid(tctrl = self._TCTRL_query, valid = False)
493 self.status_message = _('No entry in field <Conditions>.')
494 self._TCTRL_query.SetFocus()
495 else:
496 # FIXME: run SQL
497 self.display_tctrl_as_valid(tctrl = self._TCTRL_query, valid = True)
498
499 if self._TCTRL_hint.GetValue().strip() == '':
500 validity = False
501 self.display_tctrl_as_valid(tctrl = self._TCTRL_hint, valid = False)
502 self.status_message = _('No entry in field <Description>.')
503 self._TCTRL_hint.SetFocus()
504 else:
505 self.display_tctrl_as_valid(tctrl = self._TCTRL_hint, valid = True)
506
507 if self._TCTRL_title.GetValue().strip() == '':
508 validity = False
509 self.display_tctrl_as_valid(tctrl = self._TCTRL_title, valid = False)
510 self.status_message = _('No entry in field <Title>.')
511 self._TCTRL_title.SetFocus()
512 else:
513 self.display_tctrl_as_valid(tctrl = self._TCTRL_title, valid = True)
514
515 return validity
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, # it seems this MUST be a cursor or else the successfully created row will not show up -- but why ?!?
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 #----------------------------------------------------------------
582 self._TCTRL_title.SetValue('')
583 self._TCTRL_hint.SetValue('')
584 self._TCTRL_query.SetValue('')
585 self._TCTRL_recommendation_query.SetValue('')
586 self._TCTRL_source.SetValue('')
587 self._TCTRL_url.SetValue('')
588 self._CHBOX_is_active.SetValue(True)
589 self._CHBOX_highlight.SetValue(True)
590 self._RBTN_popup_single.SetValue(True)
591
592 self._TCTRL_title.SetFocus()
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 # event handlers
624 #----------------------------------------------------------------
633
634 #================================================================
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 # main
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 # def test_message_inbox():
708 # app = wx.PyWidgetTester(size = (800, 600))
709 # app.SetWidget(cProviderInboxPnl, -1)
710 # app.MainLoop()
711
712 # def test_msg_ea():
713 # app = wx.PyWidgetTester(size = (800, 600))
714 # app.SetWidget(cInboxMessageEAPnl, -1)
715 # app.MainLoop()
716
717 #test_message_inbox()
718 #test_msg_ea()
719
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |