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 gmSurgery
31
32 from Gnumed.wxpython import gmPhraseWheel
33 from Gnumed.wxpython import gmTerryGuiParts
34 from Gnumed.wxpython import gmRegetMixin
35 from Gnumed.wxpython import gmGuiHelpers
36 from Gnumed.wxpython import gmEditArea
37 from Gnumed.wxpython import gmListWidgets
38 from Gnumed.wxpython import gmFormWidgets
39 from Gnumed.wxpython import gmMacro
40
41
42 _log = logging.getLogger('gm.vaccination')
43
44
45
46
72
73 gmListWidgets.get_choices_from_list (
74 parent = parent,
75 msg = _('\nConditions preventable by vaccination as currently known to GNUmed.\n'),
76 caption = _('Showing vaccination preventable conditions.'),
77 columns = [ _('Condition'), _('ATCs: single-condition vaccines'), _('ATCs: multi-condition vaccines'), u'#' ],
78 single_selection = True,
79 refresh_callback = refresh
80 )
81
83
84 if parent is None:
85 parent = wx.GetApp().GetTopWindow()
86
87 if msg is None:
88 msg = _('Pick the relevant indications.')
89
90 if right_column is None:
91 right_columns = ['This vaccine']
92 else:
93 right_columns = [right_column]
94
95 picker = gmListWidgets.cItemPickerDlg(parent, -1, msg = msg)
96 picker.set_columns(columns = [_('Known indications')], columns_right = right_columns)
97 inds = gmVaccination.get_indications(order_by = 'l10n_description')
98 picker.set_choices (
99 choices = [ i['l10n_description'] for i in inds ],
100 data = inds
101 )
102 picker.set_picks (
103 picks = [ p['l10n_description'] for p in picks ],
104 data = picks
105 )
106 result = picker.ShowModal()
107
108 if result == wx.ID_CANCEL:
109 picker.Destroy()
110 return None
111
112 picks = picker.picks
113 picker.Destroy()
114 return picks
115
116
117
118
119 -def edit_vaccine(parent=None, vaccine=None, single_entry=True):
130
132
133 if parent is None:
134 parent = wx.GetApp().GetTopWindow()
135
136 def delete(vaccine=None):
137 deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
138 if deleted:
139 return True
140
141 gmGuiHelpers.gm_show_info (
142 _(
143 'Cannot delete vaccine\n'
144 '\n'
145 ' %s - %s (#%s)\n'
146 '\n'
147 'It is probably documented in a vaccination.'
148 ) % (
149 vaccine['vaccine'],
150 vaccine['preparation'],
151 vaccine['pk_vaccine']
152 ),
153 _('Deleting vaccine')
154 )
155
156 return False
157
158 def edit(vaccine=None):
159 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
160
161 def refresh(lctrl):
162 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
163
164 items = [ [
165 u'%s' % v['pk_brand'],
166 u'%s%s' % (
167 v['vaccine'],
168 gmTools.bool2subst (
169 v['is_fake_vaccine'],
170 u' (%s)' % _('fake'),
171 u''
172 )
173 ),
174 v['preparation'],
175
176
177 gmTools.coalesce(v['atc_code'], u''),
178 u'%s%s' % (
179 gmTools.coalesce(v['min_age'], u'?'),
180 gmTools.coalesce(v['max_age'], u'?', u' - %s'),
181 ),
182 gmTools.coalesce(v['comment'], u'')
183 ] for v in vaccines ]
184 lctrl.set_string_items(items)
185 lctrl.set_data(vaccines)
186
187 gmListWidgets.get_choices_from_list (
188 parent = parent,
189 msg = _('\nThe vaccines currently known to GNUmed.\n'),
190 caption = _('Showing vaccines.'),
191
192 columns = [ u'#', _('Brand'), _('Preparation'), _('ATC'), _('Age range'), _('Comment') ],
193 single_selection = True,
194 refresh_callback = refresh,
195 edit_callback = edit,
196 new_callback = edit,
197 delete_callback = delete
198 )
199
201
203
204 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
205
206 context = {
207 u'ctxt_vaccine': {
208 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s',
209 u'placeholder': u'pk_vaccine'
210 }
211 }
212
213 query = u"""
214 SELECT data, field_label, list_label FROM (
215
216 SELECT distinct on (field_label)
217 data,
218 field_label,
219 list_label,
220 rank
221 FROM ((
222 -- batch_no by vaccine
223 SELECT
224 batch_no AS data,
225 batch_no AS field_label,
226 batch_no || ' (' || vaccine || ')' AS list_label,
227 1 as rank
228 FROM
229 clin.v_pat_vaccinations
230 WHERE
231 batch_no %(fragment_condition)s
232 %(ctxt_vaccine)s
233 ) UNION ALL (
234 -- batch_no for any vaccine
235 SELECT
236 batch_no AS data,
237 batch_no AS field_label,
238 batch_no || ' (' || vaccine || ')' AS list_label,
239 2 AS rank
240 FROM
241 clin.v_pat_vaccinations
242 WHERE
243 batch_no %(fragment_condition)s
244 )
245
246 ) AS matching_batch_nos
247
248 ) as unique_matches
249
250 ORDER BY rank, list_label
251 LIMIT 25
252 """
253 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
254 mp.setThresholds(1, 2, 3)
255 self.matcher = mp
256
257 self.unset_context(context = u'pk_vaccine')
258 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
259 self.selection_only = False
260
262
264
265 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
266
267
268 query = u"""
269 SELECT data, list_label, field_label FROM (
270
271 SELECT DISTINCT ON (data)
272 data,
273 list_label,
274 field_label
275 FROM ((
276 -- fragment -> vaccine
277 SELECT
278 pk_vaccine AS data,
279 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
280 vaccine AS field_label
281 FROM
282 clin.v_vaccines
283 WHERE
284 vaccine %(fragment_condition)s
285
286 ) union all (
287
288 -- fragment -> localized indication -> vaccines
289 SELECT
290 pk_vaccine AS data,
291 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
292 vaccine AS field_label
293 FROM
294 clin.v_indications4vaccine
295 WHERE
296 l10n_indication %(fragment_condition)s
297
298 ) union all (
299
300 -- fragment -> indication -> vaccines
301 SELECT
302 pk_vaccine AS data,
303 vaccine || ' (' || array_to_string(indications, ', ') || ')' AS list_label,
304 vaccine AS field_label
305 FROM
306 clin.v_indications4vaccine
307 WHERE
308 indication %(fragment_condition)s
309 )
310 ) AS distinct_total
311
312 ) AS total
313
314 ORDER by list_label
315 LIMIT 25
316 """
317 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
318 mp.setThresholds(1, 2, 3)
319 self.matcher = mp
320
321 self.selection_only = True
322
325
326 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
327
328 -class cVaccineEAPnl(wxgVaccineEAPnl.wxgVaccineEAPnl, gmEditArea.cGenericEditAreaMixin):
329
344
346 self._TCTRL_indications.SetValue(u'')
347 if len(self.__indications) == 0:
348 return
349 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
350
351
352
354
355 has_errors = False
356
357 if self._PRW_brand.GetValue().strip() == u'':
358 has_errors = True
359 self._PRW_brand.display_as_valid(False)
360 else:
361 self._PRW_brand.display_as_valid(True)
362
363 if self._PRW_atc.GetValue().strip() in [u'', u'J07']:
364 self._PRW_atc.display_as_valid(True)
365 else:
366 if self._PRW_atc.GetData() is None:
367 self._PRW_atc.display_as_valid(True)
368 else:
369 has_errors = True
370 self._PRW_atc.display_as_valid(False)
371
372 val = self._PRW_age_min.GetValue().strip()
373 if val == u'':
374 self._PRW_age_min.display_as_valid(True)
375 else:
376 if gmDateTime.str2interval(val) is None:
377 has_errors = True
378 self._PRW_age_min.display_as_valid(False)
379 else:
380 self._PRW_age_min.display_as_valid(True)
381
382 val = self._PRW_age_max.GetValue().strip()
383 if val == u'':
384 self._PRW_age_max.display_as_valid(True)
385 else:
386 if gmDateTime.str2interval(val) is None:
387 has_errors = True
388 self._PRW_age_max.display_as_valid(False)
389 else:
390 self._PRW_age_max.display_as_valid(True)
391
392
393 ask_user = (self.mode == 'edit')
394
395 ask_user = (ask_user and self.data.is_in_use)
396
397 ask_user = ask_user and (
398
399 (self.data['pk_brand'] != self._PRW_route.GetData())
400 or
401
402 (set(self.data['pk_indications']) != set([ i['id'] for i in self.__indications ]))
403 )
404
405 if ask_user:
406 do_it = gmGuiHelpers.gm_show_question (
407 aTitle = _('Saving vaccine'),
408 aMessage = _(
409 u'This vaccine is already in use:\n'
410 u'\n'
411 u' "%s"\n'
412 u' (%s)\n'
413 u'\n'
414 u'Are you absolutely positively sure that\n'
415 u'you really want to edit this vaccine ?\n'
416 '\n'
417 u'This will change the vaccine name and/or target\n'
418 u'conditions in each patient this vaccine was\n'
419 u'used in to document a vaccination with.\n'
420 ) % (
421 self._PRW_brand.GetValue().strip(),
422 u', '.join(self.data['l10n_indications'])
423 )
424 )
425 if not do_it:
426 has_errors = True
427
428 return (has_errors is False)
429
431
432 if len(self.__indications) == 0:
433 gmGuiHelpers.gm_show_info (
434 aTitle = _('Saving vaccine'),
435 aMessage = _('You must select at least one indication.')
436 )
437 return False
438
439
440 data = gmVaccination.create_vaccine (
441 pk_brand = self._PRW_brand.GetData(),
442 brand_name = self._PRW_brand.GetValue(),
443 pk_indications = [ i['id'] for i in self.__indications ]
444 )
445
446
447 val = self._PRW_age_min.GetValue().strip()
448 if val != u'':
449 data['min_age'] = gmDateTime.str2interval(val)
450 val = self._PRW_age_max.GetValue().strip()
451 if val != u'':
452 data['max_age'] = gmDateTime.str2interval(val)
453 val = self._TCTRL_comment.GetValue().strip()
454 if val != u'':
455 data['comment'] = val
456
457 data.save()
458
459 drug = data.brand
460 drug['is_fake_brand'] = self._CHBOX_fake.GetValue()
461 val = self._PRW_atc.GetData()
462 if val is not None:
463 if val != u'J07':
464 drug['atc'] = val.strip()
465 drug.save()
466
467
468
469
470 self.data = data
471
472 return True
473
475
476 if len(self.__indications) == 0:
477 gmGuiHelpers.gm_show_info (
478 aTitle = _('Saving vaccine'),
479 aMessage = _('You must select at least one indication.')
480 )
481 return False
482
483 drug = self.data.brand
484 drug['brand'] = self._PRW_brand.GetValue().strip()
485 drug['is_fake_brand'] = self._CHBOX_fake.GetValue()
486 val = self._PRW_atc.GetData()
487 if val is not None:
488 if val != u'J07':
489 drug['atc'] = val.strip()
490 drug.save()
491
492
493 self.data.set_indications(pk_indications = [ i['id'] for i in self.__indications ])
494
495
496 val = self._PRW_age_min.GetValue().strip()
497 if val != u'':
498 self.data['min_age'] = gmDateTime.str2interval(val)
499 if val != u'':
500 self.data['max_age'] = gmDateTime.str2interval(val)
501 val = self._TCTRL_comment.GetValue().strip()
502 if val != u'':
503 self.data['comment'] = val
504
505 self.data.save()
506 return True
507
509 self._PRW_brand.SetText(value = u'', data = None, suppress_smarts = True)
510
511 self._CHBOX_fake.SetValue(False)
512 self._PRW_atc.SetText(value = u'', data = None, suppress_smarts = True)
513 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
514 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
515 self._TCTRL_comment.SetValue(u'')
516
517 self.__indications = []
518 self.__refresh_indications()
519
520 self._PRW_brand.SetFocus()
521
523 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand'])
524
525 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
526 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
527 if self.data['min_age'] is None:
528 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
529 else:
530 self._PRW_age_min.SetText (
531 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
532 data = self.data['min_age']
533 )
534 if self.data['max_age'] is None:
535 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
536 else:
537 self._PRW_age_max.SetText (
538 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
539 data = self.data['max_age']
540 )
541 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
542
543 self.__indications = self.data.indications
544 self.__refresh_indications()
545
546 self._PRW_brand.SetFocus()
547
549 self._refresh_as_new()
550
551
566
567
568
569
571
572 if parent is None:
573 parent = wx.GetApp().GetTopWindow()
574
575
576 template = gmFormWidgets.manage_form_templates (
577 parent = parent,
578 active_only = True,
579 template_types = [u'Medical statement', u'vaccination report', u'vaccination record']
580 )
581
582 if template is None:
583 gmDispatcher.send(signal = 'statustext', msg = _('No vaccination document template selected.'), beep = False)
584 return None
585
586
587 try:
588 vaccs_printout = template.instantiate()
589 except KeyError:
590 _log.exception('cannot instantiate vaccinations printout template [%s]', template)
591 gmGuiHelpers.gm_show_error (
592 aMessage = _('Invalid vaccinations printout template [%s - %s (%s)]') % (name, ver, template['engine']),
593 aTitle = _('Printing vaccinations')
594 )
595 return False
596
597 ph = gmMacro.gmPlaceholderHandler()
598
599 vaccs_printout.substitute_placeholders(data_source = ph)
600 pdf_name = vaccs_printout.generate_output()
601 if pdf_name is None:
602 gmGuiHelpers.gm_show_error (
603 aMessage = _('Error generating the vaccinations printout.'),
604 aTitle = _('Printing vaccinations')
605 )
606 return False
607
608
609 printed = gmPrinting.print_files(filenames = [pdf_name], jobtype = 'vaccinations')
610 if not printed:
611 gmGuiHelpers.gm_show_error (
612 aMessage = _('Error printing vaccinations.'),
613 aTitle = _('Printing vaccinations')
614 )
615 return False
616
617 pat = gmPerson.gmCurrentPatient()
618 emr = pat.get_emr()
619 epi = emr.add_episode(episode_name = 'administration', is_open = False)
620 emr.add_clin_narrative (
621 soap_cat = None,
622 note = _('vaccinations printed from template [%s - %s]') % (template['name_long'], template['external_version']),
623 episode = epi
624 )
625
626 return True
627
628
642
643
663
664 def print_vaccs(vaccination=None):
665 print_vaccinations(parent = parent)
666 return False
667
668 def edit(vaccination=None):
669 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
670
671 def delete(vaccination=None):
672 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
673 return True
674
675 def refresh(lctrl):
676
677 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
678
679 items = [ [
680 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
681 v['vaccine'],
682 u', '.join(v['l10n_indications']),
683 v['batch_no'],
684 gmTools.coalesce(v['site'], u''),
685 gmTools.coalesce(v['reaction'], u''),
686 gmTools.coalesce(v['comment'], u'')
687 ] for v in vaccs ]
688
689 lctrl.set_string_items(items)
690 lctrl.set_data(vaccs)
691
692 gmListWidgets.get_choices_from_list (
693 parent = parent,
694 msg = _('\nComplete vaccination history for this patient.\n'),
695 caption = _('Showing vaccinations.'),
696 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
697 single_selection = True,
698 refresh_callback = refresh,
699 new_callback = edit,
700 edit_callback = edit,
701 delete_callback = delete,
702 left_extra_button = (_('Print'), _('Print vaccinations using a template.'), print_vaccs),
703 right_extra_button = (_('Vaccination Plans'), _('Open a browser showing vaccination schedules.'), browse2schedules)
704 )
705
706 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
707
708 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
709 """
710 - warn on apparent duplicates
711 - ask if "missing" (= previous, non-recorded) vaccinations
712 should be estimated and saved (add note "auto-generated")
713
714 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
715 """
733
735
736 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
737 self._PRW_provider.selection_only = False
738 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
739 if self.mode == 'edit':
740 self._BTN_select_indications.Disable()
741
743
744 vaccine = self._PRW_vaccine.GetData(as_instance=True)
745
746
747 if self.mode == u'edit':
748 if vaccine is None:
749 self._PRW_batch.unset_context(context = 'pk_vaccine')
750 self.__indications = []
751 else:
752 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
753 self.__indications = vaccine.indications
754
755 else:
756 if vaccine is None:
757 self._PRW_batch.unset_context(context = 'pk_vaccine')
758 self.__indications = []
759 self._BTN_select_indications.Enable()
760 else:
761 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
762 self.__indications = vaccine.indications
763 self._BTN_select_indications.Disable()
764
765 self.__refresh_indications()
766
768 if self._PRW_reaction.GetValue().strip() == u'':
769 self._BTN_report.Enable(False)
770 else:
771 self._BTN_report.Enable(True)
772
774 self._TCTRL_indications.SetValue(u'')
775 if len(self.__indications) == 0:
776 return
777 self._TCTRL_indications.SetValue(u'- ' + u'\n- '.join([ i['l10n_description'] for i in self.__indications ]))
778
779
780
818
833
853
878
880
881 if self._CHBOX_anamnestic.GetValue() is True:
882 self.data['soap_cat'] = u's'
883 else:
884 self.data['soap_cat'] = u'p'
885
886 self.data['date_given'] = self._PRW_date_given.GetData()
887 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
888 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
889 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
890 self.data['site'] = self._PRW_site.GetValue().strip()
891 self.data['pk_provider'] = self._PRW_provider.GetData()
892 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
893 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
894
895 self.data.save()
896
897 return True
898
900 self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here())
901 self._CHBOX_anamnestic.SetValue(False)
902 self._PRW_vaccine.SetText(value = u'', data = None, suppress_smarts = True)
903 self._PRW_batch.unset_context(context = 'pk_vaccine')
904 self._PRW_batch.SetValue(u'')
905 self._PRW_episode.SetText(value = u'', data = None, suppress_smarts = True)
906 self._PRW_site.SetValue(u'')
907 self._PRW_provider.SetData(data = None)
908 self._PRW_reaction.SetText(value = u'', data = None, suppress_smarts = True)
909 self._BTN_report.Enable(False)
910 self._TCTRL_comment.SetValue(u'')
911
912 self.__indications = []
913 self.__refresh_indications()
914 self._BTN_select_indications.Enable()
915
916 self._PRW_date_given.SetFocus()
917
942
963
964
965
983
986
987
1002
1003
1004
1005
1006
1008
1010 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
1011 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1012 self.__pat = gmPerson.gmCurrentPatient()
1013
1014 self.ID_VaccinatedIndicationsList = wx.NewId()
1015 self.ID_VaccinationsPerRegimeList = wx.NewId()
1016 self.ID_MissingShots = wx.NewId()
1017 self.ID_ActiveSchedules = wx.NewId()
1018 self.__do_layout()
1019 self.__register_interests()
1020 self.__reset_ui_content()
1021
1023
1024
1025
1026 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
1027 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
1028
1029
1030
1031
1032
1033 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
1034 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
1035 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
1036 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
1037 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
1038 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
1039 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
1040
1041
1042 self.LBOX_vaccinated_indications = wx.ListBox(
1043 parent = self,
1044 id = self.ID_VaccinatedIndicationsList,
1045 choices = [],
1046 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1047 )
1048 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1049
1050
1051
1052 self.LBOX_given_shots = wx.ListBox(
1053 parent = self,
1054 id = self.ID_VaccinationsPerRegimeList,
1055 choices = [],
1056 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1057 )
1058 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1059
1060 self.LBOX_active_schedules = wx.ListBox (
1061 parent = self,
1062 id = self.ID_ActiveSchedules,
1063 choices = [],
1064 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1065 )
1066 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1067
1068 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1069 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1070 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1071 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1072
1073
1074
1075
1076 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1077 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1078 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1079
1080 self.LBOX_missing_shots = wx.ListBox (
1081 parent = self,
1082 id = self.ID_MissingShots,
1083 choices = [],
1084 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1085 )
1086 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1087
1088 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1089 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1090
1091
1092 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1093
1094
1095
1096
1097 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1098 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1099 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1100 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1101 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1102 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1103 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1104 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1105
1106 self.SetAutoLayout(True)
1107 self.SetSizer(self.mainsizer)
1108 self.mainsizer.Fit(self)
1109
1111
1112 wx.EVT_SIZE(self, self.OnSize)
1113 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1114 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1115 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1116
1117
1118
1119 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1120 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1121
1122
1123
1125 w, h = event.GetSize()
1126 self.mainsizer.SetDimension (0, 0, w, h)
1127
1129 """Paste previously given shot into edit area.
1130 """
1131 self.editarea.set_data(aVacc=event.GetClientData())
1132
1134 self.editarea.set_data(aVacc = event.GetClientData())
1135
1137 """Update right hand middle list to show vaccinations given for selected indication."""
1138 ind_list = event.GetEventObject()
1139 selected_item = ind_list.GetSelection()
1140 ind = ind_list.GetClientData(selected_item)
1141
1142 self.LBOX_given_shots.Set([])
1143 emr = self.__pat.get_emr()
1144 shots = emr.get_vaccinations(indications = [ind])
1145
1146 for shot in shots:
1147 if shot['is_booster']:
1148 marker = 'B'
1149 else:
1150 marker = '#%s' % shot['seq_no']
1151 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1152 self.LBOX_given_shots.Append(label, shot)
1153
1155
1156 self.editarea.set_data()
1157
1158 self.LBOX_vaccinated_indications.Clear()
1159 self.LBOX_given_shots.Clear()
1160 self.LBOX_active_schedules.Clear()
1161 self.LBOX_missing_shots.Clear()
1162
1164
1165 self.LBOX_vaccinated_indications.Clear()
1166 self.LBOX_given_shots.Clear()
1167 self.LBOX_active_schedules.Clear()
1168 self.LBOX_missing_shots.Clear()
1169
1170 emr = self.__pat.get_emr()
1171
1172 t1 = time.time()
1173
1174
1175
1176 status, indications = emr.get_vaccinated_indications()
1177
1178
1179
1180 for indication in indications:
1181 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1182
1183
1184 print "vaccinated indications took", time.time()-t1, "seconds"
1185
1186 t1 = time.time()
1187
1188 scheds = emr.get_scheduled_vaccination_regimes()
1189 if scheds is None:
1190 label = _('ERROR: cannot retrieve active vaccination schedules')
1191 self.LBOX_active_schedules.Append(label)
1192 elif len(scheds) == 0:
1193 label = _('no active vaccination schedules')
1194 self.LBOX_active_schedules.Append(label)
1195 else:
1196 for sched in scheds:
1197 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1198 self.LBOX_active_schedules.Append(label)
1199 print "active schedules took", time.time()-t1, "seconds"
1200
1201 t1 = time.time()
1202
1203 missing_shots = emr.get_missing_vaccinations()
1204 print "getting missing shots took", time.time()-t1, "seconds"
1205 if missing_shots is None:
1206 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1207 self.LBOX_missing_shots.Append(label, None)
1208 return True
1209
1210 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1211 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1212 for shot in missing_shots['due']:
1213 if shot['overdue']:
1214 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1215 weeks = days_left / 7
1216
1217 label = overdue_template % (
1218 years,
1219 weeks,
1220 shot['seq_no'],
1221 shot['l10n_indication'],
1222 shot['regime'],
1223 shot['vacc_comment']
1224 )
1225 self.LBOX_missing_shots.Append(label, shot)
1226 else:
1227
1228 label = due_template % (
1229 shot['time_left'].days / 7,
1230 shot['seq_no'],
1231 shot['indication'],
1232 shot['regime'],
1233 shot['latest_due'].strftime('%m/%Y'),
1234 shot['vacc_comment']
1235 )
1236 self.LBOX_missing_shots.Append(label, shot)
1237
1238 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1239 for shot in missing_shots['boosters']:
1240
1241 label = lbl_template % (
1242 shot['l10n_indication'],
1243 shot['regime'],
1244 shot['vacc_comment']
1245 )
1246 self.LBOX_missing_shots.Append(label, shot)
1247 print "displaying missing shots took", time.time()-t1, "seconds"
1248
1249 return True
1250
1251 - def _on_post_patient_selection(self, **kwargs):
1253
1254
1255
1256
1257
1258
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270 if __name__ == "__main__":
1271
1272 if len(sys.argv) < 2:
1273 sys.exit()
1274
1275 if sys.argv[1] != u'test':
1276 sys.exit()
1277
1278 app = wx.PyWidgetTester(size = (600, 600))
1279 app.SetWidget(cATCPhraseWheel, -1)
1280 app.MainLoop()
1281