Package Gnumed :: Package business :: Module gmVaccination
[frames] | no frames]

Source Code for Module Gnumed.business.gmVaccination

  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 gmI18N              #, gmDateTime, gmLog2 
 14  #       gmDateTime.init() 
 15  #       gmI18N.activate_locale() 
 16  from Gnumed.pycommon import gmBusinessDBObject, gmPG2 
 17  from Gnumed.business import gmMedication 
 18   
 19   
 20  _log = logging.getLogger('gm.vaccination') 
 21  _log.info(__version__) 
 22  #============================================================ 
 23  _sql_fetch_vaccine = u"""SELECT *, xmin_vaccine FROM clin.v_vaccines WHERE %s""" 
 24   
25 -class cVaccine(gmBusinessDBObject.cBusinessDBObject):
26 """Represents one vaccine.""" 27 28 _cmd_fetch_payload = _sql_fetch_vaccine % "pk = %s" 29 30 _cmds_store_payload = [ 31 u"""UPDATE clin.vaccine SET 32 -- internal_name = gm.nullify_empty_string(%(internal_name)s), 33 WHERE 34 pk = %(pk_vaccine)s 35 AND 36 xmin = %(xmin_vaccine)s 37 RETURNING 38 xmin as xmin_vaccine 39 """ 40 ] 41 42 _updatable_fields = [ 43 u'id_route', 44 u'is_live', 45 u'min_age', 46 u'max_age', 47 u'comment' 48 # forward fields to brand and include brand in save() 49 ] 50 #--------------------------------------------------------
51 - def __init__(self, aPK_obj=None, row=None):
52 super(cVaccine, self).__init__(aPK_obj = aPK_obj, row = row) 53 54 self.__brand = None
55 #-------------------------------------------------------- 56 # properties 57 #--------------------------------------------------------
58 - def _get_brand(self):
59 if self.__brand is None: 60 self.__brand = gmMedication.cBrandedDrug(aPK_obj = self._payload[self._idx['pk_brand']]) 61 return self.__brand
62 63 brand = property(_get_brand, lambda x:x)
64 #------------------------------------------------------------
65 -def get_vaccines(order_by=None):
66 67 if order_by is None: 68 cmd = _sql_fetch_vaccine % u'TRUE' 69 else: 70 cmd = _sql_fetch_vaccine % (u'TRUE\nORDER BY %s' % order_by) 71 72 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 73 74 return [ cVaccine(row = {'data': r, 'idx': idx, 'pk_field': 'pk_vaccine'}) for r in rows ]
75 #============================================================ 76 #============================================================ 77 #============================================================ 78 # old code 79 #============================================================
80 -class cVaccination(gmBusinessDBObject.cBusinessDBObject):
81 """Represents one vaccination event. 82 """ 83 _cmd_fetch_payload = """ 84 select *, NULL as is_booster, -1 as seq_no, xmin_vaccination from clin.v_pat_vaccinations4indication 85 where pk_vaccination=%s 86 order by date desc""" 87 _cmds_lock_rows_for_update = [ 88 """select 1 from clin.vaccination where id=%(pk_vaccination)s and xmin=%(xmin_vaccination)s for update""" 89 ] 90 _cmds_store_payload = [ 91 """update clin.vaccination set 92 clin_when=%(date)s, 93 narrative=%(narrative)s, 94 fk_provider=%(pk_provider)s, 95 fk_vaccine=(select pk from clin.vaccine where trade_name=%(vaccine)s), 96 site=%(site)s, 97 batch_no=%(batch_no)s 98 where id=%(pk_vaccination)s""", 99 """select xmin_vaccination from clin.v_pat_vaccinations4indication where pk_vaccination=%(pk_vaccination)s""" 100 ] 101 _updatable_fields = [ 102 'date', 103 'narrative', 104 'pk_provider', 105 'vaccine', 106 'site', 107 'batch_no', 108 # the following two are updatable via __setitem__ 109 # API but not persisted via _cmds_store_payload 110 'is_booster', 111 'seq_no' 112 ] 113 #--------------------------------------------------------
114 - def _init_from_row_data(self, row=None):
115 """Make sure we have is_booster/seq_no when loading from row data.""" 116 gmBusinessDBObject.cBusinessDBObject._init_from_row_data(self, row=row) 117 try: 118 idx = self._idx['is_booster'] 119 except KeyError: 120 idx = len(self._payload) 121 self._payload.append(False) 122 # make local copy so we can safely modify it, but from 123 # self._idx which is row['idx'] with possible modifications 124 self._idx = copy.copy(self._idx) 125 self._idx['is_booster'] = idx 126 try: 127 idx = self._idx['seq_no'] 128 except KeyError: 129 idx = len(self._payload) 130 self._payload.append(False) 131 self._idx = copy.copy(self._idx) 132 self._idx['seq_no'] = -1
133 #--------------------------------------------------------
134 - def __setitem__(self, attribute, value):
135 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value) 136 if attribute in ['is_booster', 'seq_no']: 137 self._is_modified = False
138 # #-------------------------------------------------------- 139 # def get_next_shot_due(self): 140 # """ 141 # Retrieves next shot due date 142 # """ 143 # # FIXME: this will break due to not being initialized 144 # return self.__next_shot_due 145 # #-------------------------------------------------------- 146 # def set_next_shot_due(self, next_shot_due): 147 # """ 148 # Sets next shot due date 149 # 150 # * next_shot_due : Scheduled date for next vaccination shot 151 # """ 152 # self.__next_shot_due = next_shot_due 153 #============================================================
154 -class cMissingVaccination(gmBusinessDBObject.cBusinessDBObject):
155 """Represents one missing vaccination. 156 157 - can be due or overdue 158 """ 159 _cmd_fetch_payload = """ 160 (select *, False as overdue 161 from clin.v_pat_missing_vaccs vpmv 162 where 163 pk_patient=%(pat_id)s 164 and 165 (select dob from dem.identity where pk=%(pat_id)s) between (now() - age_due_min) and (now() - coalesce(age_due_max, '115 years'::interval)) 166 and 167 indication=%(indication)s 168 and 169 seq_no=%(seq_no)s 170 order by time_left) 171 172 UNION 173 174 (select *, True as overdue 175 from clin.v_pat_missing_vaccs vpmv 176 where 177 pk_patient=%(pat_id)s 178 and 179 now() - ((select dob from dem.identity where pk=%(pat_id)s)) > coalesce(age_due_max, '115 years'::interval) 180 and 181 indication=%(indication)s 182 and 183 seq_no=%(seq_no)s 184 order by amount_overdue)""" 185 _cmds_lock_rows_for_update = [] 186 _cmds_store_payload = ["""select 1"""] 187 _updatable_fields = [] 188 #--------------------------------------------------------
189 - def is_overdue(self):
190 return self['overdue']
191 #--------------------------------------------------------
192 - def create_vaccination(self):
193 # FIXME: create vaccination from myself, 194 # either pass in episode/encounter/vaccine id or use default for 195 # episode/encounter or use curr_pat.* if pk_patient=curr_pat, 196 # should we auto-destroy after create_vaccination() ? 197 return (False, 'not implemented')
198 #============================================================
199 -class cMissingBooster(gmBusinessDBObject.cBusinessDBObject):
200 """Represents one due booster. 201 """ 202 _cmd_fetch_payload = """ 203 select *, now() - amount_overdue as latest_due 204 from clin.v_pat_missing_boosters vpmb 205 where 206 pk_patient=%(pat_id)s 207 and 208 indication=%(indication)s 209 order by amount_overdue""" 210 _cmds_lock_rows_for_update = [] 211 _cmds_store_payload = ["""select 1"""] 212 _updatable_fields = []
213 #============================================================
214 -class cScheduledVaccination(gmBusinessDBObject.cBusinessDBObject):
215 """Represents one vaccination scheduled following a course. 216 """ 217 _cmd_fetch_payload = u"select * from clin.v_vaccs_scheduled4pat where pk_vacc_def=%s" 218 _cmds_lock_rows_for_update = [] 219 _cmds_store_payload = ["""select 1"""] 220 _updatable_fields = []
221 #============================================================
222 -class cVaccinationCourse(gmBusinessDBObject.cBusinessDBObject):
223 """Represents one vaccination course. 224 """ 225 _cmd_fetch_payload = """ 226 select *, xmin_vaccination_course from clin.v_vaccination_courses 227 where pk_course=%s""" 228 _cmds_lock_rows_for_update = [ 229 """select 1 from clin.vaccination_course where id=%(pk_course)s and xmin=%(xmin_vaccination_course)s for update""" 230 ] 231 _cmds_store_payload = [ 232 """update clin.vaccination_course set 233 name=%(course)s, 234 fk_recommended_by=%(pk_recommended_by)s, 235 fk_indication=(select id from clin.vacc_indication where description=%(indication)s), 236 comment=%(comment)s 237 where id=%(pk_course)s""", 238 """select xmin_vaccination_course from clin.v_vaccination_courses where pk_course=%(pk_course)s""" 239 ] 240 _updatable_fields = [ 241 'course', 242 'pk_recommended_by', 243 'indication', 244 'comment' 245 ]
246 #============================================================
247 -class VaccByRecommender:
248 _recommended_courses = None
249 #============================================================ 250 # convenience functions 251 #------------------------------------------------------------
252 -def create_vaccination(patient_id=None, episode_id=None, encounter_id=None, staff_id = None, vaccine=None):
253 # sanity check 254 # 1) any of the args being None should fail the SQL code 255 # 2) do episode/encounter belong to the patient ? 256 cmd = """ 257 select pk_patient 258 from clin.v_pat_episodes 259 where pk_episode=%s 260 union 261 select pk_patient 262 from clin.v_pat_encounters 263 where pk_encounter=%s""" 264 rows = gmPG.run_ro_query('historica', cmd, None, episode_id, encounter_id) 265 if (rows is None) or (len(rows) == 0): 266 _log.error('error checking episode [%s] <-> encounter [%s] consistency' % (episode_id, encounter_id)) 267 return (False, _('internal error, check log')) 268 if len(rows) > 1: 269 _log.error('episode [%s] and encounter [%s] belong to more than one patient !?!' % (episode_id, encounter_id)) 270 return (False, _('consistency error, check log')) 271 # insert new vaccination 272 queries = [] 273 if type(vaccine) == types.IntType: 274 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine) 275 values (%s, %s, %s, %s, %s)""" 276 else: 277 cmd = """insert into clin.vaccination (fk_encounter, fk_episode, fk_patient, fk_provider, fk_vaccine) 278 values (%s, %s, %s, %s, (select pk from clin.vaccine where trade_name=%s))""" 279 vaccine = str(vaccine) 280 queries.append((cmd, [encounter_id, episode_id, patient_id, staff_id, vaccine])) 281 # get PK of inserted row 282 cmd = "select currval('clin.vaccination_id_seq')" 283 queries.append((cmd, [])) 284 result, msg = gmPG.run_commit('historica', queries, True) 285 if (result is None) or (len(result) == 0): 286 return (False, msg) 287 try: 288 vacc = cVaccination(aPK_obj = result[0][0]) 289 except gmExceptions.ConstructorError: 290 _log.exception('cannot instantiate vaccination' % (result[0][0]), sys.exc_info, verbose=0) 291 return (False, _('internal error, check log')) 292 293 return (True, vacc)
294 #--------------------------------------------------------
295 -def get_vacc_courses():
296 # FIXME: use cVaccinationCourse 297 cmd = 'select name from clin.vaccination_course' 298 rows = gmPG.run_ro_query('historica', cmd) 299 if rows is None: 300 return None 301 if len(rows) == 0: 302 return [] 303 data = [] 304 for row in rows: 305 data.extend(rows) 306 return data
307 #--------------------------------------------------------
308 -def get_vacc_regimes_by_recommender_ordered(pk_patient=None, clear_cache=False):
309 # check DbC, if it fails exception is due 310 int(pk_patient) 311 312 cmd = """ 313 select fk_regime 314 from clin.lnk_pat2vacc_reg l 315 where l.fk_patient = %s""" % pk_patient 316 317 rows = gmPG.run_ro_query('historica', cmd) 318 active = [] 319 if rows and len(rows): 320 active = [ r[0] for r in rows] 321 322 # FIXME: this is patient dependant so how would a cache 323 # FIXME: work that's not taking into account pk_patient ? 324 # recommended_regimes = VaccByRecommender._recommended_regimes 325 # if not clear_cache and recommended_regimes: 326 # return recommended_regimes, active 327 328 r = ( {}, [] ) 329 330 # FIXME: use views ? 331 cmd = """ 332 select 333 r.pk_regime , 334 r.pk_recommended_by , 335 r.indication, 336 r.regime , 337 extract (epoch from d.min_age_due) /60/60/24, 338 extract (epoch from d.max_age_due) /60/60/24, 339 extract (epoch from d.min_interval ) /60/60/24, 340 d.seq_no 341 from 342 clin.v_vaccination_courses r, clin.vacc_def d 343 where 344 d.fk_regime = r.pk_regime 345 order by 346 r.pk_recommended_by, d.min_age_due""" 347 #print cmd 348 #import pdb 349 #pdb.set_trace() 350 # 351 rows = gmPG.run_ro_query('historica', cmd) 352 if rows is None: 353 VaccByRecommender._recommended_regimes = r 354 return r, active 355 356 row_fields = ['pk_regime', 'pk_recommender', 'indication' , 'regime', 'min_age_due', 'max_age_due', 'min_interval', 'seq_no' ] 357 358 for row in rows: 359 m = {} 360 for k, i in zip(row_fields, range(len(row))): 361 m[k] = row[i] 362 pk_recommender = m['pk_recommender'] 363 364 if not pk_recommender in r[0].keys(): 365 r[0][pk_recommender] = [] 366 r[1].append(pk_recommender) 367 r[0][pk_recommender].append(m) 368 369 for k, v in r[0].items(): 370 print k 371 for x in v: 372 print '\t', x 373 374 VaccByRecommender._recommended_regimes = r 375 return r, active
376 #--------------------------------------------------------
377 -def get_missing_vaccinations_ordered_min_due(pk_patient):
378 # DbC 379 int(pk_patient) 380 381 cmd = """ 382 select 383 indication, regime, 384 pk_regime, 385 pk_recommended_by, 386 seq_no , 387 extract(epoch from age_due_min) /60/60/24 as age_due_min, 388 extract(epoch from age_due_max) /60/60/24 as age_due_max, 389 extract(epoch from min_interval)/60/60/24 as min_interval 390 from 391 clin.v_pat_missing_vaccs 392 where pk_patient = %s 393 order by age_due_min, pk_recommended_by, indication 394 """ % pk_patient 395 396 rows = gmPG.run_ro_query('historica', cmd) 397 398 return rows
399 #--------------------------------------------------------
400 -def get_indications_from_vaccinations(vaccinations=None):
401 """Retrieves vaccination bundle indications list. 402 403 * vaccinations = list of any type of vaccination 404 - indicated 405 - due vacc 406 - overdue vaccs 407 - due boosters 408 - arbitrary 409 """ 410 # FIXME: can we not achieve this by: 411 # [lambda [vacc['indication'], ['l10n_indication']] for vacc in vaccination_list] 412 # I think we could, but we would be lacking error handling 413 if vaccinations is None: 414 _log.error('list of vaccinations must be supplied') 415 return (False, [['ERROR: list of vaccinations not supplied', _('ERROR: list of vaccinations not supplied')]]) 416 if len(vaccinations) == 0: 417 return (True, [['empty list of vaccinations', _('empty list of vaccinations')]]) 418 inds = [] 419 for vacc in vaccinations: 420 try: 421 inds.append([vacc['indication'], vacc['l10n_indication']]) 422 except KeyError: 423 try: 424 inds.append([vacc['indication'], vacc['indication']]) 425 except KeyError: 426 inds.append(['vacc -> ind error: %s' % str(vacc), _('vacc -> ind error: %s') % str(vacc)]) 427 return (True, inds)
428 #--------------------------------------------------------
429 -def put_patient_on_schedule(patient_id=None, course=None):
430 """ 431 Schedules a vaccination course for a patient 432 433 * patient_id = Patient's PK 434 * course = course object or Vaccination course's PK 435 """ 436 # FIXME: add method schedule_vaccination_course() to gmPerson.cPatient 437 if isinstance(course, cVaccinationCourse): 438 course_id = course['pk_course'] 439 else: 440 course_id = course 441 442 # insert new patient - vaccination course relation 443 queries = [] 444 cmd = """insert into clin.lnk_pat2vacc_reg (fk_patient, fk_course) 445 values (%s, %s)""" 446 queries.append((cmd, [patient_id, course_id])) 447 result, msg = gmPG.run_commit('historica', queries, True) 448 if result is None: 449 return (False, msg) 450 return (True, msg)
451 #--------------------------------------------------------
452 -def remove_patient_from_schedule(patient_id=None, course=None):
453 """unSchedules a vaccination course for a patient 454 455 * patient_id = Patient's PK 456 * course = course object or Vaccination course's PK 457 """ 458 # FIXME: add method schedule_vaccination_course() to gmPerson.cPatient 459 if isinstance(course, cVaccinationCourse): 460 course_id = course['pk_course'] 461 else: 462 course_id = course 463 464 # delete patient - vaccination course relation 465 queries = [] 466 cmd = """delete from clin.lnk_pat2vacc_reg where fk_patient = %s and fk_course = %s""" 467 468 queries.append((cmd, [patient_id, course_id])) 469 result, msg = gmPG.run_commit('historica', queries, True) 470 if result is None: 471 return (False, msg) 472 return (True, msg)
473 #--------------------------------------------------------
474 -def get_matching_vaccines_for_indications( all_ind):
475 476 quoted_inds = [ "'"+x + "%'" for x in all_ind] 477 478 # cmd_inds_per_vaccine = """ 479 # select count(v.trade_name) , v.trade_name 480 # from 481 # clin.vaccine v, clin.lnk_vaccine2inds l, clin.vacc_indication i 482 # where 483 # v.pk = l.fk_vaccine and l.fk_indication = i.id 484 # group 485 # by trade_name 486 # """ 487 488 cmd_inds_per_vaccine = """ 489 select 490 count(trade_name), 491 trade_name 492 from clin.v_inds4vaccine 493 group by trade_name""" 494 495 cmd_presence_in_vaccine = """ 496 select count(v.trade_name) , v.trade_name 497 498 from 499 clin.vaccine v, clin.lnk_vaccine2inds l, clin.vacc_indication i 500 where 501 v.pk = l.fk_vaccine and l.fk_indication = i.id 502 and 503 i.description like any ( array [ %s ] ) 504 group 505 506 by trade_name 507 508 """ % ', '.join( quoted_inds ) 509 510 inds_per_vaccine = gmPG.run_ro_query( 'historica', cmd_inds_per_vaccine) 511 512 presence_in_vaccine = gmPG.run_ro_query( 'historica', cmd_presence_in_vaccine) 513 514 map_vacc_count_inds = dict ( [ (x[1], x[0]) for x in inds_per_vaccine ] ) 515 516 matched_vaccines = [] 517 for (presence, vaccine) in presence_in_vaccine: 518 if presence == len ( all_ind) : 519 # matched the number of indications selected with a vaccine 520 # is this also ALL the vaccine's indications ? 521 if map_vacc_count_inds[vaccine] == presence: 522 matched_vaccines.append(vaccine) 523 return matched_vaccines
524 #============================================================ 525 # main - unit testing 526 #------------------------------------------------------------ 527 if __name__ == '__main__': 528 529 if len(sys.argv) < 2: 530 sys.exit() 531 532 if sys.argv[1] != u'test': 533 sys.exit() 534 535 # from Gnumed.pycommon import gmPG 536 #--------------------------------------------------------
537 - def test_vacc():
538 vacc = cVaccination(aPK_obj=1) 539 print vacc 540 fields = vacc.get_fields() 541 for field in fields: 542 print field, ':', vacc[field] 543 print "updatable:", vacc.get_updatable_fields()
544 #--------------------------------------------------------
545 - def test_due_vacc():
546 # Test for a due vaccination 547 pk_args = { 548 'pat_id': 12, 549 'indication': 'meningococcus C', 550 'seq_no': 1 551 } 552 missing_vacc = cMissingVaccination(aPK_obj=pk_args) 553 fields = missing_vacc.get_fields() 554 print "\nDue vaccination:" 555 print missing_vacc 556 for field in fields: 557 print field, ':', missing_vacc[field] 558 # Test for an overdue vaccination 559 pk_args = { 560 'pat_id': 12, 561 'indication': 'haemophilus influenzae b', 562 'seq_no': 2 563 } 564 missing_vacc = cMissingVaccination(aPK_obj=pk_args) 565 fields = missing_vacc.get_fields() 566 print "\nOverdue vaccination (?):" 567 print missing_vacc 568 for field in fields: 569 print field, ':', missing_vacc[field]
570 #--------------------------------------------------------
571 - def test_due_booster():
572 pk_args = { 573 'pat_id': 12, 574 'indication': 'tetanus' 575 } 576 missing_booster = cMissingBooster(aPK_obj=pk_args) 577 fields = missing_booster.get_fields() 578 print "\nDue booster:" 579 print missing_booster 580 for field in fields: 581 print field, ':', missing_booster[field]
582 #--------------------------------------------------------
583 - def test_scheduled_vacc():
584 scheduled_vacc = cScheduledVaccination(aPK_obj=20) 585 print "\nScheduled vaccination:" 586 print scheduled_vacc 587 fields = scheduled_vacc.get_fields() 588 for field in fields: 589 print field, ':', scheduled_vacc[field] 590 print "updatable:", scheduled_vacc.get_updatable_fields()
591 #--------------------------------------------------------
592 - def test_vaccination_course():
593 vaccination_course = cVaccinationCourse(aPK_obj=7) 594 print "\nVaccination course:" 595 print vaccination_course 596 fields = vaccination_course.get_fields() 597 for field in fields: 598 print field, ':', vaccination_course[field] 599 print "updatable:", vaccination_course.get_updatable_fields()
600 #--------------------------------------------------------
601 - def test_put_patient_on_schedule():
602 result, msg = put_patient_on_schedule(patient_id=12, course_id=1) 603 print '\nPutting patient id 12 on schedule id 1... %s (%s)' % (result, msg)
604 #--------------------------------------------------------
605 - def test_get_vaccines():
606 607 for vaccine in get_vaccines(): 608 print vaccine
609 610 #-------------------------------------------------------- 611 #test_vaccination_course() 612 #test_put_patient_on_schedule() 613 #test_scheduled_vacc() 614 #test_vacc() 615 #test_due_vacc() 616 #test_due_booster() 617 618 test_get_vaccines() 619 #============================================================ 620