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

Source Code for Module Gnumed.business.gmPathLab

   1  """GNUmed measurements related business objects.""" 
   2  #============================================================ 
   3  # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/business/gmPathLab.py,v $ 
   4  # $Id: gmPathLab.py,v 1.81 2010-02-06 20:45:44 ncq Exp $ 
   5  __version__ = "$Revision: 1.81 $" 
   6  __author__ = "K.Hilbert <Karsten.Hilbert@gmx.net>" 
   7  __license__ = "GPL" 
   8   
   9   
  10  import types, sys, logging 
  11   
  12   
  13  if __name__ == '__main__': 
  14          sys.path.insert(0, '../../') 
  15          from Gnumed.pycommon import gmLog2, gmDateTime, gmI18N 
  16          gmDateTime.init() 
  17          gmI18N.activate_locale() 
  18  from Gnumed.pycommon import gmExceptions, gmBusinessDBObject, gmPG2, gmTools 
  19  from Gnumed.pycommon import gmDispatcher 
  20   
  21   
  22  _log = logging.getLogger('gm.lab') 
  23  _log.info(__version__) 
  24   
  25  # FIXME: use UCUM from Regenstrief Institute 
  26   
  27  #============================================================ 
28 -def _on_test_result_modified():
29 """Always relates to the active patient.""" 30 gmHooks.run_hook_script(hook = u'after_test_result_modified')
31 32 gmDispatcher.connect(_on_test_result_modified, u'test_result_mod_db') 33 34 #============================================================
35 -class cTestOrg(gmBusinessDBObject.cBusinessDBObject):
36 """Represents one test org/lab.""" 37 38 _cmd_fetch_payload = u"""SELECT *, xmin FROM clin.test_org WHERE pk = %s""" 39 40 _cmds_store_payload = [ 41 u"""UPDATE clin.test_org SET 42 internal_name = gm.nullify_empty_string(%(internal_name)s), 43 contact = gm.nullify_empty_string(%(contact)s), 44 comment = gm.nullify_empty_string(%(comment)s) 45 WHERE 46 pk = %(pk)s 47 AND 48 xmin = %(xmin)s 49 RETURNING 50 xmin 51 """ 52 ] 53 54 _updatable_fields = [ 55 u'internal_name', 56 u'contact', 57 u'comment' 58 ]
59 #------------------------------------------------------------
60 -def create_test_org(name=None):
61 cmd = u'insert into clin.test_org (internal_name) values (%(name)s) returning pk' 62 args = {'name': name.strip()} 63 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}], return_data = True) 64 return cTestOrg(aPK_obj = rows[0]['pk'])
65 #------------------------------------------------------------
66 -def get_test_orgs(order_by=u'internal_name'):
67 cmd = u'select *, xmin from clin.test_org order by %s' % order_by 68 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 69 return [ cTestOrg(row = {'pk_field': 'pk', 'data': r, 'idx': idx}) for r in rows ]
70 #============================================================
71 -class cMetaTestType(gmBusinessDBObject.cBusinessDBObject):
72 """Represents one meta test type under which actual test types can be aggregated.""" 73 74 _cmd_fetch_payload = u"""select * from clin.meta_test_type where pk = %s""" 75 76 _cmds_store_payload = [] 77 78 _updatable_fields = []
79 #------------------------------------------------------------
80 -def delete_meta_type(meta_type=None):
81 cmd = u'delete from clin.meta_test_type where pk = %(pk)s' 82 args = {'pk': meta_type} 83 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
84 #------------------------------------------------------------
85 -def get_meta_test_types():
86 cmd = u'select * from clin.meta_test_type' 87 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 88 return [ cMetaTestType(row = {'pk_field': 'pk', 'data': r, 'idx': idx}) for r in rows ]
89 #============================================================
90 -class cUnifiedTestType(gmBusinessDBObject.cBusinessDBObject):
91 """Represents one unified test type.""" 92 93 # FIXME: if we ever want to write we need to include XMIN in the view 94 _cmd_fetch_payload = u"""select * from clin.v_unified_test_types where pk_test_type = %s""" 95 96 _cmds_store_payload = [] 97 98 _updatable_fields = [] 99 #--------------------------------------------------------
100 - def get_most_recent_result(self, pk_patient=None):
101 cmd = u""" 102 SELECT pk_test_result, clin_when 103 FROM clin.v_test_results 104 WHERE 105 pk_patient = %(pat)s 106 AND 107 pk_meta_test_type = %(pkmtt)s 108 ORDER BY clin_when DESC 109 LIMIT 1 110 """ 111 args = {'pat': pk_patient, 'pkmtt': self._payload[self._idx['pk_meta_test_type']]} 112 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 113 if len(rows) == 0: 114 return None 115 return cTestResult(aPK_obj = rows[0]['pk_test_result'])
116 #============================================================
117 -class cMeasurementType(gmBusinessDBObject.cBusinessDBObject):
118 """Represents one test result type.""" 119 120 _cmd_fetch_payload = u"""select * from clin.v_test_types where pk_test_type = %s""" 121 122 _cmds_store_payload = [ 123 u"""update clin.test_type set 124 abbrev = %(abbrev)s, 125 name = %(name)s, 126 loinc = gm.nullify_empty_string(%(loinc)s), 127 code = gm.nullify_empty_string(%(code)s), 128 coding_system = gm.nullify_empty_string(%(coding_system)s), 129 comment = gm.nullify_empty_string(%(comment_type)s), 130 conversion_unit = gm.nullify_empty_string(%(conversion_unit)s), 131 fk_test_org = %(pk_test_org)s 132 where pk = %(pk_test_type)s""", 133 u"""select xmin_test_type from clin.v_test_types where pk_test_type = %(pk_test_type)s""" 134 ] 135 136 _updatable_fields = [ 137 'abbrev', 138 'name', 139 'loinc', 140 'code', 141 'coding_system', 142 'comment_type', 143 'conversion_unit', 144 'pk_test_org' 145 ] 146 #--------------------------------------------------------
147 - def __setitem__(self, attribute, value):
148 149 # find fk_test_org from name 150 if (attribute == 'fk_test_org') and (value is not None): 151 try: 152 int(value) 153 except: 154 cmd = u"select pk from clin.test_org where internal_name = %(val)s" 155 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'val': value}}]) 156 if len(rows) == 0: 157 raise ValueError('[%s]: no test org for [%s], cannot set <%s>' % (self.__class__.__name__, value, attribute)) 158 value = rows[0][0] 159 160 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
161 #--------------------------------------------------------
162 - def _get_in_use(self):
163 cmd = u'select exists(select 1 from clin.test_result where fk_type = %(pk_type)s)' 164 args = {'pk_type': self._payload[self._idx['pk_test_type']]} 165 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 166 return rows[0][0]
167 168 in_use = property(_get_in_use, lambda x:x)
169 #------------------------------------------------------------
170 -def get_measurement_types(order_by=None):
171 cmd = u'select * from clin.v_test_types %s' % gmTools.coalesce(order_by, u'', u'order by %s') 172 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}], get_col_idx = True) 173 return [ cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': r, 'idx': idx}) for r in rows ]
174 #------------------------------------------------------------
175 -def find_measurement_type(lab=None, abbrev=None, name=None):
176 177 if (abbrev is None) and (name is None): 178 raise ArgumentError('must have <abbrev> and/or <name> set') 179 180 where_snippets = [] 181 182 if lab is None: 183 where_snippets.append('pk_test_org is null') 184 else: 185 try: 186 int(lab) 187 where_snippets.append('pk_test_org = %(lab)s') 188 except (TypeError, ValueError): 189 where_snippets.append('pk_test_org = (select pk from clin.test_org where internal_name = %(lab)s)') 190 191 if abbrev is not None: 192 where_snippets.append('abbrev = %(abbrev)s') 193 194 if name is not None: 195 where_snippets.append('name = %(name)s') 196 197 where_clause = u' and '.join(where_snippets) 198 cmd = u"select * from clin.v_test_types where %s" % where_clause 199 args = {'lab': lab, 'abbrev': abbrev, 'name': name} 200 201 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 202 203 if len(rows) == 0: 204 return None 205 206 tt = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx}) 207 return tt
208 #------------------------------------------------------------
209 -def delete_measurement_type(measurement_type=None):
210 cmd = u'delete from clin.test_type where pk = %(pk)s' 211 args = {'pk': measurement_type} 212 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
213 #------------------------------------------------------------
214 -def create_measurement_type(lab=None, abbrev=None, unit=None, name=None):
215 """Create or get test type.""" 216 217 ttype = find_measurement_type(lab = lab, abbrev = abbrev, name = name) 218 # found ? 219 if ttype is not None: 220 return ttype 221 222 _log.debug('creating test type [%s:%s:%s:%s]', lab, abbrev, name, unit) 223 224 # not found, so create it 225 if unit is None: 226 _log.error('need <unit> to create test type: %s:%s:%s:%s' % (lab, abbrev, name, unit)) 227 raise ValueError('need <unit> to create test type') 228 229 # make query 230 cols = [] 231 val_snippets = [] 232 vals = {} 233 234 # lab 235 if lab is None: 236 lab = create_measurement_org() 237 238 cols.append('fk_test_org') 239 try: 240 vals['lab'] = int(lab) 241 val_snippets.append('%(lab)s') 242 except: 243 vals['lab'] = lab 244 val_snippets.append('(select pk from clin.test_org where internal_name = %(lab)s)') 245 246 # code 247 cols.append('abbrev') 248 val_snippets.append('%(abbrev)s') 249 vals['abbrev'] = abbrev 250 251 # unit 252 cols.append('conversion_unit') 253 val_snippets.append('%(unit)s') 254 vals['unit'] = unit 255 256 # name 257 if name is not None: 258 cols.append('name') 259 val_snippets.append('%(name)s') 260 vals['name'] = name 261 262 col_clause = u', '.join(cols) 263 val_clause = u', '.join(val_snippets) 264 queries = [ 265 {'cmd': u'insert into clin.test_type (%s) values (%s)' % (col_clause, val_clause), 'args': vals}, 266 {'cmd': u"select * from clin.v_test_types where pk_test_type = currval(pg_get_serial_sequence('clin.test_type', 'pk'))"} 267 ] 268 rows, idx = gmPG2.run_rw_queries(queries = queries, get_col_idx = True, return_data = True) 269 ttype = cMeasurementType(row = {'pk_field': 'pk_test_type', 'data': rows[0], 'idx': idx}) 270 271 return ttype
272 #------------------------------------------------------------
273 -def create_measurement_org(name=None, comment=None):
274 275 if name is None: 276 name = _('inhouse lab') 277 comment = _('auto-generated') 278 279 cmd = u'select * from clin.test_org where internal_name = %(name)s' 280 if comment is not None: 281 comment = comment.strip() 282 args = {'name': name, 'cmt': comment} 283 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 284 285 if len(rows) == 0: 286 queries = [ 287 {'cmd': u'insert into clin.test_org (fk_org, internal_name, comment) values (null, %(name)s, %(cmt)s)', 'args': args}, 288 {'cmd': u"select currval(pg_get_serial_sequence('clin.test_org', 'pk')) as pk"} 289 ] 290 else: 291 # use 1st result only, ignore if more than one 292 args['pk'] = rows[0]['pk'] 293 queries = [ 294 {'cmd': u'update clin.test_org set comment = %(cmt)s where pk = %(pk)s', 'args': args}, 295 {'cmd': u'select %(pk)s as pk', 'args': args} 296 ] 297 298 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 299 300 return rows[0]['pk']
301 #============================================================
302 -class cTestResult(gmBusinessDBObject.cBusinessDBObject):
303 """Represents one test result.""" 304 305 _cmd_fetch_payload = u"select * from clin.v_test_results where pk_test_result = %s" 306 307 _cmds_store_payload = [ 308 u"""update clin.test_result set 309 clin_when = %(clin_when)s, 310 narrative = nullif(trim(%(comment)s), ''), 311 val_num = %(val_num)s, 312 val_alpha = nullif(trim(%(val_alpha)s), ''), 313 val_unit = nullif(trim(%(val_unit)s), ''), 314 val_normal_min = %(val_normal_min)s, 315 val_normal_max = %(val_normal_max)s, 316 val_normal_range = nullif(trim(%(val_normal_range)s), ''), 317 val_target_min = %(val_target_min)s, 318 val_target_max = %(val_target_max)s, 319 val_target_range = nullif(trim(%(val_target_range)s), ''), 320 abnormality_indicator = nullif(trim(%(abnormality_indicator)s), ''), 321 norm_ref_group = nullif(trim(%(norm_ref_group)s), ''), 322 note_test_org = nullif(trim(%(note_test_org)s), ''), 323 material = nullif(trim(%(material)s), ''), 324 material_detail = nullif(trim(%(material_detail)s), ''), 325 fk_intended_reviewer = %(pk_intended_reviewer)s, 326 fk_encounter = %(pk_encounter)s, 327 fk_episode = %(pk_episode)s, 328 fk_type = %(pk_test_type)s 329 where 330 pk = %(pk_test_result)s and 331 xmin = %(xmin_test_result)s""", 332 u"""select xmin_test_result from clin.v_test_results where pk_test_result = %(pk_test_result)s""" 333 ] 334 335 _updatable_fields = [ 336 'clin_when', 337 'comment', 338 'val_num', 339 'val_alpha', 340 'val_unit', 341 'val_normal_min', 342 'val_normal_max', 343 'val_normal_range', 344 'val_target_min', 345 'val_target_max', 346 'val_target_range', 347 'abnormality_indicator', 348 'norm_ref_group', 349 'note_test_org', 350 'material', 351 'material_detail', 352 'pk_intended_reviewer', 353 'pk_encounter', 354 'pk_episode', 355 'pk_test_type' 356 ] 357 #--------------------------------------------------------
358 - def format(self, with_review=True, with_comments=True, date_format='%Y-%m-%d %H:%M'):
359 360 lines = [] 361 362 lines.append(u' %s %s (%s): %s %s%s' % ( 363 self._payload[self._idx['clin_when']].strftime(date_format), 364 self._payload[self._idx['unified_abbrev']], 365 self._payload[self._idx['unified_name']], 366 self._payload[self._idx['unified_val']], 367 self._payload[self._idx['val_unit']], 368 gmTools.coalesce(self._payload[self._idx['abnormality_indicator']], u'', u' (%s)') 369 )) 370 371 if with_comments: 372 if gmTools.coalesce(self._payload[self._idx['comment']], u'').strip() != u'': 373 lines.append(_(' Doc: %s') % self._payload[self._idx['comment']].strip()) 374 if gmTools.coalesce(self._payload[self._idx['note_test_org']], u'').strip() != u'': 375 lines.append(_(' MTA: %s') % self._payload[self._idx['note_test_org']].strip()) 376 377 if with_review: 378 if self._payload[self._idx['reviewed']]: 379 if self._payload[self._idx['is_clinically_relevant']]: 380 lines.append(u' %s %s: %s' % ( 381 self._payload[self._idx['last_reviewer']], 382 self._payload[self._idx['last_reviewed']].strftime('%Y-%m-%d %H:%M'), 383 gmTools.bool2subst ( 384 self._payload[self._idx['is_technically_abnormal']], 385 _('abnormal and relevant'), 386 _('normal but relevant') 387 ) 388 )) 389 else: 390 lines.append(_(' unreviewed')) 391 392 return lines
393 #--------------------------------------------------------
394 - def _get_reference_ranges(self):
395 396 cmd = u""" 397 select 398 distinct on (norm_ref_group_str, val_unit, val_normal_min, val_normal_max, val_normal_range, val_target_min, val_target_max, val_target_range) 399 pk_patient, 400 val_unit, 401 val_normal_min, val_normal_max, val_normal_range, 402 val_target_min, val_target_max, val_target_range, 403 norm_ref_group, 404 coalesce(norm_ref_group, '') as norm_ref_group_str 405 from 406 clin.v_test_results 407 where 408 pk_test_type = %(pk_type)s 409 """ 410 args = {'pk_type': self._payload[self._idx['pk_test_type']]} 411 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 412 return rows
413
414 - def _set_reference_ranges(self, val):
415 raise AttributeError('[%s]: reference ranges not settable') % self.__class__.__name__
416 417 reference_ranges = property(_get_reference_ranges, _set_reference_ranges) 418 #--------------------------------------------------------
419 - def set_review(self, technically_abnormal=None, clinically_relevant=None, comment=None, make_me_responsible=False):
420 421 if comment is not None: 422 comment = comment.strip() 423 424 if ((technically_abnormal is None) and 425 (clinically_relevant is None) and 426 (comment is None) and 427 (make_me_responsible is False)): 428 return True 429 430 # FIXME: this is not concurrency safe 431 if self._payload[self._idx['reviewed']]: 432 self.__change_existing_review ( 433 technically_abnormal = technically_abnormal, 434 clinically_relevant = clinically_relevant, 435 comment = comment 436 ) 437 else: 438 self.__set_new_review ( 439 technically_abnormal = technically_abnormal, 440 clinically_relevant = clinically_relevant, 441 comment = comment 442 ) 443 444 if make_me_responsible is True: 445 cmd = u"select pk from dem.staff where db_user = current_user" 446 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 447 self['pk_intended_reviewer'] = rows[0][0] 448 self.save_payload() 449 else: 450 self.refetch_payload()
451 #-------------------------------------------------------- 452 # internal API 453 #--------------------------------------------------------
454 - def __set_new_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
455 """Add a review to a row. 456 457 - if technically abnormal is not provided/None it will be set 458 to True if the lab's indicator has a meaningful value 459 - if clinically relevant is not provided/None it is set to 460 whatever technically abnormal is 461 """ 462 if technically_abnormal is None: 463 technically_abnormal = False 464 if self._payload[self._idx['abnormality_indicator']] is not None: 465 if self._payload[self._idx['abnormality_indicator']].strip() != u'': 466 technically_abnormal = True 467 468 if clinically_relevant is None: 469 clinically_relevant = technically_abnormal 470 471 cmd = u""" 472 insert into clin.reviewed_test_results ( 473 fk_reviewed_row, 474 is_technically_abnormal, 475 clinically_relevant, 476 comment 477 ) values ( 478 %(pk)s, 479 %(abnormal)s, 480 %(relevant)s, 481 %(cmt)s 482 )""" 483 args = { 484 'pk': self._payload[self._idx['pk_test_result']], 485 'abnormal': technically_abnormal, 486 'relevant': clinically_relevant, 487 'cmt': comment 488 } 489 490 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
491 #--------------------------------------------------------
492 - def __change_existing_review(self, technically_abnormal=None, clinically_relevant=None, comment=None):
493 """Change a review on a row. 494 495 - if technically abnormal/clinically relevant/comment are 496 None (or empty) they are not set 497 """ 498 args = { 499 'pk_row': self._payload[self._idx['pk_test_result']], 500 'abnormal': technically_abnormal, 501 'relevant': clinically_relevant 502 } 503 504 set_parts = [] 505 506 if technically_abnormal is not None: 507 set_parts.append(u'is_technically_abnormal = %(abnormal)s') 508 509 if clinically_relevant is not None: 510 set_parts.append(u'clinically_relevant= %(relevant)s') 511 512 if comment is not None: 513 set_parts.append('comment = %(cmt)s') 514 args['cmt'] = comment 515 516 cmd = u""" 517 update clin.reviewed_test_results set 518 fk_reviewer = (select pk from dem.staff where db_user = current_user), 519 %s 520 where 521 fk_reviewed_row = %%(pk_row)s 522 """ % u',\n '.join(set_parts) 523 524 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}])
525 #------------------------------------------------------------
526 -def delete_test_result(result=None):
527 528 try: 529 pk = int(result) 530 except (TypeError, AttributeError): 531 pk = result['pk_test_result'] 532 533 cmd = u'delete from clin.test_result where pk = %(pk)s' 534 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': {'pk': pk}}])
535 #------------------------------------------------------------
536 -def create_test_result(encounter=None, episode=None, type=None, intended_reviewer=None, val_num=None, val_alpha=None, unit=None):
537 538 cmd1 = u""" 539 insert into clin.test_result ( 540 fk_encounter, 541 fk_episode, 542 fk_type, 543 fk_intended_reviewer, 544 val_num, 545 val_alpha, 546 val_unit 547 ) values ( 548 %(enc)s, 549 %(epi)s, 550 %(type)s, 551 %(rev)s, 552 %(v_num)s, 553 %(v_alpha)s, 554 %(unit)s 555 )""" 556 557 cmd2 = u""" 558 select * 559 from 560 clin.v_test_results 561 where 562 pk_test_result = currval(pg_get_serial_sequence('clin.test_result', 'pk'))""" 563 564 args = { 565 u'enc': encounter, 566 u'epi': episode, 567 u'type': type, 568 u'rev': intended_reviewer, 569 u'v_num': val_num, 570 u'v_alpha': val_alpha, 571 u'unit': unit 572 } 573 574 rows, idx = gmPG2.run_rw_queries ( 575 queries = [ 576 {'cmd': cmd1, 'args': args}, 577 {'cmd': cmd2} 578 ], 579 return_data = True, 580 get_col_idx = True 581 ) 582 583 tr = cTestResult(row = { 584 'pk_field': 'pk_test_result', 585 'idx': idx, 586 'data': rows[0] 587 }) 588 589 return tr
590 #============================================================
591 -class cLabResult(gmBusinessDBObject.cBusinessDBObject):
592 """Represents one lab result.""" 593 594 _cmd_fetch_payload = """ 595 select *, xmin_test_result from v_results4lab_req 596 where pk_result=%s""" 597 _cmds_lock_rows_for_update = [ 598 """select 1 from test_result where pk=%(pk_result)s and xmin=%(xmin_test_result)s for update""" 599 ] 600 _cmds_store_payload = [ 601 """update test_result set 602 clin_when = %(val_when)s, 603 narrative = %(progress_note_result)s, 604 fk_type = %(pk_test_type)s, 605 val_num = %(val_num)s::numeric, 606 val_alpha = %(val_alpha)s, 607 val_unit = %(val_unit)s, 608 val_normal_min = %(val_normal_min)s, 609 val_normal_max = %(val_normal_max)s, 610 val_normal_range = %(val_normal_range)s, 611 val_target_min = %(val_target_min)s, 612 val_target_max = %(val_target_max)s, 613 val_target_range = %(val_target_range)s, 614 abnormality_indicator = %(abnormal)s, 615 norm_ref_group = %(ref_group)s, 616 note_provider = %(note_provider)s, 617 material = %(material)s, 618 material_detail = %(material_detail)s 619 where pk = %(pk_result)s""", 620 """select xmin_test_result from v_results4lab_req where pk_result=%(pk_result)s""" 621 ] 622 623 _updatable_fields = [ 624 'val_when', 625 'progress_note_result', 626 'val_num', 627 'val_alpha', 628 'val_unit', 629 'val_normal_min', 630 'val_normal_max', 631 'val_normal_range', 632 'val_target_min', 633 'val_target_max', 634 'val_target_range', 635 'abnormal', 636 'ref_group', 637 'note_provider', 638 'material', 639 'material_detail' 640 ] 641 #--------------------------------------------------------
642 - def __init__(self, aPK_obj=None, row=None):
643 """Instantiate. 644 645 aPK_obj as dict: 646 - patient_id 647 - when_field (see view definition) 648 - when 649 - test_type 650 - val_num 651 - val_alpha 652 - unit 653 """ 654 # instantiate from row data ? 655 if aPK_obj is None: 656 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row) 657 return 658 pk = aPK_obj 659 # find PK from row data ? 660 if type(aPK_obj) == types.DictType: 661 # sanity checks 662 if None in [aPK_obj['patient_id'], aPK_obj['when'], aPK_obj['when_field'], aPK_obj['test_type'], aPK_obj['unit']]: 663 raise gmExceptions.ConstructorError, 'parameter error: %s' % aPK_obj 664 if (aPK_obj['val_num'] is None) and (aPK_obj['val_alpha'] is None): 665 raise gmExceptions.ConstructorError, 'parameter error: val_num and val_alpha cannot both be None' 666 # get PK 667 where_snippets = [ 668 'pk_patient=%(patient_id)s', 669 'pk_test_type=%(test_type)s', 670 '%s=%%(when)s' % aPK_obj['when_field'], 671 'val_unit=%(unit)s' 672 ] 673 if aPK_obj['val_num'] is not None: 674 where_snippets.append('val_num=%(val_num)s::numeric') 675 if aPK_obj['val_alpha'] is not None: 676 where_snippets.append('val_alpha=%(val_alpha)s') 677 678 where_clause = ' and '.join(where_snippets) 679 cmd = "select pk_result from v_results4lab_req where %s" % where_clause 680 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj) 681 if data is None: 682 raise gmExceptions.ConstructorError, 'error getting lab result for: %s' % aPK_obj 683 if len(data) == 0: 684 raise gmExceptions.NoSuchClinItemError, 'no lab result for: %s' % aPK_obj 685 pk = data[0][0] 686 # instantiate class 687 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
688 #--------------------------------------------------------
689 - def get_patient(self):
690 cmd = """ 691 select 692 %s, 693 vbp.title, 694 vbp.firstnames, 695 vbp.lastnames, 696 vbp.dob 697 from v_basic_person vbp 698 where vbp.pk_identity=%%s""" % self._payload[self._idx['pk_patient']] 699 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_patient']]) 700 return pat[0]
701 #============================================================
702 -class cLabRequest(gmBusinessDBObject.cBusinessDBObject):
703 """Represents one lab request.""" 704 705 _cmd_fetch_payload = """ 706 select *, xmin_lab_request from v_lab_requests 707 where pk_request=%s""" 708 _cmds_lock_rows_for_update = [ 709 """select 1 from lab_request where pk=%(pk_request)s and xmin=%(xmin_lab_request)s for update""" 710 ] 711 _cmds_store_payload = [ 712 """update lab_request set 713 request_id=%(request_id)s, 714 lab_request_id=%(lab_request_id)s, 715 clin_when=%(sampled_when)s, 716 lab_rxd_when=%(lab_rxd_when)s, 717 results_reported_when=%(results_reported_when)s, 718 request_status=%(request_status)s, 719 is_pending=%(is_pending)s::bool, 720 narrative=%(progress_note)s 721 where pk=%(pk_request)s""", 722 """select xmin_lab_request from v_lab_requests where pk_request=%(pk_request)s""" 723 ] 724 _updatable_fields = [ 725 'request_id', 726 'lab_request_id', 727 'sampled_when', 728 'lab_rxd_when', 729 'results_reported_when', 730 'request_status', 731 'is_pending', 732 'progress_note' 733 ] 734 #--------------------------------------------------------
735 - def __init__(self, aPK_obj=None, row=None):
736 """Instantiate lab request. 737 738 The aPK_obj can be either a dict with the keys "req_id" 739 and "lab" or a simple primary key. 740 """ 741 # instantiate from row data ? 742 if aPK_obj is None: 743 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=row) 744 return 745 pk = aPK_obj 746 # instantiate from "req_id" and "lab" ? 747 if type(aPK_obj) == types.DictType: 748 # sanity check 749 try: 750 aPK_obj['req_id'] 751 aPK_obj['lab'] 752 except: 753 _log.exception('[%s:??]: faulty <aPK_obj> structure: [%s]' % (self.__class__.__name__, aPK_obj), sys.exc_info()) 754 raise gmExceptions.ConstructorError, '[%s:??]: cannot derive PK from [%s]' % (self.__class__.__name__, aPK_obj) 755 # generate query 756 where_snippets = [] 757 vals = {} 758 where_snippets.append('request_id=%(req_id)s') 759 if type(aPK_obj['lab']) == types.IntType: 760 where_snippets.append('pk_test_org=%(lab)s') 761 else: 762 where_snippets.append('lab_name=%(lab)s') 763 where_clause = ' and '.join(where_snippets) 764 cmd = "select pk_request from v_lab_requests where %s" % where_clause 765 # get pk 766 data = gmPG.run_ro_query('historica', cmd, None, aPK_obj) 767 if data is None: 768 raise gmExceptions.ConstructorError, '[%s:??]: error getting lab request for [%s]' % (self.__class__.__name__, aPK_obj) 769 if len(data) == 0: 770 raise gmExceptions.NoSuchClinItemError, '[%s:??]: no lab request for [%s]' % (self.__class__.__name__, aPK_obj) 771 pk = data[0][0] 772 # instantiate class 773 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
774 #--------------------------------------------------------
775 - def get_patient(self):
776 cmd = """ 777 select vpi.pk_patient, vbp.title, vbp.firstnames, vbp.lastnames, vbp.dob 778 from v_pat_items vpi, v_basic_person vbp 779 where 780 vpi.pk_item=%s 781 and 782 vbp.pk_identity=vpi.pk_patient""" 783 pat = gmPG.run_ro_query('historica', cmd, None, self._payload[self._idx['pk_item']]) 784 if pat is None: 785 _log.error('cannot get patient for lab request [%s]' % self._payload[self._idx['pk_item']]) 786 return None 787 if len(pat) == 0: 788 _log.error('no patient associated with lab request [%s]' % self._payload[self._idx['pk_item']]) 789 return None 790 return pat[0]
791 #============================================================ 792 # convenience functions 793 #------------------------------------------------------------
794 -def create_lab_request(lab=None, req_id=None, pat_id=None, encounter_id=None, episode_id=None):
795 """Create or get lab request. 796 797 returns tuple (status, value): 798 (True, lab request instance) 799 (False, error message) 800 (None, housekeeping_todo primary key) 801 """ 802 req = None 803 aPK_obj = { 804 'lab': lab, 805 'req_id': req_id 806 } 807 try: 808 req = cLabRequest (aPK_obj) 809 except gmExceptions.NoSuchClinItemError, msg: 810 _log.info('%s: will try to create lab request' % str(msg)) 811 except gmExceptions.ConstructorError, msg: 812 _log.exception(str(msg), sys.exc_info(), verbose=0) 813 return (False, msg) 814 # found 815 if req is not None: 816 db_pat = req.get_patient() 817 if db_pat is None: 818 _log.error('cannot cross-check patient on lab request') 819 return (None, '') 820 # yes but ambigous 821 if pat_id != db_pat[0]: 822 _log.error('lab request found for [%s:%s] but patient mismatch: expected [%s], in DB [%s]' % (lab, req_id, pat_id, db_pat)) 823 me = '$RCSfile: gmPathLab.py,v $ $Revision: 1.81 $' 824 to = 'user' 825 prob = _('The lab request already exists but belongs to a different patient.') 826 sol = _('Verify which patient this lab request really belongs to.') 827 ctxt = _('lab [%s], request ID [%s], expected link with patient [%s], currently linked to patient [%s]') % (lab, req_id, pat_id, db_pat) 828 cat = 'lab' 829 status, data = gmPG.add_housekeeping_todo(me, to, prob, sol, ctxt, cat) 830 return (None, data) 831 return (True, req) 832 # not found 833 queries = [] 834 if type(lab) is types.IntType: 835 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, %s, %s)" 836 else: 837 cmd = "insert into lab_request (fk_encounter, fk_episode, fk_test_org, request_id) values (%s, %s, (select pk from test_org where internal_name=%s), %s)" 838 queries.append((cmd, [encounter_id, episode_id, str(lab), req_id])) 839 cmd = "select currval('lab_request_pk_seq')" 840 queries.append((cmd, [])) 841 # insert new 842 result, err = gmPG.run_commit('historica', queries, True) 843 if result is None: 844 return (False, err) 845 try: 846 req = cLabRequest(aPK_obj=result[0][0]) 847 except gmExceptions.ConstructorError, msg: 848 _log.exception(str(msg), sys.exc_info(), verbose=0) 849 return (False, msg) 850 return (True, req)
851 #------------------------------------------------------------
852 -def create_lab_result(patient_id=None, when_field=None, when=None, test_type=None, val_num=None, val_alpha=None, unit=None, encounter_id=None, request=None):
853 tres = None 854 data = { 855 'patient_id': patient_id, 856 'when_field': when_field, 857 'when': when, 858 'test_type': test_type, 859 'val_num': val_num, 860 'val_alpha': val_alpha, 861 'unit': unit 862 } 863 try: 864 tres = cLabResult(aPK_obj=data) 865 # exists already, so fail 866 _log.error('will not overwrite existing test result') 867 _log.debug(str(tres)) 868 return (None, tres) 869 except gmExceptions.NoSuchClinItemError: 870 _log.debug('test result not found - as expected, will create it') 871 except gmExceptions.ConstructorError, msg: 872 _log.exception(str(msg), sys.exc_info(), verbose=0) 873 return (False, msg) 874 if request is None: 875 return (False, _('need lab request when inserting lab result')) 876 # not found 877 if encounter_id is None: 878 encounter_id = request['pk_encounter'] 879 queries = [] 880 cmd = "insert into test_result (fk_encounter, fk_episode, fk_type, val_num, val_alpha, val_unit) values (%s, %s, %s, %s, %s, %s)" 881 queries.append((cmd, [encounter_id, request['pk_episode'], test_type, val_num, val_alpha, unit])) 882 cmd = "insert into lnk_result2lab_req (fk_result, fk_request) values ((select currval('test_result_pk_seq')), %s)" 883 queries.append((cmd, [request['pk_request']])) 884 cmd = "select currval('test_result_pk_seq')" 885 queries.append((cmd, [])) 886 # insert new 887 result, err = gmPG.run_commit('historica', queries, True) 888 if result is None: 889 return (False, err) 890 try: 891 tres = cLabResult(aPK_obj=result[0][0]) 892 except gmExceptions.ConstructorError, msg: 893 _log.exception(str(msg), sys.exc_info(), verbose=0) 894 return (False, msg) 895 return (True, tres)
896 #------------------------------------------------------------
897 -def get_unreviewed_results(limit=50):
898 # sanity check 899 if limit < 1: 900 limit = 1 901 # retrieve one more row than needed so we know there's more available ;-) 902 lim = limit + 1 903 cmd = """ 904 select pk_result 905 from v_results4lab_req 906 where reviewed is false 907 order by pk_patient 908 limit %s""" % lim 909 rows = gmPG.run_ro_query('historica', cmd) 910 if rows is None: 911 _log.error('error retrieving unreviewed lab results') 912 return (None, _('error retrieving unreviewed lab results')) 913 if len(rows) == 0: 914 return (False, []) 915 # more than LIMIT rows ? 916 if len(rows) == lim: 917 more_avail = True 918 # but deliver only LIMIT rows so that our assumption holds true... 919 del rows[limit] 920 else: 921 more_avail = False 922 results = [] 923 for row in rows: 924 try: 925 results.append(cLabResult(aPK_obj=row[0])) 926 except gmExceptions.ConstructorError: 927 _log.exception('skipping unreviewed lab result [%s]' % row[0], sys.exc_info(), verbose=0) 928 return (more_avail, results)
929 #------------------------------------------------------------
930 -def get_pending_requests(limit=250):
931 lim = limit + 1 932 cmd = "select pk from lab_request where is_pending is true limit %s" % lim 933 rows = gmPG.run_ro_query('historica', cmd) 934 if rows is None: 935 _log.error('error retrieving pending lab requests') 936 return (None, None) 937 if len(rows) == 0: 938 return (False, []) 939 results = [] 940 # more than LIMIT rows ? 941 if len(rows) == lim: 942 too_many = True 943 # but deliver only LIMIT rows so that our assumption holds true... 944 del rows[limit] 945 else: 946 too_many = False 947 requests = [] 948 for row in rows: 949 try: 950 requests.append(cLabRequest(aPK_obj=row[0])) 951 except gmExceptions.ConstructorError: 952 _log.exception('skipping pending lab request [%s]' % row[0], sys.exc_info(), verbose=0) 953 return (too_many, requests)
954 #------------------------------------------------------------
955 -def get_next_request_ID(lab=None, incrementor_func=None):
956 """Get logically next request ID for given lab. 957 958 - lab either test_org PK or test_org.internal_name 959 - incrementor_func: 960 - if not supplied the next ID is guessed 961 - if supplied it is applied to the most recently used ID 962 """ 963 if type(lab) == types.IntType: 964 lab_snippet = 'vlr.fk_test_org=%s' 965 else: 966 lab_snippet = 'vlr.lab_name=%s' 967 lab = str(lab) 968 cmd = """ 969 select request_id 970 from lab_request lr0 971 where lr0.clin_when = ( 972 select max(vlr.sampled_when) 973 from v_lab_requests vlr 974 where %s 975 )""" % lab_snippet 976 rows = gmPG.run_ro_query('historica', cmd, None, lab) 977 if rows is None: 978 _log.warning('error getting most recently used request ID for lab [%s]' % lab) 979 return '' 980 if len(rows) == 0: 981 return '' 982 most_recent = rows[0][0] 983 # apply supplied incrementor 984 if incrementor_func is not None: 985 try: 986 next = incrementor_func(most_recent) 987 except TypeError: 988 _log.error('cannot call incrementor function [%s]' % str(incrementor_func)) 989 return most_recent 990 return next 991 # try to be smart ourselves 992 for pos in range(len(most_recent)): 993 header = most_recent[:pos] 994 trailer = most_recent[pos:] 995 try: 996 return '%s%s' % (header, str(int(trailer) + 1)) 997 except ValueError: 998 header = most_recent[:-1] 999 trailer = most_recent[-1:] 1000 return '%s%s' % (header, chr(ord(trailer) + 1))
1001 #============================================================ 1002 # main - unit testing 1003 #------------------------------------------------------------ 1004 if __name__ == '__main__': 1005 import time 1006 1007 #------------------------------------------
1008 - def test_create_test_result():
1009 tr = create_test_result ( 1010 encounter = 1, 1011 episode = 1, 1012 type = 1, 1013 intended_reviewer = 1, 1014 val_num = '12', 1015 val_alpha=None, 1016 unit = 'mg/dl' 1017 ) 1018 print tr 1019 return tr
1020 #------------------------------------------
1021 - def test_delete_test_result():
1022 tr = test_create_test_result() 1023 delete_test_result(tr)
1024 #------------------------------------------
1025 - def test_result():
1026 r = cTestResult(aPK_obj=1) 1027 print r 1028 print r.reference_ranges
1029 #------------------------------------------
1030 - def test_lab_result():
1031 print "test_result()" 1032 # lab_result = cLabResult(aPK_obj=4) 1033 data = { 1034 'patient_id': 12, 1035 'when_field': 'val_when', 1036 'when': '2000-09-17 18:23:00+02', 1037 'test_type': 9, 1038 'val_num': 17.3, 1039 'val_alpha': None, 1040 'unit': 'mg/l' 1041 } 1042 lab_result = cLabResult(aPK_obj=data) 1043 print lab_result 1044 fields = lab_result.get_fields() 1045 for field in fields: 1046 print field, ':', lab_result[field] 1047 print "updatable:", lab_result.get_updatable_fields() 1048 print time.time() 1049 print lab_result.get_patient() 1050 print time.time()
1051 #------------------------------------------
1052 - def test_request():
1053 print "test_request()" 1054 try: 1055 # lab_req = cLabRequest(aPK_obj=1) 1056 # lab_req = cLabRequest(req_id='EML#SC937-0176-CEC#11', lab=2) 1057 data = { 1058 'req_id': 'EML#SC937-0176-CEC#11', 1059 'lab': 'Enterprise Main Lab' 1060 } 1061 lab_req = cLabRequest(aPK_obj=data) 1062 except gmExceptions.ConstructorError, msg: 1063 print "no such lab request:", msg 1064 return 1065 print lab_req 1066 fields = lab_req.get_fields() 1067 for field in fields: 1068 print field, ':', lab_req[field] 1069 print "updatable:", lab_req.get_updatable_fields() 1070 print time.time() 1071 print lab_req.get_patient() 1072 print time.time()
1073 #--------------------------------------------------------
1074 - def test_unreviewed():
1075 data = get_unreviewed_results() 1076 for result in data: 1077 print result
1078 #--------------------------------------------------------
1079 - def test_pending():
1080 data = get_pending_requests() 1081 for result in data: 1082 print result
1083 #--------------------------------------------------------
1084 - def test_create_measurement_type():
1085 print create_measurement_type ( 1086 lab = None, 1087 abbrev = u'tBZ2', 1088 unit = u'mg%', 1089 name = 'BZ (test 2)' 1090 )
1091 #--------------------------------------------------------
1092 - def test_meta_test_type():
1093 mtt = cMetaTestType(aPK_obj = 1) 1094 print mtt 1095 print get_meta_test_types()
1096 #--------------------------------------------------------
1097 - def test_test_type():
1098 tt = cMeasurementType(aPK_obj = 1) 1099 print tt 1100 print get_measurement_types()
1101 #-------------------------------------------------------- 1102 if (len(sys.argv) > 1) and (sys.argv[1] == 'test'): 1103 1104 #test_result() 1105 #test_create_test_result() 1106 #test_delete_test_result() 1107 #test_create_measurement_type() 1108 #test_lab_result() 1109 #test_request() 1110 #test_create_result() 1111 #test_unreviewed() 1112 #test_pending() 1113 #test_meta_test_type() 1114 test_test_type() 1115 1116 #============================================================ 1117 # $Log: gmPathLab.py,v $ 1118 # Revision 1.81 2010-02-06 20:45:44 ncq 1119 # - fix get-most-recent-result 1120 # 1121 # Revision 1.80 2010/02/02 13:50:15 ncq 1122 # - add test org handling 1123 # 1124 # Revision 1.79 2009/12/03 17:45:29 ncq 1125 # - implement cUnifiedTestType 1126 # 1127 # Revision 1.78 2009/09/17 21:52:13 ncq 1128 # - add .in_use property to test types 1129 # 1130 # Revision 1.77 2009/09/01 22:20:40 ncq 1131 # - nullify empty strings where appropriate 1132 # - support order_by on getting measurement types 1133 # 1134 # Revision 1.76 2009/08/08 12:16:48 ncq 1135 # - must us AS to supply pk 1136 # 1137 # Revision 1.75 2009/08/03 20:47:24 ncq 1138 # - fix storing measurement type and test org 1139 # 1140 # Revision 1.74 2009/07/23 16:30:45 ncq 1141 # - test -> measurement 1142 # - code -> abbrev 1143 # 1144 # Revision 1.73 2009/07/06 14:56:54 ncq 1145 # - make update of test result more resilient re "" == null 1146 # - much improved test result formatting 1147 # 1148 # Revision 1.72 2009/06/04 14:45:15 ncq 1149 # - re-import lost adjustments 1150 # 1151 # Revision 1.72 2009/05/28 10:45:57 ncq 1152 # - adjust to test table changes 1153 # 1154 # Revision 1.71 2009/05/26 09:21:13 ncq 1155 # - delete_meta_type 1156 # - cTestType -> cMeasurementType 1157 # - delete_measurement_type 1158 # 1159 # Revision 1.70 2009/05/24 16:27:34 ncq 1160 # - support meta test types 1161 # - better support test types 1162 # 1163 # Revision 1.69 2009/03/18 14:27:28 ncq 1164 # - add comment 1165 # 1166 # Revision 1.68 2009/02/27 12:38:16 ncq 1167 # - improved results formatting 1168 # 1169 # Revision 1.67 2009/02/18 19:17:56 ncq 1170 # - properly test for NULLness when formatting results 1171 # 1172 # Revision 1.66 2009/01/02 11:34:50 ncq 1173 # - cleanup 1174 # 1175 # Revision 1.65 2008/10/22 12:04:55 ncq 1176 # - use %x in strftime 1177 # 1178 # Revision 1.64 2008/07/14 13:45:08 ncq 1179 # - add .format to test results 1180 # 1181 # Revision 1.63 2008/06/24 13:54:47 ncq 1182 # - delete_test_result and test 1183 # 1184 # Revision 1.62 2008/06/23 21:49:19 ncq 1185 # - pimp cTestType, find/create_test_type 1186 # - some tests 1187 # 1188 # Revision 1.61 2008/06/19 15:24:47 ncq 1189 # - fix updating cTestResult 1190 # 1191 # Revision 1.60 2008/06/16 15:01:53 ncq 1192 # - create_test_result 1193 # - test suite cleanup 1194 # - reference_ranges property on cTestResult 1195 # 1196 # Revision 1.59 2008/04/22 21:15:16 ncq 1197 # - cTestResult 1198 # 1199 # Revision 1.58 2008/02/25 17:31:41 ncq 1200 # - logging cleanup 1201 # 1202 # Revision 1.57 2008/01/30 13:34:50 ncq 1203 # - switch to std lib logging 1204 # 1205 # Revision 1.56 2007/07/17 11:13:25 ncq 1206 # - no more gmClinItem 1207 # 1208 # Revision 1.55 2007/03/08 11:31:08 ncq 1209 # - just cleanup 1210 # 1211 # Revision 1.54 2007/01/09 12:56:18 ncq 1212 # - comment 1213 # 1214 # Revision 1.53 2006/10/25 07:17:40 ncq 1215 # - no more gmPG 1216 # - no more cClinItem 1217 # 1218 # Revision 1.52 2006/07/19 20:25:00 ncq 1219 # - gmPyCompat.py is history 1220 # 1221 # Revision 1.51 2005/10/26 21:16:26 ncq 1222 # - adjust to changes in reviewed status handling 1223 # 1224 # Revision 1.50 2005/04/27 12:37:32 sjtan 1225 # 1226 # id_patient -> pk_patient 1227 # 1228 # Revision 1.49 2005/03/23 18:31:19 ncq 1229 # - v_patient_items -> v_pat_items 1230 # 1231 # Revision 1.48 2005/02/15 18:29:03 ncq 1232 # - test_result.id -> pk 1233 # 1234 # Revision 1.47 2005/02/13 15:45:31 ncq 1235 # - v_basic_person.i_pk -> pk_identity 1236 # 1237 # Revision 1.46 2005/01/02 19:55:30 ncq 1238 # - don't need _xmins_refetch_col_pos anymore 1239 # 1240 # Revision 1.45 2004/12/27 16:48:11 ncq 1241 # - fix create_lab_request() to use proper aPK_obj syntax 1242 # 1243 # Revision 1.44 2004/12/20 16:45:49 ncq 1244 # - gmBusinessDBObject now requires refetching of XMIN after save_payload 1245 # 1246 # Revision 1.43 2004/12/14 03:27:56 ihaywood 1247 # xmin_rest_result -> xmin_test_result 1248 # 1249 # Carlos used a very old version of the SOAP2.py for no good reason, fixed. 1250 # 1251 # Revision 1.42 2004/11/03 22:32:34 ncq 1252 # - support _cmds_lock_rows_for_update in business object base class 1253 # 1254 # Revision 1.41 2004/10/18 09:48:20 ncq 1255 # - must have been asleep at the keyboard 1256 # 1257 # Revision 1.40 2004/10/18 09:46:02 ncq 1258 # - fix create_lab_result() 1259 # 1260 # Revision 1.39 2004/10/15 09:05:08 ncq 1261 # - converted cLabResult to allow use of row __init__() 1262 # 1263 # Revision 1.38 2004/10/12 18:32:52 ncq 1264 # - allow cLabRequest and cTestType to be filled from bulk fetch row data 1265 # - cLabResult not adapted yet 1266 # 1267 # Revision 1.37 2004/09/29 10:25:04 ncq 1268 # - basic_unit->conversion_unit 1269 # 1270 # Revision 1.36 2004/09/18 13:51:56 ncq 1271 # - support val_target_* 1272 # 1273 # Revision 1.35 2004/07/02 00:20:54 ncq 1274 # - v_patient_items.id_item -> pk_item 1275 # 1276 # Revision 1.34 2004/06/28 15:14:50 ncq 1277 # - use v_lab_requests 1278 # 1279 # Revision 1.33 2004/06/28 12:18:52 ncq 1280 # - more id_* -> fk_* 1281 # 1282 # Revision 1.32 2004/06/26 07:33:55 ncq 1283 # - id_episode -> fk/pk_episode 1284 # 1285 # Revision 1.31 2004/06/18 13:33:58 ncq 1286 # - saner logging 1287 # 1288 # Revision 1.30 2004/06/16 17:16:56 ncq 1289 # - correctly handle val_num/val_alpha in create_lab_result so 1290 # we can safely detect duplicates 1291 # 1292 # Revision 1.29 2004/06/01 23:56:39 ncq 1293 # - improved error handling in several places 1294 # 1295 # Revision 1.28 2004/05/30 20:12:33 ncq 1296 # - make create_lab_result() handle request objects, not request_id 1297 # 1298 # Revision 1.27 2004/05/26 15:45:25 ncq 1299 # - get_next_request_ID() 1300 # 1301 # Revision 1.26 2004/05/25 13:29:20 ncq 1302 # - order unreviewed results by pk_patient 1303 # 1304 # Revision 1.25 2004/05/25 00:20:47 ncq 1305 # - fix reversal of is_pending in get_pending_requests() 1306 # 1307 # Revision 1.24 2004/05/25 00:07:31 ncq 1308 # - speed up get_patient in test_result 1309 # 1310 # Revision 1.23 2004/05/24 23:34:53 ncq 1311 # - optimize get_patient in cLabRequest() 1312 # 1313 # Revision 1.22 2004/05/24 14:59:45 ncq 1314 # - get_pending_requests() 1315 # 1316 # Revision 1.21 2004/05/24 14:35:00 ncq 1317 # - get_unreviewed_results() now returns status of more_available 1318 # 1319 # Revision 1.20 2004/05/24 14:15:54 ncq 1320 # - get_unreviewed_results() 1321 # 1322 # Revision 1.19 2004/05/14 13:17:27 ncq 1323 # - less useless verbosity 1324 # - cleanup 1325 # 1326 # Revision 1.18 2004/05/13 00:03:17 ncq 1327 # - aPKey -> aPK_obj 1328 # 1329 # Revision 1.17 2004/05/11 01:37:21 ncq 1330 # - create_test_result -> create_lab_result 1331 # - need to insert into lnk_result2lab_req, too, in create_lab_result 1332 # 1333 # Revision 1.16 2004/05/08 22:13:11 ncq 1334 # - cleanup 1335 # 1336 # Revision 1.15 2004/05/08 17:29:18 ncq 1337 # - us NoSuchClinItemError 1338 # 1339 # Revision 1.14 2004/05/06 23:37:19 ncq 1340 # - lab result _update_payload update 1341 # - lab result.__init__ now supports values other than the PK 1342 # - add create_test_result() 1343 # 1344 # Revision 1.13 2004/05/04 07:55:00 ncq 1345 # - correctly detect "no such lab request" condition in create_lab_request() 1346 # - fail gracefully in test_request() 1347 # 1348 # Revision 1.12 2004/05/03 22:25:10 shilbert 1349 # - some typos fixed 1350 # 1351 # Revision 1.11 2004/05/03 15:30:58 ncq 1352 # - add create_lab_request() 1353 # - add cLabResult.get_patient() 1354 # 1355 # Revision 1.10 2004/05/03 12:50:34 ncq 1356 # - relative imports 1357 # - str()ify some things 1358 # 1359 # Revision 1.9 2004/05/02 22:56:36 ncq 1360 # - add create_lab_request() 1361 # 1362 # Revision 1.8 2004/04/26 21:56:19 ncq 1363 # - add cLabRequest.get_patient() 1364 # - add create_test_type() 1365 # 1366 # Revision 1.7 2004/04/21 15:27:38 ncq 1367 # - map 8407 to string for ldt import 1368 # 1369 # Revision 1.6 2004/04/20 00:14:30 ncq 1370 # - cTestType invented 1371 # 1372 # Revision 1.5 2004/04/19 12:42:41 ncq 1373 # - fix cLabRequest._cms_store_payload 1374 # - modularize testing 1375 # 1376 # Revision 1.4 2004/04/18 18:50:36 ncq 1377 # - override __init__() thusly removing the unholy _pre/post_init() business 1378 # 1379 # Revision 1.3 2004/04/12 22:59:38 ncq 1380 # - add lab request 1381 # 1382 # Revision 1.2 2004/04/11 12:07:54 ncq 1383 # - better unit testing 1384 # 1385 # Revision 1.1 2004/04/11 12:04:55 ncq 1386 # - first version 1387 # 1388