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