| Trees | Indices | Help |
|
|---|
|
|
1 """GNUmed vaccination related business objects.
2 """
3 #============================================================
4 # $Source: /cvsroot/gnumed/gnumed/gnumed/client/business/gmVaccination.py,v $
5 # $Id: gmVaccination.py,v 1.38 2008/02/25 17:31:41 ncq Exp $
6 __version__ = "$Revision: 1.38 $"
7 __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>"
8 __license__ = "GPL"
9
10 import types, copy, logging
11
12
13 from Gnumed.pycommon import gmExceptions, gmI18N, gmBusinessDBObject
14
15
16 _log = logging.getLogger('gm.vaccination')
17 _log.info(__version__)
18 #============================================================
20 """Represents one vaccination event.
21 """
22 _cmd_fetch_payload = """
23 select *, NULL as is_booster, -1 as seq_no, xmin_vaccination from clin.v_pat_vaccinations4indication
24 where pk_vaccination=%s
25 order by date desc"""
26 _cmds_lock_rows_for_update = [
27 """select 1 from clin.vaccination where id=%(pk_vaccination)s and xmin=%(xmin_vaccination)s for update"""
28 ]
29 _cmds_store_payload = [
30 """update clin.vaccination set
31 clin_when=%(date)s,
32 narrative=%(narrative)s,
33 fk_provider=%(pk_provider)s,
34 fk_vaccine=(select pk from clin.vaccine where trade_name=%(vaccine)s),
35 site=%(site)s,
36 batch_no=%(batch_no)s
37 where id=%(pk_vaccination)s""",
38 """select xmin_vaccination from clin.v_pat_vaccinations4indication where pk_vaccination=%(pk_vaccination)s"""
39 ]
40 _updatable_fields = [
41 'date',
42 'narrative',
43 'pk_provider',
44 'vaccine',
45 'site',
46 'batch_no',
47 # the following two are updatable via __setitem__
48 # API but not persisted via _cmds_store_payload
49 'is_booster',
50 'seq_no'
51 ]
52 #--------------------------------------------------------
54 """Make sure we have is_booster/seq_no when loading from row data."""
55 gmBusinessDBObject.cBusinessDBObject._init_from_row_data(self, row=row)
56 try:
57 idx = self._idx['is_booster']
58 except KeyError:
59 idx = len(self._payload)
60 self._payload.append(False)
61 # make local copy so we can safely modify it, but from
62 # self._idx which is row['idx'] with possible modifications
63 self._idx = copy.copy(self._idx)
64 self._idx['is_booster'] = idx
65 try:
66 idx = self._idx['seq_no']
67 except KeyError:
68 idx = len(self._payload)
69 self._payload.append(False)
70 self._idx = copy.copy(self._idx)
71 self._idx['seq_no'] = -1
72 #--------------------------------------------------------
74 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
75 if attribute in ['is_booster', 'seq_no']:
76 self._is_modified = False
77 # #--------------------------------------------------------
78 # def get_next_shot_due(self):
79 # """
80 # Retrieves next shot due date
81 # """
82 # # FIXME: this will break due to not being initialized
83 # return self.__next_shot_due
84 # #--------------------------------------------------------
85 # def set_next_shot_due(self, next_shot_due):
86 # """
87 # Sets next shot due date
88 #
89 # * next_shot_due : Scheduled date for next vaccination shot
90 # """
91 # self.__next_shot_due = next_shot_due
92 #============================================================
94 """Represents one missing vaccination.
95
96 - can be due or overdue
97 """
98 _cmd_fetch_payload = """
99 (select *, False as overdue
100 from clin.v_pat_missing_vaccs vpmv
101 where
102 pk_patient=%(pat_id)s
103 and
104 (select dob from dem.identity where pk=%(pat_id)s) between (now() - age_due_min) and (now() - coalesce(age_due_max, '115 years'::interval))
105 and
106 indication=%(indication)s
107 and
108 seq_no=%(seq_no)s
109 order by time_left)
110
111 UNION
112
113 (select *, True as overdue
114 from clin.v_pat_missing_vaccs vpmv
115 where
116 pk_patient=%(pat_id)s
117 and
118 now() - ((select dob from dem.identity where pk=%(pat_id)s)) > coalesce(age_due_max, '115 years'::interval)
119 and
120 indication=%(indication)s
121 and
122 seq_no=%(seq_no)s
123 order by amount_overdue)"""
124 _cmds_lock_rows_for_update = []
125 _cmds_store_payload = ["""select 1"""]
126 _updatable_fields = []
127 #--------------------------------------------------------
130 #--------------------------------------------------------
137 #============================================================
139 """Represents one due booster.
140 """
141 _cmd_fetch_payload = """
142 select *, now() - amount_overdue as latest_due
143 from clin.v_pat_missing_boosters vpmb
144 where
145 pk_patient=%(pat_id)s
146 and
147 indication=%(indication)s
148 order by amount_overdue"""
149 _cmds_lock_rows_for_update = []
150 _cmds_store_payload = ["""select 1"""]
151 _updatable_fields = []
152 #============================================================
154 """Represents one vaccination scheduled following a course.
155 """
156 _cmd_fetch_payload = u"select * from clin.v_vaccs_scheduled4pat where pk_vacc_def=%s"
157 _cmds_lock_rows_for_update = []
158 _cmds_store_payload = ["""select 1"""]
159 _updatable_fields = []
160 #============================================================
162 """Represents one vaccination course.
163 """
164 _cmd_fetch_payload = """
165 select *, xmin_vaccination_course from clin.v_vaccination_courses
166 where pk_course=%s"""
167 _cmds_lock_rows_for_update = [
168 """select 1 from clin.vaccination_course where id=%(pk_course)s and xmin=%(xmin_vaccination_course)s for update"""
169 ]
170 _cmds_store_payload = [
171 """update clin.vaccination_course set
172 name=%(course)s,
173 fk_recommended_by=%(pk_recommended_by)s,
174 fk_indication=(select id from clin.vacc_indication where description=%(indication)s),
175 comment=%(comment)s
176 where id=%(pk_course)s""",
177 """select xmin_vaccination_course from clin.v_vaccination_courses where pk_course=%(pk_course)s"""
178 ]
179 _updatable_fields = [
180 'course',
181 'pk_recommended_by',
182 'indication',
183 'comment'
184 ]
185 #============================================================
187 _recommended_courses = None
188 #============================================================
189 # convenience functions
190 #------------------------------------------------------------
191 -def create_vaccination(patient_id=None, episode_id=None, encounter_id=None, staff_id = None, vaccine=None):
192 # sanity check
193 # 1) any of the args being None should fail the SQL code
194 # 2) do episode/encounter belong to the patient ?
195 cmd = """
196 select pk_patient
197 from clin.v_pat_episodes
198 where pk_episode=%s
199 union
200 select pk_patient
201 from clin.v_pat_encounters
202 where pk_encounter=%s"""
203 rows = gmPG.run_ro_query('historica', cmd, None, episode_id, encounter_id)
204 if (rows is None) or (len(rows) == 0):
205 _log.error('error checking episode [%s] <-> encounter [%s] consistency' % (episode_id, encounter_id))
206 return (False, _('internal error, check log'))
207 if len(rows) > 1:
208 _log.error('episode [%s] and encounter [%s] belong to more than one patient !?!' % (episode_id, encounter_id))
209 return (False, _('consistency error, check log'))
210 # insert new vaccination
211 queries = []
212 if type(vaccine) == types.IntType:
213 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine)
214 values (%s, %s, %s, %s, %s)"""
215 else:
216 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine)
217 values (%s, %s, %s, %s, (select pk from clin.vaccine where trade_name=%s))"""
218 vaccine = str(vaccine)
219 queries.append((cmd, [encounter_id, episode_id, patient_id, staff_id, vaccine]))
220 # get PK of inserted row
221 cmd = "select currval('clin.vaccination_id_seq')"
222 queries.append((cmd, []))
223 result, msg = gmPG.run_commit('historica', queries, True)
224 if (result is None) or (len(result) == 0):
225 return (False, msg)
226 try:
227 vacc = cVaccination(aPK_obj = result[0][0])
228 except gmExceptions.ConstructorError:
229 _log.exception('cannot instantiate vaccination' % (result[0][0]), sys.exc_info, verbose=0)
230 return (False, _('internal error, check log'))
231
232 return (True, vacc)
233 #--------------------------------------------------------
235 # FIXME: use cVaccinationCourse
236 cmd = 'select name from clin.vaccination_course'
237 rows = gmPG.run_ro_query('historica', cmd)
238 if rows is None:
239 return None
240 if len(rows) == 0:
241 return []
242 data = []
243 for row in rows:
244 data.extend(rows)
245 return data
246 #--------------------------------------------------------
248 # check DbC, if it fails exception is due
249 int(pk_patient)
250
251 cmd = """
252 select fk_regime
253 from clin.lnk_pat2vacc_reg l
254 where l.fk_patient = %s""" % pk_patient
255
256 rows = gmPG.run_ro_query('historica', cmd)
257 active = []
258 if rows and len(rows):
259 active = [ r[0] for r in rows]
260
261 # FIXME: this is patient dependant so how would a cache
262 # FIXME: work that's not taking into account pk_patient ?
263 # recommended_regimes = VaccByRecommender._recommended_regimes
264 # if not clear_cache and recommended_regimes:
265 # return recommended_regimes, active
266
267 r = ( {}, [] )
268
269 # FIXME: use views ?
270 cmd = """
271 select
272 r.pk_regime ,
273 r.pk_recommended_by ,
274 r.indication,
275 r.regime ,
276 extract (epoch from d.min_age_due) /60/60/24,
277 extract (epoch from d.max_age_due) /60/60/24,
278 extract (epoch from d.min_interval ) /60/60/24,
279 d.seq_no
280 from
281 clin.v_vaccination_courses r, clin.vacc_def d
282 where
283 d.fk_regime = r.pk_regime
284 order by
285 r.pk_recommended_by, d.min_age_due"""
286 #print cmd
287 #import pdb
288 #pdb.set_trace()
289 #
290 rows = gmPG.run_ro_query('historica', cmd)
291 if rows is None:
292 VaccByRecommender._recommended_regimes = r
293 return r, active
294
295 row_fields = ['pk_regime', 'pk_recommender', 'indication' , 'regime', 'min_age_due', 'max_age_due', 'min_interval', 'seq_no' ]
296
297 for row in rows:
298 m = {}
299 for k, i in zip(row_fields, range(len(row))):
300 m[k] = row[i]
301 pk_recommender = m['pk_recommender']
302
303 if not pk_recommender in r[0].keys():
304 r[0][pk_recommender] = []
305 r[1].append(pk_recommender)
306 r[0][pk_recommender].append(m)
307
308 for k, v in r[0].items():
309 print k
310 for x in v:
311 print '\t', x
312
313 VaccByRecommender._recommended_regimes = r
314 return r, active
315 #--------------------------------------------------------
317 # DbC
318 int(pk_patient)
319
320 cmd = """
321 select
322 indication, regime,
323 pk_regime,
324 pk_recommended_by,
325 seq_no ,
326 extract(epoch from age_due_min) /60/60/24 as age_due_min,
327 extract(epoch from age_due_max) /60/60/24 as age_due_max,
328 extract(epoch from min_interval)/60/60/24 as min_interval
329 from
330 clin.v_pat_missing_vaccs
331 where pk_patient = %s
332 order by age_due_min, pk_recommended_by, indication
333 """ % pk_patient
334
335 rows = gmPG.run_ro_query('historica', cmd)
336
337 return rows
338 #--------------------------------------------------------
340 """Retrieves vaccination bundle indications list.
341
342 * vaccinations = list of any type of vaccination
343 - indicated
344 - due vacc
345 - overdue vaccs
346 - due boosters
347 - arbitrary
348 """
349 # FIXME: can we not achieve this by:
350 # [lambda [vacc['indication'], ['l10n_indication']] for vacc in vaccination_list]
351 # I think we could, but we would be lacking error handling
352 if vaccinations is None:
353 _log.error('list of vaccinations must be supplied')
354 return (False, [['ERROR: list of vaccinations not supplied', _('ERROR: list of vaccinations not supplied')]])
355 if len(vaccinations) == 0:
356 return (True, [['empty list of vaccinations', _('empty list of vaccinations')]])
357 inds = []
358 for vacc in vaccinations:
359 try:
360 inds.append([vacc['indication'], vacc['l10n_indication']])
361 except KeyError:
362 try:
363 inds.append([vacc['indication'], vacc['indication']])
364 except KeyError:
365 inds.append(['vacc -> ind error: %s' % str(vacc), _('vacc -> ind error: %s') % str(vacc)])
366 return (True, inds)
367 #--------------------------------------------------------
369 """
370 Schedules a vaccination course for a patient
371
372 * patient_id = Patient's PK
373 * course = course object or Vaccination course's PK
374 """
375 # FIXME: add method schedule_vaccination_course() to gmPerson.cPatient
376 if isinstance(course, cVaccinationCourse):
377 course_id = course['pk_course']
378 else:
379 course_id = course
380
381 # insert new patient - vaccination course relation
382 queries = []
383 cmd = """insert into clin.lnk_pat2vacc_reg (fk_patient, fk_course)
384 values (%s, %s)"""
385 queries.append((cmd, [patient_id, course_id]))
386 result, msg = gmPG.run_commit('historica', queries, True)
387 if result is None:
388 return (False, msg)
389 return (True, msg)
390 #--------------------------------------------------------
392 """unSchedules a vaccination course for a patient
393
394 * patient_id = Patient's PK
395 * course = course object or Vaccination course's PK
396 """
397 # FIXME: add method schedule_vaccination_course() to gmPerson.cPatient
398 if isinstance(course, cVaccinationCourse):
399 course_id = course['pk_course']
400 else:
401 course_id = course
402
403 # delete patient - vaccination course relation
404 queries = []
405 cmd = """delete from clin.lnk_pat2vacc_reg where fk_patient = %s and fk_course = %s"""
406
407 queries.append((cmd, [patient_id, course_id]))
408 result, msg = gmPG.run_commit('historica', queries, True)
409 if result is None:
410 return (False, msg)
411 return (True, msg)
412 #--------------------------------------------------------
414
415 quoted_inds = [ "'"+x + "%'" for x in all_ind]
416
417 # cmd_inds_per_vaccine = """
418 # select count(v.trade_name) , v.trade_name
419 # from
420 # clin.vaccine v, clin.lnk_vaccine2inds l, clin.vacc_indication i
421 # where
422 # v.pk = l.fk_vaccine and l.fk_indication = i.id
423 # group
424 # by trade_name
425 # """
426
427 cmd_inds_per_vaccine = """
428 select
429 count(trade_name),
430 trade_name
431 from clin.v_inds4vaccine
432 group by trade_name"""
433
434 cmd_presence_in_vaccine = """
435 select count(v.trade_name) , v.trade_name
436
437 from
438 clin.vaccine v, clin.lnk_vaccine2inds l, clin.vacc_indication i
439 where
440 v.pk = l.fk_vaccine and l.fk_indication = i.id
441 and
442 i.description like any ( array [ %s ] )
443 group
444
445 by trade_name
446
447 """ % ', '.join( quoted_inds )
448
449 inds_per_vaccine = gmPG.run_ro_query( 'historica', cmd_inds_per_vaccine)
450
451 presence_in_vaccine = gmPG.run_ro_query( 'historica', cmd_presence_in_vaccine)
452
453 map_vacc_count_inds = dict ( [ (x[1], x[0]) for x in inds_per_vaccine ] )
454
455 matched_vaccines = []
456 for (presence, vaccine) in presence_in_vaccine:
457 if presence == len ( all_ind) :
458 # matched the number of indications selected with a vaccine
459 # is this also ALL the vaccine's indications ?
460 if map_vacc_count_inds[vaccine] == presence:
461 matched_vaccines.append(vaccine)
462 return matched_vaccines
463 #============================================================
464 # main - unit testing
465 #------------------------------------------------------------
466 if __name__ == '__main__':
467 import sys
468
469 from Gnumed.pycommon import gmPG
470 #--------------------------------------------------------
472 vacc = cVaccination(aPK_obj=1)
473 print vacc
474 fields = vacc.get_fields()
475 for field in fields:
476 print field, ':', vacc[field]
477 print "updatable:", vacc.get_updatable_fields()
478 #--------------------------------------------------------
480 # Test for a due vaccination
481 pk_args = {
482 'pat_id': 12,
483 'indication': 'meningococcus C',
484 'seq_no': 1
485 }
486 missing_vacc = cMissingVaccination(aPK_obj=pk_args)
487 fields = missing_vacc.get_fields()
488 print "\nDue vaccination:"
489 print missing_vacc
490 for field in fields:
491 print field, ':', missing_vacc[field]
492 # Test for an overdue vaccination
493 pk_args = {
494 'pat_id': 12,
495 'indication': 'haemophilus influenzae b',
496 'seq_no': 2
497 }
498 missing_vacc = cMissingVaccination(aPK_obj=pk_args)
499 fields = missing_vacc.get_fields()
500 print "\nOverdue vaccination (?):"
501 print missing_vacc
502 for field in fields:
503 print field, ':', missing_vacc[field]
504 #--------------------------------------------------------
506 pk_args = {
507 'pat_id': 12,
508 'indication': 'tetanus'
509 }
510 missing_booster = cMissingBooster(aPK_obj=pk_args)
511 fields = missing_booster.get_fields()
512 print "\nDue booster:"
513 print missing_booster
514 for field in fields:
515 print field, ':', missing_booster[field]
516 #--------------------------------------------------------
518 scheduled_vacc = cScheduledVaccination(aPK_obj=20)
519 print "\nScheduled vaccination:"
520 print scheduled_vacc
521 fields = scheduled_vacc.get_fields()
522 for field in fields:
523 print field, ':', scheduled_vacc[field]
524 print "updatable:", scheduled_vacc.get_updatable_fields()
525 #--------------------------------------------------------
527 vaccination_course = cVaccinationCourse(aPK_obj=7)
528 print "\nVaccination course:"
529 print vaccination_course
530 fields = vaccination_course.get_fields()
531 for field in fields:
532 print field, ':', vaccination_course[field]
533 print "updatable:", vaccination_course.get_updatable_fields()
534 #--------------------------------------------------------
536 result, msg = put_patient_on_schedule(patient_id=12, course_id=1)
537 print '\nPutting patient id 12 on schedule id 1... %s (%s)' % (result, msg)
538 #--------------------------------------------------------
539
540 test_vaccination_course()
541 #test_put_patient_on_schedule()
542 test_scheduled_vacc()
543 test_vacc()
544 test_due_vacc()
545 # test_due_booster()
546 #============================================================
547 # $Log: gmVaccination.py,v $
548 # Revision 1.38 2008/02/25 17:31:41 ncq
549 # - logging cleanup
550 #
551 # Revision 1.37 2008/01/30 13:34:50 ncq
552 # - switch to std lib logging
553 #
554 # Revision 1.36 2007/07/17 11:13:42 ncq
555 # - no more gmClinItem
556 #
557 # Revision 1.35 2007/03/08 11:31:08 ncq
558 # - just cleanup
559 #
560 # Revision 1.34 2007/02/22 17:27:44 ncq
561 # - no more cPerson
562 #
563 # Revision 1.33 2006/11/24 14:15:36 ncq
564 # - u'' one query
565 #
566 # Revision 1.32 2006/10/25 07:17:40 ncq
567 # - no more gmPG
568 # - no more cClinItem
569 #
570 # Revision 1.31 2006/07/19 20:25:00 ncq
571 # - gmPyCompat.py is history
572 #
573 # Revision 1.30 2006/05/06 18:53:56 ncq
574 # - select age(...) <> ...; -> select ... <> now() - ...; as per Syan
575 #
576 # Revision 1.29 2006/05/04 17:55:08 ncq
577 # - lots of DDL naming adjustments
578 # - many things spelled out at Richard's request
579 # - regime -> course
580 #
581 # Revision 1.28 2006/01/10 23:22:53 sjtan
582 #
583 # id has become pk
584 #
585 # Revision 1.27 2006/01/09 10:43:07 ncq
586 # - more dem schema qualifications
587 #
588 # Revision 1.26 2006/01/07 11:23:24 ncq
589 # - must use """ for multi-line string
590 #
591 # Revision 1.25 2006/01/06 08:32:12 ncq
592 # - some cleanup, added view to backend, may need some fixing
593 #
594 # Revision 1.24 2006/01/05 22:39:57 sjtan
595 #
596 # vaccination use case; some sql stuff may need to be added ( e.g. permissions ); sorry in a hurry
597 #
598 # Revision 1.23 2005/12/29 21:54:35 ncq
599 # - adjust to schema changes
600 #
601 # Revision 1.22 2005/11/27 12:44:57 ncq
602 # - clinical tables are in schema "clin" now
603 #
604 # Revision 1.21 2005/03/20 12:28:50 cfmoro
605 # On create_vaccination, id_patient -> pk_patient
606 #
607 # Revision 1.20 2005/02/12 13:56:49 ncq
608 # - identity.id -> identity.pk
609 #
610 # Revision 1.19 2005/01/31 10:37:26 ncq
611 # - gmPatient.py -> gmPerson.py
612 #
613 # Revision 1.18 2005/01/02 19:55:30 ncq
614 # - don't need _xmins_refetch_col_pos anymore
615 #
616 # Revision 1.17 2004/12/20 16:45:49 ncq
617 # - gmBusinessDBObject now requires refetching of XMIN after save_payload
618 #
619 # Revision 1.16 2004/11/03 22:32:34 ncq
620 # - support _cmds_lock_rows_for_update in business object base class
621 #
622 # Revision 1.15 2004/10/27 12:11:59 ncq
623 # - add is_booster/seq_no as pseudo columns to _cmd_fetch_payload so
624 # __init_from_pk() automagically creates all the right things
625 # - enhance _init_from_row_data() to construct those fields if need be
626 # - make __setitem__ aware of is_booster/seq_no being pseudo columns
627 # that do not affect _is_modified
628 #
629 # Revision 1.14 2004/10/20 21:42:28 ncq
630 # - fix faulty appending on repeated use of set_booster_status/set_seq_no()
631 #
632 # Revision 1.13 2004/10/18 11:35:42 ncq
633 # - cleanup
634 #
635 # Revision 1.12 2004/10/12 11:16:22 ncq
636 # - robustify cVaccination.set_seq_no/set_booster_status
637 # - Carlos added cVaccinationRegime/put_patient_on_schedule
638 # - some test code
639 #
640 # Revision 1.11 2004/09/28 12:28:12 ncq
641 # - cVaccination: add set_booster_status(), set_seq_no()
642 # - add cScheduledVaccination (by Carlos)
643 # - improve testing
644 #
645 # Revision 1.10 2004/08/20 13:19:52 ncq
646 # - add license
647 #
648 # Revision 1.9 2004/06/28 12:18:52 ncq
649 # - more id_* -> fk_*
650 #
651 # Revision 1.8 2004/06/26 07:33:55 ncq
652 # - id_episode -> fk/pk_episode
653 #
654 # Revision 1.7 2004/06/13 08:03:07 ncq
655 # - cleanup, better separate vaccination code from general EMR code
656 #
657 # Revision 1.6 2004/06/08 00:48:05 ncq
658 # - cleanup
659 #
660 # Revision 1.5 2004/05/14 13:17:27 ncq
661 # - less useless verbosity
662 # - cleanup
663 #
664 # Revision 1.4 2004/05/12 14:30:30 ncq
665 # - cMissingVaccination()
666 # - cMissingBooster()
667 #
668 # Revision 1.3 2004/04/24 12:59:17 ncq
669 # - all shiny and new, vastly improved vaccinations
670 # handling via clinical item objects
671 # - mainly thanks to Carlos Moro
672 #
673 # Revision 1.2 2004/04/11 12:07:54 ncq
674 # - better unit testing
675 #
676 # Revision 1.1 2004/04/11 10:16:53 ncq
677 # - first version
678 #
679
| Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Tue Feb 9 04:01:27 2010 | http://epydoc.sourceforge.net |