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