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
12 import logging
13
14
15 import wx
16
17
18 if __name__ == '__main__':
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmDispatcher
21 from Gnumed.pycommon import gmMatchProvider
22 from Gnumed.pycommon import gmTools
23 from Gnumed.pycommon import gmI18N
24 from Gnumed.pycommon import gmCfg
25 from Gnumed.pycommon import gmCfg2
26 from Gnumed.pycommon import gmDateTime
27 from Gnumed.pycommon import gmNetworkTools
28 from Gnumed.pycommon import gmPrinting
29 from Gnumed.pycommon import gmPG2
30
31 from Gnumed.business import gmPerson
32 from Gnumed.business import gmVaccination
33 from Gnumed.business import gmPraxis
34 from Gnumed.business import gmProviderInbox
35
36 from Gnumed.wxpython import gmPhraseWheel
37 from Gnumed.wxpython import gmTerryGuiParts
38 from Gnumed.wxpython import gmRegetMixin
39 from Gnumed.wxpython import gmGuiHelpers
40 from Gnumed.wxpython import gmEditArea
41 from Gnumed.wxpython import gmListWidgets
42 from Gnumed.wxpython import gmFormWidgets
43 from Gnumed.wxpython import gmMacro
44 from Gnumed.wxpython import gmAuthWidgets
45 from Gnumed.wxpython import gmSubstanceMgmtWidgets
46
47
48 _log = logging.getLogger('gm.vacc')
49
50
51
52
54
55 dbo_conn = gmAuthWidgets.get_dbowner_connection(procedure = _('Regenerating generic vaccines'))
56 if dbo_conn is None:
57 return False
58
59 wx.BeginBusyCursor()
60 _cfg = gmCfg2.gmCfgData()
61 sql_script = gmVaccination.write_generic_vaccine_sql (
62 'client-%s' % _cfg.get(option = 'client_version'),
63 include_indications_mapping = False
64 )
65 _log.debug('regenerating generic vaccines, SQL script: %s', sql_script)
66 if not gmPG2.run_sql_script(sql_script, conn = dbo_conn):
67 wx.EndBusyCursor()
68 gmGuiHelpers.gm_show_warning (
69 aMessage = _('Error regenerating generic vaccines.\n\nSee [%s]') % sql_script,
70 aTitle = _('Regenerating generic vaccines')
71 )
72 return False
73
74 gmDispatcher.send(signal = 'statustext', msg = _('Successfully regenerated generic vaccines ...'), beep = False)
75 wx.EndBusyCursor()
76 return True
77
78
79 -def edit_vaccine(parent=None, vaccine=None, single_entry=True):
90
91
132
133
134 def edit(vaccine=None):
135 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
136
137
138 def get_tooltip(vaccine):
139 if vaccine is None:
140 return None
141 return '\n'.join(vaccine.format())
142
143
144 def refresh(lctrl):
145 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
146
147 items = [ [
148 '%s' % v['pk_drug_product'],
149 '%s%s' % (
150 v['vaccine'],
151 gmTools.bool2subst (
152 v['is_fake_vaccine'],
153 ' (%s)' % _('fake'),
154 ''
155 )
156 ),
157 v['l10n_preparation'],
158 gmTools.coalesce(v['atc_code'], ''),
159 '%s - %s' % (
160 gmTools.coalesce(v['min_age'], '?'),
161 gmTools.coalesce(v['max_age'], '?'),
162 ),
163 gmTools.coalesce(v['comment'], '')
164 ] for v in vaccines ]
165 lctrl.set_string_items(items)
166 lctrl.set_data(vaccines)
167
168
169 gmListWidgets.get_choices_from_list (
170 parent = parent,
171 caption = _('Showing vaccine details'),
172 columns = [ '#', _('Vaccine'), _('Preparation'), _('ATC'), _('Age range'), _('Comment') ],
173 single_selection = True,
174 refresh_callback = refresh,
175 edit_callback = edit,
176 new_callback = edit,
177
178 list_tooltip_callback = get_tooltip,
179 left_extra_button = (_('Products'), _('Manage drug products'), manage_drug_products)
180 )
181
182
184
186
187 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
188
189 context = {
190 'ctxt_vaccine': {
191 'where_part': 'AND pk_vaccine = %(pk_vaccine)s',
192 'placeholder': 'pk_vaccine'
193 }
194 }
195
196 query = """
197 SELECT data, field_label, list_label FROM (
198
199 SELECT distinct on (field_label)
200 data,
201 field_label,
202 list_label,
203 rank
204 FROM ((
205 -- batch_no by vaccine
206 SELECT
207 batch_no AS data,
208 batch_no AS field_label,
209 batch_no || ' (' || vaccine || ')' AS list_label,
210 1 as rank
211 FROM
212 clin.v_vaccinations
213 WHERE
214 batch_no %(fragment_condition)s
215 %(ctxt_vaccine)s
216 ) UNION ALL (
217 -- batch_no for any vaccine
218 SELECT
219 batch_no AS data,
220 batch_no AS field_label,
221 batch_no || ' (' || vaccine || ')' AS list_label,
222 2 AS rank
223 FROM
224 clin.v_vaccinations
225 WHERE
226 batch_no %(fragment_condition)s
227 )
228
229 ) AS matching_batch_nos
230
231 ) as unique_matches
232
233 ORDER BY rank, list_label
234 LIMIT 25
235 """
236 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
237 mp.setThresholds(1, 2, 3)
238 self.matcher = mp
239
240 self.unset_context(context = 'pk_vaccine')
241 self.SetToolTip(_('Enter or select the batch/lot number of the vaccine used.'))
242 self.selection_only = False
243
244
246
248
249 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
250
251
252 query = """
253 SELECT data, list_label, field_label FROM (
254
255 SELECT DISTINCT ON (data)
256 data,
257 list_label,
258 field_label
259 FROM ((
260 -- fragment -> vaccine
261 SELECT
262 r_v_v.pk_vaccine
263 AS data,
264 r_v_v.vaccine || ' ('
265 || (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
266 SELECT unnest(r_v_v.indications)->>'l10n_indication' AS ind_desc
267 ) AS l10n_inds)
268 || ')'
269 AS list_label,
270 r_v_v.vaccine
271 AS field_label
272 FROM
273 ref.v_vaccines r_v_v
274 WHERE
275 r_v_v.vaccine %(fragment_condition)s
276
277 ) union all (
278
279 -- fragment -> localized indication -> vaccines
280 SELECT
281 r_vi4v.pk_vaccine
282 AS data,
283 r_vi4v.vaccine || ' ('
284 || (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
285 SELECT unnest(r_vi4v.indications)->>'l10n_indication' AS ind_desc
286 ) AS l10n_inds)
287 || ')'
288 AS list_label,
289 r_vi4v.vaccine
290 AS field_label
291 FROM
292 ref.v_indications4vaccine r_vi4v
293 WHERE
294 r_vi4v.l10n_indication %(fragment_condition)s
295
296 ) union all (
297
298 -- fragment -> indication -> vaccines
299 SELECT
300 r_vi4v.pk_vaccine
301 AS data,
302 r_vi4v.vaccine || ' ('
303 || (SELECT string_agg(l10n_inds.ind_desc::text, ', ') FROM (
304 SELECT unnest(r_vi4v.indications)->>'l10n_indication' AS ind_desc
305 ) AS l10n_inds)
306 || ')'
307 AS list_label,
308 r_vi4v.vaccine
309 AS field_label
310 FROM
311 ref.v_indications4vaccine r_vi4v
312 WHERE
313 r_vi4v.indication %(fragment_condition)s
314 )
315 ) AS distinct_total
316
317 ) AS total
318
319 ORDER by list_label
320 LIMIT 25
321 """
322 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
323 mp.setThresholds(1, 2, 3)
324 self.matcher = mp
325
326 self.selection_only = True
327
330
331
332 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
333
334 -class cVaccineEAPnl(wxgVaccineEAPnl.wxgVaccineEAPnl, gmEditArea.cGenericEditAreaMixin):
335
352
353
355 self._TCTRL_indications.SetValue('')
356 if self.data is None:
357 return
358 self._TCTRL_indications.SetValue('- ' + '\n- '.join([ i['l10n_indication'] for i in self.data['indications'] ]))
359
360
361
362
364
365 has_errors = False
366
367 if self._PRW_drug_product.GetValue().strip() == '':
368 has_errors = True
369 self._PRW_drug_product.display_as_valid(False)
370 else:
371 self._PRW_drug_product.display_as_valid(True)
372
373 atc = self._PRW_atc.GetValue().strip()
374 if (atc == '') or (atc.startswith('J07')):
375 self._PRW_atc.display_as_valid(True)
376 else:
377 if self._PRW_atc.GetData() is None:
378 self._PRW_atc.display_as_valid(True)
379 else:
380 has_errors = True
381 self._PRW_atc.display_as_valid(False)
382
383 val = self._PRW_age_min.GetValue().strip()
384 if val == '':
385 self._PRW_age_min.display_as_valid(True)
386 else:
387 if gmDateTime.str2interval(val) is None:
388 has_errors = True
389 self._PRW_age_min.display_as_valid(False)
390 else:
391 self._PRW_age_min.display_as_valid(True)
392
393 val = self._PRW_age_max.GetValue().strip()
394 if val == '':
395 self._PRW_age_max.display_as_valid(True)
396 else:
397 if gmDateTime.str2interval(val) is None:
398 has_errors = True
399 self._PRW_age_max.display_as_valid(False)
400 else:
401 self._PRW_age_max.display_as_valid(True)
402
403
404
405 if self.mode == 'edit':
406 change_of_product = self.data['pk_drug_product'] != self._PRW_drug_product.GetData()
407 if change_of_product and self.data.is_in_use:
408 do_it = gmGuiHelpers.gm_show_question (
409 aTitle = _('Saving vaccine'),
410 aMessage = _(
411 'This vaccine is already in use:\n'
412 '\n'
413 ' "%s"\n'
414 '\n'
415 'Are you absolutely positively sure that\n'
416 'you really want to edit this vaccine ?\n'
417 '\n'
418 'This will change the vaccine name and/or target\n'
419 'conditions in each patient this vaccine was\n'
420 'used in to document a vaccination with.\n'
421 ) % self._PRW_drug_product.GetValue().strip()
422 )
423 if not do_it:
424 has_errors = True
425 else:
426 if self._PRW_drug_product.GetData() is None:
427
428 if self._PRW_drug_product.GetValue().strip() != '':
429 self.__indications = gmSubstanceMgmtWidgets.manage_substance_doses(vaccine_indications_only = True)
430 if self.__indications is None:
431 has_errors = True
432 else:
433
434 pass
435
436 return (has_errors is False)
437
438
473
474
476
477 drug = self.data.product
478 drug['product'] = self._PRW_drug_product.GetValue().strip()
479 drug['is_fake_product'] = self._CHBOX_fake.GetValue()
480 val = self._PRW_atc.GetData()
481 if val is not None:
482 if val != 'J07':
483 drug['atc'] = val.strip()
484 drug.save()
485
486 self.data['is_live'] = self._CHBOX_live.GetValue()
487 val = self._PRW_age_min.GetValue().strip()
488 if val != '':
489 self.data['min_age'] = gmDateTime.str2interval(val)
490 if val != '':
491 self.data['max_age'] = gmDateTime.str2interval(val)
492 val = self._TCTRL_comment.GetValue().strip()
493 if val != '':
494 self.data['comment'] = val
495 self.data.save()
496
497 return True
498
499
501 self._PRW_drug_product.SetText(value = '', data = None, suppress_smarts = True)
502 self._CHBOX_live.SetValue(False)
503 self._CHBOX_fake.SetValue(False)
504 self._PRW_atc.SetText(value = '', data = None, suppress_smarts = True)
505 self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
506 self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
507 self._TCTRL_comment.SetValue('')
508
509 self.__refresh_indications()
510
511 self._PRW_drug_product.SetFocus()
512
513
515 self._PRW_drug_product.SetText(value = self.data['vaccine'], data = self.data['pk_drug_product'])
516 self._CHBOX_live.SetValue(self.data['is_live'])
517 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
518 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
519 if self.data['min_age'] is None:
520 self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
521 else:
522 self._PRW_age_min.SetText (
523 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
524 data = self.data['min_age']
525 )
526 if self.data['max_age'] is None:
527 self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
528 else:
529 self._PRW_age_max.SetText (
530 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
531 data = self.data['max_age']
532 )
533 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
534
535 self.__refresh_indications()
536
537 self._PRW_drug_product.SetFocus()
538
539
541 self._refresh_as_new()
542
543
544
545
546
547
548
575
576
590
591
612
613
614 def print_vaccs(vaccination=None):
615 print_vaccinations(parent = parent)
616 return False
617
618
619 def add_recall(vaccination=None):
620 if vaccination is None:
621 subject = _('vaccination recall')
622 else:
623 subject = _('vaccination recall (%s)') % vaccination['vaccine']
624
625 recall = gmProviderInbox.create_inbox_message (
626 message_type = _('Vaccination'),
627 subject = subject,
628 patient = pat.ID,
629 staff = None
630 )
631
632 if vaccination is not None:
633 recall['data'] = _('Existing vaccination:\n\n%s') % '\n'.join(vaccination.format(
634 with_indications = True,
635 with_comment = True,
636 with_reaction = False,
637 date_format = '%Y %b %d'
638 ))
639 recall.save()
640
641 from Gnumed.wxpython import gmProviderInboxWidgets
642 gmProviderInboxWidgets.edit_inbox_message (
643 parent = parent,
644 message = recall,
645 single_entry = False
646 )
647
648 return False
649
650
651 def get_tooltip(vaccination):
652 if vaccination is None:
653 return None
654 return '\n'.join(vaccination.format (
655 with_indications = True,
656 with_comment = True,
657 with_reaction = True,
658 date_format = '%Y %b %d'
659 ))
660
661
662 def edit(vaccination=None):
663 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
664
665
666 def delete(vaccination=None):
667 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
668 return True
669
670
671 def refresh(lctrl):
672
673 items = []
674 data = []
675 if latest_only:
676 latest_vaccs = emr.get_latest_vaccinations()
677 for indication in sorted(latest_vaccs.keys()):
678 no_of_shots4ind, latest_vacc4ind = latest_vaccs[indication]
679 items.append ([
680 indication,
681 _('%s (latest of %s: %s ago)') % (
682 gmDateTime.pydt_strftime(latest_vacc4ind['date_given'], format = '%Y %b'),
683 no_of_shots4ind,
684 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
685 ),
686 latest_vacc4ind['vaccine'],
687 latest_vacc4ind['batch_no'],
688 gmTools.coalesce(latest_vacc4ind['site'], ''),
689 gmTools.coalesce(latest_vacc4ind['reaction'], ''),
690 gmTools.coalesce(latest_vacc4ind['comment'], '')
691 ])
692 data.append(latest_vacc4ind)
693 else:
694 shots = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
695 if expand_indications:
696 shots_by_ind = {}
697 for shot in shots:
698 for ind in shot['indications']:
699 try:
700 shots_by_ind[ind['l10n_indication']].append(shot)
701 except KeyError:
702 shots_by_ind[ind['l10n_indication']] = [shot]
703 for ind in sorted(shots_by_ind.keys()):
704 idx = len(shots_by_ind[ind])
705 for shot in shots_by_ind[ind]:
706 items.append ([
707 '%s (#%s)' % (ind, idx),
708 _('%s (%s ago)') % (
709 gmDateTime.pydt_strftime(shot['date_given'], '%Y %b %d'),
710 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - shot['date_given'])
711 ),
712 shot['vaccine'],
713 shot['batch_no'],
714 gmTools.coalesce(shot['site'], ''),
715 gmTools.coalesce(shot['reaction'], ''),
716 gmTools.coalesce(shot['comment'], '')
717 ])
718 idx -= 1
719 data.append(shot)
720 else:
721 items = [ [
722 gmDateTime.pydt_strftime(s['date_given'], '%Y %b %d'),
723 s['vaccine'],
724 ', '.join([ i['l10n_indication'] for i in s['indications'] ]),
725 s['batch_no'],
726 gmTools.coalesce(s['site'], ''),
727 gmTools.coalesce(s['reaction'], ''),
728 gmTools.coalesce(s['comment'], '')
729 ] for s in shots ]
730 data = shots
731
732 lctrl.set_string_items(items)
733 lctrl.set_data(data)
734
735
736 if latest_only:
737 msg = _('Most recent vaccination for each indication.\n')
738 cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
739 else:
740 if expand_indications:
741 msg = _('Complete vaccination history (per indication).\n')
742 cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
743 else:
744 msg = _('Complete vaccination history (by shot).\n')
745 cols = [ _('Date'), _('Vaccine'), _('Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
746
747 gmListWidgets.get_choices_from_list (
748 parent = parent,
749 msg = msg,
750 caption = _('Showing vaccinations.'),
751 columns = cols,
752 single_selection = True,
753 refresh_callback = refresh,
754 new_callback = edit,
755 edit_callback = edit,
756 delete_callback = delete,
757 list_tooltip_callback = get_tooltip,
758 left_extra_button = (_('Print'), _('Print vaccinations or recalls.'), print_vaccs),
759 middle_extra_button = (_('Recall'), _('Add a recall for a vaccination'), add_recall),
760 right_extra_button = (_('Vx schedules'), _('Open a browser showing vaccination schedules.'), browse2schedules)
761 )
762
763
764 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
765
766 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
767 """
768 - warn on apparent duplicates
769 - ask if "missing" (= previous, non-recorded) vaccinations
770 should be estimated and saved (add note "auto-generated")
771
772 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
773 """
791
792
798
799
801
802 vaccine = self._PRW_vaccine.GetData(as_instance=True)
803
804 if self.mode == 'edit':
805 if vaccine is None:
806 self._PRW_batch.unset_context(context = 'pk_vaccine')
807 else:
808 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
809
810 else:
811 if vaccine is None:
812 self._PRW_batch.unset_context(context = 'pk_vaccine')
813 else:
814 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
815
816 self.__refresh_indications()
817
818
820 if self._PRW_reaction.GetValue().strip() == '':
821 self._BTN_report.Enable(False)
822 else:
823 self._BTN_report.Enable(True)
824
825
827 self._TCTRL_indications.SetValue('')
828 vaccine = self._PRW_vaccine.GetData(as_instance = True)
829 if vaccine is None:
830 return
831 lines = []
832 emr = gmPerson.gmCurrentPatient().emr
833 latest_vaccs = emr.get_latest_vaccinations (
834 atc_indications = [ i['atc_indication'] for i in vaccine['indications'] ]
835 )
836 for l10n_ind in [ i['l10n_indication'] for i in vaccine['indications'] ]:
837 try:
838 no_of_shots4ind, latest_vacc4ind = latest_vaccs[l10n_ind]
839 ago = gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
840 lines.append(_('%s (most recent shot of %s: %s ago)') % (l10n_ind, no_of_shots4ind, ago))
841 except KeyError:
842 lines.append(_('%s (no previous vaccination recorded)') % l10n_ind)
843
844 self._TCTRL_indications.SetValue(_('Protects against:\n ') + '\n '.join(lines))
845
846
847
848
850
851 has_errors = False
852
853 if not self._PRW_date_given.is_valid_timestamp(empty_is_valid = False):
854 has_errors = True
855
856 vaccine = self._PRW_vaccine.GetData(as_instance = True)
857 if vaccine is None:
858 has_errors = True
859 self._PRW_vaccine.display_as_valid(False)
860 else:
861 self._PRW_vaccine.display_as_valid(True)
862
863 if self._PRW_batch.GetValue().strip() == '':
864 has_errors = True
865 self._PRW_batch.display_as_valid(False)
866 else:
867 self._PRW_batch.display_as_valid(True)
868
869 if self._PRW_episode.GetValue().strip() == '':
870 self._PRW_episode.SetText(value = _('prevention'))
871
872 return (has_errors is False)
873
874
886
887
912
913
915
916 if self._CHBOX_anamnestic.GetValue() is True:
917 self.data['soap_cat'] = 's'
918 else:
919 self.data['soap_cat'] = 'p'
920
921 self.data['date_given'] = self._PRW_date_given.GetData()
922 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
923 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
924 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
925 self.data['site'] = self._PRW_site.GetValue().strip()
926 self.data['pk_provider'] = self._PRW_provider.GetData()
927 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
928 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
929
930 self.data.save()
931
932 return True
933
934
951
952
975
976
995
996
997
998
1016
1019
1020
1021
1022
1023
1024
1026
1028 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
1029 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1030 self.__pat = gmPerson.gmCurrentPatient()
1031
1032 self.ID_VaccinatedIndicationsList = wx.NewId()
1033 self.ID_VaccinationsPerRegimeList = wx.NewId()
1034 self.ID_MissingShots = wx.NewId()
1035 self.ID_ActiveSchedules = wx.NewId()
1036 self.__do_layout()
1037 self.__register_interests()
1038 self.__reset_ui_content()
1039
1041
1042
1043
1044 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
1045 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
1046
1047
1048
1049
1050
1051 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
1052 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
1053 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
1054 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
1055 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
1056 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
1057 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
1058
1059
1060 self.LBOX_vaccinated_indications = wx.ListBox(
1061 parent = self,
1062 id = self.ID_VaccinatedIndicationsList,
1063 choices = [],
1064 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1065 )
1066 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1067
1068
1069
1070 self.LBOX_given_shots = wx.ListBox(
1071 parent = self,
1072 id = self.ID_VaccinationsPerRegimeList,
1073 choices = [],
1074 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1075 )
1076 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1077
1078 self.LBOX_active_schedules = wx.ListBox (
1079 parent = self,
1080 id = self.ID_ActiveSchedules,
1081 choices = [],
1082 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1083 )
1084 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1085
1086 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1087 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1088 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1089 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1090
1091
1092
1093
1094 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1095 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1096 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1097
1098 self.LBOX_missing_shots = wx.ListBox (
1099 parent = self,
1100 id = self.ID_MissingShots,
1101 choices = [],
1102 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1103 )
1104 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1105
1106 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1107 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1108
1109
1110 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1111
1112
1113
1114
1115 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1116 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1117 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1118 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1119 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1120 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1121 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1122 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1123
1124 self.SetAutoLayout(True)
1125 self.SetSizer(self.mainsizer)
1126 self.mainsizer.Fit(self)
1127
1129
1130 wx.EVT_SIZE(self, self.OnSize)
1131 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1132 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1133 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1134
1135
1136
1137 gmDispatcher.connect(signal= 'post_patient_selection', receiver=self._schedule_data_reget)
1138 gmDispatcher.connect(signal= 'vaccinations_updated', receiver=self._schedule_data_reget)
1139
1140
1141
1143 w, h = event.GetSize()
1144 self.mainsizer.SetDimension (0, 0, w, h)
1145
1150
1153
1155 """Update right hand middle list to show vaccinations given for selected indication."""
1156 ind_list = event.GetEventObject()
1157 selected_item = ind_list.GetSelection()
1158 ind = ind_list.GetClientData(selected_item)
1159
1160 self.LBOX_given_shots.Set([])
1161 emr = self.__pat.emr
1162 shots = emr.get_vaccinations(indications = [ind])
1163
1164 for shot in shots:
1165 if shot['is_booster']:
1166 marker = 'B'
1167 else:
1168 marker = '#%s' % shot['seq_no']
1169 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1170 self.LBOX_given_shots.Append(label, shot)
1171
1173
1174 self.editarea.set_data()
1175
1176 self.LBOX_vaccinated_indications.Clear()
1177 self.LBOX_given_shots.Clear()
1178 self.LBOX_active_schedules.Clear()
1179 self.LBOX_missing_shots.Clear()
1180
1182
1183 self.LBOX_vaccinated_indications.Clear()
1184 self.LBOX_given_shots.Clear()
1185 self.LBOX_active_schedules.Clear()
1186 self.LBOX_missing_shots.Clear()
1187
1188 emr = self.__pat.emr
1189
1190 t1 = time.time()
1191
1192
1193
1194 status, indications = emr.get_vaccinated_indications()
1195
1196
1197
1198 for indication in indications:
1199 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1200
1201
1202 print("vaccinated indications took", time.time()-t1, "seconds")
1203
1204 t1 = time.time()
1205
1206 scheds = emr.get_scheduled_vaccination_regimes()
1207 if scheds is None:
1208 label = _('ERROR: cannot retrieve active vaccination schedules')
1209 self.LBOX_active_schedules.Append(label)
1210 elif len(scheds) == 0:
1211 label = _('no active vaccination schedules')
1212 self.LBOX_active_schedules.Append(label)
1213 else:
1214 for sched in scheds:
1215 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1216 self.LBOX_active_schedules.Append(label)
1217 print("active schedules took", time.time()-t1, "seconds")
1218
1219 t1 = time.time()
1220
1221 missing_shots = emr.get_missing_vaccinations()
1222 print("getting missing shots took", time.time()-t1, "seconds")
1223 if missing_shots is None:
1224 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1225 self.LBOX_missing_shots.Append(label, None)
1226 return True
1227
1228 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1229 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1230 for shot in missing_shots['due']:
1231 if shot['overdue']:
1232 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1233 weeks = days_left / 7
1234
1235 label = overdue_template % (
1236 years,
1237 weeks,
1238 shot['seq_no'],
1239 shot['l10n_indication'],
1240 shot['regime'],
1241 shot['vacc_comment']
1242 )
1243 self.LBOX_missing_shots.Append(label, shot)
1244 else:
1245
1246 label = due_template % (
1247 shot['time_left'].days / 7,
1248 shot['seq_no'],
1249 shot['indication'],
1250 shot['regime'],
1251 shot['latest_due'].strftime('%m/%Y'),
1252 shot['vacc_comment']
1253 )
1254 self.LBOX_missing_shots.Append(label, shot)
1255
1256 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1257 for shot in missing_shots['boosters']:
1258
1259 label = lbl_template % (
1260 shot['l10n_indication'],
1261 shot['regime'],
1262 shot['vacc_comment']
1263 )
1264 self.LBOX_missing_shots.Append(label, shot)
1265 print("displaying missing shots took", time.time()-t1, "seconds")
1266
1267 return True
1268
1269 - def _on_post_patient_selection(self, **kwargs):
1271
1272
1273
1274
1275
1276
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288 if __name__ == "__main__":
1289
1290 if len(sys.argv) < 2:
1291 sys.exit()
1292
1293 if sys.argv[1] != 'test':
1294 sys.exit()
1295
1296 app = wx.PyWidgetTester(size = (600, 600))
1297 app.SetWidget(cXxxPhraseWheel, -1)
1298 app.MainLoop()
1299