1 """GNUmed immunisation/vaccination widgets.
2
3 Modelled after Richard Terry's design document.
4
5 copyright: authors
6 """
7
8 __version__ = "$Revision: 1.36 $"
9 __author__ = "R.Terry, S.J.Tan, K.Hilbert"
10 __license__ = "GPL (details at http://www.gnu.org)"
11
12 import sys, time, logging
13
14
15 import wx
16 import mx.DateTime as mxDT
17
18
19 if __name__ == '__main__':
20 sys.path.insert(0, '../../')
21 from Gnumed.pycommon import gmDispatcher, gmMatchProvider, gmTools, gmI18N
22 from Gnumed.pycommon import gmCfg, gmDateTime
23 from Gnumed.business import gmPerson, gmVaccination, gmSurgery
24 from Gnumed.wxpython import gmPhraseWheel, gmTerryGuiParts, gmRegetMixin, gmGuiHelpers
25 from Gnumed.wxpython import gmEditArea, gmListWidgets
26
27
28 _log = logging.getLogger('gm.vaccination')
29 _log.info(__version__)
30
31
32
33
35
36 if parent is None:
37 parent = wx.GetApp().GetTopWindow()
38
39 def refresh(lctrl):
40 inds = gmVaccination.get_indications(order_by = 'description')
41
42 items = [ [
43 i['description'],
44 gmTools.coalesce (
45 i['atcs_single_indication'],
46 u'',
47 u'%s'
48 ),
49 gmTools.coalesce (
50 i['atcs_combi_indication'],
51 u'',
52 u'%s'
53 ),
54 u'%s' % i['id']
55 ] for i in inds ]
56
57 lctrl.set_string_items(items)
58 lctrl.set_data(inds)
59
60 gmListWidgets.get_choices_from_list (
61 parent = parent,
62 msg = _('\nThe vaccination indications currently known to GNUmed.\n'),
63 caption = _('Showing vaccination indications.'),
64 columns = [ _('Indication'), _('ATCs (single indication vaccines)'), _('ATCs (combi-indication vaccines)'), u'#' ],
65 single_selection = True,
66 refresh_callback = refresh
67 )
68
69
70
72
73 if parent is None:
74 parent = wx.GetApp().GetTopWindow()
75
76 def refresh(lctrl):
77 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
78
79 items = [ [
80 u'%s' % v['pk_brand'],
81 u'%s%s' % (
82 v['vaccine'],
83 gmTools.bool2subst (
84 v['is_fake_vaccine'],
85 u' (%s)' % _('fake'),
86 u''
87 )
88 ),
89 v['preparation'],
90 u'%s (%s)' % (v['route_abbreviation'], v['route_description']),
91 gmTools.bool2subst(v['is_live'], gmTools.u_checkmark_thin, u''),
92 gmTools.coalesce(v['atc_code'], u''),
93 u'%s%s' % (
94 gmTools.coalesce(v['min_age'], u'?'),
95 gmTools.coalesce(v['max_age'], u'?', u' - %s'),
96 ),
97 gmTools.coalesce(v['comment'], u'')
98 ] for v in vaccines ]
99 lctrl.set_string_items(items)
100 lctrl.set_data(vaccines)
101
102 gmListWidgets.get_choices_from_list (
103 parent = parent,
104 msg = _('\nThe vaccines currently known to GNUmed.\n'),
105 caption = _('Showing vaccines.'),
106 columns = [ u'#', _('Brand'), _('Preparation'), _(u'Route'), _('Live'), _('ATC'), _('Age range'), _('Comment') ],
107 single_selection = True,
108 refresh_callback = refresh
109 )
110
112
114
115 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
116
117 context = {
118 u'ctxt_vaccine': {
119 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s',
120 u'placeholder': u'pk_vaccine'
121 }
122 }
123
124 query = u"""
125 SELECT code, batch_no FROM (
126
127 SELECT distinct on (batch_no) code, batch_no, rank FROM (
128
129 (
130 -- batch_no by vaccine
131 SELECT
132 batch_no AS code,
133 batch_no,
134 1 AS rank
135 FROM
136 clin.v_pat_vaccinations
137 WHERE
138 batch_no %(fragment_condition)s
139 %(ctxt_vaccine)s
140 ) UNION ALL (
141 -- batch_no for any vaccine
142 SELECT
143 batch_no AS code,
144 batch_no,
145 2 AS rank
146 FROM
147 clin.v_pat_vaccinations
148 WHERE
149 batch_no %(fragment_condition)s
150 )
151
152 ) AS matching_batch_nos
153
154 ) as unique_matches
155
156 ORDER BY rank, batch_no
157 LIMIT 25
158 """
159 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
160 mp.setThresholds(1, 2, 3)
161 self.matcher = mp
162
163 self.unset_context(context = u'pk_vaccine')
164 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
165 self.selection_only = False
166
168
170
171 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
172
173
174 query = u"""
175 SELECT pk_vaccine, description FROM (
176
177 SELECT DISTINCT ON (pk_vaccine) pk_vaccine, description FROM (
178
179 (
180 -- fragment -> vaccine
181 SELECT
182 pk_vaccine,
183 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')'
184 AS description
185 FROM
186 clin.v_vaccines
187 WHERE
188 vaccine %(fragment_condition)s
189
190 ) union all (
191
192 -- fragment -> localized indication -> vaccines
193 SELECT
194 pk_vaccine,
195 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')'
196 AS description
197 FROM
198 clin.v_indications4vaccine
199 WHERE
200 l10n_indication %(fragment_condition)s
201
202 ) union all (
203
204 -- fragment -> indication -> vaccines
205 SELECT
206 pk_vaccine,
207 vaccine || ' (' || array_to_string(indications, ', ') || ')'
208 AS description
209 FROM
210 clin.v_indications4vaccine
211 WHERE
212 indication %(fragment_condition)s
213 )
214 ) AS distinct_total
215
216 ) AS total
217
218 ORDER by description
219 LIMIT 25
220 """
221 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
222 mp.setThresholds(1, 2, 3)
223 self.matcher = mp
224
225 self.selection_only = True
226
229
230
231
243
245
246 pat = gmPerson.gmCurrentPatient()
247 emr = pat.get_emr()
248
249 if parent is None:
250 parent = wx.GetApp().GetTopWindow()
251
252
253 def edit(vaccination=None):
254 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = True)
255
256 def delete(vaccination=None):
257 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
258 return True
259
260 def refresh(lctrl):
261
262 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
263
264 items = [ [
265 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
266 v['vaccine'],
267 u', '.join(v['indications']),
268 v['batch_no'],
269 gmTools.coalesce(v['site'], u''),
270 gmTools.coalesce(v['reaction'], u''),
271 gmTools.coalesce(v['comment'], u'')
272 ] for v in vaccs ]
273
274 lctrl.set_string_items(items)
275 lctrl.set_data(vaccs)
276
277 gmListWidgets.get_choices_from_list (
278 parent = parent,
279 msg = _('\nComplete vaccination history for this patient.\n'),
280 caption = _('Showing vaccinations.'),
281 columns = [ u'Date', _('Vaccine'), _(u'Indications'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
282 single_selection = True,
283 refresh_callback = refresh,
284 new_callback = edit,
285 edit_callback = edit,
286 delete_callback = delete
287 )
288
289 from Gnumed.wxGladeWidgets import wxgVaccinationIndicationsPnl
290
292
294
295 wxgVaccinationIndicationsPnl.wxgVaccinationIndicationsPnl.__init__(self, *args, **kwargs)
296
297 self.__indication2field = {
298 u'coxiella burnetii (Q fever)': self._CHBOX_coxq,
299 u'salmonella typhi (typhoid)': self._CHBOX_typhoid,
300 u'varicella (chickenpox, shingles)': self._CHBOX_varicella,
301 u'influenza (seasonal)': self._CHBOX_influenza,
302 u'bacillus anthracis (Anthrax)': self._CHBOX_anthrax,
303 u'human papillomavirus': self._CHBOX_hpv,
304 u'rotavirus': self._CHBOX_rota,
305 u'tuberculosis': self._CHBOX_tuberculosis,
306 u'variola virus (smallpox)': self._CHBOX_smallpox,
307 u'influenza (H1N1)': self._CHBOX_h1n1,
308 u'cholera': self._CHBOX_cholera,
309 u'diphtheria': self._CHBOX_diphtheria,
310 u'haemophilus influenzae b': self._CHBOX_hib,
311 u'hepatitis A': self._CHBOX_hepA,
312 u'hepatitis B': self._CHBOX_hepB,
313 u'japanese B encephalitis': self._CHBOX_japanese,
314 u'measles': self._CHBOX_measles,
315 u'meningococcus A': self._CHBOX_menA,
316 u'meningococcus C': self._CHBOX_menC,
317 u'meningococcus W': self._CHBOX_menW,
318 u'meningococcus Y': self._CHBOX_menY,
319 u'mumps': self._CHBOX_mumps,
320 u'pertussis': self._CHBOX_pertussis,
321 u'pneumococcus': self._CHBOX_pneumococcus,
322 u'poliomyelitis': self._CHBOX_polio,
323 u'rabies': self._CHBOX_rabies,
324 u'rubella': self._CHBOX_rubella,
325 u'tetanus': self._CHBOX_tetanus,
326 u'tick-borne meningoencephalitis': self._CHBOX_fsme,
327 u'yellow fever': self._CHBOX_yellow_fever,
328 u'yersinia pestis': self._CHBOX_yersinia_pestis
329 }
330
332 for field in self.__dict__.keys():
333 if field.startswith('_CHBOX_'):
334 self.__dict__[field].Enable()
335 self.Enable()
336
338 for field in self.__dict__.keys():
339 if field.startswith('_CHBOX_'):
340 self.__dict__[field].Disable()
341 self.Disable()
342
344 for field in self.__dict__.keys():
345 if field.startswith('_CHBOX_'):
346 self.__dict__[field].SetValue(False)
347
348 - def select(self, indications=None):
349 for indication in indications:
350 self.__indication2field[indication].SetValue(True)
351
353 indications = []
354 for indication in self.__indication2field.keys():
355 if self.__indication2field[indication].IsChecked():
356 indications.append(indication)
357 return indications
358
359 selected_indications = property(_get_selected_indications, lambda x:x)
360
362 for indication in self.__indication2field.keys():
363 if self.__indication2field[indication].IsChecked():
364 return True
365 return False
366
367 has_selection = property(_get_has_selection, lambda x:x)
368
369
370 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
371
372 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
373
391
398
400
401 vaccine = self._PRW_vaccine.GetData(as_instance=True)
402
403
404 if self.mode == u'edit':
405 self._PNL_indications.clear_all()
406 if vaccine is None:
407 self._PRW_batch.unset_context(context = 'pk_vaccine')
408 else:
409 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
410 self._PNL_indications.select(indications = vaccine['indications'])
411 self._PNL_indications.disable_all()
412
413
414 else:
415 if vaccine is None:
416 self._PRW_batch.unset_context(context = 'pk_vaccine')
417 self._PNL_indications.enable_all()
418 else:
419 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
420 self._PNL_indications.clear_all()
421 self._PNL_indications.select(indications = vaccine['indications'])
422 self._PNL_indications.disable_all()
423
425 if self._PRW_reaction.GetValue().strip() == u'':
426 self._BTN_report.Enable(False)
427 else:
428 self._BTN_report.Enable(True)
429
430
431
433
434 has_errors = False
435
436 if not self._DP_date_given.is_valid_timestamp(allow_none = False):
437 has_errors = True
438
439 vaccine = self._PRW_vaccine.GetData(as_instance = True)
440
441
442 if self.mode == u'edit':
443 if vaccine is None:
444 has_errors = True
445 self._PRW_vaccine.display_as_valid(False)
446 else:
447 self._PRW_vaccine.display_as_valid(True)
448 self._PNL_indications.clear_all()
449 self._PNL_indications.select(indications = vaccine['indications'])
450 self._PNL_indications.disable_all()
451
452 else:
453 if vaccine is None:
454 if self._PNL_indications.has_selection:
455 self._PRW_vaccine.display_as_valid(True)
456 else:
457 has_errors = True
458 self._PRW_vaccine.display_as_valid(False)
459 else:
460 self._PRW_vaccine.display_as_valid(True)
461
462 if self._PRW_batch.GetValue().strip() == u'':
463 has_errors = True
464 self._PRW_batch.display_as_valid(False)
465 else:
466 self._PRW_batch.display_as_valid(True)
467
468 if self._PRW_episode.GetValue().strip() == u'':
469 self._PRW_episode.SetText(value = _('prevention'))
470
471 return (has_errors is False)
472
474
475 vaccine = self._PRW_vaccine.GetData()
476 if vaccine is None:
477 data = self.__save_new_from_indications()
478 else:
479 data = self.__save_new_from_vaccine(vaccine = vaccine)
480
481
482
483
484 self.data = data
485
486 return True
487
501
526
528
529 if self._CHBOX_anamnestic.GetValue() is True:
530 self.data['soap_cat'] = u's'
531 else:
532 self.data['soap_cat'] = u'p'
533
534 self.data['date_given'] = self._DP_date_given.get_pydt()
535 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
536 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
537 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
538 self.data['site'] = self._PRW_site.GetValue().strip()
539 self.data['pk_provider'] = self._PRW_provider.GetData()
540 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
541 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
542
543 self.data.save()
544
545 return True
546
564
589
610
632
633
634
636 """
637 - warn on apparent duplicates
638 - ask if "missing" (= previous, non-recorded) vaccinations
639 should be estimated and saved (add note "auto-generated")
640 """
641 - def __init__(self, parent, id, pos, size, style, data_sink=None):
644
646
647
648
649
650
651
652
653
654
655
656
657
658
659 query = """
660 select
661 pk,
662 trade_name
663 from
664 vaccine
665 where
666 short_name || ' ' || trade_name %(fragment_condition)s
667 limit 25"""
668 mp = gmMatchProvider.cMatchProvider_SQL2([query])
669 mp.setThresholds(aWord=2, aSubstring=4)
670 self.fld_vaccine = gmPhraseWheel.cPhraseWheel(
671 parent = parent
672 , id = -1
673 , style = wx.SIMPLE_BORDER
674 )
675 self.fld_vaccine.matcher = mp
676 gmEditArea._decorate_editarea_field(self.fld_vaccine)
677 self._add_field(
678 line = 1,
679 pos = 1,
680 widget = self.fld_vaccine,
681 weight = 3
682 )
683
684
685 self.fld_date_given = gmEditArea.cEditAreaField(parent)
686 self._add_field(
687 line = 2,
688 pos = 1,
689 widget = self.fld_date_given,
690 weight = 2
691 )
692
693
694 self.fld_batch_no = gmEditArea.cEditAreaField(parent)
695 self._add_field(
696 line = 3,
697 pos = 1,
698 widget = self.fld_batch_no,
699 weight = 1
700 )
701
702
703 query = """
704 select distinct on (tmp.site)
705 tmp.id, tmp.site
706 from (
707 select id, site
708 from vaccination
709 group by id, site
710 order by count(site)
711 ) as tmp
712 where
713 tmp.site %(fragment_condition)s
714 limit 10"""
715 mp = gmMatchProvider.cMatchProvider_SQL2([query])
716 mp.setThresholds(aWord=1, aSubstring=3)
717 self.fld_site_given = gmPhraseWheel.cPhraseWheel(
718 parent = parent
719 , id = -1
720 , style = wx.SIMPLE_BORDER
721 )
722 self.fld_site_given.matcher = mp
723 gmEditArea._decorate_editarea_field(self.fld_site_given)
724 self._add_field(
725 line = 4,
726 pos = 1,
727 widget = self.fld_site_given,
728 weight = 1
729 )
730
731
732 query = """
733 select distinct on (narrative)
734 id, narrative
735 from
736 vaccination
737 where
738 narrative %(fragment_condition)s
739 limit 30"""
740 mp = gmMatchProvider.cMatchProvider_SQL2([query])
741 mp.setThresholds(aWord=3, aSubstring=5)
742 self.fld_progress_note = gmPhraseWheel.cPhraseWheel(
743 parent = parent
744 , id = -1
745 , style = wx.SIMPLE_BORDER
746 )
747 self.fld_progress_note = mp
748 gmEditArea._decorate_editarea_field(self.fld_progress_note)
749 self._add_field(
750 line = 5,
751 pos = 1,
752 widget = self.fld_progress_note,
753 weight = 1
754 )
755 return 1
756
758 self._add_prompt(line = 1, label = _("Vaccine"))
759 self._add_prompt(line = 2, label = _("Date given"))
760 self._add_prompt(line = 3, label = _("Serial #"))
761 self._add_prompt(line = 4, label = _("Site injected"))
762 self._add_prompt(line = 5, label = _("Progress Note"))
763
764 - def _save_new_entry(self, episode):
765
766 if self.__data_sink is None:
767
768 emr = self._patient.get_emr()
769
770 successfull, data = emr.add_vaccination(vaccine=self.fld_vaccine.GetValue(), episode=episode)
771 if not successfull:
772 gmDispatcher.send(signal = 'statustext', msg =_('Cannot save vaccination: %s') % data)
773 return False
774
775 data['pk_provider'] = gmPerson.gmCurrentProvider()['pk_staff']
776 data['date'] = self.fld_date_given.GetValue()
777 data['narrative'] = self.fld_progress_note.GetValue()
778 data['site'] = self.fld_site_given.GetValue()
779 data['batch_no'] = self.fld_batch_no.GetValue()
780 successful, err = data.save_payload()
781 if not successful:
782 gmDispatcher.send(signal = 'statustext', msg =_('Cannot save new vaccination: %s') % err)
783 return False
784 gmDispatcher.send(signal = 'statustext', msg =_('Vaccination saved.'))
785 self.data = data
786 return True
787 else:
788
789 data = {
790 'vaccine': self.fld_vaccine.GetValue(),
791 'pk_provider': gmPerson.gmCurrentProvider()['pk_staff'],
792 'date': self.fld_date_given.GetValue(),
793 'narrative': self.fld_progress_note.GetValue(),
794 'site': self.fld_site_given.GetValue(),
795 'batch_no': self.fld_batch_no.GetValue()
796 }
797
798 successful = self.__data_sink (
799 popup_type = 'vaccination',
800 data = data,
801 desc = _('shot: %s, %s, %s') % (data['date'], data['vaccine'], data['site'])
802 )
803 if not successful:
804 gmDispatcher.send(signal = 'statustext', msg =_('Cannot queue new vaccination.'))
805 return False
806 gmDispatcher.send(signal = 'statustext', msg =_('Vaccination queued for saving.'))
807 return True
808
810 """Update vaccination object and persist to backend.
811 """
812 self.data['vaccine'] = self.fld_vaccine.GetValue()
813 self.data['batch_no'] = self.fld_batch_no.GetValue()
814 self.data['date'] = self.fld_date_given.GetValue()
815 self.data['site'] = self.fld_site_given.GetValue()
816 self.data['narrative'] = self.fld_progress_note.GetValue()
817 successfull, data = self.data.save_payload()
818 if not successfull:
819 gmDispatcher.send(signal = 'statustext', msg =_('Cannot update vaccination: %s') % err)
820 return False
821 gmDispatcher.send(signal = 'statustext', msg =_('Vaccination updated.'))
822 return True
823
825 if self.data is None:
826 return self._save_new_entry(episode=episode)
827 else:
828 return self._save_modified_entry()
829
831 """Set edit area fields with vaccination object data.
832
833 - set defaults if no object is passed in, this will
834 result in a new object being created upon saving
835 """
836
837 if aVacc is None:
838 self.data = None
839 self.fld_vaccine.SetValue('')
840 self.fld_batch_no.SetValue('')
841 self.fld_date_given.SetValue((time.strftime('%Y-%m-%d', time.localtime())))
842 self.fld_site_given.SetValue(_('left/right deltoid'))
843 self.fld_progress_note.SetValue('')
844 return True
845
846
847 if isinstance(aVacc, gmVaccination.cVaccination):
848 self.data = aVacc
849 self.fld_vaccine.SetValue(aVacc['vaccine'])
850 self.fld_batch_no.SetValue(aVacc['batch_no'])
851 self.fld_date_given.SetValue(aVacc['date'].strftime('%Y-%m-%d'))
852 self.fld_site_given.SetValue(aVacc['site'])
853 self.fld_progress_note.SetValue(aVacc['narrative'])
854 return True
855
856
857 if isinstance(aVacc, gmVaccination.cMissingVaccination):
858 self.data = None
859
860 self.fld_vaccine.SetValue('')
861 self.fld_batch_no.SetValue('')
862 self.fld_date_given.SetValue((time.strftime('%Y-%m-%d', time.localtime())))
863
864 self.fld_site_given.SetValue(_('left/right deltoid'))
865 if aVacc['overdue']:
866 self.fld_progress_note.SetValue(_('was due: %s, delayed because:') % aVacc['latest_due'].strftime('%x'))
867 else:
868 self.fld_progress_note.SetValue('')
869 return True
870
871
872 if isinstance(aVacc, gmVaccination.cMissingBooster):
873 self.data = None
874 self.fld_vaccine.SetValue('')
875 self.fld_batch_no.SetValue('')
876 self.fld_date_given.SetValue((time.strftime('%Y-%m-%d', time.localtime())))
877
878 self.fld_site_given.SetValue(_('left/right deltoid'))
879 if aVacc['overdue']:
880 self.fld_progress_note.SetValue(_('booster: was due: %s, delayed because:') % aVacc['latest_due'].strftime('%Y-%m-%d'))
881 else:
882 self.fld_progress_note.SetValue(_('booster'))
883 return True
884
885 _log.Log(gmLog.lErr, 'do not know how to handle [%s:%s]' % (type(aVacc), str(aVacc)))
886 return False
887
889
891 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
892 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
893 self.__pat = gmPerson.gmCurrentPatient()
894
895 self.ID_VaccinatedIndicationsList = wx.NewId()
896 self.ID_VaccinationsPerRegimeList = wx.NewId()
897 self.ID_MissingShots = wx.NewId()
898 self.ID_ActiveSchedules = wx.NewId()
899 self.__do_layout()
900 self.__register_interests()
901 self.__reset_ui_content()
902
904
905
906
907 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
908 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
909
910
911
912
913
914 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
915 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
916 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
917 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
918 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
919 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
920 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
921
922
923 self.LBOX_vaccinated_indications = wx.ListBox(
924 parent = self,
925 id = self.ID_VaccinatedIndicationsList,
926 choices = [],
927 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
928 )
929 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
930
931
932
933 self.LBOX_given_shots = wx.ListBox(
934 parent = self,
935 id = self.ID_VaccinationsPerRegimeList,
936 choices = [],
937 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
938 )
939 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
940
941 self.LBOX_active_schedules = wx.ListBox (
942 parent = self,
943 id = self.ID_ActiveSchedules,
944 choices = [],
945 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
946 )
947 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
948
949 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
950 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
951 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
952 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
953
954
955
956
957 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
958 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
959 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
960
961 self.LBOX_missing_shots = wx.ListBox (
962 parent = self,
963 id = self.ID_MissingShots,
964 choices = [],
965 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
966 )
967 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
968
969 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
970 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
971
972
973 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
974
975
976
977
978 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
979 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
980 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
981 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
982 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
983 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
984 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
985 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
986
987 self.SetAutoLayout(True)
988 self.SetSizer(self.mainsizer)
989 self.mainsizer.Fit(self)
990
992
993 wx.EVT_SIZE(self, self.OnSize)
994 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
995 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
996 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
997
998
999
1000 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1001 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1002
1003
1004
1006 w, h = event.GetSize()
1007 self.mainsizer.SetDimension (0, 0, w, h)
1008
1010 """Paste previously given shot into edit area.
1011 """
1012 self.editarea.set_data(aVacc=event.GetClientData())
1013
1015 self.editarea.set_data(aVacc = event.GetClientData())
1016
1018 """Update right hand middle list to show vaccinations given for selected indication."""
1019 ind_list = event.GetEventObject()
1020 selected_item = ind_list.GetSelection()
1021 ind = ind_list.GetClientData(selected_item)
1022
1023 self.LBOX_given_shots.Set([])
1024 emr = self.__pat.get_emr()
1025 shots = emr.get_vaccinations(indications = [ind])
1026
1027 for shot in shots:
1028 if shot['is_booster']:
1029 marker = 'B'
1030 else:
1031 marker = '#%s' % shot['seq_no']
1032 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1033 self.LBOX_given_shots.Append(label, shot)
1034
1036
1037 self.editarea.set_data()
1038
1039 self.LBOX_vaccinated_indications.Clear()
1040 self.LBOX_given_shots.Clear()
1041 self.LBOX_active_schedules.Clear()
1042 self.LBOX_missing_shots.Clear()
1043
1045
1046 self.LBOX_vaccinated_indications.Clear()
1047 self.LBOX_given_shots.Clear()
1048 self.LBOX_active_schedules.Clear()
1049 self.LBOX_missing_shots.Clear()
1050
1051 emr = self.__pat.get_emr()
1052
1053 t1 = time.time()
1054
1055
1056
1057 status, indications = emr.get_vaccinated_indications()
1058
1059
1060
1061 for indication in indications:
1062 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1063
1064
1065 print "vaccinated indications took", time.time()-t1, "seconds"
1066
1067 t1 = time.time()
1068
1069 scheds = emr.get_scheduled_vaccination_regimes()
1070 if scheds is None:
1071 label = _('ERROR: cannot retrieve active vaccination schedules')
1072 self.LBOX_active_schedules.Append(label)
1073 elif len(scheds) == 0:
1074 label = _('no active vaccination schedules')
1075 self.LBOX_active_schedules.Append(label)
1076 else:
1077 for sched in scheds:
1078 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1079 self.LBOX_active_schedules.Append(label)
1080 print "active schedules took", time.time()-t1, "seconds"
1081
1082 t1 = time.time()
1083
1084 missing_shots = emr.get_missing_vaccinations()
1085 print "getting missing shots took", time.time()-t1, "seconds"
1086 if missing_shots is None:
1087 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1088 self.LBOX_missing_shots.Append(label, None)
1089 return True
1090
1091 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1092 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1093 for shot in missing_shots['due']:
1094 if shot['overdue']:
1095 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1096 weeks = days_left / 7
1097
1098 label = overdue_template % (
1099 years,
1100 weeks,
1101 shot['seq_no'],
1102 shot['l10n_indication'],
1103 shot['regime'],
1104 shot['vacc_comment']
1105 )
1106 self.LBOX_missing_shots.Append(label, shot)
1107 else:
1108
1109 label = due_template % (
1110 shot['time_left'].days / 7,
1111 shot['seq_no'],
1112 shot['indication'],
1113 shot['regime'],
1114 shot['latest_due'].strftime('%m/%Y'),
1115 shot['vacc_comment']
1116 )
1117 self.LBOX_missing_shots.Append(label, shot)
1118
1119 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1120 for shot in missing_shots['boosters']:
1121
1122 label = lbl_template % (
1123 shot['l10n_indication'],
1124 shot['regime'],
1125 shot['vacc_comment']
1126 )
1127 self.LBOX_missing_shots.Append(label, shot)
1128 print "displaying missing shots took", time.time()-t1, "seconds"
1129
1130 return True
1131
1132 - def _on_post_patient_selection(self, **kwargs):
1134
1135
1136
1137
1138
1139
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151 if __name__ == "__main__":
1152
1153 if len(sys.argv) < 2:
1154 sys.exit()
1155
1156 if sys.argv[1] != u'test':
1157 sys.exit()
1158
1159 app = wxPyWidgetTester(size = (600, 600))
1160 app.SetWidget(cImmunisationsPanel, -1)
1161 app.MainLoop()
1162
1163