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