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