| Home | Trees | Indices | Help |
|
|---|
|
|
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 # vaccine related widgets
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 #----------------------------------------------------------------------
80 ea = cVaccineEAPnl(parent, -1)
81 ea.data = vaccine
82 ea.mode = gmTools.coalesce(vaccine, 'new', 'edit')
83 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
84 dlg.SetTitle(gmTools.coalesce(vaccine, _('Adding new vaccine'), _('Editing vaccine')))
85 if dlg.ShowModal() == wx.ID_OK:
86 dlg.Destroy()
87 return True
88 dlg.Destroy()
89 return False
90
91 #----------------------------------------------------------------------
93
94 if parent is None:
95 parent = wx.GetApp().GetTopWindow()
96
97 # #------------------------------------------------------------
98 # def delete(vaccine=None):
99 # product = vaccine.product
100 # deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
101 # if not deleted:
102 # gmGuiHelpers.gm_show_info (
103 # _( 'Cannot delete vaccine\n'
104 # '\n'
105 # ' %s - %s (#%s)\n'
106 # '\n'
107 # 'It is probably documented in a vaccination.'
108 # ) % (
109 # vaccine['vaccine'],
110 # vaccine['l10n_preparation'],
111 # vaccine['pk_vaccine']
112 # ),
113 # _('Deleting vaccine')
114 # )
115 # return False
116 # delete_product = gmGuiHelpers.gm_show_question (
117 # title = _('Deleting vaccine'),
118 # question = _(
119 # u'Fully delete the vaccine (including the associated drug product) ?\n'
120 # u'\n'
121 # u' "%s" (%s)'
122 # ) % (product['product'], product['l10n_preparation'])
123 # )
124 # if delete_product:
125 # pass
126 # return True
127
128 #------------------------------------------------------------
129 def manage_drug_products(vaccine):
130 gmSubstanceMgmtWidgets.manage_drug_products(parent = parent)
131 return True
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 #delete_callback = delete,
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 # consider ATCs in ref.drug_product and ref.vacc_indication
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
335
337 try:
338 data = kwargs['vaccine']
339 del kwargs['vaccine']
340 except KeyError:
341 data = None
342
343 wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs)
344 gmEditArea.cGenericEditAreaMixin.__init__(self)
345
346 self.mode = 'new'
347 self.data = data
348 if data is not None:
349 self.mode = 'edit'
350
351 self.__indications = None
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 # generic Edit Area mixin API
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 # complex conditions
404 # are we editing ?
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 # need to ask for indications ?
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 # existing drug product selected
434 pass
435
436 return (has_errors is False)
437
438 #----------------------------------------------------------------
440
441 # save the data as a new instance
442 vaccine = gmVaccination.create_vaccine (
443 pk_drug_product = self._PRW_drug_product.GetData(),
444 product_name = self._PRW_drug_product.GetValue().strip(),
445 indications = self.__indications
446 )
447
448 vaccine['is_live'] = self._CHBOX_live.GetValue()
449 val = self._PRW_age_min.GetValue().strip()
450 if val != '':
451 vaccine['min_age'] = gmDateTime.str2interval(val)
452 val = self._PRW_age_max.GetValue().strip()
453 if val != '':
454 vaccine['max_age'] = gmDateTime.str2interval(val)
455 val = self._TCTRL_comment.GetValue().strip()
456 if val != '':
457 vaccine['comment'] = val
458 vaccine.save()
459
460 drug = vaccine.product
461 drug['is_fake_product'] = self._CHBOX_fake.GetValue()
462 val = self._PRW_atc.GetData()
463 if val is not None:
464 if val != 'J07':
465 drug['atc'] = val.strip()
466 drug.save()
467
468 # must be done very late or else the property access
469 # will refresh the display such that later field
470 # access will return empty values
471 self.data = vaccine
472
473 return True
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 #----------------------------------------------------------------
543
544 #----------------------------------------------------------------
545 #----------------------------------------------------------------
546
547 #======================================================================
548 # vaccination related widgets
549 #----------------------------------------------------------------------
551
552 if parent is None:
553 parent = wx.GetApp().GetTopWindow()
554
555 vaccs_printout = gmFormWidgets.generate_form_from_template (
556 parent = parent,
557 template_types = [
558 'Medical statement',
559 'vaccination report',
560 'vaccination record',
561 'reminder'
562 ],
563 edit = False
564 )
565
566 if vaccs_printout is None:
567 return False
568
569 return gmFormWidgets.act_on_generated_forms (
570 parent = parent,
571 forms = [vaccs_printout],
572 jobtype = 'vaccinations',
573 episode_name = 'administrative',
574 review_copy_as_normal = True
575 )
576
577 #----------------------------------------------------------------------
579 ea = cVaccinationEAPnl(parent, -1)
580 ea.data = vaccination
581 ea.mode = gmTools.coalesce(vaccination, 'new', 'edit')
582 dlg = gmEditArea.cGenericEditAreaDlg2(parent, -1, edit_area = ea, single_entry = single_entry)
583 dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination')))
584 if dlg.ShowModal() == wx.ID_OK:
585 dlg.Destroy()
586 return True
587 dlg.Destroy()
588 if not single_entry:
589 return True
590 return False
591
592 #----------------------------------------------------------------------
594
595 pat = gmPerson.gmCurrentPatient()
596 emr = pat.emr
597
598 if parent is None:
599 parent = wx.GetApp().GetTopWindow()
600
601 #------------------------------------------------------------
602 def browse2schedules(vaccination=None):
603 dbcfg = gmCfg.cCfgSQL()
604 url = dbcfg.get2 (
605 option = 'external.urls.vaccination_plans',
606 workplace = gmPraxis.gmCurrentPraxisBranch().active_workplace,
607 bias = 'user',
608 default = gmVaccination.URL_vaccination_plan
609 )
610
611 gmNetworkTools.open_url_in_browser(url = url)
612 return False
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 """
776
777 try:
778 data = kwargs['vaccination']
779 del kwargs['vaccination']
780 except KeyError:
781 data = None
782
783 wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs)
784 gmEditArea.cGenericEditAreaMixin.__init__(self)
785
786 self.mode = 'new'
787 self.data = data
788 if data is not None:
789 self.mode = 'edit'
790
791 self.__init_ui()
792
793 #----------------------------------------------------------------
795 # adjust phrasewheels etc
796 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
797 self._PRW_provider.selection_only = False
798 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
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 # we are entering a new vaccination
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 # generic Edit Area mixin API
849 #----------------------------------------------------------------
851
852 has_errors = False
853
854 if not self._PRW_date_given.is_valid_timestamp(allow_empty = False):
855 has_errors = True
856
857 vaccine = self._PRW_vaccine.GetData(as_instance = True)
858 if vaccine is None:
859 has_errors = True
860 self._PRW_vaccine.display_as_valid(False)
861 else:
862 self._PRW_vaccine.display_as_valid(True)
863
864 if self._PRW_batch.GetValue().strip() == '':
865 has_errors = True
866 self._PRW_batch.display_as_valid(False)
867 else:
868 self._PRW_batch.display_as_valid(True)
869
870 if self._PRW_episode.GetValue().strip() == '':
871 self._PRW_episode.SetText(value = _('prevention'))
872
873 return (has_errors is False)
874
875 #----------------------------------------------------------------
877
878 vaccine = self._PRW_vaccine.GetData()
879 data = self.__save_new_from_vaccine(vaccine = vaccine)
880
881 # must be done very late or else the property access
882 # will refresh the display such that later field
883 # access will return empty values
884 self.data = data
885
886 return True
887
888 #----------------------------------------------------------------
890
891 emr = gmPerson.gmCurrentPatient().emr
892
893 data = emr.add_vaccination (
894 episode = self._PRW_episode.GetData(can_create = True, is_open = False),
895 vaccine = vaccine,
896 batch_no = self._PRW_batch.GetValue().strip()
897 )
898
899 if self._CHBOX_anamnestic.GetValue() is True:
900 data['soap_cat'] = 's'
901 else:
902 data['soap_cat'] = 'p'
903
904 data['date_given'] = self._PRW_date_given.GetData()
905 data['site'] = self._PRW_site.GetValue().strip()
906 data['pk_provider'] = self._PRW_provider.GetData()
907 data['reaction'] = self._PRW_reaction.GetValue().strip()
908 data['comment'] = self._TCTRL_comment.GetValue().strip()
909
910 data.save()
911
912 return data
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 #----------------------------------------------------------------
937 self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here())
938 self._CHBOX_anamnestic.SetValue(False)
939 self._PRW_vaccine.SetText(value = '', data = None, suppress_smarts = True)
940 self._PRW_batch.unset_context(context = 'pk_vaccine')
941 self._PRW_batch.SetValue('')
942 self._PRW_episode.SetText(value = '', data = None, suppress_smarts = True)
943 self._PRW_site.SetValue('')
944 self._PRW_provider.SetData(data = None)
945 self._PRW_reaction.SetText(value = '', data = None, suppress_smarts = True)
946 self._BTN_report.Enable(False)
947 self._TCTRL_comment.SetValue('')
948
949 self.__refresh_indications()
950
951 self._PRW_date_given.SetFocus()
952
953 #----------------------------------------------------------------
955 self._PRW_date_given.SetText(data = self.data['date_given'])
956 if self.data['soap_cat'] == 's':
957 self._CHBOX_anamnestic.SetValue(True)
958 else:
959 self._CHBOX_anamnestic.SetValue(False)
960 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
961
962 self._PRW_batch.SetValue(self.data['batch_no'])
963 self._PRW_episode.SetData(data = self.data['pk_episode'])
964 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], ''))
965 self._PRW_provider.SetData(self.data['pk_provider'])
966 self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], ''))
967 if self.data['reaction'] is None:
968 self._BTN_report.Enable(False)
969 else:
970 self._BTN_report.Enable(True)
971 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], ''))
972
973 self.__refresh_indications()
974
975 self._PRW_date_given.SetFocus()
976
977 #----------------------------------------------------------------
979 self._PRW_date_given.SetText(data = self.data['date_given'])
980 #self._CHBOX_anamnestic.SetValue(False)
981 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
982
983 self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine'])
984 self._PRW_batch.SetValue('')
985
986 self._PRW_episode.SetData(data = self.data['pk_episode'])
987 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], ''))
988 self._PRW_provider.SetData(self.data['pk_provider'])
989 self._PRW_reaction.SetValue('')
990 self._BTN_report.Enable(False)
991 self._TCTRL_comment.SetValue('')
992
993 self.__refresh_indications()
994
995 self._PRW_date_given.SetFocus()
996
997 #----------------------------------------------------------------
998 # event handlers
999 #----------------------------------------------------------------
1017 #----------------------------------------------------------------
1020 # FIXME: could set newly generated vaccine here
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 # do this here so "import cImmunisationsPanel from gmVaccWidgets" works
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 # top part
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 # middle part
1050 #-----------------------------------------------
1051 # divider headings below editing area
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 # left list: indications for which vaccinations have been given
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 # right list: when an indication has been selected on the left
1070 # display the corresponding vaccinations on the right
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 # bottom part
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 # alert caption
1111 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1112
1113 #---------------------------------------------
1114 # add all elements to the main background sizer
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 # wxPython events
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 # wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton)
1136
1137 # client internal signals
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 # event handlers
1142 #----------------------------------------------------
1146 #----------------------------------------------------
1148 """Paste previously given shot into edit area.
1149 """
1150 self.editarea.set_data(aVacc=event.GetClientData())
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 # clear list
1161 self.LBOX_given_shots.Set([])
1162 emr = self.__pat.emr
1163 shots = emr.get_vaccinations(indications = [ind])
1164 # FIXME: use Set() for entire array (but problem with client_data)
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 # clear edit area
1175 self.editarea.set_data()
1176 # clear lists
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 # clear lists
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 # populate vaccinated-indications list
1193 # FIXME: consider adding virtual indication "most recent" to
1194 # FIXME: display most recent of all indications as suggested by Syan
1195 status, indications = emr.get_vaccinated_indications()
1196 # FIXME: would be faster to use Set() but can't
1197 # use Set(labels, client_data), and have to know
1198 # line position in SetClientData :-(
1199 for indication in indications:
1200 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1201 # self.LBOX_vaccinated_indications.Set(lines)
1202 # self.LBOX_vaccinated_indications.SetClientData(data)
1203 print("vaccinated indications took", time.time()-t1, "seconds")
1204
1205 t1 = time.time()
1206 # populate active schedules list
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 # populate missing-shots list
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 # due
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 # amount_overdue, seq_no, indication, regime, vacc_comment
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 # time_left, seq_no, regime, latest_due, vacc_comment
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 # booster
1257 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1258 for shot in missing_shots['boosters']:
1259 # indication, regime, vacc_comment
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 #----------------------------------------------------
1272 # FIXME:
1273 # if has_focus:
1274 # wxCallAfter(self.__reset_ui_content)
1275 # else:
1276 # return 1
1277 #----------------------------------------------------
1280 # FIXME:
1281 # if has_focus:
1282 # wxCallAfter(self.__reset_ui_content)
1283 # else:
1284 # is_stale == True
1285 # return 1
1286 #======================================================================
1287 # main
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
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |