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

Source Code for Module Gnumed.business.gmPathLab

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