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

Source Code for Module Gnumed.business.gmEMRStructItems

   1  # -*- coding: utf8 -*- 
   2  """GNUmed health related business object. 
   3   
   4  license: GPL 
   5  """ 
   6  #============================================================ 
   7  __version__ = "$Revision: 1.157 $" 
   8  __author__ = "Carlos Moro <cfmoro1976@yahoo.es>, <karsten.hilbert@gmx.net>" 
   9   
  10  import types, sys, string, datetime, logging, time 
  11   
  12   
  13  if __name__ == '__main__': 
  14          sys.path.insert(0, '../../') 
  15  from Gnumed.pycommon import gmPG2, gmExceptions, gmNull, gmBusinessDBObject, gmDateTime, gmTools, gmI18N 
  16  from Gnumed.business import gmClinNarrative 
  17   
  18   
  19  _log = logging.getLogger('gm.emr') 
  20  _log.info(__version__) 
  21   
  22  try: _ 
  23  except NameError: _ = lambda x:x 
  24  #============================================================ 
  25  # diagnostic certainty classification 
  26  #============================================================ 
  27  __diagnostic_certainty_classification_map = None 
  28   
29 -def diagnostic_certainty_classification2str(classification):
30 31 global __diagnostic_certainty_classification_map 32 33 if __diagnostic_certainty_classification_map is None: 34 __diagnostic_certainty_classification_map = { 35 None: u'', 36 u'A': _(u'A: Sign'), 37 u'B': _(u'B: Cluster of signs'), 38 u'C': _(u'C: Syndromic diagnosis'), 39 u'D': _(u'D: Scientific diagnosis') 40 } 41 42 try: 43 return __diagnostic_certainty_classification_map[classification] 44 except KeyError: 45 return _(u'%s: unknown diagnostic certainty classification') % classification
46 #============================================================ 47 # Health Issues API 48 #============================================================ 49 laterality2str = { 50 None: u'?', 51 u'na': u'', 52 u'sd': _('bilateral'), 53 u'ds': _('bilateral'), 54 u's': _('left'), 55 u'd': _('right') 56 } 57 58 #============================================================
59 -class cHealthIssue(gmBusinessDBObject.cBusinessDBObject):
60 """Represents one health issue.""" 61 62 _cmd_fetch_payload = u"select *, xmin_health_issue from clin.v_health_issues where pk_health_issue=%s" 63 _cmds_store_payload = [ 64 u"""update clin.health_issue set 65 description = %(description)s, 66 age_noted = %(age_noted)s, 67 laterality = gm.nullify_empty_string(%(laterality)s), 68 grouping = gm.nullify_empty_string(%(grouping)s), 69 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s), 70 is_active = %(is_active)s, 71 clinically_relevant = %(clinically_relevant)s, 72 is_confidential = %(is_confidential)s, 73 is_cause_of_death = %(is_cause_of_death)s 74 where 75 pk = %(pk_health_issue)s and 76 xmin = %(xmin_health_issue)s""", 77 u"select xmin as xmin_health_issue from clin.health_issue where pk = %(pk_health_issue)s" 78 ] 79 _updatable_fields = [ 80 'description', 81 'grouping', 82 'age_noted', 83 'laterality', 84 'is_active', 85 'clinically_relevant', 86 'is_confidential', 87 'is_cause_of_death', 88 'diagnostic_certainty_classification' 89 ] 90 #--------------------------------------------------------
91 - def __init__(self, aPK_obj=None, encounter=None, name='xxxDEFAULTxxx', patient=None, row=None):
92 pk = aPK_obj 93 94 if (pk is not None) or (row is not None): 95 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row) 96 return 97 98 if patient is None: 99 cmd = u"""select *, xmin_health_issue from clin.v_health_issues 100 where 101 description = %(desc)s 102 and 103 pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)""" 104 else: 105 cmd = u"""select *, xmin_health_issue from clin.v_health_issues 106 where 107 description = %(desc)s 108 and 109 pk_patient = %(pat)s""" 110 111 queries = [{'cmd': cmd, 'args': {'enc': encounter, 'desc': name, 'pat': patient}}] 112 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 113 114 if len(rows) == 0: 115 raise gmExceptions.NoSuchBusinessObjectError, 'no health issue for [enc:%s::desc:%s::pat:%s]' % (encounter, name, patient) 116 117 pk = rows[0][0] 118 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_health_issue'} 119 120 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)
121 #--------------------------------------------------------
122 - def rename(self, description=None):
123 """Method for issue renaming. 124 125 @param description 126 - the new descriptive name for the issue 127 @type description 128 - a string instance 129 """ 130 # sanity check 131 if not type(description) in [str, unicode] or description.strip() == '': 132 _log.error('<description> must be a non-empty string') 133 return False 134 # update the issue description 135 old_description = self._payload[self._idx['description']] 136 self._payload[self._idx['description']] = description.strip() 137 self._is_modified = True 138 successful, data = self.save_payload() 139 if not successful: 140 _log.error('cannot rename health issue [%s] with [%s]' % (self, description)) 141 self._payload[self._idx['description']] = old_description 142 return False 143 return True
144 #--------------------------------------------------------
145 - def get_episodes(self):
146 cmd = u"select * from clin.v_pat_episodes where pk_health_issue = %(pk)s" 147 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = True) 148 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]
149 #--------------------------------------------------------
150 - def close_expired_episode(self, ttl=180):
151 """ttl in days""" 152 open_episode = self.get_open_episode() 153 if open_episode is None: 154 return True 155 earliest, latest = open_episode.get_access_range() 156 ttl = datetime.timedelta(ttl) 157 now = datetime.datetime.now(tz=latest.tzinfo) 158 if (latest + ttl) > now: 159 return False 160 open_episode['episode_open'] = False 161 success, data = open_episode.save_payload() 162 if success: 163 return True 164 return False # should be an exception
165 #--------------------------------------------------------
166 - def close_episode(self):
167 open_episode = self.get_open_episode() 168 open_episode['episode_open'] = False 169 success, data = open_episode.save_payload() 170 if success: 171 return True 172 return False
173 #--------------------------------------------------------
174 - def has_open_episode(self):
175 cmd = u"select exists (select 1 from clin.episode where fk_health_issue = %s and is_open is True)" 176 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 177 return rows[0][0]
178 #--------------------------------------------------------
179 - def get_open_episode(self):
180 cmd = u"select pk from clin.episode where fk_health_issue = %s and is_open is True" 181 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 182 if len(rows) == 0: 183 return None 184 return cEpisode(aPK_obj=rows[0][0])
185 #--------------------------------------------------------
186 - def age_noted_human_readable(self):
187 if self._payload[self._idx['age_noted']] is None: 188 return u'<???>' 189 190 return gmDateTime.format_interval_medically(self._payload[self._idx['age_noted']])
191 #--------------------------------------------------------
193 try: 194 return laterality2str[self._payload[self._idx['laterality']]] 195 except KeyError: 196 return u'<???>'
197 198 laterality_description = property(_get_laterality_description, lambda x:x) 199 #--------------------------------------------------------
201 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
202 203 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x) 204 #--------------------------------------------------------
205 - def format(self, left_margin=0, patient=None):
206 207 if patient.ID != self._payload[self._idx['pk_patient']]: 208 msg = '<patient>.ID = %s but health issue %s belongs to patient %s' % ( 209 patient.ID, 210 self._payload[self._idx['pk_health_issue']], 211 self._payload[self._idx['pk_patient']] 212 ) 213 raise ValueError(msg) 214 215 lines = [] 216 217 lines.append(_('Health Issue %s%s%s%s [#%s]') % ( 218 u'\u00BB', 219 self._payload[self._idx['description']], 220 u'\u00AB', 221 gmTools.coalesce ( 222 initial = self.laterality_description, 223 instead = u'', 224 template_initial = u' (%s)', 225 none_equivalents = [None, u'', u'?'] 226 ), 227 self._payload[self._idx['pk_health_issue']] 228 )) 229 230 if self._payload[self._idx['is_confidential']]: 231 lines.append('') 232 lines.append(_(' ***** CONFIDENTIAL *****')) 233 lines.append('') 234 235 if self._payload[self._idx['is_cause_of_death']]: 236 lines.append('') 237 lines.append(_(' contributed to death of patient')) 238 lines.append('') 239 240 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']]) 241 lines.append (_(' Created during encounter: %s (%s - %s) [#%s]') % ( 242 enc['l10n_type'], 243 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 244 enc['last_affirmed_original_tz'].strftime('%H:%M'), 245 self._payload[self._idx['pk_encounter']] 246 )) 247 248 if self._payload[self._idx['age_noted']] is not None: 249 lines.append(_(' Noted at age: %s') % self.age_noted_human_readable()) 250 251 lines.append(_(' Status: %s, %s%s') % ( 252 gmTools.bool2subst(self._payload[self._idx['is_active']], _('active'), _('inactive')), 253 gmTools.bool2subst(self._payload[self._idx['clinically_relevant']], _('clinically relevant'), _('not clinically relevant')), 254 gmTools.coalesce ( 255 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]), 256 instead = u'', 257 template_initial = u', %s', 258 none_equivalents = [None, u''] 259 ), 260 )) 261 lines.append('') 262 263 emr = patient.get_emr() 264 265 # episodes 266 epis = emr.get_episodes(issues = [self._payload[self._idx['pk_health_issue']]]) 267 if epis is None: 268 lines.append(_('Error retrieving episodes for this health issue.')) 269 elif len(epis) == 0: 270 lines.append(_('There are no episodes for this health issue.')) 271 else: 272 lines.append ( 273 _('Episodes: %s (most recent: %s%s%s)') % ( 274 len(epis), 275 gmTools.u_left_double_angle_quote, 276 emr.get_most_recent_episode(issue = self._payload[self._idx['pk_health_issue']])['description'], 277 gmTools.u_right_double_angle_quote 278 ) 279 ) 280 lines.append('') 281 for epi in epis: 282 lines.append(u' \u00BB%s\u00AB (%s)' % ( 283 epi['description'], 284 gmTools.bool2subst(epi['episode_open'], _('ongoing'), _('closed')) 285 )) 286 287 lines.append('') 288 289 # encounters 290 first_encounter = emr.get_first_encounter(issue_id = self._payload[self._idx['pk_health_issue']]) 291 last_encounter = emr.get_last_encounter(issue_id = self._payload[self._idx['pk_health_issue']]) 292 293 if first_encounter is None or last_encounter is None: 294 lines.append(_('No encounters found for this health issue.')) 295 else: 296 encs = emr.get_encounters(issues = [self._payload[self._idx['pk_health_issue']]]) 297 lines.append(_('Encounters: %s (%s - %s):') % ( 298 len(encs), 299 first_encounter['started_original_tz'].strftime('%m/%Y'), 300 last_encounter['last_affirmed_original_tz'].strftime('%m/%Y') 301 )) 302 lines.append(_(' Most recent: %s - %s') % ( 303 last_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 304 last_encounter['last_affirmed_original_tz'].strftime('%H:%M') 305 )) 306 307 # medications 308 meds = emr.get_current_substance_intake ( 309 issues = [ self._payload[self._idx['pk_health_issue']] ], 310 order_by = u'is_currently_active, started, substance' 311 ) 312 313 if len(meds) > 0: 314 lines.append(u'') 315 lines.append(_('Active medications: %s') % len(meds)) 316 for m in meds: 317 lines.append(m.format(left_margin = (left_margin + 1))) 318 del meds 319 320 # hospital stays 321 stays = emr.get_hospital_stays ( 322 issues = [ self._payload[self._idx['pk_health_issue']] ] 323 ) 324 325 if len(stays) > 0: 326 lines.append(u'') 327 lines.append(_('Hospital stays: %s') % len(stays)) 328 for s in stays: 329 lines.append(s.format(left_margin = (left_margin + 1))) 330 del stays 331 332 # procedures 333 procs = emr.get_performed_procedures ( 334 issues = [ self._payload[self._idx['pk_health_issue']] ] 335 ) 336 337 if len(procs) > 0: 338 lines.append(u'') 339 lines.append(_('Procedures performed: %s') % len(procs)) 340 for p in procs: 341 lines.append(p.format(left_margin = (left_margin + 1))) 342 del procs 343 344 epis = self.get_episodes() 345 if len(epis) > 0: 346 # documents 347 doc_folder = patient.get_document_folder() 348 docs = doc_folder.get_documents(episodes = [ e['pk_episode'] for e in epis ]) 349 350 if len(docs) > 0: 351 lines.append(u'') 352 lines.append(_('Documents: %s') % len(docs)) 353 del docs 354 355 # test results 356 tests = emr.get_test_results_by_date(episodes = [ e['pk_episode'] for e in epis ]) 357 if len(tests) > 0: 358 lines.append(u'') 359 lines.append(_('Measurements and Results: %s') % len(tests)) 360 del tests 361 362 del epis 363 364 left_margin = u' ' * left_margin 365 eol_w_margin = u'\n%s' % left_margin 366 return left_margin + eol_w_margin.join(lines) + u'\n'
367 #============================================================
368 -def create_health_issue(description=None, encounter=None, patient=None):
369 """Creates a new health issue for a given patient. 370 371 description - health issue name 372 """ 373 try: 374 h_issue = cHealthIssue(name = description, encounter = encounter, patient = patient) 375 return h_issue 376 except gmExceptions.NoSuchBusinessObjectError: 377 pass 378 379 queries = [] 380 cmd = u"insert into clin.health_issue (description, fk_encounter) values (%(desc)s, %(enc)s)" 381 queries.append({'cmd': cmd, 'args': {'desc': description, 'enc': encounter}}) 382 383 cmd = u"select currval('clin.health_issue_pk_seq')" 384 queries.append({'cmd': cmd}) 385 386 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 387 h_issue = cHealthIssue(aPK_obj = rows[0][0]) 388 389 return h_issue
390 #-----------------------------------------------------------
391 -def delete_health_issue(health_issue=None):
392 if isinstance(health_issue, cHealthIssue): 393 pk = health_issue['pk_health_issue'] 394 else: 395 pk = int(health_issue) 396 397 try: 398 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.health_issue where pk=%(pk)s', 'args': {'pk': pk}}]) 399 except gmPG2.dbapi.IntegrityError: 400 # should be parsing pgcode/and or error message 401 _log.exception('cannot delete health issue') 402 raise gmExceptions.DatabaseObjectInUseError('cannot delete health issue, it is in use')
403 #------------------------------------------------------------ 404 # use as dummy for unassociated episodes
405 -def get_dummy_health_issue():
406 issue = { 407 'pk_health_issue': None, 408 'description': _('Unattributed episodes'), 409 'age_noted': None, 410 'laterality': u'na', 411 'is_active': True, 412 'clinically_relevant': True, 413 'is_confidential': None, 414 'is_cause_of_death': False, 415 'is_dummy': True 416 } 417 return issue
418 #-----------------------------------------------------------
419 -def health_issue2problem(health_issue=None, allow_irrelevant=False):
420 return cProblem ( 421 aPK_obj = { 422 'pk_patient': health_issue['pk_patient'], 423 'pk_health_issue': health_issue['pk_health_issue'], 424 'pk_episode': None 425 }, 426 try_potential_problems = allow_irrelevant 427 )
428 #============================================================ 429 # episodes API 430 #============================================================
431 -class cEpisode(gmBusinessDBObject.cBusinessDBObject):
432 """Represents one clinical episode. 433 """ 434 _cmd_fetch_payload = u"select * from clin.v_pat_episodes where pk_episode=%s" 435 _cmds_store_payload = [ 436 u"""update clin.episode set 437 fk_health_issue = %(pk_health_issue)s, 438 is_open = %(episode_open)s::boolean, 439 description = %(description)s, 440 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s) 441 where 442 pk = %(pk_episode)s and 443 xmin = %(xmin_episode)s""", 444 u"""select xmin_episode from clin.v_pat_episodes where pk_episode = %(pk_episode)s""" 445 ] 446 _updatable_fields = [ 447 'pk_health_issue', 448 'episode_open', 449 'description', 450 'diagnostic_certainty_classification' 451 ] 452 #--------------------------------------------------------
453 - def __init__(self, aPK_obj=None, id_patient=None, name='xxxDEFAULTxxx', health_issue=None, row=None, encounter=None):
454 pk = aPK_obj 455 if pk is None and row is None: 456 457 where_parts = [u'description = %(desc)s'] 458 459 if id_patient is not None: 460 where_parts.append(u'pk_patient = %(pat)s') 461 462 if health_issue is not None: 463 where_parts.append(u'pk_health_issue = %(issue)s') 464 465 if encounter is not None: 466 where_parts.append(u'pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)') 467 468 args = { 469 'pat': id_patient, 470 'issue': health_issue, 471 'enc': encounter, 472 'desc': name 473 } 474 475 cmd = u"select * from clin.v_pat_episodes where %s" % u' and '.join(where_parts) 476 477 rows, idx = gmPG2.run_ro_queries( 478 queries = [{'cmd': cmd, 'args': args}], 479 get_col_idx=True 480 ) 481 482 if len(rows) == 0: 483 raise gmExceptions.NoSuchBusinessObjectError, 'no episode for [%s:%s:%s:%s]' % (id_patient, name, health_issue, encounter) 484 485 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_episode'} 486 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r) 487 488 else: 489 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)
490 #--------------------------------------------------------
491 - def get_access_range(self):
492 """Get earliest and latest access to this episode. 493 494 Returns a tuple(earliest, latest). 495 """ 496 cmd = u""" 497 select 498 min(earliest), 499 max(latest) 500 from ( 501 (select 502 (case when clin_when < modified_when 503 then clin_when 504 else modified_when 505 end) as earliest, 506 (case when clin_when > modified_when 507 then clin_when 508 else modified_when 509 end) as latest 510 from 511 clin.clin_root_item 512 where 513 fk_episode = %(pk)s 514 515 ) union all ( 516 517 select 518 modified_when as earliest, 519 modified_when as latest 520 from 521 clin.episode 522 where 523 pk = %(pk)s 524 ) 525 ) as ranges""" 526 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}]) 527 if len(rows) == 0: 528 return (gmNull.cNull(warn=False), gmNull.cNull(warn=False)) 529 return (rows[0][0], rows[0][1])
530 #--------------------------------------------------------
531 - def get_patient(self):
532 return self._payload[self._idx['pk_patient']]
533 #--------------------------------------------------------
534 - def rename(self, description=None):
535 """Method for episode editing, that is, episode renaming. 536 537 @param description 538 - the new descriptive name for the encounter 539 @type description 540 - a string instance 541 """ 542 # sanity check 543 if description.strip() == '': 544 _log.error('<description> must be a non-empty string instance') 545 return False 546 # update the episode description 547 old_description = self._payload[self._idx['description']] 548 self._payload[self._idx['description']] = description.strip() 549 self._is_modified = True 550 successful, data = self.save_payload() 551 if not successful: 552 _log.error('cannot rename episode [%s] to [%s]' % (self, description)) 553 self._payload[self._idx['description']] = old_description 554 return False 555 return True
556 #--------------------------------------------------------
558 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
559 560 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x) 561 #--------------------------------------------------------
562 - def format(self, left_margin=0, patient=None):
563 564 if patient.ID != self._payload[self._idx['pk_patient']]: 565 msg = '<patient>.ID = %s but episode %s belongs to patient %s' % ( 566 patient.ID, 567 self._payload[self._idx['pk_episode']], 568 self._payload[self._idx['pk_patient']] 569 ) 570 raise ValueError(msg) 571 572 lines = [] 573 574 # episode details 575 lines.append (_('Episode %s%s%s (%s%s) [#%s]\n') % ( 576 gmTools.u_left_double_angle_quote, 577 self._payload[self._idx['description']], 578 gmTools.u_right_double_angle_quote, 579 gmTools.coalesce ( 580 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]), 581 instead = u'', 582 template_initial = u'%s, ', 583 none_equivalents = [None, u''] 584 ), 585 gmTools.bool2subst(self._payload[self._idx['episode_open']], _('active'), _('finished')), 586 self._payload[self._idx['pk_episode']] 587 )) 588 589 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']]) 590 lines.append (_('Created during encounter: %s (%s - %s) [#%s]\n') % ( 591 enc['l10n_type'], 592 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 593 enc['last_affirmed_original_tz'].strftime('%H:%M'), 594 self._payload[self._idx['pk_encounter']] 595 )) 596 597 # encounters 598 emr = patient.get_emr() 599 encs = emr.get_encounters(episodes = [self._payload[self._idx['pk_episode']]]) 600 first_encounter = None 601 last_encounter = None 602 if encs is None: 603 lines.append(_('Error retrieving encounters for this episode.')) 604 elif len(encs) == 0: 605 lines.append(_('There are no encounters for this episode.')) 606 else: 607 first_encounter = emr.get_first_encounter(episode_id = self._payload[self._idx['pk_episode']]) 608 last_encounter = emr.get_last_encounter(episode_id = self._payload[self._idx['pk_episode']]) 609 610 lines.append(_('Last worked on: %s\n') % last_encounter['last_affirmed_original_tz'].strftime('%Y-%m-%d %H:%M')) 611 612 lines.append(_('1st and (up to 3) most recent (of %s) encounters (%s - %s):') % ( 613 len(encs), 614 first_encounter['started'].strftime('%m/%Y'), 615 last_encounter['last_affirmed'].strftime('%m/%Y') 616 )) 617 618 lines.append(u' %s - %s (%s):%s' % ( 619 first_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 620 first_encounter['last_affirmed_original_tz'].strftime('%H:%M'), 621 first_encounter['l10n_type'], 622 gmTools.coalesce ( 623 first_encounter['assessment_of_encounter'], 624 gmTools.coalesce ( 625 first_encounter['reason_for_encounter'], 626 u'', 627 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE')) 628 ), 629 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE')) 630 ) 631 )) 632 633 if len(encs) > 4: 634 lines.append(_(' ... %s skipped ...') % (len(encs) - 4)) 635 636 for enc in encs[1:][-3:]: 637 lines.append(u' %s - %s (%s):%s' % ( 638 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 639 enc['last_affirmed_original_tz'].strftime('%H:%M'), 640 enc['l10n_type'], 641 gmTools.coalesce ( 642 enc['assessment_of_encounter'], 643 gmTools.coalesce ( 644 enc['reason_for_encounter'], 645 u'', 646 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE')) 647 ), 648 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE')) 649 ) 650 )) 651 del encs 652 653 # spell out last encounter 654 if last_encounter is not None: 655 lines.append('') 656 lines.append(_('Progress notes in most recent encounter:')) 657 lines.extend(last_encounter.format_soap ( 658 episodes = [ self._payload[self._idx['pk_episode']] ], 659 left_margin = left_margin, 660 soap_cats = 'soap', 661 emr = emr 662 )) 663 664 # documents 665 doc_folder = patient.get_document_folder() 666 docs = doc_folder.get_documents ( 667 episodes = [ self._payload[self._idx['pk_episode']] ] 668 ) 669 670 if len(docs) > 0: 671 lines.append('') 672 lines.append(_('Documents: %s') % len(docs)) 673 674 for d in docs: 675 lines.append(u' %s %s:%s%s' % ( 676 d['clin_when'].strftime('%Y-%m-%d'), 677 d['l10n_type'], 678 gmTools.coalesce(d['comment'], u'', u' "%s"'), 679 gmTools.coalesce(d['ext_ref'], u'', u' (%s)') 680 )) 681 del docs 682 683 # hospital stays 684 stays = emr.get_hospital_stays ( 685 episodes = [ self._payload[self._idx['pk_episode']] ] 686 ) 687 688 if len(stays) > 0: 689 lines.append('') 690 lines.append(_('Hospital stays: %s') % len(stays)) 691 692 for s in stays: 693 lines.append(s.format(left_margin = (left_margin + 1))) 694 del stays 695 696 # procedures 697 procs = emr.get_performed_procedures ( 698 episodes = [ self._payload[self._idx['pk_episode']] ] 699 ) 700 701 if len(procs) > 0: 702 lines.append(u'') 703 lines.append(_('Procedures performed: %s') % len(procs)) 704 for p in procs: 705 lines.append(p.format(left_margin = (left_margin + 1), include_episode = False)) 706 del procs 707 708 # test results 709 tests = emr.get_test_results_by_date(episodes = [ self._payload[self._idx['pk_episode']] ]) 710 711 if len(tests) > 0: 712 lines.append('') 713 lines.append(_('Measurements and Results:')) 714 715 for t in tests: 716 lines.extend(t.format ( 717 with_review = False, 718 with_comments = False, 719 date_format = '%Y-%m-%d' 720 )) 721 del tests 722 723 left_margin = u' ' * left_margin 724 eol_w_margin = u'\n%s' % left_margin 725 return left_margin + eol_w_margin.join(lines) + u'\n'
726 #============================================================
727 -def create_episode(pk_health_issue=None, episode_name=None, is_open=False, allow_dupes=False, encounter=None):
728 """Creates a new episode for a given patient's health issue. 729 730 pk_health_issue - given health issue PK 731 episode_name - name of episode 732 """ 733 if not allow_dupes: 734 try: 735 episode = cEpisode(name=episode_name, health_issue=pk_health_issue, encounter = encounter) 736 if episode['episode_open'] != is_open: 737 episode['episode_open'] = is_open 738 episode.save_payload() 739 return episode 740 except gmExceptions.ConstructorError: 741 pass 742 743 queries = [] 744 cmd = u"insert into clin.episode (fk_health_issue, description, is_open, fk_encounter) values (%s, %s, %s::boolean, %s)" 745 queries.append({'cmd': cmd, 'args': [pk_health_issue, episode_name, is_open, encounter]}) 746 queries.append({'cmd': cEpisode._cmd_fetch_payload % u"currval('clin.episode_pk_seq')"}) 747 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True, get_col_idx=True) 748 749 episode = cEpisode(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_episode'}) 750 return episode
751 #-----------------------------------------------------------
752 -def delete_episode(episode=None):
753 if isinstance(episode, cEpisode): 754 pk = episode['pk_episode'] 755 else: 756 pk = int(episode) 757 758 try: 759 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.episode where pk=%(pk)s', 'args': {'pk': pk}}]) 760 except gmPG2.dbapi.IntegrityError: 761 # should be parsing pgcode/and or error message 762 _log.exception('cannot delete episode') 763 raise gmExceptions.DatabaseObjectInUseError('cannot delete episode, it is in use')
764 #-----------------------------------------------------------
765 -def episode2problem(episode=None, allow_closed=False):
766 return cProblem ( 767 aPK_obj = { 768 'pk_patient': episode['pk_patient'], 769 'pk_episode': episode['pk_episode'], 770 'pk_health_issue': episode['pk_health_issue'] 771 }, 772 try_potential_problems = allow_closed 773 )
774 #============================================================ 775 # encounter API 776 #============================================================
777 -class cEncounter(gmBusinessDBObject.cBusinessDBObject):
778 """Represents one encounter.""" 779 _cmd_fetch_payload = u"select * from clin.v_pat_encounters where pk_encounter = %s" 780 _cmds_store_payload = [ 781 u"""update clin.encounter set 782 started = %(started)s, 783 last_affirmed = %(last_affirmed)s, 784 fk_location = %(pk_location)s, 785 fk_type = %(pk_type)s, 786 reason_for_encounter = gm.nullify_empty_string(%(reason_for_encounter)s), 787 assessment_of_encounter = gm.nullify_empty_string(%(assessment_of_encounter)s) 788 where 789 pk = %(pk_encounter)s and 790 xmin = %(xmin_encounter)s""", 791 u"""select xmin_encounter from clin.v_pat_encounters where pk_encounter=%(pk_encounter)s""" 792 ] 793 _updatable_fields = [ 794 'started', 795 'last_affirmed', 796 'pk_location', 797 'pk_type', 798 'reason_for_encounter', 799 'assessment_of_encounter' 800 ] 801 #--------------------------------------------------------
802 - def set_active(self):
803 """Set the enconter as the active one. 804 805 "Setting active" means making sure the encounter 806 row has the youngest "last_affirmed" timestamp of 807 all encounter rows for this patient. 808 """ 809 self['last_affirmed'] = gmDateTime.pydt_now_here() 810 self.save()
811 #--------------------------------------------------------
812 - def transfer_clinical_data(self, source_episode=None, target_episode=None):
813 """ 814 Moves every element currently linked to the current encounter 815 and the source_episode onto target_episode. 816 817 @param source_episode The episode the elements are currently linked to. 818 @type target_episode A cEpisode intance. 819 @param target_episode The episode the elements will be relinked to. 820 @type target_episode A cEpisode intance. 821 """ 822 if source_episode['pk_episode'] == target_episode['pk_episode']: 823 return True 824 825 queries = [] 826 cmd = u""" 827 UPDATE clin.clin_root_item 828 SET fk_episode = %(trg)s 829 WHERE 830 fk_encounter = %(enc)s AND 831 fk_episode = %(src)s 832 """ 833 rows, idx = gmPG2.run_rw_queries(queries = [{ 834 'cmd': cmd, 835 'args': { 836 'trg': target_episode['pk_episode'], 837 'enc': self.pk_obj, 838 'src': source_episode['pk_episode'] 839 } 840 }]) 841 self.refetch_payload() 842 return True
843 #--------------------------------------------------------
844 - def same_payload(self, another_object=None):
845 846 relevant_fields = [ 847 'pk_location', 848 'pk_type', 849 'pk_patient', 850 'reason_for_encounter', 851 'assessment_of_encounter' 852 ] 853 for field in relevant_fields: 854 if self._payload[self._idx[field]] != another_object[field]: 855 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 856 return False 857 858 relevant_fields = [ 859 'started', 860 'last_affirmed', 861 ] 862 for field in relevant_fields: 863 if self._payload[self._idx[field]] is None: 864 if another_object[field] is None: 865 continue 866 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 867 return False 868 869 if another_object[field] is None: 870 return False 871 872 #if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M:%S %Z') != another_object[field].strftime('%Y-%m-%d %H:%M:%S %Z'): 873 if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M') != another_object[field].strftime('%Y-%m-%d %H:%M'): 874 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 875 return False 876 877 return True
878 #--------------------------------------------------------
879 - def has_clinical_data(self):
880 cmd = u""" 881 select exists ( 882 select 1 from clin.v_pat_items where pk_patient = %(pat)s and pk_encounter = %(enc)s 883 union all 884 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s 885 )""" 886 args = { 887 'pat': self._payload[self._idx['pk_patient']], 888 'enc': self.pk_obj 889 } 890 rows, idx = gmPG2.run_ro_queries ( 891 queries = [{ 892 'cmd': cmd, 893 'args': args 894 }] 895 ) 896 return rows[0][0]
897 #--------------------------------------------------------
898 - def has_narrative(self):
899 cmd = u""" 900 select exists ( 901 select 1 from clin.v_pat_items where pk_patient=%(pat)s and pk_encounter=%(enc)s 902 )""" 903 args = { 904 'pat': self._payload[self._idx['pk_patient']], 905 'enc': self.pk_obj 906 } 907 rows, idx = gmPG2.run_ro_queries ( 908 queries = [{ 909 'cmd': cmd, 910 'args': args 911 }] 912 ) 913 return rows[0][0]
914 #--------------------------------------------------------
915 - def has_documents(self):
916 cmd = u""" 917 select exists ( 918 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s 919 )""" 920 args = { 921 'pat': self._payload[self._idx['pk_patient']], 922 'enc': self.pk_obj 923 } 924 rows, idx = gmPG2.run_ro_queries ( 925 queries = [{ 926 'cmd': cmd, 927 'args': args 928 }] 929 ) 930 return rows[0][0]
931 #--------------------------------------------------------
932 - def get_latest_soap(self, soap_cat=None, episode=None):
933 934 if soap_cat is not None: 935 soap_cat = soap_cat.lower() 936 937 if episode is None: 938 epi_part = u'fk_episode is null' 939 else: 940 epi_part = u'fk_episode = %(epi)s' 941 942 cmd = u""" 943 select narrative 944 from clin.clin_narrative 945 where 946 fk_encounter = %%(enc)s 947 and 948 soap_cat = %%(cat)s 949 and 950 %s 951 order by clin_when desc 952 limit 1 953 """ % epi_part 954 955 args = {'enc': self.pk_obj, 'cat': soap_cat, 'epi': episode} 956 957 rows, idx = gmPG2.run_ro_queries ( 958 queries = [{ 959 'cmd': cmd, 960 'args': args 961 }] 962 ) 963 if len(rows) == 0: 964 return None 965 966 return rows[0][0]
967 #--------------------------------------------------------
968 - def format_soap(self, episodes=None, left_margin=0, soap_cats='soap', emr=None, issues=None):
969 970 lines = [] 971 for soap_cat in soap_cats: 972 soap_cat_narratives = emr.get_clin_narrative ( 973 episodes = episodes, 974 issues = issues, 975 encounters = [self._payload[self._idx['pk_encounter']]], 976 soap_cats = [soap_cat] 977 ) 978 if soap_cat_narratives is None: 979 continue 980 if len(soap_cat_narratives) == 0: 981 continue 982 983 lines.append(u'-- %s ----------' % gmClinNarrative.soap_cat2l10n_str[soap_cat]) 984 for soap_entry in soap_cat_narratives: 985 txt = gmTools.wrap ( 986 text = u'%s\n (%.8s %s)' % ( 987 soap_entry['narrative'], 988 soap_entry['provider'], 989 soap_entry['date'].strftime('%Y-%m-%d %H:%M') 990 ), 991 width = 75, 992 initial_indent = u'', 993 subsequent_indent = (u' ' * left_margin) 994 ) 995 lines.append(txt) 996 lines.append('') 997 998 return lines
999 #--------------------------------------------------------
1000 - def format(self, episodes=None, with_soap=False, left_margin=0, patient=None, issues=None, with_docs=True, with_tests=True, fancy_header=True):
1001 1002 lines = [] 1003 1004 if fancy_header: 1005 lines.append(u'%s%s: %s - %s (@%s)%s [#%s]' % ( 1006 u' ' * left_margin, 1007 self._payload[self._idx['l10n_type']], 1008 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'), 1009 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'), 1010 self._payload[self._idx['source_time_zone']], 1011 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB'), 1012 self._payload[self._idx['pk_encounter']] 1013 )) 1014 1015 lines.append(_(' your time: %s - %s (@%s = %s%s)\n') % ( 1016 self._payload[self._idx['started']].strftime('%Y-%m-%d %H:%M'), 1017 self._payload[self._idx['last_affirmed']].strftime('%H:%M'), 1018 gmDateTime.current_local_iso_numeric_timezone_string, 1019 gmTools.bool2subst ( 1020 gmDateTime.dst_currently_in_effect, 1021 gmDateTime.py_dst_timezone_name, 1022 gmDateTime.py_timezone_name 1023 ), 1024 gmTools.bool2subst(gmDateTime.dst_currently_in_effect, u' - ' + _('daylight savings time in effect'), u'') 1025 )) 1026 1027 lines.append(u'%s: %s' % ( 1028 _('RFE'), 1029 gmTools.coalesce(self._payload[self._idx['reason_for_encounter']], u'') 1030 )) 1031 lines.append(u'%s: %s' % ( 1032 _('AOE'), 1033 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'') 1034 )) 1035 1036 else: 1037 lines.append(u'%s%s: %s - %s%s' % ( 1038 u' ' * left_margin, 1039 self._payload[self._idx['l10n_type']], 1040 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'), 1041 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'), 1042 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB') 1043 )) 1044 1045 if with_soap: 1046 lines.append(u'') 1047 1048 if patient.ID != self._payload[self._idx['pk_patient']]: 1049 msg = '<patient>.ID = %s but encounter %s belongs to patient %s' % ( 1050 patient.ID, 1051 self._payload[self._idx['pk_encounter']], 1052 self._payload[self._idx['pk_patient']] 1053 ) 1054 raise ValueError(msg) 1055 1056 emr = patient.get_emr() 1057 1058 lines.extend(self.format_soap ( 1059 episodes = episodes, 1060 left_margin = left_margin, 1061 soap_cats = 'soap', 1062 emr = emr, 1063 issues = issues 1064 )) 1065 1066 # test results 1067 if with_tests: 1068 tests = emr.get_test_results_by_date ( 1069 episodes = episodes, 1070 encounter = self._payload[self._idx['pk_encounter']] 1071 ) 1072 if len(tests) > 0: 1073 lines.append('') 1074 lines.append(_('Measurements and Results:')) 1075 1076 for t in tests: 1077 lines.extend(t.format()) 1078 1079 del tests 1080 1081 if with_docs: 1082 doc_folder = patient.get_document_folder() 1083 docs = doc_folder.get_documents ( 1084 episodes = episodes, 1085 encounter = self._payload[self._idx['pk_encounter']] 1086 ) 1087 1088 if len(docs) > 0: 1089 lines.append('') 1090 lines.append(_('Documents:')) 1091 1092 for d in docs: 1093 lines.append(u' %s %s:%s%s' % ( 1094 d['clin_when'].strftime('%Y-%m-%d'), 1095 d['l10n_type'], 1096 gmTools.coalesce(d['comment'], u'', u' "%s"'), 1097 gmTools.coalesce(d['ext_ref'], u'', u' (%s)') 1098 )) 1099 1100 del docs 1101 1102 eol_w_margin = u'\n%s' % (u' ' * left_margin) 1103 return u'%s\n' % eol_w_margin.join(lines)
1104 1105 # special items (vaccinations, ...) 1106 1107 # try: 1108 # filtered_items.extend(emr.get_vaccinations( 1109 # since=self.__constraints['since'], 1110 # until=self.__constraints['until'], 1111 # encounters=self.__constraints['encounters'], 1112 # episodes=self.__constraints['episodes'], 1113 # issues=self.__constraints['issues'])) 1114 # except: 1115 # _log.error("vaccination error? outside regime") 1116 1117 #-----------------------------------------------------------
1118 -def create_encounter(fk_patient=None, fk_location=-1, enc_type=None):
1119 """Creates a new encounter for a patient. 1120 1121 fk_patient - patient PK 1122 fk_location - encounter location 1123 enc_type - type of encounter 1124 1125 FIXME: we don't deal with location yet 1126 """ 1127 if enc_type is None: 1128 enc_type = u'in surgery' 1129 # insert new encounter 1130 queries = [] 1131 try: 1132 enc_type = int(enc_type) 1133 cmd = u""" 1134 insert into clin.encounter ( 1135 fk_patient, fk_location, fk_type 1136 ) values ( 1137 %s, -1, %s 1138 )""" 1139 except ValueError: 1140 enc_type = enc_type 1141 cmd = u""" 1142 insert into clin.encounter ( 1143 fk_patient, fk_location, fk_type 1144 ) values ( 1145 %s, -1, coalesce((select pk from clin.encounter_type where description=%s), 0) 1146 )""" 1147 queries.append({'cmd': cmd, 'args': [fk_patient, enc_type]}) 1148 queries.append({'cmd': cEncounter._cmd_fetch_payload % u"currval('clin.encounter_pk_seq')"}) 1149 rows, idx = gmPG2.run_rw_queries(queries=queries, return_data=True, get_col_idx=True) 1150 encounter = cEncounter(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_encounter'}) 1151 1152 return encounter
1153 #-----------------------------------------------------------
1154 -def update_encounter_type(description=None, l10n_description=None):
1155 1156 rows, idx = gmPG2.run_rw_queries( 1157 queries = [{ 1158 'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 1159 'args': {'desc': description, 'l10n_desc': l10n_description} 1160 }], 1161 return_data = True 1162 ) 1163 1164 success = rows[0][0] 1165 if not success: 1166 _log.warning('updating encounter type [%s] to [%s] failed', description, l10n_description) 1167 1168 return {'description': description, 'l10n_description': l10n_description}
1169 #-----------------------------------------------------------
1170 -def create_encounter_type(description=None, l10n_description=None):
1171 """This will attempt to create a NEW encounter type.""" 1172 1173 # need a system name, so derive one if necessary 1174 if description is None: 1175 description = l10n_description 1176 1177 args = { 1178 'desc': description, 1179 'l10n_desc': l10n_description 1180 } 1181 1182 _log.debug('creating encounter type: %s, %s', description, l10n_description) 1183 1184 # does it exist already ? 1185 cmd = u"select description, _(description) from clin.encounter_type where description = %(desc)s" 1186 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1187 1188 # yes 1189 if len(rows) > 0: 1190 # both system and l10n name are the same so all is well 1191 if (rows[0][0] == description) and (rows[0][1] == l10n_description): 1192 _log.info('encounter type [%s] already exists with the proper translation') 1193 return {'description': description, 'l10n_description': l10n_description} 1194 1195 # or maybe there just wasn't a translation to 1196 # the current language for this type yet ? 1197 cmd = u"select exists (select 1 from i18n.translations where orig = %(desc)s and lang = i18n.get_curr_lang())" 1198 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1199 1200 # there was, so fail 1201 if rows[0][0]: 1202 _log.error('encounter type [%s] already exists but with another translation') 1203 return None 1204 1205 # else set it 1206 cmd = u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)" 1207 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1208 return {'description': description, 'l10n_description': l10n_description} 1209 1210 # no 1211 queries = [ 1212 {'cmd': u"insert into clin.encounter_type (description) values (%(desc)s)", 'args': args}, 1213 {'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 'args': args} 1214 ] 1215 rows, idx = gmPG2.run_rw_queries(queries = queries) 1216 1217 return {'description': description, 'l10n_description': l10n_description}
1218 #-----------------------------------------------------------
1219 -def get_encounter_types():
1220 cmd = u"select _(description) as l10n_description, description from clin.encounter_type" 1221 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 1222 return rows
1223 #-----------------------------------------------------------
1224 -def get_encounter_type(description=None):
1225 cmd = u"SELECT * from clin.encounter_type where description = %s" 1226 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [description]}]) 1227 return rows
1228 #-----------------------------------------------------------
1229 -def delete_encounter_type(description=None):
1230 cmd = u"delete from clin.encounter_type where description = %(desc)s" 1231 args = {'desc': description} 1232 try: 1233 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1234 except gmPG2.dbapi.IntegrityError, e: 1235 if e.pgcode == gmPG2.sql_error_codes.FOREIGN_KEY_VIOLATION: 1236 return False 1237 raise 1238 1239 return True
1240 #============================================================
1241 -class cProblem(gmBusinessDBObject.cBusinessDBObject):
1242 """Represents one problem. 1243 1244 problems are the aggregation of 1245 .clinically_relevant=True issues and 1246 .is_open=True episodes 1247 """ 1248 _cmd_fetch_payload = u'' # will get programmatically defined in __init__ 1249 _cmds_store_payload = [u"select 1"] 1250 _updatable_fields = [] 1251 1252 #--------------------------------------------------------
1253 - def __init__(self, aPK_obj=None, try_potential_problems=False):
1254 """Initialize. 1255 1256 aPK_obj must contain the keys 1257 pk_patient 1258 pk_episode 1259 pk_health_issue 1260 """ 1261 if aPK_obj is None: 1262 raise gmExceptions.ConstructorError, 'cannot instatiate cProblem for PK: [%s]' % (aPK_obj) 1263 1264 # As problems are rows from a view of different emr struct items, 1265 # the PK can't be a single field and, as some of the values of the 1266 # composed PK may be None, they must be queried using 'is null', 1267 # so we must programmatically construct the SQL query 1268 where_parts = [] 1269 pk = {} 1270 for col_name in aPK_obj.keys(): 1271 val = aPK_obj[col_name] 1272 if val is None: 1273 where_parts.append('%s IS NULL' % col_name) 1274 else: 1275 where_parts.append('%s = %%(%s)s' % (col_name, col_name)) 1276 pk[col_name] = val 1277 1278 # try to instantiate from true problem view 1279 cProblem._cmd_fetch_payload = u""" 1280 SELECT *, False as is_potential_problem 1281 FROM clin.v_problem_list 1282 WHERE %s""" % u' AND '.join(where_parts) 1283 1284 try: 1285 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk) 1286 return 1287 except gmExceptions.ConstructorError: 1288 _log.exception('problem not found, trying potential problems') 1289 if try_potential_problems is False: 1290 raise 1291 1292 # try to instantiate from non-problem view 1293 cProblem._cmd_fetch_payload = u""" 1294 SELECT *, True as is_potential_problem 1295 FROM clin.v_potential_problem_list 1296 WHERE %s""" % u' AND '.join(where_parts) 1297 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)
1298 #--------------------------------------------------------
1299 - def get_as_episode(self):
1300 """ 1301 Retrieve the cEpisode instance equivalent to this problem. 1302 The problem's type attribute must be 'episode' 1303 """ 1304 if self._payload[self._idx['type']] != 'episode': 1305 _log.error('cannot convert problem [%s] of type [%s] to episode' % (self._payload[self._idx['problem']], self._payload[self._idx['type']])) 1306 return None 1307 return cEpisode(aPK_obj=self._payload[self._idx['pk_episode']])
1308 #-------------------------------------------------------- 1309 # doubles as 'diagnostic_certainty_description' getter:
1311 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])
1312 1313 diagnostic_certainty_description = property(get_diagnostic_certainty_description, lambda x:x)
1314 #============================================================
1315 -class cHospitalStay(gmBusinessDBObject.cBusinessDBObject):
1316 1317 _cmd_fetch_payload = u"select * from clin.v_pat_hospital_stays where pk_hospital_stay = %s" 1318 _cmds_store_payload = [ 1319 u"""update clin.hospital_stay set 1320 clin_when = %(admission)s, 1321 discharge = %(discharge)s, 1322 narrative = gm.nullify_empty_string(%(hospital)s), 1323 fk_episode = %(pk_episode)s, 1324 fk_encounter = %(pk_encounter)s 1325 where 1326 pk = %(pk_hospital_stay)s and 1327 xmin = %(xmin_hospital_stay)s""", 1328 u"""select xmin_hospital_stay from clin.v_pat_hospital_stays where pk_hospital_stay = %(pk_hospital_stay)s""" 1329 ] 1330 _updatable_fields = [ 1331 'admission', 1332 'discharge', 1333 'hospital', 1334 'pk_episode', 1335 'pk_encounter' 1336 ] 1337 #-------------------------------------------------------
1338 - def format(self, left_margin=0, include_procedures=False, include_docs=False):
1339 1340 if self._payload[self._idx['discharge']] is not None: 1341 dis = u' - %s' % self._payload[self._idx['discharge']].strftime('%Y %b %d').decode(gmI18N.get_encoding()) 1342 else: 1343 dis = u'' 1344 1345 line = u'%s%s%s%s: %s%s%s' % ( 1346 u' ' * left_margin, 1347 self._payload[self._idx['admission']].strftime('%Y %b %d').decode(gmI18N.get_encoding()), 1348 dis, 1349 gmTools.coalesce(self._payload[self._idx['hospital']], u'', u' (%s)'), 1350 gmTools.u_left_double_angle_quote, 1351 self._payload[self._idx['episode']], 1352 gmTools.u_right_double_angle_quote 1353 ) 1354 1355 return line
1356 #-----------------------------------------------------------
1357 -def get_patient_hospital_stays(patient=None):
1358 1359 queries = [ 1360 { 1361 'cmd': u'select * from clin.v_pat_hospital_stays where pk_patient = %(pat)s order by admission', 1362 'args': {'pat': patient} 1363 } 1364 ] 1365 1366 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 1367 1368 return [ cHospitalStay(row = {'idx': idx, 'data': r, 'pk_field': 'pk_hospital_stay'}) for r in rows ]
1369 #-----------------------------------------------------------
1370 -def create_hospital_stay(encounter=None, episode=None):
1371 1372 queries = [ 1373 { 1374 'cmd': u'insert into clin.hospital_stay (fk_encounter, fk_episode) values (%(enc)s, %(epi)s)', 1375 'args': {'enc': encounter, 'epi': episode} 1376 }, 1377 {'cmd': u"select currval('clin.hospital_stay_pk_seq')"} 1378 ] 1379 1380 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 1381 1382 return cHospitalStay(aPK_obj = rows[0][0])
1383 #-----------------------------------------------------------
1384 -def delete_hospital_stay(stay=None):
1385 cmd = u'delete from clin.hospital_stay where pk = %(pk)s' 1386 args = {'pk': stay} 1387 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1388 return True
1389 #============================================================
1390 -class cPerformedProcedure(gmBusinessDBObject.cBusinessDBObject):
1391 1392 _cmd_fetch_payload = u"select * from clin.v_pat_procedures where pk_procedure = %s" 1393 _cmds_store_payload = [ 1394 u"""update clin.procedure set 1395 clin_when = %(clin_when)s, 1396 clin_where = gm.nullify_empty_string(%(clin_where)s), 1397 narrative = gm.nullify_empty_string(%(performed_procedure)s), 1398 fk_hospital_stay = %(pk_hospital_stay)s, 1399 fk_episode = %(pk_episode)s, 1400 fk_encounter = %(pk_encounter)s 1401 where 1402 pk = %(pk_procedure)s and 1403 xmin = %(xmin_procedure)s 1404 """, 1405 u"""select xmin_procedure from clin.v_pat_procedures where pk_procedure = %(pk_procedure)s""" 1406 ] 1407 _updatable_fields = [ 1408 'clin_when', 1409 'clin_where', 1410 'performed_procedure', 1411 'pk_hospital_stay', 1412 'pk_episode', 1413 'pk_encounter' 1414 ] 1415 #-------------------------------------------------------
1416 - def __setitem__(self, attribute, value):
1417 1418 if (attribute == 'pk_hospital_stay') and (value is not None): 1419 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'clin_where', None) 1420 1421 if (attribute == 'clin_where') and (value is not None) and (value.strip() != u''): 1422 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'pk_hospital_stay', None) 1423 1424 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)
1425 #-------------------------------------------------------
1426 - def format(self, left_margin=0, include_episode=True):
1427 1428 line = u'%s%s (%s): %s' % ( 1429 (u' ' * left_margin), 1430 self._payload[self._idx['clin_when']].strftime('%Y %b %d').decode(gmI18N.get_encoding()), 1431 self._payload[self._idx['clin_where']], 1432 self._payload[self._idx['performed_procedure']] 1433 ) 1434 if include_episode: 1435 line = u'%s (%s)' % (line, self._payload[self._idx['episode']]) 1436 1437 return line
1438 #-----------------------------------------------------------
1439 -def get_performed_procedures(patient=None):
1440 1441 queries = [ 1442 { 1443 'cmd': u'select * from clin.v_pat_procedures where pk_patient = %(pat)s order by clin_when', 1444 'args': {'pat': patient} 1445 } 1446 ] 1447 1448 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 1449 1450 return [ cPerformedProcedure(row = {'idx': idx, 'data': r, 'pk_field': 'pk_procedure'}) for r in rows ]
1451 #-----------------------------------------------------------
1452 -def create_performed_procedure(encounter=None, episode=None, location=None, hospital_stay=None, procedure=None):
1453 1454 queries = [{ 1455 'cmd': u""" 1456 insert into clin.procedure ( 1457 fk_encounter, 1458 fk_episode, 1459 clin_where, 1460 fk_hospital_stay, 1461 narrative 1462 ) values ( 1463 %(enc)s, 1464 %(epi)s, 1465 gm.nullify_empty_string(%(loc)s), 1466 %(stay)s, 1467 %(proc)s 1468 ) 1469 returning pk""", 1470 'args': {'enc': encounter, 'epi': episode, 'loc': location, 'stay': hospital_stay, 'proc': procedure} 1471 }] 1472 1473 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 1474 1475 return cPerformedProcedure(aPK_obj = rows[0][0])
1476 #-----------------------------------------------------------
1477 -def delete_performed_procedure(procedure=None):
1478 cmd = u'delete from clin.procedure where pk = %(pk)s' 1479 args = {'pk': procedure} 1480 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1481 return True
1482 #============================================================ 1483 # main - unit testing 1484 #------------------------------------------------------------ 1485 if __name__ == '__main__': 1486 1487 if len(sys.argv) < 2: 1488 sys.exit() 1489 1490 if sys.argv[1] != 'test': 1491 sys.exit() 1492 1493 #-------------------------------------------------------- 1494 # define tests 1495 #--------------------------------------------------------
1496 - def test_problem():
1497 print "\nProblem test" 1498 print "------------" 1499 prob = cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': None}) 1500 print prob 1501 fields = prob.get_fields() 1502 for field in fields: 1503 print field, ':', prob[field] 1504 print '\nupdatable:', prob.get_updatable_fields() 1505 epi = prob.get_as_episode() 1506 print '\nas episode:' 1507 if epi is not None: 1508 for field in epi.get_fields(): 1509 print ' .%s : %s' % (field, epi[field])
1510 #--------------------------------------------------------
1511 - def test_health_issue():
1512 print "\nhealth issue test" 1513 print "-----------------" 1514 h_issue = cHealthIssue(aPK_obj=2) 1515 print h_issue 1516 fields = h_issue.get_fields() 1517 for field in fields: 1518 print field, ':', h_issue[field] 1519 print "has open episode:", h_issue.has_open_episode() 1520 print "open episode:", h_issue.get_open_episode() 1521 print "updateable:", h_issue.get_updatable_fields() 1522 h_issue.close_expired_episode(ttl=7300) 1523 h_issue = cHealthIssue(encounter = 1, name = u'post appendectomy/peritonitis') 1524 print h_issue
1525 #--------------------------------------------------------
1526 - def test_episode():
1527 print "\nepisode test" 1528 print "------------" 1529 episode = cEpisode(aPK_obj=1) 1530 print episode 1531 fields = episode.get_fields() 1532 for field in fields: 1533 print field, ':', episode[field] 1534 print "updatable:", episode.get_updatable_fields() 1535 raw_input('ENTER to continue') 1536 1537 old_description = episode['description'] 1538 old_enc = cEncounter(aPK_obj = 1) 1539 1540 desc = '1-%s' % episode['description'] 1541 print "==> renaming to", desc 1542 successful = episode.rename ( 1543 description = desc 1544 ) 1545 if not successful: 1546 print "error" 1547 else: 1548 print "success" 1549 for field in fields: 1550 print field, ':', episode[field] 1551 1552 print "episode range:", episode.get_access_range() 1553 1554 raw_input('ENTER to continue')
1555 1556 #--------------------------------------------------------
1557 - def test_encounter():
1558 print "\nencounter test" 1559 print "--------------" 1560 encounter = cEncounter(aPK_obj=1) 1561 print encounter 1562 fields = encounter.get_fields() 1563 for field in fields: 1564 print field, ':', encounter[field] 1565 print "updatable:", encounter.get_updatable_fields()
1566 #--------------------------------------------------------
1567 - def test_performed_procedure():
1568 procs = get_performed_procedures(patient = 12) 1569 for proc in procs: 1570 print proc.format(left_margin=2)
1571 #--------------------------------------------------------
1572 - def test_hospital_stay():
1573 stay = create_hospital_stay(encounter = 1, episode = 2) 1574 stay['hospital'] = u'Starfleet Galaxy General Hospital' 1575 stay.save_payload() 1576 print stay 1577 for s in get_patient_hospital_stays(12): 1578 print s 1579 delete_hospital_stay(stay['pk_hospital_stay']) 1580 stay = create_hospital_stay(encounter = 1, episode = 4)
1581 #--------------------------------------------------------
1582 - def test_diagnostic_certainty_classification_map():
1583 tests = [None, 'A', 'B', 'C', 'D', 'E'] 1584 1585 for t in tests: 1586 print type(t), t 1587 print type(diagnostic_certainty_classification2str(t)), diagnostic_certainty_classification2str(t)
1588 1589 #-------------------------------------------------------- 1590 # run them 1591 #test_episode() 1592 #test_problem() 1593 #test_encounter() 1594 #test_health_issue() 1595 #test_hospital_stay() 1596 #test_performed_procedure() 1597 test_diagnostic_certainty_classification_map() 1598 #============================================================ 1599