| 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 __version__ = "$Revision: 1.36 $"
9 __author__ = "R.Terry, S.J.Tan, K.Hilbert"
10 __license__ = "GPL (details at http://www.gnu.org)"
11
12 import sys, time, logging, webbrowser
13
14
15 import wx
16
17
18 if __name__ == '__main__':
19 sys.path.insert(0, '../../')
20 from Gnumed.pycommon import gmDispatcher, gmMatchProvider, gmTools, gmI18N
21 from Gnumed.pycommon import gmCfg, gmDateTime
22 from Gnumed.business import gmPerson, gmVaccination, gmSurgery
23 from Gnumed.wxpython import gmPhraseWheel, gmTerryGuiParts, gmRegetMixin, gmGuiHelpers
24 from Gnumed.wxpython import gmEditArea, gmListWidgets
25
26
27 _log = logging.getLogger('gm.vaccination')
28 _log.info(__version__)
29
30 #======================================================================
31 # vaccination indication related widgets
32 #----------------------------------------------------------------------
34
35 if parent is None:
36 parent = wx.GetApp().GetTopWindow()
37 #------------------------------------------------------------
38 def refresh(lctrl):
39 inds = gmVaccination.get_indications(order_by = 'description')
40
41 items = [ [
42 i['description'],
43 gmTools.coalesce (
44 i['atcs_single_indication'],
45 u'',
46 u'%s'
47 ),
48 gmTools.coalesce (
49 i['atcs_combi_indication'],
50 u'',
51 u'%s'
52 ),
53 u'%s' % i['id']
54 ] for i in inds ]
55
56 lctrl.set_string_items(items)
57 lctrl.set_data(inds)
58 #------------------------------------------------------------
59 gmListWidgets.get_choices_from_list (
60 parent = parent,
61 msg = _('\nConditions preventable by vaccination as currently known to GNUmed.\n'),
62 caption = _('Showing vaccination preventable conditions.'),
63 columns = [ _('Condition'), _('ATCs: single-condition vaccines'), _('ATCs: multi-condition vaccines'), u'#' ],
64 single_selection = True,
65 refresh_callback = refresh
66 )
67 #----------------------------------------------------------------------
68 from Gnumed.wxGladeWidgets import wxgVaccinationIndicationsPnl
69
71
73
74 wxgVaccinationIndicationsPnl.wxgVaccinationIndicationsPnl.__init__(self, *args, **kwargs)
75
76 self.__indication2field = {
77 u'coxiella burnetii (Q fever)': self._CHBOX_coxq,
78 u'salmonella typhi (typhoid)': self._CHBOX_typhoid,
79 u'varicella (chickenpox, shingles)': self._CHBOX_varicella,
80 u'influenza (seasonal)': self._CHBOX_influenza,
81 u'bacillus anthracis (Anthrax)': self._CHBOX_anthrax,
82 u'human papillomavirus': self._CHBOX_hpv,
83 u'rotavirus': self._CHBOX_rota,
84 u'tuberculosis': self._CHBOX_tuberculosis,
85 u'variola virus (smallpox)': self._CHBOX_smallpox,
86 u'influenza (H1N1)': self._CHBOX_h1n1,
87 u'cholera': self._CHBOX_cholera,
88 u'diphtheria': self._CHBOX_diphtheria,
89 u'haemophilus influenzae b': self._CHBOX_hib,
90 u'hepatitis A': self._CHBOX_hepA,
91 u'hepatitis B': self._CHBOX_hepB,
92 u'japanese B encephalitis': self._CHBOX_japanese,
93 u'measles': self._CHBOX_measles,
94 u'meningococcus A': self._CHBOX_menA,
95 u'meningococcus C': self._CHBOX_menC,
96 u'meningococcus W': self._CHBOX_menW,
97 u'meningococcus Y': self._CHBOX_menY,
98 u'mumps': self._CHBOX_mumps,
99 u'pertussis': self._CHBOX_pertussis,
100 u'pneumococcus': self._CHBOX_pneumococcus,
101 u'poliomyelitis': self._CHBOX_polio,
102 u'rabies': self._CHBOX_rabies,
103 u'rubella': self._CHBOX_rubella,
104 u'tetanus': self._CHBOX_tetanus,
105 u'tick-borne meningoencephalitis': self._CHBOX_fsme,
106 u'yellow fever': self._CHBOX_yellow_fever,
107 u'yersinia pestis': self._CHBOX_yersinia_pestis
108 }
109 #------------------------------------------------------------------
111 for field in self.__dict__.keys():
112 if field.startswith('_CHBOX_'):
113 self.__dict__[field].Enable()
114 self.Enable()
115 #------------------------------------------------------------------
117 for field in self.__dict__.keys():
118 if field.startswith('_CHBOX_'):
119 self.__dict__[field].Disable()
120 self.Disable()
121 #------------------------------------------------------------------
123 for field in self.__dict__.keys():
124 if field.startswith('_CHBOX_'):
125 self.__dict__[field].SetValue(False)
126 #------------------------------------------------------------------
130 #------------------------------------------------------------------
132 indications = []
133 for indication in self.__indication2field.keys():
134 if self.__indication2field[indication].IsChecked():
135 indications.append(indication)
136 return indications
137
138 selected_indications = property(_get_selected_indications, lambda x:x)
139 #------------------------------------------------------------------
141 for indication in self.__indication2field.keys():
142 if self.__indication2field[indication].IsChecked():
143 return True
144 return False
145
146 has_selection = property(_get_has_selection, lambda x:x)
147
148 #======================================================================
149 # vaccines related widgets
150 #----------------------------------------------------------------------
152 ea = cVaccineEAPnl(parent = parent, id = -1)
153 ea.data = vaccine
154 ea.mode = gmTools.coalesce(vaccine, 'new', 'edit')
155 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
156 dlg.SetTitle(gmTools.coalesce(vaccine, _('Adding new vaccine'), _('Editing vaccine')))
157 if dlg.ShowModal() == wx.ID_OK:
158 dlg.Destroy()
159 return True
160 dlg.Destroy()
161 return False
162 #----------------------------------------------------------------------
164
165 if parent is None:
166 parent = wx.GetApp().GetTopWindow()
167 #------------------------------------------------------------
168 def delete(vaccine=None):
169 deleted = gmVaccination.delete_vaccine(vaccine = vaccine['pk_vaccine'])
170 if deleted:
171 return True
172
173 gmGuiHelpers.gm_show_info (
174 _(
175 'Cannot delete vaccine\n'
176 '\n'
177 ' %s - %s (#%s)\n'
178 '\n'
179 'It is probably documented in a vaccination.'
180 ) % (
181 vaccine['vaccine'],
182 vaccine['preparation'],
183 vaccine['pk_vaccine']
184 ),
185 _('Deleting vaccine')
186 )
187
188 return False
189 #------------------------------------------------------------
190 def edit(vaccine=None):
191 return edit_vaccine(parent = parent, vaccine = vaccine, single_entry = True)
192 #------------------------------------------------------------
193 def refresh(lctrl):
194 vaccines = gmVaccination.get_vaccines(order_by = 'vaccine')
195
196 items = [ [
197 u'%s' % v['pk_brand'],
198 u'%s%s' % (
199 v['vaccine'],
200 gmTools.bool2subst (
201 v['is_fake_vaccine'],
202 u' (%s)' % _('fake'),
203 u''
204 )
205 ),
206 v['preparation'],
207 u'%s (%s)' % (v['route_abbreviation'], v['route_description']),
208 gmTools.bool2subst(v['is_live'], gmTools.u_checkmark_thin, u''),
209 gmTools.coalesce(v['atc_code'], u''),
210 u'%s%s' % (
211 gmTools.coalesce(v['min_age'], u'?'),
212 gmTools.coalesce(v['max_age'], u'?', u' - %s'),
213 ),
214 gmTools.coalesce(v['comment'], u'')
215 ] for v in vaccines ]
216 lctrl.set_string_items(items)
217 lctrl.set_data(vaccines)
218 #------------------------------------------------------------
219 gmListWidgets.get_choices_from_list (
220 parent = parent,
221 msg = _('\nThe vaccines currently known to GNUmed.\n'),
222 caption = _('Showing vaccines.'),
223 columns = [ u'#', _('Brand'), _('Preparation'), _(u'Route'), _('Live'), _('ATC'), _('Age range'), _('Comment') ],
224 single_selection = True,
225 refresh_callback = refresh,
226 edit_callback = edit,
227 new_callback = edit,
228 delete_callback = delete
229 )
230 #----------------------------------------------------------------------
232
234
235 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
236
237 context = {
238 u'ctxt_vaccine': {
239 u'where_part': u'AND pk_vaccine = %(pk_vaccine)s',
240 u'placeholder': u'pk_vaccine'
241 }
242 }
243
244 query = u"""
245 SELECT code, batch_no FROM (
246
247 SELECT distinct on (batch_no) code, batch_no, rank FROM (
248
249 (
250 -- batch_no by vaccine
251 SELECT
252 batch_no AS code,
253 batch_no,
254 1 AS rank
255 FROM
256 clin.v_pat_vaccinations
257 WHERE
258 batch_no %(fragment_condition)s
259 %(ctxt_vaccine)s
260 ) UNION ALL (
261 -- batch_no for any vaccine
262 SELECT
263 batch_no AS code,
264 batch_no,
265 2 AS rank
266 FROM
267 clin.v_pat_vaccinations
268 WHERE
269 batch_no %(fragment_condition)s
270 )
271
272 ) AS matching_batch_nos
273
274 ) as unique_matches
275
276 ORDER BY rank, batch_no
277 LIMIT 25
278 """
279 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
280 mp.setThresholds(1, 2, 3)
281 self.matcher = mp
282
283 self.unset_context(context = u'pk_vaccine')
284 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
285 self.selection_only = False
286 #----------------------------------------------------------------------
288
290
291 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
292
293 # consider ATCs in ref.branded_drug and vacc_indication
294 query = u"""
295 SELECT pk_vaccine, description FROM (
296
297 SELECT DISTINCT ON (pk_vaccine) pk_vaccine, description FROM (
298
299 (
300 -- fragment -> vaccine
301 SELECT
302 pk_vaccine,
303 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')'
304 AS description
305 FROM
306 clin.v_vaccines
307 WHERE
308 vaccine %(fragment_condition)s
309
310 ) union all (
311
312 -- fragment -> localized indication -> vaccines
313 SELECT
314 pk_vaccine,
315 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')'
316 AS description
317 FROM
318 clin.v_indications4vaccine
319 WHERE
320 l10n_indication %(fragment_condition)s
321
322 ) union all (
323
324 -- fragment -> indication -> vaccines
325 SELECT
326 pk_vaccine,
327 vaccine || ' (' || array_to_string(indications, ', ') || ')'
328 AS description
329 FROM
330 clin.v_indications4vaccine
331 WHERE
332 indication %(fragment_condition)s
333 )
334 ) AS distinct_total
335
336 ) AS total
337
338 ORDER by description
339 LIMIT 25
340 """
341 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
342 mp.setThresholds(1, 2, 3)
343 self.matcher = mp
344
345 self.selection_only = True
346 #------------------------------------------------------------------
349 #----------------------------------------------------------------------
350 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
351
353
355
356 try:
357 data = kwargs['vaccine']
358 del kwargs['vaccine']
359 except KeyError:
360 data = None
361
362 wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs)
363 gmEditArea.cGenericEditAreaMixin.__init__(self)
364
365 self.mode = 'new'
366 self.data = data
367 if data is not None:
368 self.mode = 'edit'
369
370 self.__init_ui()
371 #----------------------------------------------------------------
373
374 # route
375 query = u"""
376 SELECT DISTINCT ON (abbreviation)
377 id,
378 abbreviation || ' (' || _(description) || ')'
379 FROM
380 clin.vacc_route
381 WHERE
382 abbreviation %(fragment_condition)s
383 OR
384 description %(fragment_condition)s
385 ORDER BY
386 abbreviation
387 """
388 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
389 mp.setThresholds(1, 2, 3)
390 self._PRW_route.matcher = mp
391 self._PRW_route.selection_only = True
392
393 #self._PRW_age_min = gmPhraseWheel.cPhraseWheel(self, -1, "", style=wx.NO_BORDER)
394 #self._PRW_age_max = gmPhraseWheel.cPhraseWheel(self, -1, "", style=wx.NO_BORDER)
395
396 self.Layout()
397 self.Fit()
398 #----------------------------------------------------------------
399 # generic Edit Area mixin API
400 #----------------------------------------------------------------
402
403 has_errors = False
404
405 if self._PRW_brand.GetValue().strip() == u'':
406 has_errors = True
407 self._PRW_brand.display_as_valid(False)
408 else:
409 self._PRW_brand.display_as_valid(True)
410
411 if self._PRW_route.GetData() is None:
412 has_errors = True
413 self._PRW_route.display_as_valid(False)
414 else:
415 self._PRW_route.display_as_valid(True)
416
417 if not self._PNL_indications.has_selection:
418 has_errors = True
419
420 if self._PRW_atc.GetValue().strip() in [u'', u'J07']:
421 self._PRW_atc.display_as_valid(True)
422 else:
423 if self._PRW_atc.GetData() is None:
424 self._PRW_atc.display_as_valid(True)
425 else:
426 has_errors = True
427 self._PRW_atc.display_as_valid(False)
428
429 val = self._PRW_age_min.GetValue().strip()
430 if val == u'':
431 self._PRW_age_min.display_as_valid(True)
432 else:
433 if gmDateTime.str2interval(val) is None:
434 has_errors = True
435 self._PRW_age_min.display_as_valid(False)
436 else:
437 self._PRW_age_min.display_as_valid(True)
438
439 val = self._PRW_age_max.GetValue().strip()
440 if val == u'':
441 self._PRW_age_max.display_as_valid(True)
442 else:
443 if gmDateTime.str2interval(val) is None:
444 has_errors = True
445 self._PRW_age_max.display_as_valid(False)
446 else:
447 self._PRW_age_max.display_as_valid(True)
448
449 # are we editing ?
450 ask_user = (self.mode == 'edit')
451 # is this vaccine in use ?
452 ask_user = (ask_user and self.data.is_in_use)
453 # a change ...
454 ask_user = ask_user and (
455 # ... of brand ...
456 (self.data['pk_brand'] != self._PRW_route.GetData())
457 or
458 # ... or indications ?
459 (self.data['indications'] != self._PNL_indications.selected_indications)
460 )
461
462 if ask_user:
463 do_it = gmGuiHelpers.gm_show_question (
464 aTitle = _('Saving vaccine'),
465 aMessage = _(
466 u'This vaccine is already in use:\n'
467 u'\n'
468 u' "%s"\n'
469 u' (%s)\n'
470 u'\n'
471 u'Are you absolutely positively sure that\n'
472 u'you really want to edit this vaccine ?\n'
473 '\n'
474 u'This will change the vaccine name and/or target\n'
475 u'conditions in each patient this vaccine was\n'
476 u'used in to document a vaccination with.\n'
477 ) % (
478 self._PRW_brand.GetValue().strip(),
479 u', '.join(self.data['l10n_indications'])
480 )
481 )
482 if not do_it:
483 has_errors = True
484
485 return (has_errors is False)
486 #----------------------------------------------------------------
488 # save the data as a new instance
489 data = gmVaccination.create_vaccine (
490 pk_brand = self._PRW_brand.GetData(),
491 brand_name = self._PRW_brand.GetValue(),
492 indications = self._PNL_indications.selected_indications
493 )
494
495 data['pk_route'] = self._PRW_route.GetData()
496 data['is_live'] = self._CHBOX_live.GetValue()
497 val = self._PRW_age_min.GetValue().strip()
498 if val != u'':
499 data['min_age'] = gmDateTime.str2interval(val)
500 val = self._PRW_age_max.GetValue().strip()
501 if val != u'':
502 data['max_age'] = gmDateTime.str2interval(val)
503 val = self._TCTRL_comment.GetValue().strip()
504 if val != u'':
505 data['comment'] = val
506
507 data.save()
508
509 drug = data.brand
510 drug['is_fake'] = self._CHBOX_fake.GetValue()
511 val = self._PRW_atc.GetData()
512 if val is not None:
513 if val != u'J07':
514 drug['atc_code'] = val.strip()
515 drug.save()
516
517 # must be done very late or else the property access
518 # will refresh the display such that later field
519 # access will return empty values
520 self.data = data
521
522 return True
523 #----------------------------------------------------------------
525
526 drug = self.data.brand
527 drug['description'] = self._PRW_brand.GetValue().strip()
528 drug['is_fake'] = self._CHBOX_fake.GetValue()
529 val = self._PRW_atc.GetData()
530 if val is not None:
531 if val != u'J07':
532 drug['atc_code'] = val.strip()
533 drug.save()
534
535 # the validator already asked for changes so just do it
536 self.data.set_indications(indications = self._PNL_indications.selected_indications)
537
538 self.data['pk_route'] = self._PRW_route.GetData()
539 self.data['is_live'] = self._CHBOX_live.GetValue()
540 val = self._PRW_age_min.GetValue().strip()
541 if val != u'':
542 self.data['min_age'] = gmDateTime.str2interval(val)
543 if val != u'':
544 self.data['max_age'] = gmDateTime.str2interval(val)
545 val = self._TCTRL_comment.GetValue().strip()
546 if val != u'':
547 self.data['comment'] = val
548
549 self.data.save()
550 return True
551 #----------------------------------------------------------------
553 self._PRW_brand.SetText(value = u'', data = None, suppress_smarts = True)
554 self._PRW_route.SetText(value = u'intramuscular')
555 self._CHBOX_live.SetValue(False)
556 self._CHBOX_fake.SetValue(False)
557 self._PNL_indications.clear_all()
558 self._PRW_atc.SetText(value = u'', data = None, suppress_smarts = True)
559 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
560 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
561 self._TCTRL_comment.SetValue(u'')
562
563 self._PRW_brand.SetFocus()
564 #----------------------------------------------------------------
566 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand'])
567 self._PRW_route.SetText(value = self.data['route_description'], data = self.data['pk_route'])
568 self._CHBOX_live.SetValue(self.data['is_live'])
569 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
570 self._PNL_indications.select(self.data['indications'])
571 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
572 if self.data['min_age'] is None:
573 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
574 else:
575 self._PRW_age_min.SetText (
576 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
577 data = self.data['min_age']
578 )
579 if self.data['max_age'] is None:
580 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
581 else:
582 self._PRW_age_max.SetText (
583 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
584 data = self.data['max_age']
585 )
586 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
587
588 self._PRW_brand.SetFocus()
589 #----------------------------------------------------------------
592 #======================================================================
593 # vaccination related widgets
594 #----------------------------------------------------------------------
596 ea = cVaccinationEAPnl(parent = parent, id = -1)
597 ea.data = vaccination
598 ea.mode = gmTools.coalesce(vaccination, 'new', 'edit')
599 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
600 dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination')))
601 if dlg.ShowModal() == wx.ID_OK:
602 dlg.Destroy()
603 return True
604 dlg.Destroy()
605 if not single_entry:
606 return True
607 return False
608 #----------------------------------------------------------------------
610
611 pat = gmPerson.gmCurrentPatient()
612 emr = pat.get_emr()
613
614 if parent is None:
615 parent = wx.GetApp().GetTopWindow()
616 #------------------------------------------------------------
617 def browse2schedules(vaccination=None):
618 dbcfg = gmCfg.cCfgSQL()
619 url = dbcfg.get2 (
620 option = 'external.urls.vaccination_plans',
621 workplace = gmSurgery.gmCurrentPractice().active_workplace,
622 bias = 'user',
623 default = u'http://www.bundesaerztekammer.de/downloads/ImpfempfehlungenRKI2009.pdf'
624 )
625
626 webbrowser.open (
627 url = url,
628 new = False,
629 autoraise = True
630 )
631 return False
632 #------------------------------------------------------------
633 def edit(vaccination=None):
634 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
635 #------------------------------------------------------------
636 def delete(vaccination=None):
637 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
638 return True
639 #------------------------------------------------------------
640 def refresh(lctrl):
641
642 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
643
644 items = [ [
645 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
646 v['vaccine'],
647 u', '.join(v['l10n_indications']),
648 v['batch_no'],
649 gmTools.coalesce(v['site'], u''),
650 gmTools.coalesce(v['reaction'], u''),
651 gmTools.coalesce(v['comment'], u'')
652 ] for v in vaccs ]
653
654 lctrl.set_string_items(items)
655 lctrl.set_data(vaccs)
656 #------------------------------------------------------------
657 gmListWidgets.get_choices_from_list (
658 parent = parent,
659 msg = _('\nComplete vaccination history for this patient.\n'),
660 caption = _('Showing vaccinations.'),
661 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
662 single_selection = True,
663 refresh_callback = refresh,
664 new_callback = edit,
665 edit_callback = edit,
666 delete_callback = delete,
667 left_extra_button = (_('Vaccination Plans'), _('Open a browser showing vaccination schedules.'), browse2schedules)
668 )
669 #----------------------------------------------------------------------
670 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
671
672 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
673 """
674 - warn on apparent duplicates
675 - ask if "missing" (= previous, non-recorded) vaccinations
676 should be estimated and saved (add note "auto-generated")
677
678 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
679 """
681
682 try:
683 data = kwargs['vaccination']
684 del kwargs['vaccination']
685 except KeyError:
686 data = None
687
688 wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs)
689 gmEditArea.cGenericEditAreaMixin.__init__(self)
690
691 self.mode = 'new'
692 self.data = data
693 if data is not None:
694 self.mode = 'edit'
695
696 self.__init_ui()
697 #----------------------------------------------------------------
699 # adjust phrasewheels etc
700 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
701 self._PRW_provider.selection_only = False
702 # self._PRW_batch.unset_context(context = 'pk_vaccine') # done in PRW init()
703 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
704 #----------------------------------------------------------------
706
707 vaccine = self._PRW_vaccine.GetData(as_instance=True)
708
709 # if we are editing we do not allow using indications rather than a vaccine
710 if self.mode == u'edit':
711 self._PNL_indications.clear_all()
712 if vaccine is None:
713 self._PRW_batch.unset_context(context = 'pk_vaccine')
714 else:
715 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
716 self._PNL_indications.select(indications = vaccine['indications'])
717 self._PNL_indications.disable_all()
718
719 # we are entering a new vaccination
720 else:
721 if vaccine is None:
722 self._PRW_batch.unset_context(context = 'pk_vaccine')
723 self._PNL_indications.enable_all()
724 else:
725 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
726 self._PNL_indications.clear_all()
727 self._PNL_indications.select(indications = vaccine['indications'])
728 self._PNL_indications.disable_all()
729 #----------------------------------------------------------------
731 if self._PRW_reaction.GetValue().strip() == u'':
732 self._BTN_report.Enable(False)
733 else:
734 self._BTN_report.Enable(True)
735 #----------------------------------------------------------------
736 # generic Edit Area mixin API
737 #----------------------------------------------------------------
739
740 has_errors = False
741
742 if not self._DP_date_given.is_valid_timestamp(allow_none = False):
743 has_errors = True
744
745 vaccine = self._PRW_vaccine.GetData(as_instance = True)
746
747 # we are editing, require vaccine rather than indications
748 if self.mode == u'edit':
749 if vaccine is None:
750 has_errors = True
751 self._PRW_vaccine.display_as_valid(False)
752 else:
753 self._PRW_vaccine.display_as_valid(True)
754 self._PNL_indications.clear_all()
755 self._PNL_indications.select(indications = vaccine['indications'])
756 self._PNL_indications.disable_all()
757 # we are creating, allow either vaccine or indications
758 else:
759 if vaccine is None:
760 if self._PNL_indications.has_selection:
761 self._PRW_vaccine.display_as_valid(True)
762 else:
763 has_errors = True
764 self._PRW_vaccine.display_as_valid(False)
765 else:
766 self._PRW_vaccine.display_as_valid(True)
767
768 if self._PRW_batch.GetValue().strip() == u'':
769 has_errors = True
770 self._PRW_batch.display_as_valid(False)
771 else:
772 self._PRW_batch.display_as_valid(True)
773
774 if self._PRW_episode.GetValue().strip() == u'':
775 self._PRW_episode.SetText(value = _('prevention'))
776
777 return (has_errors is False)
778 #----------------------------------------------------------------
780
781 vaccine = self._PRW_vaccine.GetData()
782 if vaccine is None:
783 data = self.__save_new_from_indications()
784 else:
785 data = self.__save_new_from_vaccine(vaccine = vaccine)
786
787 # must be done very late or else the property access
788 # will refresh the display such that later field
789 # access will return empty values
790 self.data = data
791
792 return True
793 #----------------------------------------------------------------
795
796 inds = self._PNL_indications.selected_indications
797 vaccine = gmVaccination.map_indications2generic_vaccine(indications = inds)
798
799 if vaccine is None:
800 for ind in inds:
801 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ind])
802 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
803 else:
804 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
805
806 return data
807 #----------------------------------------------------------------
809
810 emr = gmPerson.gmCurrentPatient().get_emr()
811
812 data = emr.add_vaccination (
813 episode = self._PRW_episode.GetData(can_create = True, is_open = False),
814 vaccine = vaccine,
815 batch_no = self._PRW_batch.GetValue().strip()
816 )
817
818 if self._CHBOX_anamnestic.GetValue() is True:
819 data['soap_cat'] = u's'
820 else:
821 data['soap_cat'] = u'p'
822
823 data['date_given'] = self._DP_date_given.get_pydt()
824 data['site'] = self._PRW_site.GetValue().strip()
825 data['pk_provider'] = self._PRW_provider.GetData()
826 data['reaction'] = self._PRW_reaction.GetValue().strip()
827 data['comment'] = self._TCTRL_comment.GetValue().strip()
828
829 data.save()
830
831 return data
832 #----------------------------------------------------------------
834
835 if self._CHBOX_anamnestic.GetValue() is True:
836 self.data['soap_cat'] = u's'
837 else:
838 self.data['soap_cat'] = u'p'
839
840 self.data['date_given'] = self._DP_date_given.get_pydt()
841 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
842 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
843 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
844 self.data['site'] = self._PRW_site.GetValue().strip()
845 self.data['pk_provider'] = self._PRW_provider.GetData()
846 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
847 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
848
849 self.data.save()
850
851 return True
852 #----------------------------------------------------------------
854 self._DP_date_given.SetValue(gmDateTime.pydt_now_here())
855 self._CHBOX_anamnestic.SetValue(False)
856 self._PRW_vaccine.SetText(value = u'', data = None, suppress_smarts = True)
857
858 self._PNL_indications.clear_all()
859 self._PRW_batch.unset_context(context = 'pk_vaccine')
860 self._PRW_batch.SetValue(u'')
861
862 self._PRW_episode.SetText(value = u'', data = None, suppress_smarts = True)
863 self._PRW_site.SetValue(u'')
864 self._PRW_provider.SetData(data = None)
865 self._PRW_reaction.SetText(value = u'', data = None, suppress_smarts = True)
866 self._BTN_report.Enable(False)
867 self._TCTRL_comment.SetValue(u'')
868
869 self._DP_date_given.SetFocus()
870 #----------------------------------------------------------------
872 self._DP_date_given.SetValue(self.data['date_given'])
873 if self.data['soap_cat'] == u's':
874 self._CHBOX_anamnestic.SetValue(True)
875 else:
876 self._CHBOX_anamnestic.SetValue(False)
877 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
878
879 self._PNL_indications.clear_all()
880 self._PNL_indications.select(indications = self.data['indications'])
881 self._PNL_indications.disable_all()
882
883 self._PRW_batch.SetValue(self.data['batch_no'])
884 self._PRW_episode.SetData(data = self.data['pk_episode'])
885 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
886 self._PRW_provider.SetData(self.data['pk_provider'])
887 self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], u''))
888 if self.data['reaction'] is None:
889 self._BTN_report.Enable(False)
890 else:
891 self._BTN_report.Enable(True)
892 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
893
894 self._DP_date_given.SetFocus()
895 #----------------------------------------------------------------
897 #self._DP_date_given.SetValue(gmDateTime.pydt_now_here())
898 self._DP_date_given.SetValue(self.data['date_given'])
899 #self._CHBOX_anamnestic.SetValue(False)
900 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
901
902 self._PNL_indications.clear_all()
903 self._PNL_indications.select(indications = self.data['indications'])
904 self._PNL_indications.disable_all()
905
906 self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine'])
907 self._PRW_batch.SetValue(u'')
908
909 self._PRW_episode.SetData(data = self.data['pk_episode'])
910 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
911 self._PRW_provider.SetData(self.data['pk_provider'])
912 self._PRW_reaction.SetValue(u'')
913 self._BTN_report.Enable(False)
914 self._TCTRL_comment.SetValue(u'')
915
916 self._DP_date_given.SetFocus()
917 #----------------------------------------------------------------
918 # event handlers
919 #----------------------------------------------------------------
941 #----------------------------------------------------------------
944 # FIXME: could set newly generated vaccine here
945 #======================================================================
946 #======================================================================
948
950 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
951 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
952 self.__pat = gmPerson.gmCurrentPatient()
953 # do this here so "import cImmunisationsPanel from gmVaccWidgets" works
954 self.ID_VaccinatedIndicationsList = wx.NewId()
955 self.ID_VaccinationsPerRegimeList = wx.NewId()
956 self.ID_MissingShots = wx.NewId()
957 self.ID_ActiveSchedules = wx.NewId()
958 self.__do_layout()
959 self.__register_interests()
960 self.__reset_ui_content()
961 #----------------------------------------------------
963 #-----------------------------------------------
964 # top part
965 #-----------------------------------------------
966 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
967 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
968
969 #-----------------------------------------------
970 # middle part
971 #-----------------------------------------------
972 # divider headings below editing area
973 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
974 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
975 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
976 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
977 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
978 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
979 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
980
981 # left list: indications for which vaccinations have been given
982 self.LBOX_vaccinated_indications = wx.ListBox(
983 parent = self,
984 id = self.ID_VaccinatedIndicationsList,
985 choices = [],
986 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
987 )
988 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
989
990 # right list: when an indication has been selected on the left
991 # display the corresponding vaccinations on the right
992 self.LBOX_given_shots = wx.ListBox(
993 parent = self,
994 id = self.ID_VaccinationsPerRegimeList,
995 choices = [],
996 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
997 )
998 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
999
1000 self.LBOX_active_schedules = wx.ListBox (
1001 parent = self,
1002 id = self.ID_ActiveSchedules,
1003 choices = [],
1004 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1005 )
1006 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1007
1008 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1009 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1010 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1011 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1012
1013 #---------------------------------------------
1014 # bottom part
1015 #---------------------------------------------
1016 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1017 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1018 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1019
1020 self.LBOX_missing_shots = wx.ListBox (
1021 parent = self,
1022 id = self.ID_MissingShots,
1023 choices = [],
1024 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1025 )
1026 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1027
1028 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1029 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1030
1031 # alert caption
1032 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1033
1034 #---------------------------------------------
1035 # add all elements to the main background sizer
1036 #---------------------------------------------
1037 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1038 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1039 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1040 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1041 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1042 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1043 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1044 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1045
1046 self.SetAutoLayout(True)
1047 self.SetSizer(self.mainsizer)
1048 self.mainsizer.Fit(self)
1049 #----------------------------------------------------
1051 # wxPython events
1052 wx.EVT_SIZE(self, self.OnSize)
1053 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1054 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1055 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1056 # wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton)
1057
1058 # client internal signals
1059 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1060 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1061 #----------------------------------------------------
1062 # event handlers
1063 #----------------------------------------------------
1067 #----------------------------------------------------
1069 """Paste previously given shot into edit area.
1070 """
1071 self.editarea.set_data(aVacc=event.GetClientData())
1072 #----------------------------------------------------
1074 self.editarea.set_data(aVacc = event.GetClientData())
1075 #----------------------------------------------------
1077 """Update right hand middle list to show vaccinations given for selected indication."""
1078 ind_list = event.GetEventObject()
1079 selected_item = ind_list.GetSelection()
1080 ind = ind_list.GetClientData(selected_item)
1081 # clear list
1082 self.LBOX_given_shots.Set([])
1083 emr = self.__pat.get_emr()
1084 shots = emr.get_vaccinations(indications = [ind])
1085 # FIXME: use Set() for entire array (but problem with client_data)
1086 for shot in shots:
1087 if shot['is_booster']:
1088 marker = 'B'
1089 else:
1090 marker = '#%s' % shot['seq_no']
1091 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1092 self.LBOX_given_shots.Append(label, shot)
1093 #----------------------------------------------------
1095 # clear edit area
1096 self.editarea.set_data()
1097 # clear lists
1098 self.LBOX_vaccinated_indications.Clear()
1099 self.LBOX_given_shots.Clear()
1100 self.LBOX_active_schedules.Clear()
1101 self.LBOX_missing_shots.Clear()
1102 #----------------------------------------------------
1104 # clear lists
1105 self.LBOX_vaccinated_indications.Clear()
1106 self.LBOX_given_shots.Clear()
1107 self.LBOX_active_schedules.Clear()
1108 self.LBOX_missing_shots.Clear()
1109
1110 emr = self.__pat.get_emr()
1111
1112 t1 = time.time()
1113 # populate vaccinated-indications list
1114 # FIXME: consider adding virtual indication "most recent" to
1115 # FIXME: display most recent of all indications as suggested by Syan
1116 status, indications = emr.get_vaccinated_indications()
1117 # FIXME: would be faster to use Set() but can't
1118 # use Set(labels, client_data), and have to know
1119 # line position in SetClientData :-(
1120 for indication in indications:
1121 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1122 # self.LBOX_vaccinated_indications.Set(lines)
1123 # self.LBOX_vaccinated_indications.SetClientData(data)
1124 print "vaccinated indications took", time.time()-t1, "seconds"
1125
1126 t1 = time.time()
1127 # populate active schedules list
1128 scheds = emr.get_scheduled_vaccination_regimes()
1129 if scheds is None:
1130 label = _('ERROR: cannot retrieve active vaccination schedules')
1131 self.LBOX_active_schedules.Append(label)
1132 elif len(scheds) == 0:
1133 label = _('no active vaccination schedules')
1134 self.LBOX_active_schedules.Append(label)
1135 else:
1136 for sched in scheds:
1137 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1138 self.LBOX_active_schedules.Append(label)
1139 print "active schedules took", time.time()-t1, "seconds"
1140
1141 t1 = time.time()
1142 # populate missing-shots list
1143 missing_shots = emr.get_missing_vaccinations()
1144 print "getting missing shots took", time.time()-t1, "seconds"
1145 if missing_shots is None:
1146 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1147 self.LBOX_missing_shots.Append(label, None)
1148 return True
1149 # due
1150 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1151 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1152 for shot in missing_shots['due']:
1153 if shot['overdue']:
1154 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1155 weeks = days_left / 7
1156 # amount_overdue, seq_no, indication, regime, vacc_comment
1157 label = overdue_template % (
1158 years,
1159 weeks,
1160 shot['seq_no'],
1161 shot['l10n_indication'],
1162 shot['regime'],
1163 shot['vacc_comment']
1164 )
1165 self.LBOX_missing_shots.Append(label, shot)
1166 else:
1167 # time_left, seq_no, regime, latest_due, vacc_comment
1168 label = due_template % (
1169 shot['time_left'].days / 7,
1170 shot['seq_no'],
1171 shot['indication'],
1172 shot['regime'],
1173 shot['latest_due'].strftime('%m/%Y'),
1174 shot['vacc_comment']
1175 )
1176 self.LBOX_missing_shots.Append(label, shot)
1177 # booster
1178 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1179 for shot in missing_shots['boosters']:
1180 # indication, regime, vacc_comment
1181 label = lbl_template % (
1182 shot['l10n_indication'],
1183 shot['regime'],
1184 shot['vacc_comment']
1185 )
1186 self.LBOX_missing_shots.Append(label, shot)
1187 print "displaying missing shots took", time.time()-t1, "seconds"
1188
1189 return True
1190 #----------------------------------------------------
1193 # FIXME:
1194 # if has_focus:
1195 # wxCallAfter(self.__reset_ui_content)
1196 # else:
1197 # return 1
1198 #----------------------------------------------------
1201 # FIXME:
1202 # if has_focus:
1203 # wxCallAfter(self.__reset_ui_content)
1204 # else:
1205 # is_stale == True
1206 # return 1
1207 #======================================================================
1208 # main
1209 #----------------------------------------------------------------------
1210 if __name__ == "__main__":
1211
1212 if len(sys.argv) < 2:
1213 sys.exit()
1214
1215 if sys.argv[1] != u'test':
1216 sys.exit()
1217
1218 app = wx.PyWidgetTester(size = (600, 600))
1219 app.SetWidget(cATCPhraseWheel, -1)
1220 app.MainLoop()
1221 #======================================================================
1222
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Nov 29 04:06:03 2010 | http://epydoc.sourceforge.net |