1 """GNUmed vaccination related business objects.
2 """
3
4 __version__ = "$Revision: 1.38 $"
5 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
6 __license__ = "GPL"
7
8 import sys, copy, logging
9
10
11 if __name__ == '__main__':
12 sys.path.insert(0, '../../')
13 from Gnumed.pycommon import gmBusinessDBObject, gmPG2, gmI18N, gmTools, gmDateTime
14 from Gnumed.business import gmMedication
15
16
17 _log = logging.getLogger('gm.vaccination')
18 _log.info(__version__)
19
20
22 cmd = u'SELECT *, _(description) AS l10n_description FROM clin.vacc_indication'
23 args = {}
24
25 if pk_indications is not None:
26 if len(pk_indications) != 0:
27 cmd += u' WHERE id IN %(pks)s'
28 args['pks'] = tuple(pk_indications)
29
30 if order_by is not None:
31 cmd += u' ORDER BY %s' % order_by
32
33 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
34
35 return rows
36
37 _sql_fetch_vaccine = u"""SELECT *, xmin_vaccine FROM clin.v_vaccines WHERE %s"""
38
39 -class cVaccine(gmBusinessDBObject.cBusinessDBObject):
40 """Represents one vaccine."""
41
42 _cmd_fetch_payload = _sql_fetch_vaccine % u"pk_vaccine = %s"
43
44 _cmds_store_payload = [
45 u"""UPDATE clin.vaccine SET
46 --id_route = %(pk_route)s,
47 --is_live = %(is_live)s,
48 min_age = %(min_age)s,
49 max_age = %(max_age)s,
50 comment = gm.nullify_empty_string(%(comment)s),
51 fk_brand = %(pk_brand)s
52 WHERE
53 pk = %(pk_vaccine)s
54 AND
55 xmin = %(xmin_vaccine)s
56 RETURNING
57 xmin as xmin_vaccine
58 """
59 ]
60
61 _updatable_fields = [
62
63
64 u'min_age',
65 u'max_age',
66 u'comment',
67 u'pk_brand'
68 ]
69
71 return get_indications(order_by = 'l10n_description', pk_indications = self._payload[self._idx['pk_indications']])
72
74 queries = [{
75 'cmd': u'DELETE FROM clin.lnk_vaccine2inds WHERE fk_vaccine = %(pk_vacc)s',
76 'args': {'pk_vacc': self._payload[self._idx['pk_vaccine']]}
77 }]
78
79 if pk_indications is None:
80 if set(self._payload[self._idx['indications']]) == set(indications):
81 return
82
83 for ind in indications:
84 queries.append ({
85 'cmd': u"""
86 INSERT INTO clin.lnk_vaccine2inds (
87 fk_vaccine,
88 fk_indication
89 ) VALUES (
90 %(pk_vacc)s,
91 (SELECT id FROM clin.vacc_indication WHERE description = %(ind)s)
92 )""",
93 'args': {'pk_vacc': self._payload[self._idx['pk_vaccine']], 'ind': ind}
94 })
95 else:
96 if set(self._payload[self._idx['pk_indications']]) == set(pk_indications):
97 return
98
99 for pk_ind in pk_indications:
100 queries.append ({
101 'cmd': u"""
102 INSERT INTO clin.lnk_vaccine2inds (
103 fk_vaccine,
104 fk_indication
105 ) VALUES (
106 %(pk_vacc)s,
107 %(pk_ind)s
108 )""",
109 'args': {'pk_vacc': self._payload[self._idx['pk_vaccine']], 'pk_ind': pk_ind}
110 })
111
112 gmPG2.run_rw_queries(queries = queries)
113 self.refetch_payload()
114
115 indications = property(get_indications, lambda x:x)
116
117
118
121
122 brand = property(_get_brand, lambda x:x)
123
125 cmd = u'SELECT EXISTS(SELECT 1 FROM clin.vaccination WHERE fk_vaccine = %(pk)s)'
126 args = {'pk': self._payload[self._idx['pk_vaccine']]}
127 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False)
128 return rows[0][0]
129
130 is_in_use = property(_get_is_in_use, lambda x:x)
131
132 -def create_vaccine(pk_brand=None, brand_name=None, indications=None, pk_indications=None):
133
134 if pk_brand is None:
135 prep = _('vaccine')
136 _log.debug('creating branded drug [%s %s]', brand_name, prep)
137 drug = gmMedication.create_branded_drug (
138 brand_name = brand_name,
139 preparation = prep,
140 return_existing = True
141 )
142 drug['atc'] = u'J07'
143 drug.save()
144 pk_brand = drug['pk_brand']
145
146 cmd = u'INSERT INTO clin.vaccine (fk_brand) values (%(pk_brand)s) RETURNING pk'
147 queries = [{'cmd': cmd, 'args': {'pk_brand': pk_brand}}]
148
149
150 if pk_indications is None:
151 for indication in indications:
152 cmd = u"""
153 INSERT INTO clin.lnk_vaccine2inds (
154 fk_vaccine,
155 fk_indication
156 ) VALUES (
157 currval(pg_get_serial_sequence('clin.vaccine', 'pk')),
158 (SELECT id
159 FROM clin.vacc_indication
160 WHERE
161 lower(description) = lower(%(ind)s)
162 LIMIT 1
163 )
164 )
165 RETURNING fk_vaccine
166 """
167 queries.append({'cmd': cmd, 'args': {'ind': indication}})
168 else:
169 for pk_indication in pk_indications:
170 cmd = u"""
171 INSERT INTO clin.lnk_vaccine2inds (
172 fk_vaccine,
173 fk_indication
174 ) VALUES (
175 currval(pg_get_serial_sequence('clin.vaccine', 'pk')),
176 %(pk_ind)s
177 )
178 RETURNING fk_vaccine
179 """
180 queries.append({'cmd': cmd, 'args': {'pk_ind': pk_indication}})
181
182 rows, idx = gmPG2.run_rw_queries(queries = queries, get_col_idx = False, return_data = True)
183
184 return cVaccine(aPK_obj = rows[0]['fk_vaccine'])
185
187
188 cmd = u'DELETE FROM clin.vaccine WHERE pk = %(pk)s'
189 args = {'pk': vaccine}
190
191 try:
192 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
193 except gmPG2.dbapi.IntegrityError:
194 _log.exception('cannot delete vaccine [%s]', vaccine)
195 return False
196
197 return True
198
209
211
212 args = {'inds': indications}
213 cmd = _sql_fetch_vaccine % (u'is_fake_vaccine is True AND indications @> %(inds)s AND %(inds)s @> indications')
214
215 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True)
216
217 if len(rows) == 0:
218 _log.warning('no fake, generic vaccine found for [%s]', indications)
219 return None
220
221 return cVaccine(row = {'data': rows[0], 'idx': idx, 'pk_field': 'pk_vaccine'})
222
224
225 cmd = u'select gm.create_generic_monovalent_vaccines()'
226 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd}], return_data = True)
227
228 cmd = u'select gm.create_generic_combi_vaccines()'
229 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd}], return_data = True)
230
231 return rows[0][0]
232
233
234
235 sql_fetch_vaccination = u"""SELECT * FROM clin.v_pat_vaccinations WHERE %s"""
236
238
239 _cmd_fetch_payload = sql_fetch_vaccination % u"pk_vaccination = %s"
240
241 _cmds_store_payload = [
242 u"""UPDATE clin.vaccination SET
243 soap_cat = %(soap_cat)s,
244 clin_when = %(date_given)s,
245 site = gm.nullify_empty_string(%(site)s),
246 batch_no = gm.nullify_empty_string(%(batch_no)s),
247 reaction = gm.nullify_empty_string(%(reaction)s),
248 narrative = gm.nullify_empty_string(%(comment)s),
249 fk_vaccine = %(pk_vaccine)s,
250 fk_provider = %(pk_provider)s,
251 fk_encounter = %(pk_encounter)s,
252 fk_episode = %(pk_episode)s
253 WHERE
254 pk = %(pk_vaccination)s
255 AND
256 xmin = %(xmin_vaccination)s
257 RETURNING
258 xmin as xmin_vaccination
259 """
260 ]
261
262 _updatable_fields = [
263 u'soap_cat',
264 u'date_given',
265 u'site',
266 u'batch_no',
267 u'reaction',
268 u'comment',
269 u'pk_vaccine',
270 u'pk_provider',
271 u'pk_encounter',
272 u'pk_episode'
273 ]
274
298
300 return cVaccine(aPK_obj = self._payload[self._idx['pk_vaccine']])
301
302 vaccine = property(_get_vaccine, lambda x:x)
303
305
306 cmd = u"""
307 INSERT INTO clin.vaccination (
308 fk_encounter,
309 fk_episode,
310 fk_vaccine,
311 batch_no
312 ) VALUES (
313 %(enc)s,
314 %(epi)s,
315 %(vacc)s,
316 %(batch)s
317 ) RETURNING pk;
318 """
319 args = {
320 u'enc': encounter,
321 u'epi': episode,
322 u'vacc': vaccine,
323 u'batch': batch_no
324 }
325
326 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False, return_data = True)
327
328 return cVaccination(aPK_obj = rows[0][0])
329
330
331
333 cmd = u"""DELETE FROM clin.vaccination WHERE pk = %(pk)s"""
334 args = {'pk': vaccination}
335
336 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
337
338
339
352
353
354
407
408
409
410
411
412
414 """Represents one missing vaccination.
415
416 - can be due or overdue
417 """
418 _cmd_fetch_payload = """
419 (select *, False as overdue
420 from clin.v_pat_missing_vaccs vpmv
421 where
422 pk_patient=%(pat_id)s
423 and
424 (select dob from dem.identity where pk=%(pat_id)s) between (now() - age_due_min) and (now() - coalesce(age_due_max, '115 years'::interval))
425 and
426 indication=%(indication)s
427 and
428 seq_no=%(seq_no)s
429 order by time_left)
430
431 UNION
432
433 (select *, True as overdue
434 from clin.v_pat_missing_vaccs vpmv
435 where
436 pk_patient=%(pat_id)s
437 and
438 now() - ((select dob from dem.identity where pk=%(pat_id)s)) > coalesce(age_due_max, '115 years'::interval)
439 and
440 indication=%(indication)s
441 and
442 seq_no=%(seq_no)s
443 order by amount_overdue)"""
444 _cmds_lock_rows_for_update = []
445 _cmds_store_payload = ["""select 1"""]
446 _updatable_fields = []
447
449 return self['overdue']
450
452
453
454
455
456 return (False, 'not implemented')
457
472
480
482 """Represents one vaccination course.
483 """
484 _cmd_fetch_payload = """
485 select *, xmin_vaccination_course from clin.v_vaccination_courses
486 where pk_course=%s"""
487 _cmds_lock_rows_for_update = [
488 """select 1 from clin.vaccination_course where id=%(pk_course)s and xmin=%(xmin_vaccination_course)s for update"""
489 ]
490 _cmds_store_payload = [
491 """update clin.vaccination_course set
492 name=%(course)s,
493 fk_recommended_by=%(pk_recommended_by)s,
494 fk_indication=(select id from clin.vacc_indication where description=%(indication)s),
495 comment=%(comment)s
496 where id=%(pk_course)s""",
497 """select xmin_vaccination_course from clin.v_vaccination_courses where pk_course=%(pk_course)s"""
498 ]
499 _updatable_fields = [
500 'course',
501 'pk_recommended_by',
502 'indication',
503 'comment'
504 ]
505
506
507
508
509 -def create_vaccination_old(patient_id=None, episode_id=None, encounter_id=None, staff_id = None, vaccine=None):
510
511
512
513 cmd = """
514 select pk_patient
515 from clin.v_pat_episodes
516 where pk_episode=%s
517 union
518 select pk_patient
519 from clin.v_pat_encounters
520 where pk_encounter=%s"""
521 rows = gmPG.run_ro_query('historica', cmd, None, episode_id, encounter_id)
522 if (rows is None) or (len(rows) == 0):
523 _log.error('error checking episode [%s] <-> encounter [%s] consistency' % (episode_id, encounter_id))
524 return (False, _('internal error, check log'))
525 if len(rows) > 1:
526 _log.error('episode [%s] and encounter [%s] belong to more than one patient !?!' % (episode_id, encounter_id))
527 return (False, _('consistency error, check log'))
528
529 queries = []
530 if type(vaccine) == types.IntType:
531 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine)
532 values (%s, %s, %s, %s, %s)"""
533 else:
534 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine)
535 values (%s, %s, %s, %s, (select pk from clin.vaccine where trade_name=%s))"""
536 vaccine = str(vaccine)
537 queries.append((cmd, [encounter_id, episode_id, patient_id, staff_id, vaccine]))
538
539 cmd = "select currval('clin.vaccination_id_seq')"
540 queries.append((cmd, []))
541 result, msg = gmPG.run_commit('historica', queries, True)
542 if (result is None) or (len(result) == 0):
543 return (False, msg)
544 try:
545 vacc = cVaccination(aPK_obj = result[0][0])
546 except gmExceptions.ConstructorError:
547 _log.exception('cannot instantiate vaccination' % (result[0][0]), sys.exc_info, verbose=0)
548 return (False, _('internal error, check log'))
549
550 return (True, vacc)
551
553
554 cmd = 'select name from clin.vaccination_course'
555 rows = gmPG.run_ro_query('historica', cmd)
556 if rows is None:
557 return None
558 if len(rows) == 0:
559 return []
560 data = []
561 for row in rows:
562 data.extend(rows)
563 return data
564
566
567 int(pk_patient)
568
569 cmd = """
570 select fk_regime
571 from clin.lnk_pat2vacc_reg l
572 where l.fk_patient = %s""" % pk_patient
573
574 rows = gmPG.run_ro_query('historica', cmd)
575 active = []
576 if rows and len(rows):
577 active = [ r[0] for r in rows]
578
579
580
581
582
583
584
585 r = ( {}, [] )
586
587
588 cmd = """
589 select
590 r.pk_regime ,
591 r.pk_recommended_by ,
592 r.indication,
593 r.regime ,
594 extract (epoch from d.min_age_due) /60/60/24,
595 extract (epoch from d.max_age_due) /60/60/24,
596 extract (epoch from d.min_interval ) /60/60/24,
597 d.seq_no
598 from
599 clin.v_vaccination_courses r, clin.vacc_def d
600 where
601 d.fk_regime = r.pk_regime
602 order by
603 r.pk_recommended_by, d.min_age_due"""
604
605
606
607
608 rows = gmPG.run_ro_query('historica', cmd)
609 if rows is None:
610 VaccByRecommender._recommended_regimes = r
611 return r, active
612
613 row_fields = ['pk_regime', 'pk_recommender', 'indication' , 'regime', 'min_age_due', 'max_age_due', 'min_interval', 'seq_no' ]
614
615 for row in rows:
616 m = {}
617 for k, i in zip(row_fields, range(len(row))):
618 m[k] = row[i]
619 pk_recommender = m['pk_recommender']
620
621 if not pk_recommender in r[0].keys():
622 r[0][pk_recommender] = []
623 r[1].append(pk_recommender)
624 r[0][pk_recommender].append(m)
625
626 for k, v in r[0].items():
627 print k
628 for x in v:
629 print '\t', x
630
631 VaccByRecommender._recommended_regimes = r
632 return r, active
633
635
636 int(pk_patient)
637
638 cmd = """
639 select
640 indication, regime,
641 pk_regime,
642 pk_recommended_by,
643 seq_no ,
644 extract(epoch from age_due_min) /60/60/24 as age_due_min,
645 extract(epoch from age_due_max) /60/60/24 as age_due_max,
646 extract(epoch from min_interval)/60/60/24 as min_interval
647 from
648 clin.v_pat_missing_vaccs
649 where pk_patient = %s
650 order by age_due_min, pk_recommended_by, indication
651 """ % pk_patient
652
653 rows = gmPG.run_ro_query('historica', cmd)
654
655 return rows
656
658 """Retrieves vaccination bundle indications list.
659
660 * vaccinations = list of any type of vaccination
661 - indicated
662 - due vacc
663 - overdue vaccs
664 - due boosters
665 - arbitrary
666 """
667
668
669
670 if vaccinations is None:
671 _log.error('list of vaccinations must be supplied')
672 return (False, [['ERROR: list of vaccinations not supplied', _('ERROR: list of vaccinations not supplied')]])
673 if len(vaccinations) == 0:
674 return (True, [['empty list of vaccinations', _('empty list of vaccinations')]])
675 inds = []
676 for vacc in vaccinations:
677 try:
678 inds.append([vacc['indication'], vacc['l10n_indication']])
679 except KeyError:
680 try:
681 inds.append([vacc['indication'], vacc['indication']])
682 except KeyError:
683 inds.append(['vacc -> ind error: %s' % str(vacc), _('vacc -> ind error: %s') % str(vacc)])
684 return (True, inds)
685
687 """
688 Schedules a vaccination course for a patient
689
690 * patient_id = Patient's PK
691 * course = course object or Vaccination course's PK
692 """
693
694 if isinstance(course, cVaccinationCourse):
695 course_id = course['pk_course']
696 else:
697 course_id = course
698
699
700 queries = []
701 cmd = """insert into clin.lnk_pat2vacc_reg (fk_patient, fk_course)
702 values (%s, %s)"""
703 queries.append((cmd, [patient_id, course_id]))
704 result, msg = gmPG.run_commit('historica', queries, True)
705 if result is None:
706 return (False, msg)
707 return (True, msg)
708
710 """unSchedules a vaccination course for a patient
711
712 * patient_id = Patient's PK
713 * course = course object or Vaccination course's PK
714 """
715
716 if isinstance(course, cVaccinationCourse):
717 course_id = course['pk_course']
718 else:
719 course_id = course
720
721
722 queries = []
723 cmd = """delete from clin.lnk_pat2vacc_reg where fk_patient = %s and fk_course = %s"""
724
725 queries.append((cmd, [patient_id, course_id]))
726 result, msg = gmPG.run_commit('historica', queries, True)
727 if result is None:
728 return (False, msg)
729 return (True, msg)
730
732
733 quoted_inds = [ "'"+x + "%'" for x in all_ind]
734
735
736
737
738
739
740
741
742
743
744
745 cmd_inds_per_vaccine = """
746 select
747 count(trade_name),
748 trade_name
749 from clin.v_inds4vaccine
750 group by trade_name"""
751
752 cmd_presence_in_vaccine = """
753 select count(v.trade_name) , v.trade_name
754
755 from
756 clin.vaccine v, clin.lnk_vaccine2inds l, clin.vacc_indication i
757 where
758 v.pk = l.fk_vaccine and l.fk_indication = i.id
759 and
760 i.description like any ( array [ %s ] )
761 group
762
763 by trade_name
764
765 """ % ', '.join( quoted_inds )
766
767 inds_per_vaccine = gmPG.run_ro_query( 'historica', cmd_inds_per_vaccine)
768
769 presence_in_vaccine = gmPG.run_ro_query( 'historica', cmd_presence_in_vaccine)
770
771 map_vacc_count_inds = dict ( [ (x[1], x[0]) for x in inds_per_vaccine ] )
772
773 matched_vaccines = []
774 for (presence, vaccine) in presence_in_vaccine:
775 if presence == len ( all_ind) :
776
777
778 if map_vacc_count_inds[vaccine] == presence:
779 matched_vaccines.append(vaccine)
780 return matched_vaccines
781
782
783
784 if __name__ == '__main__':
785
786 if len(sys.argv) < 2:
787 sys.exit()
788
789 if sys.argv[1] != u'test':
790 sys.exit()
791
792
793
801
803
804 pk_args = {
805 'pat_id': 12,
806 'indication': 'meningococcus C',
807 'seq_no': 1
808 }
809 missing_vacc = cMissingVaccination(aPK_obj=pk_args)
810 fields = missing_vacc.get_fields()
811 print "\nDue vaccination:"
812 print missing_vacc
813 for field in fields:
814 print field, ':', missing_vacc[field]
815
816 pk_args = {
817 'pat_id': 12,
818 'indication': 'haemophilus influenzae b',
819 'seq_no': 2
820 }
821 missing_vacc = cMissingVaccination(aPK_obj=pk_args)
822 fields = missing_vacc.get_fields()
823 print "\nOverdue vaccination (?):"
824 print missing_vacc
825 for field in fields:
826 print field, ':', missing_vacc[field]
827
829 pk_args = {
830 'pat_id': 12,
831 'indication': 'tetanus'
832 }
833 missing_booster = cMissingBooster(aPK_obj=pk_args)
834 fields = missing_booster.get_fields()
835 print "\nDue booster:"
836 print missing_booster
837 for field in fields:
838 print field, ':', missing_booster[field]
839
841 scheduled_vacc = cScheduledVaccination(aPK_obj=20)
842 print "\nScheduled vaccination:"
843 print scheduled_vacc
844 fields = scheduled_vacc.get_fields()
845 for field in fields:
846 print field, ':', scheduled_vacc[field]
847 print "updatable:", scheduled_vacc.get_updatable_fields()
848
850 vaccination_course = cVaccinationCourse(aPK_obj=7)
851 print "\nVaccination course:"
852 print vaccination_course
853 fields = vaccination_course.get_fields()
854 for field in fields:
855 print field, ':', vaccination_course[field]
856 print "updatable:", vaccination_course.get_updatable_fields()
857
859 result, msg = put_patient_on_schedule(patient_id=12, course_id=1)
860 print '\nPutting patient id 12 on schedule id 1... %s (%s)' % (result, msg)
861
866
867
868
869
870
871
872
873
874
875 test_get_vaccines()
876
877