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

Source Code for Module Gnumed.business.gmPathLab

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