| 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 v2 or later (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 data, field_label, list_label FROM (
246
247 SELECT distinct on (field_label)
248 data,
249 field_label,
250 list_label,
251 rank
252 FROM ((
253 -- batch_no by vaccine
254 SELECT
255 batch_no AS data,
256 batch_no AS field_label,
257 batch_no || ' (' || vaccine || ')' AS list_label,
258 1 as rank
259 FROM
260 clin.v_pat_vaccinations
261 WHERE
262 batch_no %(fragment_condition)s
263 %(ctxt_vaccine)s
264 ) UNION ALL (
265 -- batch_no for any vaccine
266 SELECT
267 batch_no AS data,
268 batch_no AS field_label,
269 batch_no || ' (' || vaccine || ')' AS list_label,
270 2 AS rank
271 FROM
272 clin.v_pat_vaccinations
273 WHERE
274 batch_no %(fragment_condition)s
275 )
276
277 ) AS matching_batch_nos
278
279 ) as unique_matches
280
281 ORDER BY rank, list_label
282 LIMIT 25
283 """
284 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query, context = context)
285 mp.setThresholds(1, 2, 3)
286 self.matcher = mp
287
288 self.unset_context(context = u'pk_vaccine')
289 self.SetToolTipString(_('Enter or select the batch/lot number of the vaccine used.'))
290 self.selection_only = False
291 #----------------------------------------------------------------------
293
295
296 gmPhraseWheel.cPhraseWheel.__init__(self, *args, **kwargs)
297
298 # consider ATCs in ref.branded_drug and vacc_indication
299 query = u"""
300 SELECT data, list_label, field_label FROM (
301
302 SELECT DISTINCT ON (data)
303 data,
304 list_label,
305 field_label
306 FROM ((
307 -- fragment -> vaccine
308 SELECT
309 pk_vaccine AS data,
310 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
311 vaccine AS field_label
312 FROM
313 clin.v_vaccines
314 WHERE
315 vaccine %(fragment_condition)s
316
317 ) union all (
318
319 -- fragment -> localized indication -> vaccines
320 SELECT
321 pk_vaccine AS data,
322 vaccine || ' (' || array_to_string(l10n_indications, ', ') || ')' AS list_label,
323 vaccine AS field_label
324 FROM
325 clin.v_indications4vaccine
326 WHERE
327 l10n_indication %(fragment_condition)s
328
329 ) union all (
330
331 -- fragment -> indication -> vaccines
332 SELECT
333 pk_vaccine AS data,
334 vaccine || ' (' || array_to_string(indications, ', ') || ')' AS list_label,
335 vaccine AS field_label
336 FROM
337 clin.v_indications4vaccine
338 WHERE
339 indication %(fragment_condition)s
340 )
341 ) AS distinct_total
342
343 ) AS total
344
345 ORDER by list_label
346 LIMIT 25
347 """
348 mp = gmMatchProvider.cMatchProvider_SQL2(queries = query)
349 mp.setThresholds(1, 2, 3)
350 self.matcher = mp
351
352 self.selection_only = True
353 #------------------------------------------------------------------
356 #----------------------------------------------------------------------
357 from Gnumed.wxGladeWidgets import wxgVaccineEAPnl
358
360
362
363 try:
364 data = kwargs['vaccine']
365 del kwargs['vaccine']
366 except KeyError:
367 data = None
368
369 wxgVaccineEAPnl.wxgVaccineEAPnl.__init__(self, *args, **kwargs)
370 gmEditArea.cGenericEditAreaMixin.__init__(self)
371
372 self.mode = 'new'
373 self.data = data
374 if data is not None:
375 self.mode = 'edit'
376
377 self.__init_ui()
378 #----------------------------------------------------------------
380
381 # route
382 query = u"""
383 SELECT DISTINCT ON (abbreviation)
384 id,
385 abbreviation || ' (' || _(description) || ')'
386 FROM
387 clin.vacc_route
388 WHERE
389 abbreviation %(fragment_condition)s
390 OR
391 description %(fragment_condition)s
392 ORDER BY
393 abbreviation
394 """
395 mp = gmMatchProvider.cMatchProvider_SQL2(queries=query)
396 mp.setThresholds(1, 2, 3)
397 self._PRW_route.matcher = mp
398 self._PRW_route.selection_only = True
399
400 #self._PRW_age_min = gmPhraseWheel.cPhraseWheel(self, -1, "", style=wx.NO_BORDER)
401 #self._PRW_age_max = gmPhraseWheel.cPhraseWheel(self, -1, "", style=wx.NO_BORDER)
402
403 self.Layout()
404 self.Fit()
405 #----------------------------------------------------------------
406 # generic Edit Area mixin API
407 #----------------------------------------------------------------
409
410 has_errors = False
411
412 if self._PRW_brand.GetValue().strip() == u'':
413 has_errors = True
414 self._PRW_brand.display_as_valid(False)
415 else:
416 self._PRW_brand.display_as_valid(True)
417
418 if self._PRW_route.GetData() is None:
419 has_errors = True
420 self._PRW_route.display_as_valid(False)
421 else:
422 self._PRW_route.display_as_valid(True)
423
424 if not self._PNL_indications.has_selection:
425 has_errors = True
426
427 if self._PRW_atc.GetValue().strip() in [u'', u'J07']:
428 self._PRW_atc.display_as_valid(True)
429 else:
430 if self._PRW_atc.GetData() is None:
431 self._PRW_atc.display_as_valid(True)
432 else:
433 has_errors = True
434 self._PRW_atc.display_as_valid(False)
435
436 val = self._PRW_age_min.GetValue().strip()
437 if val == u'':
438 self._PRW_age_min.display_as_valid(True)
439 else:
440 if gmDateTime.str2interval(val) is None:
441 has_errors = True
442 self._PRW_age_min.display_as_valid(False)
443 else:
444 self._PRW_age_min.display_as_valid(True)
445
446 val = self._PRW_age_max.GetValue().strip()
447 if val == u'':
448 self._PRW_age_max.display_as_valid(True)
449 else:
450 if gmDateTime.str2interval(val) is None:
451 has_errors = True
452 self._PRW_age_max.display_as_valid(False)
453 else:
454 self._PRW_age_max.display_as_valid(True)
455
456 # are we editing ?
457 ask_user = (self.mode == 'edit')
458 # is this vaccine in use ?
459 ask_user = (ask_user and self.data.is_in_use)
460 # a change ...
461 ask_user = ask_user and (
462 # ... of brand ...
463 (self.data['pk_brand'] != self._PRW_route.GetData())
464 or
465 # ... or indications ?
466 (self.data['indications'] != self._PNL_indications.selected_indications)
467 )
468
469 if ask_user:
470 do_it = gmGuiHelpers.gm_show_question (
471 aTitle = _('Saving vaccine'),
472 aMessage = _(
473 u'This vaccine is already in use:\n'
474 u'\n'
475 u' "%s"\n'
476 u' (%s)\n'
477 u'\n'
478 u'Are you absolutely positively sure that\n'
479 u'you really want to edit this vaccine ?\n'
480 '\n'
481 u'This will change the vaccine name and/or target\n'
482 u'conditions in each patient this vaccine was\n'
483 u'used in to document a vaccination with.\n'
484 ) % (
485 self._PRW_brand.GetValue().strip(),
486 u', '.join(self.data['l10n_indications'])
487 )
488 )
489 if not do_it:
490 has_errors = True
491
492 return (has_errors is False)
493 #----------------------------------------------------------------
495 # save the data as a new instance
496 data = gmVaccination.create_vaccine (
497 pk_brand = self._PRW_brand.GetData(),
498 brand_name = self._PRW_brand.GetValue(),
499 indications = self._PNL_indications.selected_indications
500 )
501
502 data['pk_route'] = self._PRW_route.GetData()
503 data['is_live'] = self._CHBOX_live.GetValue()
504 val = self._PRW_age_min.GetValue().strip()
505 if val != u'':
506 data['min_age'] = gmDateTime.str2interval(val)
507 val = self._PRW_age_max.GetValue().strip()
508 if val != u'':
509 data['max_age'] = gmDateTime.str2interval(val)
510 val = self._TCTRL_comment.GetValue().strip()
511 if val != u'':
512 data['comment'] = val
513
514 data.save()
515
516 drug = data.brand
517 drug['is_fake'] = self._CHBOX_fake.GetValue()
518 val = self._PRW_atc.GetData()
519 if val is not None:
520 if val != u'J07':
521 drug['atc_code'] = val.strip()
522 drug.save()
523
524 # must be done very late or else the property access
525 # will refresh the display such that later field
526 # access will return empty values
527 self.data = data
528
529 return True
530 #----------------------------------------------------------------
532
533 drug = self.data.brand
534 drug['description'] = self._PRW_brand.GetValue().strip()
535 drug['is_fake'] = self._CHBOX_fake.GetValue()
536 val = self._PRW_atc.GetData()
537 if val is not None:
538 if val != u'J07':
539 drug['atc_code'] = val.strip()
540 drug.save()
541
542 # the validator already asked for changes so just do it
543 self.data.set_indications(indications = self._PNL_indications.selected_indications)
544
545 self.data['pk_route'] = self._PRW_route.GetData()
546 self.data['is_live'] = self._CHBOX_live.GetValue()
547 val = self._PRW_age_min.GetValue().strip()
548 if val != u'':
549 self.data['min_age'] = gmDateTime.str2interval(val)
550 if val != u'':
551 self.data['max_age'] = gmDateTime.str2interval(val)
552 val = self._TCTRL_comment.GetValue().strip()
553 if val != u'':
554 self.data['comment'] = val
555
556 self.data.save()
557 return True
558 #----------------------------------------------------------------
560 self._PRW_brand.SetText(value = u'', data = None, suppress_smarts = True)
561 self._PRW_route.SetText(value = u'intramuscular')
562 self._CHBOX_live.SetValue(False)
563 self._CHBOX_fake.SetValue(False)
564 self._PNL_indications.clear_all()
565 self._PRW_atc.SetText(value = u'', data = None, suppress_smarts = True)
566 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
567 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
568 self._TCTRL_comment.SetValue(u'')
569
570 self._PRW_brand.SetFocus()
571 #----------------------------------------------------------------
573 self._PRW_brand.SetText(value = self.data['vaccine'], data = self.data['pk_brand'])
574 self._PRW_route.SetText(value = self.data['route_description'], data = self.data['pk_route'])
575 self._CHBOX_live.SetValue(self.data['is_live'])
576 self._CHBOX_fake.SetValue(self.data['is_fake_vaccine'])
577 self._PNL_indications.select(self.data['indications'])
578 self._PRW_atc.SetText(value = self.data['atc_code'], data = self.data['atc_code'])
579 if self.data['min_age'] is None:
580 self._PRW_age_min.SetText(value = u'', data = None, suppress_smarts = True)
581 else:
582 self._PRW_age_min.SetText (
583 value = gmDateTime.format_interval(self.data['min_age'], gmDateTime.acc_years),
584 data = self.data['min_age']
585 )
586 if self.data['max_age'] is None:
587 self._PRW_age_max.SetText(value = u'', data = None, suppress_smarts = True)
588 else:
589 self._PRW_age_max.SetText (
590 value = gmDateTime.format_interval(self.data['max_age'], gmDateTime.acc_years),
591 data = self.data['max_age']
592 )
593 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
594
595 self._PRW_brand.SetFocus()
596 #----------------------------------------------------------------
599 #======================================================================
600 # vaccination related widgets
601 #----------------------------------------------------------------------
603 ea = cVaccinationEAPnl(parent = parent, id = -1)
604 ea.data = vaccination
605 ea.mode = gmTools.coalesce(vaccination, 'new', 'edit')
606 dlg = gmEditArea.cGenericEditAreaDlg2(parent = parent, id = -1, edit_area = ea, single_entry = single_entry)
607 dlg.SetTitle(gmTools.coalesce(vaccination, _('Adding new vaccinations'), _('Editing vaccination')))
608 if dlg.ShowModal() == wx.ID_OK:
609 dlg.Destroy()
610 return True
611 dlg.Destroy()
612 if not single_entry:
613 return True
614 return False
615 #----------------------------------------------------------------------
617
618 pat = gmPerson.gmCurrentPatient()
619 emr = pat.get_emr()
620
621 if parent is None:
622 parent = wx.GetApp().GetTopWindow()
623 #------------------------------------------------------------
624 def browse2schedules(vaccination=None):
625 dbcfg = gmCfg.cCfgSQL()
626 url = dbcfg.get2 (
627 option = 'external.urls.vaccination_plans',
628 workplace = gmSurgery.gmCurrentPractice().active_workplace,
629 bias = 'user',
630 default = u'http://www.bundesaerztekammer.de/downloads/ImpfempfehlungenRKI2009.pdf'
631 )
632
633 webbrowser.open (
634 url = url,
635 new = False,
636 autoraise = True
637 )
638 return False
639 #------------------------------------------------------------
640 def edit(vaccination=None):
641 return edit_vaccination(parent = parent, vaccination = vaccination, single_entry = (vaccination is not None))
642 #------------------------------------------------------------
643 def delete(vaccination=None):
644 gmVaccination.delete_vaccination(vaccination = vaccination['pk_vaccination'])
645 return True
646 #------------------------------------------------------------
647 def refresh(lctrl):
648
649 vaccs = emr.get_vaccinations(order_by = 'date_given DESC, pk_vaccination')
650
651 items = [ [
652 v['date_given'].strftime('%Y %B %d').decode(gmI18N.get_encoding()),
653 v['vaccine'],
654 u', '.join(v['l10n_indications']),
655 v['batch_no'],
656 gmTools.coalesce(v['site'], u''),
657 gmTools.coalesce(v['reaction'], u''),
658 gmTools.coalesce(v['comment'], u'')
659 ] for v in vaccs ]
660
661 lctrl.set_string_items(items)
662 lctrl.set_data(vaccs)
663 #------------------------------------------------------------
664 gmListWidgets.get_choices_from_list (
665 parent = parent,
666 msg = _('\nComplete vaccination history for this patient.\n'),
667 caption = _('Showing vaccinations.'),
668 columns = [ _('Date'), _('Vaccine'), _(u'Intended to protect from'), _('Batch'), _('Site'), _('Reaction'), _('Comment') ],
669 single_selection = True,
670 refresh_callback = refresh,
671 new_callback = edit,
672 edit_callback = edit,
673 delete_callback = delete,
674 left_extra_button = (_('Vaccination Plans'), _('Open a browser showing vaccination schedules.'), browse2schedules)
675 )
676 #----------------------------------------------------------------------
677 from Gnumed.wxGladeWidgets import wxgVaccinationEAPnl
678
679 -class cVaccinationEAPnl(wxgVaccinationEAPnl.wxgVaccinationEAPnl, gmEditArea.cGenericEditAreaMixin):
680 """
681 - warn on apparent duplicates
682 - ask if "missing" (= previous, non-recorded) vaccinations
683 should be estimated and saved (add note "auto-generated")
684
685 Batch No (http://www.fao.org/docrep/003/v9952E12.htm)
686 """
688
689 try:
690 data = kwargs['vaccination']
691 del kwargs['vaccination']
692 except KeyError:
693 data = None
694
695 wxgVaccinationEAPnl.wxgVaccinationEAPnl.__init__(self, *args, **kwargs)
696 gmEditArea.cGenericEditAreaMixin.__init__(self)
697
698 self.mode = 'new'
699 self.data = data
700 if data is not None:
701 self.mode = 'edit'
702
703 self.__init_ui()
704 #----------------------------------------------------------------
706 # adjust phrasewheels etc
707 self._PRW_vaccine.add_callback_on_lose_focus(self._on_PRW_vaccine_lost_focus)
708 self._PRW_provider.selection_only = False
709 # self._PRW_batch.unset_context(context = 'pk_vaccine') # done in PRW init()
710 self._PRW_reaction.add_callback_on_lose_focus(self._on_PRW_reaction_lost_focus)
711 #----------------------------------------------------------------
713
714 vaccine = self._PRW_vaccine.GetData(as_instance=True)
715
716 # if we are editing we do not allow using indications rather than a vaccine
717 if self.mode == u'edit':
718 self._PNL_indications.clear_all()
719 if vaccine is None:
720 self._PRW_batch.unset_context(context = 'pk_vaccine')
721 else:
722 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
723 self._PNL_indications.select(indications = vaccine['indications'])
724 self._PNL_indications.disable_all()
725
726 # we are entering a new vaccination
727 else:
728 if vaccine is None:
729 self._PRW_batch.unset_context(context = 'pk_vaccine')
730 self._PNL_indications.enable_all()
731 else:
732 self._PRW_batch.set_context(context = 'pk_vaccine', val = vaccine['pk_vaccine'])
733 self._PNL_indications.clear_all()
734 self._PNL_indications.select(indications = vaccine['indications'])
735 self._PNL_indications.disable_all()
736 #----------------------------------------------------------------
738 if self._PRW_reaction.GetValue().strip() == u'':
739 self._BTN_report.Enable(False)
740 else:
741 self._BTN_report.Enable(True)
742 #----------------------------------------------------------------
743 # generic Edit Area mixin API
744 #----------------------------------------------------------------
746
747 has_errors = False
748
749 if not self._PRW_date_given.is_valid_timestamp(allow_empty = False):
750 has_errors = True
751
752 vaccine = self._PRW_vaccine.GetData(as_instance = True)
753
754 # we are editing, require vaccine rather than indications
755 if self.mode == u'edit':
756 if vaccine is None:
757 has_errors = True
758 self._PRW_vaccine.display_as_valid(False)
759 else:
760 self._PRW_vaccine.display_as_valid(True)
761 self._PNL_indications.clear_all()
762 self._PNL_indications.select(indications = vaccine['indications'])
763 self._PNL_indications.disable_all()
764 # we are creating, allow either vaccine or indications
765 else:
766 if vaccine is None:
767 if self._PNL_indications.has_selection:
768 self._PRW_vaccine.display_as_valid(True)
769 else:
770 has_errors = True
771 self._PRW_vaccine.display_as_valid(False)
772 else:
773 self._PRW_vaccine.display_as_valid(True)
774
775 if self._PRW_batch.GetValue().strip() == u'':
776 has_errors = True
777 self._PRW_batch.display_as_valid(False)
778 else:
779 self._PRW_batch.display_as_valid(True)
780
781 if self._PRW_episode.GetValue().strip() == u'':
782 self._PRW_episode.SetText(value = _('prevention'))
783
784 return (has_errors is False)
785 #----------------------------------------------------------------
787
788 vaccine = self._PRW_vaccine.GetData()
789 if vaccine is None:
790 data = self.__save_new_from_indications()
791 else:
792 data = self.__save_new_from_vaccine(vaccine = vaccine)
793
794 # must be done very late or else the property access
795 # will refresh the display such that later field
796 # access will return empty values
797 self.data = data
798
799 return True
800 #----------------------------------------------------------------
802
803 inds = self._PNL_indications.selected_indications
804 vaccine = gmVaccination.map_indications2generic_vaccine(indications = inds)
805
806 if vaccine is None:
807 for ind in inds:
808 vaccine = gmVaccination.map_indications2generic_vaccine(indications = [ind])
809 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
810 else:
811 data = self.__save_new_from_vaccine(vaccine = vaccine['pk_vaccine'])
812
813 return data
814 #----------------------------------------------------------------
816
817 emr = gmPerson.gmCurrentPatient().get_emr()
818
819 data = emr.add_vaccination (
820 episode = self._PRW_episode.GetData(can_create = True, is_open = False),
821 vaccine = vaccine,
822 batch_no = self._PRW_batch.GetValue().strip()
823 )
824
825 if self._CHBOX_anamnestic.GetValue() is True:
826 data['soap_cat'] = u's'
827 else:
828 data['soap_cat'] = u'p'
829
830 data['date_given'] = self._PRW_date_given.GetData()
831 data['site'] = self._PRW_site.GetValue().strip()
832 data['pk_provider'] = self._PRW_provider.GetData()
833 data['reaction'] = self._PRW_reaction.GetValue().strip()
834 data['comment'] = self._TCTRL_comment.GetValue().strip()
835
836 data.save()
837
838 return data
839 #----------------------------------------------------------------
841
842 if self._CHBOX_anamnestic.GetValue() is True:
843 self.data['soap_cat'] = u's'
844 else:
845 self.data['soap_cat'] = u'p'
846
847 self.data['date_given'] = self._PRW_date_given.GetData()
848 self.data['pk_vaccine'] = self._PRW_vaccine.GetData()
849 self.data['batch_no'] = self._PRW_batch.GetValue().strip()
850 self.data['pk_episode'] = self._PRW_episode.GetData(can_create = True, is_open = False)
851 self.data['site'] = self._PRW_site.GetValue().strip()
852 self.data['pk_provider'] = self._PRW_provider.GetData()
853 self.data['reaction'] = self._PRW_reaction.GetValue().strip()
854 self.data['comment'] = self._TCTRL_comment.GetValue().strip()
855
856 self.data.save()
857
858 return True
859 #----------------------------------------------------------------
861 self._PRW_date_given.SetText(data = gmDateTime.pydt_now_here())
862 self._CHBOX_anamnestic.SetValue(False)
863 self._PRW_vaccine.SetText(value = u'', data = None, suppress_smarts = True)
864
865 self._PNL_indications.clear_all()
866 self._PRW_batch.unset_context(context = 'pk_vaccine')
867 self._PRW_batch.SetValue(u'')
868
869 self._PRW_episode.SetText(value = u'', data = None, suppress_smarts = True)
870 self._PRW_site.SetValue(u'')
871 self._PRW_provider.SetData(data = None)
872 self._PRW_reaction.SetText(value = u'', data = None, suppress_smarts = True)
873 self._BTN_report.Enable(False)
874 self._TCTRL_comment.SetValue(u'')
875
876 self._PRW_date_given.SetFocus()
877 #----------------------------------------------------------------
879 self._PRW_date_given.SetText(data = self.data['date_given'])
880 if self.data['soap_cat'] == u's':
881 self._CHBOX_anamnestic.SetValue(True)
882 else:
883 self._CHBOX_anamnestic.SetValue(False)
884 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
885
886 self._PNL_indications.clear_all()
887 self._PNL_indications.select(indications = self.data['indications'])
888 self._PNL_indications.disable_all()
889
890 self._PRW_batch.SetValue(self.data['batch_no'])
891 self._PRW_episode.SetData(data = self.data['pk_episode'])
892 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
893 self._PRW_provider.SetData(self.data['pk_provider'])
894 self._PRW_reaction.SetValue(gmTools.coalesce(self.data['reaction'], u''))
895 if self.data['reaction'] is None:
896 self._BTN_report.Enable(False)
897 else:
898 self._BTN_report.Enable(True)
899 self._TCTRL_comment.SetValue(gmTools.coalesce(self.data['comment'], u''))
900
901 self._PRW_date_given.SetFocus()
902 #----------------------------------------------------------------
904 self._PRW_date_given.SetText(data = self.data['date_given'])
905 #self._CHBOX_anamnestic.SetValue(False)
906 self._PRW_vaccine.SetText(value = self.data['vaccine'], data = self.data['pk_vaccine'])
907
908 self._PNL_indications.clear_all()
909 self._PNL_indications.select(indications = self.data['indications'])
910 self._PNL_indications.disable_all()
911
912 self._PRW_batch.set_context(context = 'pk_vaccine', val = self.data['pk_vaccine'])
913 self._PRW_batch.SetValue(u'')
914
915 self._PRW_episode.SetData(data = self.data['pk_episode'])
916 self._PRW_site.SetValue(gmTools.coalesce(self.data['site'], u''))
917 self._PRW_provider.SetData(self.data['pk_provider'])
918 self._PRW_reaction.SetValue(u'')
919 self._BTN_report.Enable(False)
920 self._TCTRL_comment.SetValue(u'')
921
922 self._PRW_date_given.SetFocus()
923 #----------------------------------------------------------------
924 # event handlers
925 #----------------------------------------------------------------
947 #----------------------------------------------------------------
950 # FIXME: could set newly generated vaccine here
951 #======================================================================
952 #======================================================================
954
956 wx.Panel.__init__(self, parent, id, wx.DefaultPosition, wx.DefaultSize, wx.RAISED_BORDER)
957 gmRegetMixin.cRegetOnPaintMixin.__init__(self)
958 self.__pat = gmPerson.gmCurrentPatient()
959 # do this here so "import cImmunisationsPanel from gmVaccWidgets" works
960 self.ID_VaccinatedIndicationsList = wx.NewId()
961 self.ID_VaccinationsPerRegimeList = wx.NewId()
962 self.ID_MissingShots = wx.NewId()
963 self.ID_ActiveSchedules = wx.NewId()
964 self.__do_layout()
965 self.__register_interests()
966 self.__reset_ui_content()
967 #----------------------------------------------------
969 #-----------------------------------------------
970 # top part
971 #-----------------------------------------------
972 pnl_UpperCaption = gmTerryGuiParts.cHeadingCaption(self, -1, _(" IMMUNISATIONS "))
973 self.editarea = cVaccinationEditArea(self, -1, wx.DefaultPosition, wx.DefaultSize, wx.NO_BORDER)
974
975 #-----------------------------------------------
976 # middle part
977 #-----------------------------------------------
978 # divider headings below editing area
979 indications_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Indications"))
980 vaccinations_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Vaccinations"))
981 schedules_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Active Schedules"))
982 szr_MiddleCap = wx.BoxSizer(wx.HORIZONTAL)
983 szr_MiddleCap.Add(indications_heading, 4, wx.EXPAND)
984 szr_MiddleCap.Add(vaccinations_heading, 6, wx.EXPAND)
985 szr_MiddleCap.Add(schedules_heading, 10, wx.EXPAND)
986
987 # left list: indications for which vaccinations have been given
988 self.LBOX_vaccinated_indications = wx.ListBox(
989 parent = self,
990 id = self.ID_VaccinatedIndicationsList,
991 choices = [],
992 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
993 )
994 self.LBOX_vaccinated_indications.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
995
996 # right list: when an indication has been selected on the left
997 # display the corresponding vaccinations on the right
998 self.LBOX_given_shots = wx.ListBox(
999 parent = self,
1000 id = self.ID_VaccinationsPerRegimeList,
1001 choices = [],
1002 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1003 )
1004 self.LBOX_given_shots.SetFont(wx.Font(12,wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1005
1006 self.LBOX_active_schedules = wx.ListBox (
1007 parent = self,
1008 id = self.ID_ActiveSchedules,
1009 choices = [],
1010 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1011 )
1012 self.LBOX_active_schedules.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1013
1014 szr_MiddleLists = wx.BoxSizer(wx.HORIZONTAL)
1015 szr_MiddleLists.Add(self.LBOX_vaccinated_indications, 4, wx.EXPAND)
1016 szr_MiddleLists.Add(self.LBOX_given_shots, 6, wx.EXPAND)
1017 szr_MiddleLists.Add(self.LBOX_active_schedules, 10, wx.EXPAND)
1018
1019 #---------------------------------------------
1020 # bottom part
1021 #---------------------------------------------
1022 missing_heading = gmTerryGuiParts.cDividerCaption(self, -1, _("Missing Immunisations"))
1023 szr_BottomCap = wx.BoxSizer(wx.HORIZONTAL)
1024 szr_BottomCap.Add(missing_heading, 1, wx.EXPAND)
1025
1026 self.LBOX_missing_shots = wx.ListBox (
1027 parent = self,
1028 id = self.ID_MissingShots,
1029 choices = [],
1030 style = wx.LB_HSCROLL | wx.LB_NEEDED_SB | wx.SUNKEN_BORDER
1031 )
1032 self.LBOX_missing_shots.SetFont(wx.Font(12, wx.SWISS, wx.NORMAL, wx.NORMAL, False, ''))
1033
1034 szr_BottomLists = wx.BoxSizer(wx.HORIZONTAL)
1035 szr_BottomLists.Add(self.LBOX_missing_shots, 1, wx.EXPAND)
1036
1037 # alert caption
1038 pnl_AlertCaption = gmTerryGuiParts.cAlertCaption(self, -1, _(' Alerts '))
1039
1040 #---------------------------------------------
1041 # add all elements to the main background sizer
1042 #---------------------------------------------
1043 self.mainsizer = wx.BoxSizer(wx.VERTICAL)
1044 self.mainsizer.Add(pnl_UpperCaption, 0, wx.EXPAND)
1045 self.mainsizer.Add(self.editarea, 6, wx.EXPAND)
1046 self.mainsizer.Add(szr_MiddleCap, 0, wx.EXPAND)
1047 self.mainsizer.Add(szr_MiddleLists, 4, wx.EXPAND)
1048 self.mainsizer.Add(szr_BottomCap, 0, wx.EXPAND)
1049 self.mainsizer.Add(szr_BottomLists, 4, wx.EXPAND)
1050 self.mainsizer.Add(pnl_AlertCaption, 0, wx.EXPAND)
1051
1052 self.SetAutoLayout(True)
1053 self.SetSizer(self.mainsizer)
1054 self.mainsizer.Fit(self)
1055 #----------------------------------------------------
1057 # wxPython events
1058 wx.EVT_SIZE(self, self.OnSize)
1059 wx.EVT_LISTBOX(self, self.ID_VaccinatedIndicationsList, self._on_vaccinated_indication_selected)
1060 wx.EVT_LISTBOX_DCLICK(self, self.ID_VaccinationsPerRegimeList, self._on_given_shot_selected)
1061 wx.EVT_LISTBOX_DCLICK(self, self.ID_MissingShots, self._on_missing_shot_selected)
1062 # wx.EVT_RIGHT_UP(self.lb1, self.EvtRightButton)
1063
1064 # client internal signals
1065 gmDispatcher.connect(signal= u'post_patient_selection', receiver=self._schedule_data_reget)
1066 gmDispatcher.connect(signal= u'vaccinations_updated', receiver=self._schedule_data_reget)
1067 #----------------------------------------------------
1068 # event handlers
1069 #----------------------------------------------------
1073 #----------------------------------------------------
1075 """Paste previously given shot into edit area.
1076 """
1077 self.editarea.set_data(aVacc=event.GetClientData())
1078 #----------------------------------------------------
1080 self.editarea.set_data(aVacc = event.GetClientData())
1081 #----------------------------------------------------
1083 """Update right hand middle list to show vaccinations given for selected indication."""
1084 ind_list = event.GetEventObject()
1085 selected_item = ind_list.GetSelection()
1086 ind = ind_list.GetClientData(selected_item)
1087 # clear list
1088 self.LBOX_given_shots.Set([])
1089 emr = self.__pat.get_emr()
1090 shots = emr.get_vaccinations(indications = [ind])
1091 # FIXME: use Set() for entire array (but problem with client_data)
1092 for shot in shots:
1093 if shot['is_booster']:
1094 marker = 'B'
1095 else:
1096 marker = '#%s' % shot['seq_no']
1097 label = '%s - %s: %s' % (marker, shot['date'].strftime('%m/%Y'), shot['vaccine'])
1098 self.LBOX_given_shots.Append(label, shot)
1099 #----------------------------------------------------
1101 # clear edit area
1102 self.editarea.set_data()
1103 # clear lists
1104 self.LBOX_vaccinated_indications.Clear()
1105 self.LBOX_given_shots.Clear()
1106 self.LBOX_active_schedules.Clear()
1107 self.LBOX_missing_shots.Clear()
1108 #----------------------------------------------------
1110 # clear lists
1111 self.LBOX_vaccinated_indications.Clear()
1112 self.LBOX_given_shots.Clear()
1113 self.LBOX_active_schedules.Clear()
1114 self.LBOX_missing_shots.Clear()
1115
1116 emr = self.__pat.get_emr()
1117
1118 t1 = time.time()
1119 # populate vaccinated-indications list
1120 # FIXME: consider adding virtual indication "most recent" to
1121 # FIXME: display most recent of all indications as suggested by Syan
1122 status, indications = emr.get_vaccinated_indications()
1123 # FIXME: would be faster to use Set() but can't
1124 # use Set(labels, client_data), and have to know
1125 # line position in SetClientData :-(
1126 for indication in indications:
1127 self.LBOX_vaccinated_indications.Append(indication[1], indication[0])
1128 # self.LBOX_vaccinated_indications.Set(lines)
1129 # self.LBOX_vaccinated_indications.SetClientData(data)
1130 print "vaccinated indications took", time.time()-t1, "seconds"
1131
1132 t1 = time.time()
1133 # populate active schedules list
1134 scheds = emr.get_scheduled_vaccination_regimes()
1135 if scheds is None:
1136 label = _('ERROR: cannot retrieve active vaccination schedules')
1137 self.LBOX_active_schedules.Append(label)
1138 elif len(scheds) == 0:
1139 label = _('no active vaccination schedules')
1140 self.LBOX_active_schedules.Append(label)
1141 else:
1142 for sched in scheds:
1143 label = _('%s for %s (%s shots): %s') % (sched['regime'], sched['l10n_indication'], sched['shots'], sched['comment'])
1144 self.LBOX_active_schedules.Append(label)
1145 print "active schedules took", time.time()-t1, "seconds"
1146
1147 t1 = time.time()
1148 # populate missing-shots list
1149 missing_shots = emr.get_missing_vaccinations()
1150 print "getting missing shots took", time.time()-t1, "seconds"
1151 if missing_shots is None:
1152 label = _('ERROR: cannot retrieve due/overdue vaccinations')
1153 self.LBOX_missing_shots.Append(label, None)
1154 return True
1155 # due
1156 due_template = _('%.0d weeks left: shot %s for %s in %s, due %s (%s)')
1157 overdue_template = _('overdue %.0dyrs %.0dwks: shot %s for %s in schedule "%s" (%s)')
1158 for shot in missing_shots['due']:
1159 if shot['overdue']:
1160 years, days_left = divmod(shot['amount_overdue'].days, 364.25)
1161 weeks = days_left / 7
1162 # amount_overdue, seq_no, indication, regime, vacc_comment
1163 label = overdue_template % (
1164 years,
1165 weeks,
1166 shot['seq_no'],
1167 shot['l10n_indication'],
1168 shot['regime'],
1169 shot['vacc_comment']
1170 )
1171 self.LBOX_missing_shots.Append(label, shot)
1172 else:
1173 # time_left, seq_no, regime, latest_due, vacc_comment
1174 label = due_template % (
1175 shot['time_left'].days / 7,
1176 shot['seq_no'],
1177 shot['indication'],
1178 shot['regime'],
1179 shot['latest_due'].strftime('%m/%Y'),
1180 shot['vacc_comment']
1181 )
1182 self.LBOX_missing_shots.Append(label, shot)
1183 # booster
1184 lbl_template = _('due now: booster for %s in schedule "%s" (%s)')
1185 for shot in missing_shots['boosters']:
1186 # indication, regime, vacc_comment
1187 label = lbl_template % (
1188 shot['l10n_indication'],
1189 shot['regime'],
1190 shot['vacc_comment']
1191 )
1192 self.LBOX_missing_shots.Append(label, shot)
1193 print "displaying missing shots took", time.time()-t1, "seconds"
1194
1195 return True
1196 #----------------------------------------------------
1199 # FIXME:
1200 # if has_focus:
1201 # wxCallAfter(self.__reset_ui_content)
1202 # else:
1203 # return 1
1204 #----------------------------------------------------
1207 # FIXME:
1208 # if has_focus:
1209 # wxCallAfter(self.__reset_ui_content)
1210 # else:
1211 # is_stale == True
1212 # return 1
1213 #======================================================================
1214 # main
1215 #----------------------------------------------------------------------
1216 if __name__ == "__main__":
1217
1218 if len(sys.argv) < 2:
1219 sys.exit()
1220
1221 if sys.argv[1] != u'test':
1222 sys.exit()
1223
1224 app = wx.PyWidgetTester(size = (600, 600))
1225 app.SetWidget(cATCPhraseWheel, -1)
1226 app.MainLoop()
1227 #======================================================================
1228
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu Jul 28 03:57:06 2011 | http://epydoc.sourceforge.net |