| Home | Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed immunisation/vaccination widgets.
2
3 Modelled after Richard Terry's design document.
4
5 copyright: authors
6 """
7 #======================================================================
8 __author__ = "R.Terry, S.J.Tan, K.Hilbert"
9 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
10
11 import sys, time, logging
12
13
14 import wx
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.pycommon import gmDispatcher
20 from Gnumed.pycommon import gmMatchProvider
21 from Gnumed.pycommon import gmTools
22 from Gnumed.pycommon import gmI18N
23 from Gnumed.pycommon import gmCfg
24 from Gnumed.pycommon import gmDateTime
25 from Gnumed.pycommon import gmNetworkTools
26 from Gnumed.pycommon import gmPrinting
27
28 from Gnumed.business import gmPerson
29 from Gnumed.business import gmVaccination
30 from Gnumed.business import gmPraxis
31 from Gnumed.business import gmProviderInbox
32
33 from Gnumed.wxpython import gmPhraseWheel
34 from Gnumed.wxpython import gmTerryGuiParts
35 from Gnumed.wxpython import gmRegetMixin
36 from Gnumed.wxpython import gmGuiHelpers
37 from Gnumed.wxpython import gmEditArea
38 from Gnumed.wxpython import gmListWidgets
39 from Gnumed.wxpython import gmFormWidgets
40 from Gnumed.wxpython import gmMacro
41
42
43 _log = logging.getLogger('gm.vaccination')
44
45 #======================================================================
46 # vaccination indication related widgets
47 #----------------------------------------------------------------------
49
50 if parent is None:
51 parent = wx.GetApp().GetTopWindow()
52 #------------------------------------------------------------
53 def refresh(lctrl):
54 inds = gmVaccination.get_indications(order_by = 'l10n_description')
55
56 items = [ [
57 i['l10n_description'],
58 gmTools.coalesce (
59 i['atcs_single_indication'],
60 u'',
61 u'%s'
62 ),
63 gmTools.coalesce (
64 i['atcs_combi_indication'],
65 u'',
66 u'%s'
67 ),
68 u'%s' % i['id']
69 ] for i in inds ]
70
71 lctrl.set_string_items(items)
72 lctrl.set_data(inds)
73 #------------------------------------------------------------
74 gmListWidgets.get_choices_from_list (
75 parent = parent,
76 msg = _('\nConditions preventable by vaccination as currently known to GNUmed.\n'),
77 caption = _('Showing vaccination preventable conditions.'),
78 columns = [ _('Condition'), _('ATCs: single-condition vaccines'), _('ATCs: multi-condition vaccines'), u'#' ],
79 single_selection = True,
80 refresh_callback = refresh
81 )
82 #----------------------------------------------------------------------
84
85 if parent is None:
86 parent = wx.GetApp().GetTopWindow()
87
88 if msg is None:
89 msg = _('Pick the relevant indications.')
90
91 if right_column is None:
92 right_columns = ['This vaccine']
93 else:
94 right_columns = [right_column]
95
96 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
97 picker.set_columns(columns = [_('Known indications')], columns_right = right_columns)
98 inds = gmVaccination.get_indications(order_by = 'l10n_description')
99 picker.set_choices (
100 choices = [ i['l10n_description'] for i in inds ],
101 data = inds
102 )
103 picker.set_picks (
104 picks = [ p['l10n_description'] for p in picks ],
105 data = picks
106 )
107 result = picker.ShowModal()
108
109 if result == wx.ID_CANCEL:
110 picker.Destroy()
111 return None
112
113 picks = picker.picks
114 picker.Destroy()
115 return picks
116
117 #======================================================================
118 # vaccines related widgets
119 #----------------------------------------------------------------------
121 ea = cVaccineEAPnl(parent = parent, id = -1)
122 ea.data = vaccine
123 ea.mode = gmTools.coalesce(vaccine, 'new', 'edit')
124 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
125 dlg.SetTitle(gmTools.coalesce(vaccine, _('Adding new vaccine'), _('Editing vaccine')))
126 if dlg.ShowModal() == wx.ID_OK:
127 dlg.Destroy()
128 return True
129 dlg.Destroy()
130 return False
131 #----------------------------------------------------------------------
133
134 if parent is None:
135 parent = wx.GetApp().GetTopWindow()
136 #------------------------------------------------------------
137 def delete(vaccine=None):
138 deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
139 if deleted:
140 return True
141
142 gmGuiHelpers.gm_show_info (
143 _(
144 'Cannot delete vaccine\n'
145 '\n'
146 ' %s - %s (#%s)\n'
147 '\n'
148 'It is probably documented in a vaccination.'
149 ) % (
150 vaccine['vaccine'],
151 vaccine['preparation'],
152 vaccine['pk_vaccine']
153 ),
154 _('Deleting vaccine')
155 )
156
157 return False
158 #------------------------------------------------------------
159 def edit(vaccine=None):
160 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
161 #------------------------------------------------------------
162 def refresh(lctrl):
163 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
164
165 items = [ [
166 u'%s' % v['pk_brand'],
167 u'%s%s' % (
168 v['vaccine'],
169 gmTools.bool2subst (
170 v['is_fake_vaccine'],
171 u' (%s)' % _('fake'),
172 u''
173 )
174 ),
175 v['preparation'],
176 #u'%s (%s)' % (v['route_abbreviation'], v['route_description']),
177 #gmTools.bool2subst(v['is_live'], gmTools.u_checkmark_thin, u'', u'?'),
178 gmTools.coalesce(v['atc_code'], u''),
179 u'%s%s' % (
180 gmTools.coalesce(v['min_age'], u'?'),
181 gmTools.coalesce(v['max_age'], u'?', u' - %s'),
182 ),
183 gmTools.coalesce(v['comment'], u'')
184 ] for v in vaccines ]
185 lctrl.set_string_items(items)
186 lctrl.set_data(vaccines)
187 #------------------------------------------------------------
188 gmListWidgets.get_choices_from_list (
189 parent = parent,
190 msg = _('\nThe vaccines currently known to GNUmed.\n'),
191 caption = _('Showing vaccines.'),
192 #columns = [ u'#', _('Brand'), _('Preparation'), _(u'Route'), _('Live'), _('ATC'), _('Age range'), _('Comment') ],
193 columns = [ u'#', _('Brand'), _('Preparation'), _('ATC'), _('Age range'), _('Comment') ],
194 single_selection = True,
195 refresh_callback = refresh,
196 edit_callback = edit,
197 new_callback = edit,
198 delete_callback = delete
199 )
200 #----------------------------------------------------------------------
202
204
205 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
206
207 context = {
208 u'ctxt_vaccine': {
209 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s',
210 u'placeholder': u'pk_vaccine'
211 }
212 }
213
214 query = u"""
215 SELECT data, field_label, list_label FROM (
216
217 SELECT distinct on (field_label)
218 data,
219 field_label,
220 list_label,
221 rank
222 FROM ((
223 -- batch_no by vaccine
224 SELECT
225 batch_no AS data,
226 batch_no AS field_label,
227 batch_no || ' (' || vaccine || ')' AS list_label,
228 1 as rank
229 FROM
230 clin.v_pat_vaccinations
231 WHERE
232 batch_no %(fragment_condition)s
233 %(ctxt_vaccine)s
234 ) UNION ALL (
235 -- batch_no for any vaccine
236 SELECT
237 batch_no AS data,
238 batch_no AS field_label,
239 batch_no || ' (' || vaccine || ')' AS list_label,
240 2 AS rank
241 FROM
242 clin.v_pat_vaccinations
243 WHERE
244 batch_no %(fragment_condition)s
245 )
246
247 ) AS matching_batch_nos
248
249 ) as unique_matches
250
251 ORDER BY rank, list_label
252 LIMIT 25
253 """
254 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
255 mp.setThresholds(1, 2, 3)
256 self.matcher = mp
257
258 self.unset_context(context = u'pk_vaccine')
259 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
260 self.selection_only = False
261 #----------------------------------------------------------------------
263
265
266 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
267
268 # consider ATCs in ref.branded_drug and vacc_indication
269 query = u"""
270 SELECT data, list_label, field_label FROM (
271
272 SELECT DISTINCT ON (data)
273 data,
274 list_label,
275 field_label
276 FROM ((
277 -- fragment -> vaccine
278 SELECT
279 pk_vaccine AS data,
280 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
281 vaccine AS field_label
282 FROM
283 clin.v_vaccines
284 WHERE
285 vaccine %(fragment_condition)s
286
287 ) union all (
288
289 -- fragment -> localized indication -> vaccines
290 SELECT
291 pk_vaccine AS data,
292 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
293 vaccine AS field_label
294 FROM
295 clin.v_indications4vaccine
296 WHERE
297 l10n_indication %(fragment_condition)s
298
299 ) union all (
300
301 -- fragment -> indication -> vaccines
302 SELECT
303 pk_vaccine AS data,
304 vaccine || ' (' || array_to_string(indications, ', ') || ')' AS list_label,
305 vaccine AS field_label
306 FROM
307 clin.v_indications4vaccine
308 WHERE
309 indication %(fragment_condition)s
310 )
311 ) AS distinct_total
312
313 ) AS total
314
315 ORDER by list_label
316 LIMIT 25
317 """
318 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
319 mp.setThresholds(1, 2, 3)
320 self.matcher = mp
321
322 self.selection_only = True
323 #------------------------------------------------------------------
326 #----------------------------------------------------------------------
327 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
328
330
332 try:
333 data = kwargs['vaccine']
334 del kwargs['vaccine']
335 except KeyError:
336 data = None
337
338 wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs)
339 gmEditArea.cGenericEditAreaMixin.__init__(self)
340
341 self.mode = 'new'
342 self.data = data
343 if data is not None:
344 self.mode = 'edit'
345 #----------------------------------------------------------------
347 self._TCTRL_indications.SetValue(u'')
348 if len(self.__indications) == 0:
349 return
350 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
351 #----------------------------------------------------------------
352 # generic Edit Area mixin API
353 #----------------------------------------------------------------
355
356 has_errors = False
357
358 if self._PRW_brand.GetValue().strip() == u'':
359 has_errors = True
360 self._PRW_brand.display_as_valid(False)
361 else:
362 self._PRW_brand.display_as_valid(True)
363
364 if self._PRW_atc.GetValue().strip() in [u'', u'J07']:
365 self._PRW_atc.display_as_valid(True)
366 else:
367 if self._PRW_atc.GetData() is None:
368 self._PRW_atc.display_as_valid(True)
369 else:
370 has_errors = True
371 self._PRW_atc.display_as_valid(False)
372
373 val = self._PRW_age_min.GetValue().strip()
374 if val == u'':
375 self._PRW_age_min.display_as_valid(True)
376 else:
377 if gmDateTime.str2interval(val) is None:
378 has_errors = True
379 self._PRW_age_min.display_as_valid(False)
380 else:
381 self._PRW_age_min.display_as_valid(True)
382
383 val = self._PRW_age_max.GetValue().strip()
384 if val == u'':
385 self._PRW_age_max.display_as_valid(True)
386 else:
387 if gmDateTime.str2interval(val) is None:
388 has_errors = True
389 self._PRW_age_max.display_as_valid(False)
390 else:
391 self._PRW_age_max.display_as_valid(True)
392
393 # are we editing ?
394 ask_user = (self.mode == 'edit')
395 # is this vaccine in use ?
396 ask_user = (ask_user and self.data.is_in_use)
397 # a change ...
398 ask_user = ask_user and (
399 # ... of brand ...
400 (self.data['pk_brand'] != self._PRW_route.GetData())
401 or
402 # ... or indications ?
403 (set(self.data['pk_indications']) != set([ i['id'] for i in self.__indications ]))
404 )
405
406 if ask_user:
407 do_it = gmGuiHelpers.gm_show_question (
408 aTitle = _('Saving vaccine'),
409 aMessage = _(
410 u'This vaccine is already in use:\n'
411 u'\n'
412 u' "%s"\n'
413 u' (%s)\n'
414 u'\n'
415 u'Are you absolutely positively sure that\n'
416 u'you really want to edit this vaccine ?\n'
417 '\n'
418 u'This will change the vaccine name and/or target\n'
419 u'conditions in each patient this vaccine was\n'
420 u'used in to document a vaccination with.\n'
421 ) % (
422 self._PRW_brand.GetValue().strip(),
423 u', '.join(self.data['l10n_indications'])
424 )
425 )
426 if not do_it:
427 has_errors = True
428
429 return (has_errors is False)
430 #----------------------------------------------------------------
432
433 if len(self.__indications) == 0:
434 gmGuiHelpers.gm_show_info (
435 aTitle = _('Saving vaccine'),
436 aMessage = _('You must select at least one indication.')
437 )
438 return False
439
440 # save the data as a new instance
441 data = gmVaccination.create_vaccine (
442 pk_brand = self._PRW_brand.GetData(),
443 brand_name = self._PRW_brand.GetValue(),
444 pk_indications = [ i['id'] for i in self.__indications ]
445 )
446
447 # data['is_live'] = self._CHBOX_live.GetValue()
448 val = self._PRW_age_min.GetValue().strip()
449 if val != u'':
450 data['min_age'] = gmDateTime.str2interval(val)
451 val = self._PRW_age_max.GetValue().strip()
452 if val != u'':
453 data['max_age'] = gmDateTime.str2interval(val)
454 val = self._TCTRL_comment.GetValue().strip()
455 if val != u'':
456 data['comment'] = val
457
458 data.save()
459
460 drug = data.brand
461 drug['is_fake_brand'] = self._CHBOX_fake.GetValue()
462 val = self._PRW_atc.GetData()
463 if val is not None:
464 if val != u'J07':
465 drug['atc'] = val.strip()
466 drug.save()
467
468 # must be done very late or else the property access
469 # will refresh the display such that later field
470 # access will return empty values
471 self.data = data
472
473 return True
474 #----------------------------------------------------------------
476
477 if len(self.__indications) == 0:
478 gmGuiHelpers.gm_show_info (
479 aTitle = _('Saving vaccine'),
480 aMessage = _('You must select at least one indication.')
481 )
482 return False
483
484 drug = self.data.brand
485 drug['brand'] = self._PRW_brand.GetValue().strip()
486 drug['is_fake_brand'] = self._CHBOX_fake.GetValue()
487 val = self._PRW_atc.GetData()
488 if val is not None:
489 if val != u'J07':
490 drug['atc'] = val.strip()
491 drug.save()
492
493 # the validator already asked for changes so just do it
494 self.data.set_indications(pk_indications = [ i['id'] for i in self.__indications ])
495
496 # self.data['is_live'] = self._CHBOX_live.GetValue()
497 val = self._PRW_age_min.GetValue().strip()
498 if val != u'':
499 self.data['min_age'] = gmDateTime.str2interval(val)
500 if val != u'':
501 self.data['max_age'] = gmDateTime.str2interval(val)
502 val = self._TCTRL_comment.GetValue().strip()
503 if val != u'':
504 self.data['comment'] = val
505
506 self.data.save()
507 return True
508 #----------------------------------------------------------------
510 self._PRW_brand.SetText(value = u'', data = None, suppress_smarts = True)
511 # self._CHBOX_live.SetValue(True)
512 self._CHBOX_fake.SetValue(False)
513 self._PRW_atc.SetText(value = u'', data = None, suppress_smarts = True)
514 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
515 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
516 self._TCTRL_comment.SetValue(u'')
517
518 self.__indications = []
519 self.__refresh_indications()
520
521 self._PRW_brand.SetFocus()
522 #----------------------------------------------------------------
524 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand'])
525 # self._CHBOX_live.SetValue(self.data['is_live'])
526 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
527 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
528 if self.data['min_age'] is None:
529 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
530 else:
531 self._PRW_age_min.SetText (
532 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
533 data = self.data['min_age']
534 )
535 if self.data['max_age'] is None:
536 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
537 else:
538 self._PRW_age_max.SetText (
539 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
540 data = self.data['max_age']
541 )
542 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
543
544 self.__indications = self.data.indications
545 self.__refresh_indications()
546
547 self._PRW_brand.SetFocus()
548 #----------------------------------------------------------------
551 #----------------------------------------------------------------
552 #----------------------------------------------------------------
567
568 #======================================================================
569 # vaccination related widgets
570 #----------------------------------------------------------------------
572
573 if parent is None:
574 parent = wx.GetApp().GetTopWindow()
575
576 vaccs_printout = gmFormWidgets.generate_form_from_template (
577 parent = parent,
578 template_types = [
579 u'Medical statement',
580 u'vaccination report',
581 u'vaccination record',
582 u'reminder'
583 ],
584 edit = False
585 )
586
587 if vaccs_printout is None:
588 return False
589
590 return gmFormWidgets.act_on_generated_forms (
591 parent = parent,
592 forms = [vaccs_printout],
593 jobtype = 'vaccinations',
594 episode_name = u'administrative',
595 review_copy_as_normal = True
596 )
597
598 #----------------------------------------------------------------------
600 ea = cVaccinationEAPnl(parent = parent, id = -1)
601 ea.data = vaccination
602 ea.mode = gmTools.coalesce(vaccination, 'new', 'edit')
603 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
604 dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination')))
605 if dlg.ShowModal() == wx.ID_OK:
606 dlg.Destroy()
607 return True
608 dlg.Destroy()
609 if not single_entry:
610 return True
611 return False
612
613 #----------------------------------------------------------------------
615
616 pat = gmPerson.gmCurrentPatient()
617 emr = pat.get_emr()
618
619 if parent is None:
620 parent = wx.GetApp().GetTopWindow()
621 #------------------------------------------------------------
622 def browse2schedules(vaccination=None):
623 dbcfg = gmCfg.cCfgSQL()
624 url = dbcfg.get2 (
625 option = 'external.urls.vaccination_plans',
626 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
627 bias = 'user',
628 default = u'http://www.bundesaerztekammer.de/downloads/STIKOEmpf2011.pdf'
629 )
630
631 gmNetworkTools.open_url_in_browser(url = url)
632 return False
633 #------------------------------------------------------------
634 def print_vaccs(vaccination=None):
635 print_vaccinations(parent = parent)
636 return False
637 #------------------------------------------------------------
638 def add_recall(vaccination=None):
639 if vaccination is None:
640 subject = _('vaccination recall')
641 else:
642 subject = _('vaccination recall (%s)') % vaccination['vaccine']
643
644 recall = gmProviderInbox.create_inbox_message (
645 message_type = _('Vaccination'),
646 subject = subject,
647 patient = pat.ID,
648 staff = None
649 )
650
651 if vaccination is not None:
652 recall['data'] = _('Existing vaccination:\n\n%s') % u'\n'.join(vaccination.format(
653 with_indications = True,
654 with_comment = True,
655 with_reaction = False,
656 date_format = '%Y %b %d'
657 ))
658 recall.save()
659
660 from Gnumed.wxpython import gmProviderInboxWidgets
661 gmProviderInboxWidgets.edit_inbox_message (
662 parent = parent,
663 message = recall,
664 single_entry = False
665 )
666
667 return False
668 #------------------------------------------------------------
669 def get_tooltip(vaccination):
670 if vaccination is None:
671 return None
672 return u'\n'.join(vaccination.format (
673 with_indications = True,
674 with_comment = True,
675 with_reaction = True,
676 date_format = '%Y %b %d'
677 ))
678 #------------------------------------------------------------
679 def edit(vaccination=None):
680 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
681 #------------------------------------------------------------
682 def delete(vaccination=None):
683 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
684 return True
685 #------------------------------------------------------------
686 def refresh(lctrl):
687
688 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
689
690 items = [ [
691 gmDateTime.pydt_strftime(v['date_given'], '%Y %b %d'),
692 v['vaccine'],
693 u', '.join(v['l10n_indications']),
694 v['batch_no'],
695 gmTools.coalesce(v['site'], u''),
696 gmTools.coalesce(v['reaction'], u''),
697 gmTools.coalesce(v['comment'], u'')
698 ] for v in vaccs ]
699
700 lctrl.set_string_items(items)
701 lctrl.set_data(vaccs)
702 #------------------------------------------------------------
703 gmListWidgets.get_choices_from_list (
704 parent = parent,
705 msg = _('\nComplete vaccination history for this patient.\n'),
706 caption = _('Showing vaccinations.'),
707 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
708 single_selection = True,
709 refresh_callback = refresh,
710 new_callback = edit,
711 edit_callback = edit,
712 delete_callback = delete,
713 list_tooltip_callback = get_tooltip,
714 left_extra_button = (_('Print'), _('Print vaccinations or recalls.'), print_vaccs),
715 middle_extra_button = (_('Recall'), _('Add a recall for a vaccination'), add_recall),
716 right_extra_button = (_('Vx schedules'), _('Open a browser showing vaccination schedules.'), browse2schedules)
717 )
718 #----------------------------------------------------------------------
719 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
720
721 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
722 """
723 - warn on apparent duplicates
724 - ask if "missing" (= previous, non-recorded) vaccinations
725 should be estimated and saved (add note "auto-generated")
726
727 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
728 """
730
731 try:
732 data = kwargs['vaccination']
733 del kwargs['vaccination']
734 except KeyError:
735 data = None
736
737 wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs)
738 gmEditArea.cGenericEditAreaMixin.__init__(self)
739
740 self.mode = 'new'
741 self.data = data
742 if data is not None:
743 self.mode = 'edit'
744
745 self.__init_ui()
746 #----------------------------------------------------------------
748 # adjust phrasewheels etc
749 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
750 self._PRW_provider.selection_only = False
751 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
752 if self.mode == 'edit':
753 self._BTN_select_indications.Disable()
754 #----------------------------------------------------------------
756
757 vaccine = self._PRW_vaccine.GetData(as_instance=True)
758
759 # if we are editing we do not allow using indications rather than a vaccine
760 if self.mode == u'edit':
761 if vaccine is None:
762 self._PRW_batch.unset_context(context = 'pk_vaccine')
763 self.__indications = []
764 else:
765 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
766 self.__indications = vaccine.indications
767 # we are entering a new vaccination
768 else:
769 if vaccine is None:
770 self._PRW_batch.unset_context(context = 'pk_vaccine')
771 self.__indications = []
772 self._BTN_select_indications.Enable()
773 else:
774 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
775 self.__indications = vaccine.indications
776 self._BTN_select_indications.Disable()
777
778 self.__refresh_indications()
779 #----------------------------------------------------------------
781 if self._PRW_reaction.GetValue().strip() == u'':
782 self._BTN_report.Enable(False)
783 else:
784 self._BTN_report.Enable(True)
785 #----------------------------------------------------------------
787 self._TCTRL_indications.SetValue(u'')
788 if len(self.__indications) == 0:
789 return
790 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
791 #----------------------------------------------------------------
792 # generic Edit Area mixin API
793 #----------------------------------------------------------------
795
796 has_errors = False
797
798 if not self._PRW_date_given.is_valid_timestamp(allow_empty = False):
799 has_errors = True
800
801 vaccine = self._PRW_vaccine.GetData(as_instance = True)
802
803 # we are editing, require vaccine rather than indications
804 if self.mode == u'edit':
805 if vaccine is None:
806 has_errors = True
807 self._PRW_vaccine.display_as_valid(False)
808 else:
809 self._PRW_vaccine.display_as_valid(True)
810 # we are creating, allow either vaccine or indications
811 else:
812 if vaccine is None:
813 if len(self.__indications) == 0:
814 self._PRW_vaccine.display_as_valid(False)
815 has_errors = True
816 else:
817 self._PRW_vaccine.display_as_valid(True)
818 else:
819 self._PRW_vaccine.display_as_valid(True)
820
821 if self._PRW_batch.GetValue().strip() == u'':
822 has_errors = True
823 self._PRW_batch.display_as_valid(False)
824 else:
825 self._PRW_batch.display_as_valid(True)
826
827 if self._PRW_episode.GetValue().strip() == u'':
828 self._PRW_episode.SetText(value = _('prevention'))
829
830 return (has_errors is False)
831 #----------------------------------------------------------------
833
834 vaccine = self._PRW_vaccine.GetData()
835 if vaccine is None:
836 data = self.__save_new_from_indications()
837 else:
838 data = self.__save_new_from_vaccine(vaccine = vaccine)
839
840 # must be done very late or else the property access
841 # will refresh the display such that later field
842 # access will return empty values
843 self.data = data
844
845 return True
846 #----------------------------------------------------------------
848
849 if len(self.__indications) == 0:
850 gmGuiHelpers.gm_show_info (
851 aTitle = _('Saving vaccination'),
852 aMessage = _('You must select at least one indication.')
853 )
854 return False
855
856 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ i['description'] for i in self.__indications ])
857
858 if vaccine is None:
859 for ind in self.__indications:
860 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ind['description']])
861 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
862 else:
863 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
864
865 return data
866 #----------------------------------------------------------------
868
869 emr = gmPerson.gmCurrentPatient().get_emr()
870
871 data = emr.add_vaccination (
872 episode = self._PRW_episode.GetData(can_create = True, is_open = False),
873 vaccine = vaccine,
874 batch_no = self._PRW_batch.GetValue().strip()
875 )
876
877 if self._CHBOX_anamnestic.GetValue() is True:
878 data['soap_cat'] = u's'
879 else:
880 data['soap_cat'] = u'p'
881
882 data['date_given'] = self._PRW_date_given.GetData()
883 data['site'] = self._PRW_site.GetValue().strip()
884 data['pk_provider'] = self._PRW_provider.GetData()
885 data['reaction'] = self._PRW_reaction.GetValue().strip()
886 data['comment'] = self._TCTRL_comment.GetValue().strip()
887
888 data.save()
889
890 return data
891 #----------------------------------------------------------------
893
894 if self._CHBOX_anamnestic.GetValue() is True:
895 self.data['soap_cat'] = u's'
896 else:
897 self.data['soap_cat'] = u'p'
898
899 self.data['date_given'] = self._PRW_date_given.GetData()
900 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
901 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
902 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
903 self.data['site'] = self._PRW_site.GetValue().strip()
904 self.data['pk_provider'] = self._PRW_provider.GetData()
905 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
906 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
907
908 self.data.save()
909
910 return True
911 #----------------------------------------------------------------
913 self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here())
914 self._CHBOX_anamnestic.SetValue(False)
915 self._PRW_vaccine.SetText(value = u'', data = None, suppress_smarts = True)
916 self._PRW_batch.unset_context(context = 'pk_vaccine')
917 self._PRW_batch.SetValue(u'')
918 self._PRW_episode.SetText(value = u'', data = None, suppress_smarts = True)
919 self._PRW_site.SetValue(u'')
920 self._PRW_provider.SetData(data = None)
921 self._PRW_reaction.SetText(value = u'', data = None, suppress_smarts = True)
922 self._BTN_report.Enable(False)
923 self._TCTRL_comment.SetValue(u'')
924
925 self.__indications = []
926 self.__refresh_indications()
927 self._BTN_select_indications.Enable()
928
929 self._PRW_date_given.SetFocus()
930 #----------------------------------------------------------------
932 self._PRW_date_given.SetText(data = self.data['date_given'])
933 if self.data['soap_cat'] == u's':
934 self._CHBOX_anamnestic.SetValue(True)
935 else:
936 self._CHBOX_anamnestic.SetValue(False)
937 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
938
939 self._PRW_batch.SetValue(self.data['batch_no'])
940 self._PRW_episode.SetData(data = self.data['pk_episode'])
941 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
942 self._PRW_provider.SetData(self.data['pk_provider'])
943 self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], u''))
944 if self.data['reaction'] is None:
945 self._BTN_report.Enable(False)
946 else:
947 self._BTN_report.Enable(True)
948 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
949
950 self.__indications = self.data.vaccine.indications
951 self.__refresh_indications()
952 self._BTN_select_indications.Disable()
953
954 self._PRW_date_given.SetFocus()
955 #----------------------------------------------------------------
957 self._PRW_date_given.SetText(data = self.data['date_given'])
958 #self._CHBOX_anamnestic.SetValue(False)
959 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
960
961 self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine'])
962 self._PRW_batch.SetValue(u'')
963
964 self._PRW_episode.SetData(data = self.data['pk_episode'])
965 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
966 self._PRW_provider.SetData(self.data['pk_provider'])
967 self._PRW_reaction.SetValue(u'')
968 self._BTN_report.Enable(False)
969 self._TCTRL_comment.SetValue(u'')
970
971 self.__indications = self.data.vaccine.indications
972 self.__refresh_indications()
973 self._BTN_select_indications.Enable()
974
975 self._PRW_date_given.SetFocus()
976 #----------------------------------------------------------------
977 # event handlers
978 #----------------------------------------------------------------
996 #----------------------------------------------------------------
999 # FIXME: could set newly generated vaccine here
1000 #----------------------------------------------------------------
1015
1016 #======================================================================
1017 #======================================================================
1018 #======================================================================
1019 #======================================================================
1021
1023 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
1024 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1025 self.__pat = gmPerson.gmCurrentPatient()
1026 # do this here so "import cImmunisationsPanel from gmVaccWidgets" works
1027 self.ID_VaccinatedIndicationsList = wx.NewId()
1028 self.ID_VaccinationsPerRegimeList = wx.NewId()
1029 self.ID_MissingShots = wx.NewId()
1030 self.ID_ActiveSchedules = wx.NewId()
1031 self.__do_layout()
1032 self.__register_interests()
1033 self.__reset_ui_content()
1034 #----------------------------------------------------
1036 #-----------------------------------------------
1037 # top part
1038 #-----------------------------------------------
1039 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
1040 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
1041
1042 #-----------------------------------------------
1043 # middle part
1044 #-----------------------------------------------
1045 # divider headings below editing area
1046 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
1047 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
1048 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
1049 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
1050 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
1051 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
1052 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
1053
1054 # left list: indications for which vaccinations have been given
1055 self.LBOX_vaccinated_indications = wx.ListBox(
1056 parent = self,
1057 id = self.ID_VaccinatedIndicationsList,
1058 choices = [],
1059 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1060 )
1061 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1062
1063 # right list: when an indication has been selected on the left
1064 # display the corresponding vaccinations on the right
1065 self.LBOX_given_shots = wx.ListBox(
1066 parent = self,
1067 id = self.ID_VaccinationsPerRegimeList,
1068 choices = [],
1069 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1070 )
1071 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1072
1073 self.LBOX_active_schedules = wx.ListBox (
1074 parent = self,
1075 id = self.ID_ActiveSchedules,
1076 choices = [],
1077 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1078 )
1079 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1080
1081 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1082 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1083 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1084 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1085
1086 #---------------------------------------------
1087 # bottom part
1088 #---------------------------------------------
1089 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1090 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1091 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1092
1093 self.LBOX_missing_shots = wx.ListBox (
1094 parent = self,
1095 id = self.ID_MissingShots,
1096 choices = [],
1097 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1098 )
1099 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1100
1101 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1102 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1103
1104 # alert caption
1105 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1106
1107 #---------------------------------------------
1108 # add all elements to the main background sizer
1109 #---------------------------------------------
1110 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1111 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1112 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1113 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1114 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1115 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1116 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1117 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1118
1119 self.SetAutoLayout(True)
1120 self.SetSizer(self.mainsizer)
1121 self.mainsizer.Fit(self)
1122 #----------------------------------------------------
1124 # wxPython events
1125 wx.EVT_SIZE(self, self.OnSize)
1126 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1127 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1128 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1129 # wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton)
1130
1131 # client internal signals
1132 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1133 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1134 #----------------------------------------------------
1135 # event handlers
1136 #----------------------------------------------------
1140 #----------------------------------------------------
1142 """Paste previously given shot into edit area.
1143 """
1144 self.editarea.set_data(aVacc=event.GetClientData())
1145 #----------------------------------------------------
1148 #----------------------------------------------------
1150 """Update right hand middle list to show vaccinations given for selected indication."""
1151 ind_list = event.GetEventObject()
1152 selected_item = ind_list.GetSelection()
1153 ind = ind_list.GetClientData(selected_item)
1154 # clear list
1155 self.LBOX_given_shots.Set([])
1156 emr = self.__pat.get_emr()
1157 shots = emr.get_vaccinations(indications = [ind])
1158 # FIXME: use Set() for entire array (but problem with client_data)
1159 for shot in shots:
1160 if shot['is_booster']:
1161 marker = 'B'
1162 else:
1163 marker = '#%s' % shot['seq_no']
1164 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1165 self.LBOX_given_shots.Append(label, shot)
1166 #----------------------------------------------------
1168 # clear edit area
1169 self.editarea.set_data()
1170 # clear lists
1171 self.LBOX_vaccinated_indications.Clear()
1172 self.LBOX_given_shots.Clear()
1173 self.LBOX_active_schedules.Clear()
1174 self.LBOX_missing_shots.Clear()
1175 #----------------------------------------------------
1177 # clear lists
1178 self.LBOX_vaccinated_indications.Clear()
1179 self.LBOX_given_shots.Clear()
1180 self.LBOX_active_schedules.Clear()
1181 self.LBOX_missing_shots.Clear()
1182
1183 emr = self.__pat.get_emr()
1184
1185 t1 = time.time()
1186 # populate vaccinated-indications list
1187 # FIXME: consider adding virtual indication "most recent" to
1188 # FIXME: display most recent of all indications as suggested by Syan
1189 status, indications = emr.get_vaccinated_indications()
1190 # FIXME: would be faster to use Set() but can't
1191 # use Set(labels, client_data), and have to know
1192 # line position in SetClientData :-(
1193 for indication in indications:
1194 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1195 # self.LBOX_vaccinated_indications.Set(lines)
1196 # self.LBOX_vaccinated_indications.SetClientData(data)
1197 print "vaccinated indications took", time.time()-t1, "seconds"
1198
1199 t1 = time.time()
1200 # populate active schedules list
1201 scheds = emr.get_scheduled_vaccination_regimes()
1202 if scheds is None:
1203 label = _('ERROR: cannot retrieve active vaccination schedules')
1204 self.LBOX_active_schedules.Append(label)
1205 elif len(scheds) == 0:
1206 label = _('no active vaccination schedules')
1207 self.LBOX_active_schedules.Append(label)
1208 else:
1209 for sched in scheds:
1210 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1211 self.LBOX_active_schedules.Append(label)
1212 print "active schedules took", time.time()-t1, "seconds"
1213
1214 t1 = time.time()
1215 # populate missing-shots list
1216 missing_shots = emr.get_missing_vaccinations()
1217 print "getting missing shots took", time.time()-t1, "seconds"
1218 if missing_shots is None:
1219 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1220 self.LBOX_missing_shots.Append(label, None)
1221 return True
1222 # due
1223 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1224 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1225 for shot in missing_shots['due']:
1226 if shot['overdue']:
1227 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1228 weeks = days_left / 7
1229 # amount_overdue, seq_no, indication, regime, vacc_comment
1230 label = overdue_template % (
1231 years,
1232 weeks,
1233 shot['seq_no'],
1234 shot['l10n_indication'],
1235 shot['regime'],
1236 shot['vacc_comment']
1237 )
1238 self.LBOX_missing_shots.Append(label, shot)
1239 else:
1240 # time_left, seq_no, regime, latest_due, vacc_comment
1241 label = due_template % (
1242 shot['time_left'].days / 7,
1243 shot['seq_no'],
1244 shot['indication'],
1245 shot['regime'],
1246 shot['latest_due'].strftime('%m/%Y'),
1247 shot['vacc_comment']
1248 )
1249 self.LBOX_missing_shots.Append(label, shot)
1250 # booster
1251 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1252 for shot in missing_shots['boosters']:
1253 # indication, regime, vacc_comment
1254 label = lbl_template % (
1255 shot['l10n_indication'],
1256 shot['regime'],
1257 shot['vacc_comment']
1258 )
1259 self.LBOX_missing_shots.Append(label, shot)
1260 print "displaying missing shots took", time.time()-t1, "seconds"
1261
1262 return True
1263 #----------------------------------------------------
1266 # FIXME:
1267 # if has_focus:
1268 # wxCallAfter(self.__reset_ui_content)
1269 # else:
1270 # return 1
1271 #----------------------------------------------------
1274 # FIXME:
1275 # if has_focus:
1276 # wxCallAfter(self.__reset_ui_content)
1277 # else:
1278 # is_stale == True
1279 # return 1
1280 #======================================================================
1281 # main
1282 #----------------------------------------------------------------------
1283 if __name__ == "__main__":
1284
1285 if len(sys.argv) < 2:
1286 sys.exit()
1287
1288 if sys.argv[1] != u'test':
1289 sys.exit()
1290
1291 app = wx.PyWidgetTester(size = (600, 600))
1292 app.SetWidget(cATCPhraseWheel, -1)
1293 app.MainLoop()
1294
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Sat Oct 5 03:57:24 2013 | http://epydoc.sourceforge.net |