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 v2 or later (details at http://www.gnu.org)"
11
12 import sys, time, logging, webbrowser
13
14
15 import wx
16
17
18 if __name__ == '__main__':
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmDispatcher, gmMatchProvider, gmTools, gmI18N
21 from Gnumed.pycommon import gmCfg, gmDateTime
22 from Gnumed.business import gmPerson, gmVaccination, gmSurgery
23 from Gnumed.wxpython import gmPhraseWheel, gmTerryGuiParts, gmRegetMixin, gmGuiHelpers
24 from Gnumed.wxpython import gmEditArea, gmListWidgets
25
26
27 _log = logging.getLogger('gm.vaccination')
28 _log.info(__version__)
29
30
31
32
34
35 if parent is None:
36 parent = wx.GetApp().GetTopWindow()
37
38 def refresh(lctrl):
39 inds = gmVaccination.get_indications(order_by = 'description')
40
41 items = [ [
42 i['description'],
43 gmTools.coalesce (
44 i['atcs_single_indication'],
45 u'',
46 u'%s'
47 ),
48 gmTools.coalesce (
49 i['atcs_combi_indication'],
50 u'',
51 u'%s'
52 ),
53 u'%s' % i['id']
54 ] for i in inds ]
55
56 lctrl.set_string_items(items)
57 lctrl.set_data(inds)
58
59 gmListWidgets.get_choices_from_list (
60 parent = parent,
61 msg = _('\nConditions preventable by vaccination as currently known to GNUmed.\n'),
62 caption = _('Showing vaccination preventable conditions.'),
63 columns = [ _('Condition'), _('ATCs: single-condition vaccines'), _('ATCs: multi-condition vaccines'), u'#' ],
64 single_selection = True,
65 refresh_callback = refresh
66 )
67
68 from Gnumed.wxGladeWidgets import wxgVaccinationIndicationsPnl
69
71
73
74 wxgVaccinationIndicationsPnl.wxgVaccinationIndicationsPnl.__init__(self, *args, **kwargs)
75
76 self.__indication2field = {
77 u'coxiella burnetii (Q fever)': self._CHBOX_coxq,
78 u'salmonella typhi (typhoid)': self._CHBOX_typhoid,
79 u'varicella (chickenpox, shingles)': self._CHBOX_varicella,
80 u'influenza (seasonal)': self._CHBOX_influenza,
81 u'bacillus anthracis (Anthrax)': self._CHBOX_anthrax,
82 u'human papillomavirus': self._CHBOX_hpv,
83 u'rotavirus': self._CHBOX_rota,
84 u'tuberculosis': self._CHBOX_tuberculosis,
85 u'variola virus (smallpox)': self._CHBOX_smallpox,
86 u'influenza (H1N1)': self._CHBOX_h1n1,
87 u'cholera': self._CHBOX_cholera,
88 u'diphtheria': self._CHBOX_diphtheria,
89 u'haemophilus influenzae b': self._CHBOX_hib,
90 u'hepatitis A': self._CHBOX_hepA,
91 u'hepatitis B': self._CHBOX_hepB,
92 u'japanese B encephalitis': self._CHBOX_japanese,
93 u'measles': self._CHBOX_measles,
94 u'meningococcus A': self._CHBOX_menA,
95 u'meningococcus C': self._CHBOX_menC,
96 u'meningococcus W': self._CHBOX_menW,
97 u'meningococcus Y': self._CHBOX_menY,
98 u'mumps': self._CHBOX_mumps,
99 u'pertussis': self._CHBOX_pertussis,
100 u'pneumococcus': self._CHBOX_pneumococcus,
101 u'poliomyelitis': self._CHBOX_polio,
102 u'rabies': self._CHBOX_rabies,
103 u'rubella': self._CHBOX_rubella,
104 u'tetanus': self._CHBOX_tetanus,
105 u'tick-borne meningoencephalitis': self._CHBOX_fsme,
106 u'yellow fever': self._CHBOX_yellow_fever,
107 u'yersinia pestis': self._CHBOX_yersinia_pestis
108 }
109
111 for field in self.__dict__.keys():
112 if field.startswith('_CHBOX_'):
113 self.__dict__[field].Enable()
114 self.Enable()
115
117 for field in self.__dict__.keys():
118 if field.startswith('_CHBOX_'):
119 self.__dict__[field].Disable()
120 self.Disable()
121
123 for field in self.__dict__.keys():
124 if field.startswith('_CHBOX_'):
125 self.__dict__[field].SetValue(False)
126
127 - def select(self, indications=None):
128 for indication in indications:
129 self.__indication2field[indication].SetValue(True)
130
132 indications = []
133 for indication in self.__indication2field.keys():
134 if self.__indication2field[indication].IsChecked():
135 indications.append(indication)
136 return indications
137
138 selected_indications = property(_get_selected_indications, lambda x:x)
139
141 for indication in self.__indication2field.keys():
142 if self.__indication2field[indication].IsChecked():
143 return True
144 return False
145
146 has_selection = property(_get_has_selection, lambda x:x)
147
148
149
150
151 -def edit_vaccine(parent=None, vaccine=None, single_entry=True):
162
164
165 if parent is None:
166 parent = wx.GetApp().GetTopWindow()
167
168 def delete(vaccine=None):
169 deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
170 if deleted:
171 return True
172
173 gmGuiHelpers.gm_show_info (
174 _(
175 'Cannot delete vaccine\n'
176 '\n'
177 ' %s - %s (#%s)\n'
178 '\n'
179 'It is probably documented in a vaccination.'
180 ) % (
181 vaccine['vaccine'],
182 vaccine['preparation'],
183 vaccine['pk_vaccine']
184 ),
185 _('Deleting vaccine')
186 )
187
188 return False
189
190 def edit(vaccine=None):
191 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
192
193 def refresh(lctrl):
194 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
195
196 items = [ [
197 u'%s' % v['pk_brand'],
198 u'%s%s' % (
199 v['vaccine'],
200 gmTools.bool2subst (
201 v['is_fake_vaccine'],
202 u' (%s)' % _('fake'),
203 u''
204 )
205 ),
206 v['preparation'],
207 u'%s (%s)' % (v['route_abbreviation'], v['route_description']),
208 gmTools.bool2subst(v['is_live'], gmTools.u_checkmark_thin, u''),
209 gmTools.coalesce(v['atc_code'], u''),
210 u'%s%s' % (
211 gmTools.coalesce(v['min_age'], u'?'),
212 gmTools.coalesce(v['max_age'], u'?', u' - %s'),
213 ),
214 gmTools.coalesce(v['comment'], u'')
215 ] for v in vaccines ]
216 lctrl.set_string_items(items)
217 lctrl.set_data(vaccines)
218
219 gmListWidgets.get_choices_from_list (
220 parent = parent,
221 msg = _('\nThe vaccines currently known to GNUmed.\n'),
222 caption = _('Showing vaccines.'),
223 columns = [ u'#', _('Brand'), _('Preparation'), _(u'Route'), _('Live'), _('ATC'), _('Age range'), _('Comment') ],
224 single_selection = True,
225 refresh_callback = refresh,
226 edit_callback = edit,
227 new_callback = edit,
228 delete_callback = delete
229 )
230
232
234
235 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
236
237 context = {
238 u'ctxt_vaccine': {
239 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s',
240 u'placeholder': u'pk_vaccine'
241 }
242 }
243
244 query = u"""
245 SELECT data, field_label, list_label FROM (
246
247 SELECT distinct on (field_label)
248 data,
249 field_label,
250 list_label,
251 rank
252 FROM ((
253 -- batch_no by vaccine
254 SELECT
255 batch_no AS data,
256 batch_no AS field_label,
257 batch_no || ' (' || vaccine || ')' AS list_label,
258 1 as rank
259 FROM
260 clin.v_pat_vaccinations
261 WHERE
262 batch_no %(fragment_condition)s
263 %(ctxt_vaccine)s
264 ) UNION ALL (
265 -- batch_no for any vaccine
266 SELECT
267 batch_no AS data,
268 batch_no AS field_label,
269 batch_no || ' (' || vaccine || ')' AS list_label,
270 2 AS rank
271 FROM
272 clin.v_pat_vaccinations
273 WHERE
274 batch_no %(fragment_condition)s
275 )
276
277 ) AS matching_batch_nos
278
279 ) as unique_matches
280
281 ORDER BY rank, list_label
282 LIMIT 25
283 """
284 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
285 mp.setThresholds(1, 2, 3)
286 self.matcher = mp
287
288 self.unset_context(context = u'pk_vaccine')
289 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
290 self.selection_only = False
291
293
295
296 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
297
298
299 query = u"""
300 SELECT data, list_label, field_label FROM (
301
302 SELECT DISTINCT ON (data)
303 data,
304 list_label,
305 field_label
306 FROM ((
307 -- fragment -> vaccine
308 SELECT
309 pk_vaccine AS data,
310 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
311 vaccine AS field_label
312 FROM
313 clin.v_vaccines
314 WHERE
315 vaccine %(fragment_condition)s
316
317 ) union all (
318
319 -- fragment -> localized indication -> vaccines
320 SELECT
321 pk_vaccine AS data,
322 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
323 vaccine AS field_label
324 FROM
325 clin.v_indications4vaccine
326 WHERE
327 l10n_indication %(fragment_condition)s
328
329 ) union all (
330
331 -- fragment -> indication -> vaccines
332 SELECT
333 pk_vaccine AS data,
334 vaccine || ' (' || array_to_string(indications, ', ') || ')' AS list_label,
335 vaccine AS field_label
336 FROM
337 clin.v_indications4vaccine
338 WHERE
339 indication %(fragment_condition)s
340 )
341 ) AS distinct_total
342
343 ) AS total
344
345 ORDER by list_label
346 LIMIT 25
347 """
348 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
349 mp.setThresholds(1, 2, 3)
350 self.matcher = mp
351
352 self.selection_only = True
353
356
357 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
358
359 -class cVaccineEAPnl(wxgVaccineEAPnl.wxgVaccineEAPnl, gmEditArea.cGenericEditAreaMixin):
360
378
380
381
382 query = u"""
383 SELECT DISTINCT ON (abbreviation)
384 id,
385 abbreviation || ' (' || _(description) || ')'
386 FROM
387 clin.vacc_route
388 WHERE
389 abbreviation %(fragment_condition)s
390 OR
391 description %(fragment_condition)s
392 ORDER BY
393 abbreviation
394 """
395 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
396 mp.setThresholds(1, 2, 3)
397 self._PRW_route.matcher = mp
398 self._PRW_route.selection_only = True
399
400
401
402
403 self.Layout()
404 self.Fit()
405
406
407
409
410 has_errors = False
411
412 if self._PRW_brand.GetValue().strip() == u'':
413 has_errors = True
414 self._PRW_brand.display_as_valid(False)
415 else:
416 self._PRW_brand.display_as_valid(True)
417
418 if self._PRW_route.GetData() is None:
419 has_errors = True
420 self._PRW_route.display_as_valid(False)
421 else:
422 self._PRW_route.display_as_valid(True)
423
424 if not self._PNL_indications.has_selection:
425 has_errors = True
426
427 if self._PRW_atc.GetValue().strip() in [u'', u'J07']:
428 self._PRW_atc.display_as_valid(True)
429 else:
430 if self._PRW_atc.GetData() is None:
431 self._PRW_atc.display_as_valid(True)
432 else:
433 has_errors = True
434 self._PRW_atc.display_as_valid(False)
435
436 val = self._PRW_age_min.GetValue().strip()
437 if val == u'':
438 self._PRW_age_min.display_as_valid(True)
439 else:
440 if gmDateTime.str2interval(val) is None:
441 has_errors = True
442 self._PRW_age_min.display_as_valid(False)
443 else:
444 self._PRW_age_min.display_as_valid(True)
445
446 val = self._PRW_age_max.GetValue().strip()
447 if val == u'':
448 self._PRW_age_max.display_as_valid(True)
449 else:
450 if gmDateTime.str2interval(val) is None:
451 has_errors = True
452 self._PRW_age_max.display_as_valid(False)
453 else:
454 self._PRW_age_max.display_as_valid(True)
455
456
457 ask_user = (self.mode == 'edit')
458
459 ask_user = (ask_user and self.data.is_in_use)
460
461 ask_user = ask_user and (
462
463 (self.data['pk_brand'] != self._PRW_route.GetData())
464 or
465
466 (self.data['indications'] != self._PNL_indications.selected_indications)
467 )
468
469 if ask_user:
470 do_it = gmGuiHelpers.gm_show_question (
471 aTitle = _('Saving vaccine'),
472 aMessage = _(
473 u'This vaccine is already in use:\n'
474 u'\n'
475 u' "%s"\n'
476 u' (%s)\n'
477 u'\n'
478 u'Are you absolutely positively sure that\n'
479 u'you really want to edit this vaccine ?\n'
480 '\n'
481 u'This will change the vaccine name and/or target\n'
482 u'conditions in each patient this vaccine was\n'
483 u'used in to document a vaccination with.\n'
484 ) % (
485 self._PRW_brand.GetValue().strip(),
486 u', '.join(self.data['l10n_indications'])
487 )
488 )
489 if not do_it:
490 has_errors = True
491
492 return (has_errors is False)
493
530
558
571
573 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand'])
574 self._PRW_route.SetText(value = self.data['route_description'], data = self.data['pk_route'])
575 self._CHBOX_live.SetValue(self.data['is_live'])
576 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
577 self._PNL_indications.select(self.data['indications'])
578 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
579 if self.data['min_age'] is None:
580 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
581 else:
582 self._PRW_age_min.SetText (
583 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
584 data = self.data['min_age']
585 )
586 if self.data['max_age'] is None:
587 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
588 else:
589 self._PRW_age_max.SetText (
590 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
591 data = self.data['max_age']
592 )
593 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
594
595 self._PRW_brand.SetFocus()
596
598 self._refresh_as_new()
599
600
601
615
617
618 pat = gmPerson.gmCurrentPatient()
619 emr = pat.get_emr()
620
621 if parent is None:
622 parent = wx.GetApp().GetTopWindow()
623
624 def browse2schedules(vaccination=None):
625 dbcfg = gmCfg.cCfgSQL()
626 url = dbcfg.get2 (
627 option = 'external.urls.vaccination_plans',
628 workplace = gmSurgery.gmCurrentPractice().active_workplace,
629 bias = 'user',
630 default = u'http://www.bundesaerztekammer.de/downloads/ImpfempfehlungenRKI2009.pdf'
631 )
632
633 webbrowser.open (
634 url = url,
635 new = False,
636 autoraise = True
637 )
638 return False
639
640 def edit(vaccination=None):
641 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
642
643 def delete(vaccination=None):
644 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
645 return True
646
647 def refresh(lctrl):
648
649 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
650
651 items = [ [
652 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
653 v['vaccine'],
654 u', '.join(v['l10n_indications']),
655 v['batch_no'],
656 gmTools.coalesce(v['site'], u''),
657 gmTools.coalesce(v['reaction'], u''),
658 gmTools.coalesce(v['comment'], u'')
659 ] for v in vaccs ]
660
661 lctrl.set_string_items(items)
662 lctrl.set_data(vaccs)
663
664 gmListWidgets.get_choices_from_list (
665 parent = parent,
666 msg = _('\nComplete vaccination history for this patient.\n'),
667 caption = _('Showing vaccinations.'),
668 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
669 single_selection = True,
670 refresh_callback = refresh,
671 new_callback = edit,
672 edit_callback = edit,
673 delete_callback = delete,
674 left_extra_button = (_('Vaccination Plans'), _('Open a browser showing vaccination schedules.'), browse2schedules)
675 )
676
677 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
678
679 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
680 """
681 - warn on apparent duplicates
682 - ask if "missing" (= previous, non-recorded) vaccinations
683 should be estimated and saved (add note "auto-generated")
684
685 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
686 """
704
711
713
714 vaccine = self._PRW_vaccine.GetData(as_instance=True)
715
716
717 if self.mode == u'edit':
718 self._PNL_indications.clear_all()
719 if vaccine is None:
720 self._PRW_batch.unset_context(context = 'pk_vaccine')
721 else:
722 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
723 self._PNL_indications.select(indications = vaccine['indications'])
724 self._PNL_indications.disable_all()
725
726
727 else:
728 if vaccine is None:
729 self._PRW_batch.unset_context(context = 'pk_vaccine')
730 self._PNL_indications.enable_all()
731 else:
732 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
733 self._PNL_indications.clear_all()
734 self._PNL_indications.select(indications = vaccine['indications'])
735 self._PNL_indications.disable_all()
736
738 if self._PRW_reaction.GetValue().strip() == u'':
739 self._BTN_report.Enable(False)
740 else:
741 self._BTN_report.Enable(True)
742
743
744
746
747 has_errors = False
748
749 if not self._PRW_date_given.is_valid_timestamp(allow_empty = False):
750 has_errors = True
751
752 vaccine = self._PRW_vaccine.GetData(as_instance = True)
753
754
755 if self.mode == u'edit':
756 if vaccine is None:
757 has_errors = True
758 self._PRW_vaccine.display_as_valid(False)
759 else:
760 self._PRW_vaccine.display_as_valid(True)
761 self._PNL_indications.clear_all()
762 self._PNL_indications.select(indications = vaccine['indications'])
763 self._PNL_indications.disable_all()
764
765 else:
766 if vaccine is None:
767 if self._PNL_indications.has_selection:
768 self._PRW_vaccine.display_as_valid(True)
769 else:
770 has_errors = True
771 self._PRW_vaccine.display_as_valid(False)
772 else:
773 self._PRW_vaccine.display_as_valid(True)
774
775 if self._PRW_batch.GetValue().strip() == u'':
776 has_errors = True
777 self._PRW_batch.display_as_valid(False)
778 else:
779 self._PRW_batch.display_as_valid(True)
780
781 if self._PRW_episode.GetValue().strip() == u'':
782 self._PRW_episode.SetText(value = _('prevention'))
783
784 return (has_errors is False)
785
787
788 vaccine = self._PRW_vaccine.GetData()
789 if vaccine is None:
790 data = self.__save_new_from_indications()
791 else:
792 data = self.__save_new_from_vaccine(vaccine = vaccine)
793
794
795
796
797 self.data = data
798
799 return True
800
814
839
841
842 if self._CHBOX_anamnestic.GetValue() is True:
843 self.data['soap_cat'] = u's'
844 else:
845 self.data['soap_cat'] = u'p'
846
847 self.data['date_given'] = self._PRW_date_given.GetData()
848 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
849 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
850 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
851 self.data['site'] = self._PRW_site.GetValue().strip()
852 self.data['pk_provider'] = self._PRW_provider.GetData()
853 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
854 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
855
856 self.data.save()
857
858 return True
859
877
902
923
924
925
947
950
951
952
954
956 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
957 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
958 self.__pat = gmPerson.gmCurrentPatient()
959
960 self.ID_VaccinatedIndicationsList = wx.NewId()
961 self.ID_VaccinationsPerRegimeList = wx.NewId()
962 self.ID_MissingShots = wx.NewId()
963 self.ID_ActiveSchedules = wx.NewId()
964 self.__do_layout()
965 self.__register_interests()
966 self.__reset_ui_content()
967
969
970
971
972 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
973 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
974
975
976
977
978
979 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
980 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
981 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
982 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
983 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
984 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
985 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
986
987
988 self.LBOX_vaccinated_indications = wx.ListBox(
989 parent = self,
990 id = self.ID_VaccinatedIndicationsList,
991 choices = [],
992 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
993 )
994 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
995
996
997
998 self.LBOX_given_shots = wx.ListBox(
999 parent = self,
1000 id = self.ID_VaccinationsPerRegimeList,
1001 choices = [],
1002 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1003 )
1004 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1005
1006 self.LBOX_active_schedules = wx.ListBox (
1007 parent = self,
1008 id = self.ID_ActiveSchedules,
1009 choices = [],
1010 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1011 )
1012 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1013
1014 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1015 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1016 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1017 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1018
1019
1020
1021
1022 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1023 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1024 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1025
1026 self.LBOX_missing_shots = wx.ListBox (
1027 parent = self,
1028 id = self.ID_MissingShots,
1029 choices = [],
1030 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1031 )
1032 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1033
1034 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1035 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1036
1037
1038 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1039
1040
1041
1042
1043 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1044 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1045 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1046 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1047 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1048 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1049 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1050 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1051
1052 self.SetAutoLayout(True)
1053 self.SetSizer(self.mainsizer)
1054 self.mainsizer.Fit(self)
1055
1057
1058 wx.EVT_SIZE(self, self.OnSize)
1059 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1060 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1061 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1062
1063
1064
1065 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1066 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1067
1068
1069
1071 w, h = event.GetSize()
1072 self.mainsizer.SetDimension (0, 0, w, h)
1073
1075 """Paste previously given shot into edit area.
1076 """
1077 self.editarea.set_data(aVacc=event.GetClientData())
1078
1080 self.editarea.set_data(aVacc = event.GetClientData())
1081
1083 """Update right hand middle list to show vaccinations given for selected indication."""
1084 ind_list = event.GetEventObject()
1085 selected_item = ind_list.GetSelection()
1086 ind = ind_list.GetClientData(selected_item)
1087
1088 self.LBOX_given_shots.Set([])
1089 emr = self.__pat.get_emr()
1090 shots = emr.get_vaccinations(indications = [ind])
1091
1092 for shot in shots:
1093 if shot['is_booster']:
1094 marker = 'B'
1095 else:
1096 marker = '#%s' % shot['seq_no']
1097 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1098 self.LBOX_given_shots.Append(label, shot)
1099
1101
1102 self.editarea.set_data()
1103
1104 self.LBOX_vaccinated_indications.Clear()
1105 self.LBOX_given_shots.Clear()
1106 self.LBOX_active_schedules.Clear()
1107 self.LBOX_missing_shots.Clear()
1108
1110
1111 self.LBOX_vaccinated_indications.Clear()
1112 self.LBOX_given_shots.Clear()
1113 self.LBOX_active_schedules.Clear()
1114 self.LBOX_missing_shots.Clear()
1115
1116 emr = self.__pat.get_emr()
1117
1118 t1 = time.time()
1119
1120
1121
1122 status, indications = emr.get_vaccinated_indications()
1123
1124
1125
1126 for indication in indications:
1127 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1128
1129
1130 print "vaccinated indications took", time.time()-t1, "seconds"
1131
1132 t1 = time.time()
1133
1134 scheds = emr.get_scheduled_vaccination_regimes()
1135 if scheds is None:
1136 label = _('ERROR: cannot retrieve active vaccination schedules')
1137 self.LBOX_active_schedules.Append(label)
1138 elif len(scheds) == 0:
1139 label = _('no active vaccination schedules')
1140 self.LBOX_active_schedules.Append(label)
1141 else:
1142 for sched in scheds:
1143 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1144 self.LBOX_active_schedules.Append(label)
1145 print "active schedules took", time.time()-t1, "seconds"
1146
1147 t1 = time.time()
1148
1149 missing_shots = emr.get_missing_vaccinations()
1150 print "getting missing shots took", time.time()-t1, "seconds"
1151 if missing_shots is None:
1152 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1153 self.LBOX_missing_shots.Append(label, None)
1154 return True
1155
1156 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1157 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1158 for shot in missing_shots['due']:
1159 if shot['overdue']:
1160 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1161 weeks = days_left / 7
1162
1163 label = overdue_template % (
1164 years,
1165 weeks,
1166 shot['seq_no'],
1167 shot['l10n_indication'],
1168 shot['regime'],
1169 shot['vacc_comment']
1170 )
1171 self.LBOX_missing_shots.Append(label, shot)
1172 else:
1173
1174 label = due_template % (
1175 shot['time_left'].days / 7,
1176 shot['seq_no'],
1177 shot['indication'],
1178 shot['regime'],
1179 shot['latest_due'].strftime('%m/%Y'),
1180 shot['vacc_comment']
1181 )
1182 self.LBOX_missing_shots.Append(label, shot)
1183
1184 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1185 for shot in missing_shots['boosters']:
1186
1187 label = lbl_template % (
1188 shot['l10n_indication'],
1189 shot['regime'],
1190 shot['vacc_comment']
1191 )
1192 self.LBOX_missing_shots.Append(label, shot)
1193 print "displaying missing shots took", time.time()-t1, "seconds"
1194
1195 return True
1196
1197 - def _on_post_patient_selection(self, **kwargs):
1199
1200
1201
1202
1203
1204
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216 if __name__ == "__main__":
1217
1218 if len(sys.argv) < 2:
1219 sys.exit()
1220
1221 if sys.argv[1] != u'test':
1222 sys.exit()
1223
1224 app = wx.PyWidgetTester(size = (600, 600))
1225 app.SetWidget(cATCPhraseWheel, -1)
1226 app.MainLoop()
1227
1228