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
474
475
477
478 drug = self.data.product
479 drug['product'] = self._PRW_drug_product.GetValue().strip()
480 drug['is_fake_product'] = self._CHBOX_fake.GetValue()
481 val = self._PRW_atc.GetData()
482 if val is not None:
483 if val != 'J07':
484 drug['atc'] = val.strip()
485 drug.save()
486
487 self.data['is_live'] = self._CHBOX_live.GetValue()
488 val = self._PRW_age_min.GetValue().strip()
489 if val != '':
490 self.data['min_age'] = gmDateTime.str2interval(val)
491 if val != '':
492 self.data['max_age'] = gmDateTime.str2interval(val)
493 val = self._TCTRL_comment.GetValue().strip()
494 if val != '':
495 self.data['comment'] = val
496 self.data.save()
497
498 return True
499
500
502 self._PRW_drug_product.SetText(value = '', data = None, suppress_smarts = True)
503 self._CHBOX_live.SetValue(False)
504 self._CHBOX_fake.SetValue(False)
505 self._PRW_atc.SetText(value = '', data = None, suppress_smarts = True)
506 self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
507 self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
508 self._TCTRL_comment.SetValue('')
509
510 self.__refresh_indications()
511
512 self._PRW_drug_product.SetFocus()
513
514
516 self._PRW_drug_product.SetText(value = self.data['vaccine'], data = self.data['pk_drug_product'])
517 self._CHBOX_live.SetValue(self.data['is_live'])
518 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
519 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
520 if self.data['min_age'] is None:
521 self._PRW_age_min.SetText(value = '', data = None, suppress_smarts = True)
522 else:
523 self._PRW_age_min.SetText (
524 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
525 data = self.data['min_age']
526 )
527 if self.data['max_age'] is None:
528 self._PRW_age_max.SetText(value = '', data = None, suppress_smarts = True)
529 else:
530 self._PRW_age_max.SetText (
531 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
532 data = self.data['max_age']
533 )
534 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
535
536 self.__refresh_indications()
537
538 self._PRW_drug_product.SetFocus()
539
540
542 self._refresh_as_new()
543
544
545
546
547
548
549
576
577
591
592
613
614
615 def print_vaccs(vaccination=None):
616 print_vaccinations(parent = parent)
617 return False
618
619
620 def add_recall(vaccination=None):
621 if vaccination is None:
622 subject = _('vaccination recall')
623 else:
624 subject = _('vaccination recall (%s)') % vaccination['vaccine']
625
626 recall = gmProviderInbox.create_inbox_message (
627 message_type = _('Vaccination'),
628 subject = subject,
629 patient = pat.ID,
630 staff = None
631 )
632
633 if vaccination is not None:
634 recall['data'] = _('Existing vaccination:\n\n%s') % '\n'.join(vaccination.format(
635 with_indications = True,
636 with_comment = True,
637 with_reaction = False,
638 date_format = '%Y %b %d'
639 ))
640 recall.save()
641
642 from Gnumed.wxpython import gmProviderInboxWidgets
643 gmProviderInboxWidgets.edit_inbox_message (
644 parent = parent,
645 message = recall,
646 single_entry = False
647 )
648
649 return False
650
651
652 def get_tooltip(vaccination):
653 if vaccination is None:
654 return None
655 return '\n'.join(vaccination.format (
656 with_indications = True,
657 with_comment = True,
658 with_reaction = True,
659 date_format = '%Y %b %d'
660 ))
661
662
663 def edit(vaccination=None):
664 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
665
666
667 def delete(vaccination=None):
668 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
669 return True
670
671
672 def refresh(lctrl):
673
674 items = []
675 data = []
676 if latest_only:
677 latest_vaccs = emr.get_latest_vaccinations()
678 for indication in sorted(latest_vaccs.keys()):
679 no_of_shots4ind, latest_vacc4ind = latest_vaccs[indication]
680 items.append ([
681 indication,
682 _('%s (latest of %s: %s ago)') % (
683 gmDateTime.pydt_strftime(latest_vacc4ind['date_given'], format = '%Y %b'),
684 no_of_shots4ind,
685 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
686 ),
687 latest_vacc4ind['vaccine'],
688 latest_vacc4ind['batch_no'],
689 gmTools.coalesce(latest_vacc4ind['site'], ''),
690 gmTools.coalesce(latest_vacc4ind['reaction'], ''),
691 gmTools.coalesce(latest_vacc4ind['comment'], '')
692 ])
693 data.append(latest_vacc4ind)
694 else:
695 shots = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
696 if expand_indications:
697 shots_by_ind = {}
698 for shot in shots:
699 for ind in shot['indications']:
700 try:
701 shots_by_ind[ind['l10n_indication']].append(shot)
702 except KeyError:
703 shots_by_ind[ind['l10n_indication']] = [shot]
704 for ind in sorted(shots_by_ind.keys()):
705 idx = len(shots_by_ind[ind])
706 for shot in shots_by_ind[ind]:
707 items.append ([
708 '%s (#%s)' % (ind, idx),
709 _('%s (%s ago)') % (
710 gmDateTime.pydt_strftime(shot['date_given'], '%Y %b %d'),
711 gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - shot['date_given'])
712 ),
713 shot['vaccine'],
714 shot['batch_no'],
715 gmTools.coalesce(shot['site'], ''),
716 gmTools.coalesce(shot['reaction'], ''),
717 gmTools.coalesce(shot['comment'], '')
718 ])
719 idx -= 1
720 data.append(shot)
721 else:
722 items = [ [
723 gmDateTime.pydt_strftime(s['date_given'], '%Y %b %d'),
724 s['vaccine'],
725 ', '.join([ i['l10n_indication'] for i in s['indications'] ]),
726 s['batch_no'],
727 gmTools.coalesce(s['site'], ''),
728 gmTools.coalesce(s['reaction'], ''),
729 gmTools.coalesce(s['comment'], '')
730 ] for s in shots ]
731 data = shots
732
733 lctrl.set_string_items(items)
734 lctrl.set_data(data)
735
736
737 if latest_only:
738 msg = _('Most recent vaccination for each indication.\n')
739 cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
740 else:
741 if expand_indications:
742 msg = _('Complete vaccination history (per indication).\n')
743 cols = [ _('Indication'), _('Date'), _('Vaccine'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
744 else:
745 msg = _('Complete vaccination history (by shot).\n')
746 cols = [ _('Date'), _('Vaccine'), _('Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ]
747
748 gmListWidgets.get_choices_from_list (
749 parent = parent,
750 msg = msg,
751 caption = _('Showing vaccinations.'),
752 columns = cols,
753 single_selection = True,
754 refresh_callback = refresh,
755 new_callback = edit,
756 edit_callback = edit,
757 delete_callback = delete,
758 list_tooltip_callback = get_tooltip,
759 left_extra_button = (_('Print'), _('Print vaccinations or recalls.'), print_vaccs),
760 middle_extra_button = (_('Recall'), _('Add a recall for a vaccination'), add_recall),
761 right_extra_button = (_('Vx schedules'), _('Open a browser showing vaccination schedules.'), browse2schedules)
762 )
763
764
765 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
766
767 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
768 """
769 - warn on apparent duplicates
770 - ask if "missing" (= previous, non-recorded) vaccinations
771 should be estimated and saved (add note "auto-generated")
772
773 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
774 """
792
793
799
800
802
803 vaccine = self._PRW_vaccine.GetData(as_instance=True)
804
805 if self.mode == 'edit':
806 if vaccine is None:
807 self._PRW_batch.unset_context(context = 'pk_vaccine')
808 else:
809 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
810
811 else:
812 if vaccine is None:
813 self._PRW_batch.unset_context(context = 'pk_vaccine')
814 else:
815 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
816
817 self.__refresh_indications()
818
819
821 if self._PRW_reaction.GetValue().strip() == '':
822 self._BTN_report.Enable(False)
823 else:
824 self._BTN_report.Enable(True)
825
826
828 self._TCTRL_indications.SetValue('')
829 vaccine = self._PRW_vaccine.GetData(as_instance = True)
830 if vaccine is None:
831 return
832 lines = []
833 emr = gmPerson.gmCurrentPatient().emr
834 latest_vaccs = emr.get_latest_vaccinations (
835 atc_indications = [ i['atc_indication'] for i in vaccine['indications'] ]
836 )
837 for l10n_ind in [ i['l10n_indication'] for i in vaccine['indications'] ]:
838 try:
839 no_of_shots4ind, latest_vacc4ind = latest_vaccs[l10n_ind]
840 ago = gmDateTime.format_interval_medically(gmDateTime.pydt_now_here() - latest_vacc4ind['date_given'])
841 lines.append(_('%s (most recent shot of %s: %s ago)') % (l10n_ind, no_of_shots4ind, ago))
842 except KeyError:
843 lines.append(_('%s (no previous vaccination recorded)') % l10n_ind)
844
845 self._TCTRL_indications.SetValue(_('Protects against:\n ') + '\n '.join(lines))
846
847
848
849
874
875
887
888
913
914
916
917 if self._CHBOX_anamnestic.GetValue() is True:
918 self.data['soap_cat'] = 's'
919 else:
920 self.data['soap_cat'] = 'p'
921
922 self.data['date_given'] = self._PRW_date_given.GetData()
923 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
924 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
925 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
926 self.data['site'] = self._PRW_site.GetValue().strip()
927 self.data['pk_provider'] = self._PRW_provider.GetData()
928 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
929 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
930
931 self.data.save()
932
933 return True
934
935
952
953
976
977
996
997
998
999
1017
1020
1021
1022
1023
1024
1025
1027
1029 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
1030 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
1031 self.__pat = gmPerson.gmCurrentPatient()
1032
1033 self.ID_VaccinatedIndicationsList = wx.NewId()
1034 self.ID_VaccinationsPerRegimeList = wx.NewId()
1035 self.ID_MissingShots = wx.NewId()
1036 self.ID_ActiveSchedules = wx.NewId()
1037 self.__do_layout()
1038 self.__register_interests()
1039 self.__reset_ui_content()
1040
1042
1043
1044
1045 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
1046 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
1047
1048
1049
1050
1051
1052 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
1053 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
1054 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
1055 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
1056 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
1057 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
1058 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
1059
1060
1061 self.LBOX_vaccinated_indications = wx.ListBox(
1062 parent = self,
1063 id = self.ID_VaccinatedIndicationsList,
1064 choices = [],
1065 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1066 )
1067 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1068
1069
1070
1071 self.LBOX_given_shots = wx.ListBox(
1072 parent = self,
1073 id = self.ID_VaccinationsPerRegimeList,
1074 choices = [],
1075 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1076 )
1077 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1078
1079 self.LBOX_active_schedules = wx.ListBox (
1080 parent = self,
1081 id = self.ID_ActiveSchedules,
1082 choices = [],
1083 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1084 )
1085 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1086
1087 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1088 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1089 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1090 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1091
1092
1093
1094
1095 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1096 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1097 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1098
1099 self.LBOX_missing_shots = wx.ListBox (
1100 parent = self,
1101 id = self.ID_MissingShots,
1102 choices = [],
1103 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1104 )
1105 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1106
1107 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1108 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1109
1110
1111 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1112
1113
1114
1115
1116 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1117 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1118 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1119 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1120 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1121 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1122 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1123 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1124
1125 self.SetAutoLayout(True)
1126 self.SetSizer(self.mainsizer)
1127 self.mainsizer.Fit(self)
1128
1130
1131 wx.EVT_SIZE(self, self.OnSize)
1132 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1133 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1134 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1135
1136
1137
1138 gmDispatcher.connect(signal= 'post_patient_selection', receiver=self._schedule_data_reget)
1139 gmDispatcher.connect(signal= 'vaccinations_updated', receiver=self._schedule_data_reget)
1140
1141
1142
1144 w, h = event.GetSize()
1145 self.mainsizer.SetDimension (0, 0, w, h)
1146
1151
1154
1156 """Update right hand middle list to show vaccinations given for selected indication."""
1157 ind_list = event.GetEventObject()
1158 selected_item = ind_list.GetSelection()
1159 ind = ind_list.GetClientData(selected_item)
1160
1161 self.LBOX_given_shots.Set([])
1162 emr = self.__pat.emr
1163 shots = emr.get_vaccinations(indications = [ind])
1164
1165 for shot in shots:
1166 if shot['is_booster']:
1167 marker = 'B'
1168 else:
1169 marker = '#%s' % shot['seq_no']
1170 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1171 self.LBOX_given_shots.Append(label, shot)
1172
1174
1175 self.editarea.set_data()
1176
1177 self.LBOX_vaccinated_indications.Clear()
1178 self.LBOX_given_shots.Clear()
1179 self.LBOX_active_schedules.Clear()
1180 self.LBOX_missing_shots.Clear()
1181
1183
1184 self.LBOX_vaccinated_indications.Clear()
1185 self.LBOX_given_shots.Clear()
1186 self.LBOX_active_schedules.Clear()
1187 self.LBOX_missing_shots.Clear()
1188
1189 emr = self.__pat.emr
1190
1191 t1 = time.time()
1192
1193
1194
1195 status, indications = emr.get_vaccinated_indications()
1196
1197
1198
1199 for indication in indications:
1200 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1201
1202
1203 print("vaccinated indications took", time.time()-t1, "seconds")
1204
1205 t1 = time.time()
1206
1207 scheds = emr.get_scheduled_vaccination_regimes()
1208 if scheds is None:
1209 label = _('ERROR: cannot retrieve active vaccination schedules')
1210 self.LBOX_active_schedules.Append(label)
1211 elif len(scheds) == 0:
1212 label = _('no active vaccination schedules')
1213 self.LBOX_active_schedules.Append(label)
1214 else:
1215 for sched in scheds:
1216 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1217 self.LBOX_active_schedules.Append(label)
1218 print("active schedules took", time.time()-t1, "seconds")
1219
1220 t1 = time.time()
1221
1222 missing_shots = emr.get_missing_vaccinations()
1223 print("getting missing shots took", time.time()-t1, "seconds")
1224 if missing_shots is None:
1225 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1226 self.LBOX_missing_shots.Append(label, None)
1227 return True
1228
1229 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1230 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1231 for shot in missing_shots['due']:
1232 if shot['overdue']:
1233 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1234 weeks = days_left / 7
1235
1236 label = overdue_template % (
1237 years,
1238 weeks,
1239 shot['seq_no'],
1240 shot['l10n_indication'],
1241 shot['regime'],
1242 shot['vacc_comment']
1243 )
1244 self.LBOX_missing_shots.Append(label, shot)
1245 else:
1246
1247 label = due_template % (
1248 shot['time_left'].days / 7,
1249 shot['seq_no'],
1250 shot['indication'],
1251 shot['regime'],
1252 shot['latest_due'].strftime('%m/%Y'),
1253 shot['vacc_comment']
1254 )
1255 self.LBOX_missing_shots.Append(label, shot)
1256
1257 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1258 for shot in missing_shots['boosters']:
1259
1260 label = lbl_template % (
1261 shot['l10n_indication'],
1262 shot['regime'],
1263 shot['vacc_comment']
1264 )
1265 self.LBOX_missing_shots.Append(label, shot)
1266 print("displaying missing shots took", time.time()-t1, "seconds")
1267
1268 return True
1269
1270 - def _on_post_patient_selection(self, **kwargs):
1272
1273
1274
1275
1276
1277
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289 if __name__ == "__main__":
1290
1291 if len(sys.argv) < 2:
1292 sys.exit()
1293
1294 if sys.argv[1] != 'test':
1295 sys.exit()
1296
1297 app = wx.PyWidgetTester(size = (600, 600))
1298 app.SetWidget(cXxxPhraseWheel, -1)
1299 app.MainLoop()
1300