Home | Trees | Indices | Help |
|
---|
|
1 # -*- coding: utf8 -*- 2 """GNUmed health related business object. 3 4 license: GPL v2 or later 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 16 from Gnumed.pycommon import gmI18N 17 from Gnumed.pycommon import gmTools 18 from Gnumed.pycommon import gmDateTime 19 from Gnumed.pycommon import gmBusinessDBObject 20 from Gnumed.pycommon import gmNull 21 from Gnumed.pycommon import gmExceptions 22 23 from Gnumed.business import gmClinNarrative 24 from Gnumed.business import gmCoding 25 26 27 _log = logging.getLogger('gm.emr') 28 _log.info(__version__) 29 30 try: _ 31 except NameError: _ = lambda x:x 32 #============================================================ 33 # diagnostic certainty classification 34 #============================================================ 35 __diagnostic_certainty_classification_map = None 3638 39 global __diagnostic_certainty_classification_map 40 41 if __diagnostic_certainty_classification_map is None: 42 __diagnostic_certainty_classification_map = { 43 None: u'', 44 u'A': _(u'A: Sign'), 45 u'B': _(u'B: Cluster of signs'), 46 u'C': _(u'C: Syndromic diagnosis'), 47 u'D': _(u'D: Scientific diagnosis') 48 } 49 50 try: 51 return __diagnostic_certainty_classification_map[classification] 52 except KeyError: 53 return _(u'<%s>: unknown diagnostic certainty classification') % classification54 #============================================================ 55 # Health Issues API 56 #============================================================ 57 laterality2str = { 58 None: u'?', 59 u'na': u'', 60 u'sd': _('bilateral'), 61 u'ds': _('bilateral'), 62 u's': _('left'), 63 u'd': _('right') 64 } 65 66 #============================================================68 """Represents one health issue.""" 69 70 _cmd_fetch_payload = u"select *, xmin_health_issue from clin.v_health_issues where pk_health_issue=%s" 71 _cmds_store_payload = [ 72 u"""update clin.health_issue set 73 description = %(description)s, 74 summary = gm.nullify_empty_string(%(summary)s), 75 age_noted = %(age_noted)s, 76 laterality = gm.nullify_empty_string(%(laterality)s), 77 grouping = gm.nullify_empty_string(%(grouping)s), 78 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s), 79 is_active = %(is_active)s, 80 clinically_relevant = %(clinically_relevant)s, 81 is_confidential = %(is_confidential)s, 82 is_cause_of_death = %(is_cause_of_death)s 83 where 84 pk = %(pk_health_issue)s and 85 xmin = %(xmin_health_issue)s""", 86 u"select xmin as xmin_health_issue from clin.health_issue where pk = %(pk_health_issue)s" 87 ] 88 _updatable_fields = [ 89 'description', 90 'summary', 91 'grouping', 92 'age_noted', 93 'laterality', 94 'is_active', 95 'clinically_relevant', 96 'is_confidential', 97 'is_cause_of_death', 98 'diagnostic_certainty_classification' 99 ] 100 #--------------------------------------------------------556 #============================================================101 - def __init__(self, aPK_obj=None, encounter=None, name='xxxDEFAULTxxx', patient=None, row=None):102 pk = aPK_obj 103 104 if (pk is not None) or (row is not None): 105 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row) 106 return 107 108 if patient is None: 109 cmd = u"""select *, xmin_health_issue from clin.v_health_issues 110 where 111 description = %(desc)s 112 and 113 pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)""" 114 else: 115 cmd = u"""select *, xmin_health_issue from clin.v_health_issues 116 where 117 description = %(desc)s 118 and 119 pk_patient = %(pat)s""" 120 121 queries = [{'cmd': cmd, 'args': {'enc': encounter, 'desc': name, 'pat': patient}}] 122 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 123 124 if len(rows) == 0: 125 raise gmExceptions.NoSuchBusinessObjectError, 'no health issue for [enc:%s::desc:%s::pat:%s]' % (encounter, name, patient) 126 127 pk = rows[0][0] 128 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_health_issue'} 129 130 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r)131 #-------------------------------------------------------- 132 # external API 133 #--------------------------------------------------------135 """Method for issue renaming. 136 137 @param description 138 - the new descriptive name for the issue 139 @type description 140 - a string instance 141 """ 142 # sanity check 143 if not type(description) in [str, unicode] or description.strip() == '': 144 _log.error('<description> must be a non-empty string') 145 return False 146 # update the issue description 147 old_description = self._payload[self._idx['description']] 148 self._payload[self._idx['description']] = description.strip() 149 self._is_modified = True 150 successful, data = self.save_payload() 151 if not successful: 152 _log.error('cannot rename health issue [%s] with [%s]' % (self, description)) 153 self._payload[self._idx['description']] = old_description 154 return False 155 return True156 #--------------------------------------------------------158 cmd = u"SELECT * FROM clin.v_pat_episodes WHERE pk_health_issue = %(pk)s" 159 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}], get_col_idx = True) 160 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]161 #--------------------------------------------------------163 """ttl in days""" 164 open_episode = self.get_open_episode() 165 if open_episode is None: 166 return True 167 earliest, latest = open_episode.get_access_range() 168 ttl = datetime.timedelta(ttl) 169 now = datetime.datetime.now(tz=latest.tzinfo) 170 if (latest + ttl) > now: 171 return False 172 open_episode['episode_open'] = False 173 success, data = open_episode.save_payload() 174 if success: 175 return True 176 return False # should be an exception177 #--------------------------------------------------------179 open_episode = self.get_open_episode() 180 open_episode['episode_open'] = False 181 success, data = open_episode.save_payload() 182 if success: 183 return True 184 return False185 #--------------------------------------------------------187 cmd = u"select exists (select 1 from clin.episode where fk_health_issue = %s and is_open is True)" 188 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 189 return rows[0][0]190 #--------------------------------------------------------192 cmd = u"select pk from clin.episode where fk_health_issue = %s and is_open is True" 193 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [self.pk_obj]}]) 194 if len(rows) == 0: 195 return None 196 return cEpisode(aPK_obj=rows[0][0])197 #--------------------------------------------------------199 if self._payload[self._idx['age_noted']] is None: 200 return u'<???>' 201 202 # since we've already got an interval we are bound to use it, 203 # further transformation will only introduce more errors, 204 # later we can improve this deeper inside 205 return gmDateTime.format_interval_medically(self._payload[self._idx['age_noted']])206 #--------------------------------------------------------208 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 209 cmd = u"INSERT INTO clin.lnk_code2h_issue (fk_item, fk_generic_code) values (%(item)s, %(code)s)" 210 args = { 211 'item': self._payload[self._idx['pk_health_issue']], 212 'code': pk_code 213 } 214 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 215 return True216 #--------------------------------------------------------218 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 219 cmd = u"DELETE FROM clin.lnk_code2h_issue WHERE fk_item = %(item)s AND fk_generic_code = %(code)s" 220 args = { 221 'item': self._payload[self._idx['pk_health_issue']], 222 'code': pk_code 223 } 224 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 225 return True226 #--------------------------------------------------------228 rows = gmClinNarrative.get_as_journal ( 229 issues = (self.pk_obj,), 230 order_by = u'pk_episode, pk_encounter, clin_when, scr, src_table' 231 ) 232 233 if len(rows) == 0: 234 return u'' 235 236 left_margin = u' ' * left_margin 237 238 lines = [] 239 lines.append(_('Clinical data generated during encounters under this health issue:')) 240 241 prev_epi = None 242 for row in rows: 243 if row['pk_episode'] != prev_epi: 244 lines.append(u'') 245 prev_epi = row['pk_episode'] 246 247 when = row['clin_when'].strftime(date_format).decode(gmI18N.get_encoding()) 248 top_row = u'%s%s %s (%s) %s' % ( 249 gmTools.u_box_top_left_arc, 250 gmTools.u_box_horiz_single, 251 gmClinNarrative.soap_cat2l10n_str[row['real_soap_cat']], 252 when, 253 gmTools.u_box_horiz_single * 5 254 ) 255 soap = gmTools.wrap ( 256 text = row['narrative'], 257 width = 60, 258 initial_indent = u' ', 259 subsequent_indent = u' ' + left_margin 260 ) 261 row_ver = u'' 262 if row['row_version'] > 0: 263 row_ver = u'v%s: ' % row['row_version'] 264 bottom_row = u'%s%s %s, %s%s %s' % ( 265 u' ' * 40, 266 gmTools.u_box_horiz_light_heavy, 267 row['modified_by'], 268 row_ver, 269 row['date_modified'], 270 gmTools.u_box_horiz_heavy_light 271 ) 272 273 lines.append(top_row) 274 lines.append(soap) 275 lines.append(bottom_row) 276 277 eol_w_margin = u'\n%s' % left_margin 278 return left_margin + eol_w_margin.join(lines) + u'\n'279 #--------------------------------------------------------281 282 if patient.ID != self._payload[self._idx['pk_patient']]: 283 msg = '<patient>.ID = %s but health issue %s belongs to patient %s' % ( 284 patient.ID, 285 self._payload[self._idx['pk_health_issue']], 286 self._payload[self._idx['pk_patient']] 287 ) 288 raise ValueError(msg) 289 290 lines = [] 291 292 lines.append(_('Health Issue %s%s%s%s [#%s]') % ( 293 u'\u00BB', 294 self._payload[self._idx['description']], 295 u'\u00AB', 296 gmTools.coalesce ( 297 initial = self.laterality_description, 298 instead = u'', 299 template_initial = u' (%s)', 300 none_equivalents = [None, u'', u'?'] 301 ), 302 self._payload[self._idx['pk_health_issue']] 303 )) 304 305 if self._payload[self._idx['is_confidential']]: 306 lines.append('') 307 lines.append(_(' ***** CONFIDENTIAL *****')) 308 lines.append('') 309 310 if self._payload[self._idx['is_cause_of_death']]: 311 lines.append('') 312 lines.append(_(' contributed to death of patient')) 313 lines.append('') 314 315 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']]) 316 lines.append (_(' Created during encounter: %s (%s - %s) [#%s]') % ( 317 enc['l10n_type'], 318 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 319 enc['last_affirmed_original_tz'].strftime('%H:%M'), 320 self._payload[self._idx['pk_encounter']] 321 )) 322 323 if self._payload[self._idx['age_noted']] is not None: 324 lines.append(_(' Noted at age: %s') % self.age_noted_human_readable()) 325 326 lines.append(u' ' + _('Status') + u': %s, %s%s' % ( 327 gmTools.bool2subst(self._payload[self._idx['is_active']], _('active'), _('inactive')), 328 gmTools.bool2subst(self._payload[self._idx['clinically_relevant']], _('clinically relevant'), _('not clinically relevant')), 329 gmTools.coalesce ( 330 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]), 331 instead = u'', 332 template_initial = u', %s', 333 none_equivalents = [None, u''] 334 ) 335 )) 336 337 if self._payload[self._idx['summary']] is not None: 338 lines.append(u'') 339 lines.append(gmTools.wrap ( 340 text = self._payload[self._idx['summary']], 341 width = 60, 342 initial_indent = u' ', 343 subsequent_indent = u' ' 344 )) 345 346 # codes 347 codes = self.generic_codes 348 if len(codes) > 0: 349 lines.append(u'') 350 for c in codes: 351 lines.append(u' %s: %s (%s - %s)' % ( 352 c['code'], 353 c['term'], 354 c['name_short'], 355 c['version'] 356 )) 357 del codes 358 359 lines.append(u'') 360 361 emr = patient.get_emr() 362 363 # episodes 364 epis = emr.get_episodes(issues = [self._payload[self._idx['pk_health_issue']]]) 365 if epis is None: 366 lines.append(_('Error retrieving episodes for this health issue.')) 367 elif len(epis) == 0: 368 lines.append(_('There are no episodes for this health issue.')) 369 else: 370 lines.append ( 371 _('Episodes: %s (most recent: %s%s%s)') % ( 372 len(epis), 373 gmTools.u_left_double_angle_quote, 374 emr.get_most_recent_episode(issue = self._payload[self._idx['pk_health_issue']])['description'], 375 gmTools.u_right_double_angle_quote 376 ) 377 ) 378 for epi in epis: 379 lines.append(u' \u00BB%s\u00AB (%s)' % ( 380 epi['description'], 381 gmTools.bool2subst(epi['episode_open'], _('ongoing'), _('closed')) 382 )) 383 384 lines.append('') 385 386 # encounters 387 first_encounter = emr.get_first_encounter(issue_id = self._payload[self._idx['pk_health_issue']]) 388 last_encounter = emr.get_last_encounter(issue_id = self._payload[self._idx['pk_health_issue']]) 389 390 if first_encounter is None or last_encounter is None: 391 lines.append(_('No encounters found for this health issue.')) 392 else: 393 encs = emr.get_encounters(issues = [self._payload[self._idx['pk_health_issue']]]) 394 lines.append(_('Encounters: %s (%s - %s):') % ( 395 len(encs), 396 first_encounter['started_original_tz'].strftime('%m/%Y'), 397 last_encounter['last_affirmed_original_tz'].strftime('%m/%Y') 398 )) 399 lines.append(_(' Most recent: %s - %s') % ( 400 last_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 401 last_encounter['last_affirmed_original_tz'].strftime('%H:%M') 402 )) 403 404 # medications 405 meds = emr.get_current_substance_intake ( 406 issues = [ self._payload[self._idx['pk_health_issue']] ], 407 order_by = u'is_currently_active, started, substance' 408 ) 409 410 if len(meds) > 0: 411 lines.append(u'') 412 lines.append(_('Active medications: %s') % len(meds)) 413 for m in meds: 414 lines.append(m.format(left_margin = (left_margin + 1))) 415 del meds 416 417 # hospital stays 418 stays = emr.get_hospital_stays ( 419 issues = [ self._payload[self._idx['pk_health_issue']] ] 420 ) 421 if len(stays) > 0: 422 lines.append(u'') 423 lines.append(_('Hospital stays: %s') % len(stays)) 424 for s in stays: 425 lines.append(s.format(left_margin = (left_margin + 1))) 426 del stays 427 428 # procedures 429 procs = emr.get_performed_procedures ( 430 issues = [ self._payload[self._idx['pk_health_issue']] ] 431 ) 432 if len(procs) > 0: 433 lines.append(u'') 434 lines.append(_('Procedures performed: %s') % len(procs)) 435 for p in procs: 436 lines.append(p.format(left_margin = (left_margin + 1))) 437 del procs 438 439 # family history 440 fhx = emr.get_family_history(issues = [ self._payload[self._idx['pk_health_issue']] ]) 441 if len(fhx) > 0: 442 lines.append(u'') 443 lines.append(_('Family History: %s') % len(fhx)) 444 for f in fhx: 445 lines.append(f.format ( 446 left_margin = (left_margin + 1), 447 include_episode = True, 448 include_comment = True, 449 include_codes = False 450 )) 451 del fhx 452 453 epis = self.get_episodes() 454 if len(epis) > 0: 455 epi_pks = [ e['pk_episode'] for e in epis ] 456 457 # documents 458 doc_folder = patient.get_document_folder() 459 docs = doc_folder.get_documents(episodes = epi_pks) 460 if len(docs) > 0: 461 lines.append(u'') 462 lines.append(_('Documents: %s') % len(docs)) 463 del docs 464 465 # test results 466 tests = emr.get_test_results_by_date(episodes = epi_pks) 467 if len(tests) > 0: 468 lines.append(u'') 469 lines.append(_('Measurements and Results: %s') % len(tests)) 470 del tests 471 472 # vaccinations 473 vaccs = emr.get_vaccinations(episodes = epi_pks) 474 if len(vaccs) > 0: 475 lines.append(u'') 476 lines.append(_('Vaccinations:')) 477 for vacc in vaccs: 478 lines.extend(vacc.format(with_reaction = True)) 479 del vaccs 480 481 del epis 482 483 left_margin = u' ' * left_margin 484 eol_w_margin = u'\n%s' % left_margin 485 return left_margin + eol_w_margin.join(lines) + u'\n'486 #-------------------------------------------------------- 487 # properties 488 #-------------------------------------------------------- 489 episodes = property(get_episodes, lambda x:x) 490 #-------------------------------------------------------- 491 open_episode = property(get_open_episode, lambda x:x) 492 #--------------------------------------------------------494 cmd = u"""SELECT 495 coalesce ( 496 (SELECT pk FROM clin.episode WHERE fk_health_issue = %(issue)s AND is_open IS TRUE), 497 (SELECT pk FROM clin.v_pat_episodes WHERE fk_health_issue = %(issue)s ORDER BY last_affirmed DESC limit 1) 498 )""" 499 args = {'issue': self.pk_obj} 500 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 501 if len(rows) == 0: 502 return None 503 return cEpisode(aPK_obj = rows[0][0])504 505 latest_episode = property(_get_latest_episode, lambda x:x) 506 #--------------------------------------------------------508 try: 509 return laterality2str[self._payload[self._idx['laterality']]] 510 except KeyError: 511 return u'<???>'512 513 laterality_description = property(_get_laterality_description, lambda x:x) 514 #--------------------------------------------------------516 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])517 518 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x) 519 #--------------------------------------------------------521 if len(self._payload[self._idx['pk_generic_codes']]) == 0: 522 return [] 523 524 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s' 525 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])} 526 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 527 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]528530 queries = [] 531 # remove all codes 532 if len(self._payload[self._idx['pk_generic_codes']]) > 0: 533 queries.append ({ 534 'cmd': u'DELETE FROM clin.lnk_code2h_issue WHERE fk_item = %(issue)s AND fk_generic_code IN %(codes)s', 535 'args': { 536 'issue': self._payload[self._idx['pk_health_issue']], 537 'codes': tuple(self._payload[self._idx['pk_generic_codes']]) 538 } 539 }) 540 # add new codes 541 for pk_code in pk_codes: 542 queries.append ({ 543 'cmd': u'INSERT INTO clin.lnk_code2h_issue (fk_item, fk_generic_code) VALUES (%(issue)s, %(pk_code)s)', 544 'args': { 545 'issue': self._payload[self._idx['pk_health_issue']], 546 'pk_code': pk_code 547 } 548 }) 549 if len(queries) == 0: 550 return 551 # run it all in one transaction 552 rows, idx = gmPG2.run_rw_queries(queries = queries) 553 return554 555 generic_codes = property(_get_generic_codes, _set_generic_codes)558 """Creates a new health issue for a given patient. 559 560 description - health issue name 561 """ 562 try: 563 h_issue = cHealthIssue(name = description, encounter = encounter, patient = patient) 564 return h_issue 565 except gmExceptions.NoSuchBusinessObjectError: 566 pass 567 568 queries = [] 569 cmd = u"insert into clin.health_issue (description, fk_encounter) values (%(desc)s, %(enc)s)" 570 queries.append({'cmd': cmd, 'args': {'desc': description, 'enc': encounter}}) 571 572 cmd = u"select currval('clin.health_issue_pk_seq')" 573 queries.append({'cmd': cmd}) 574 575 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 576 h_issue = cHealthIssue(aPK_obj = rows[0][0]) 577 578 return h_issue579 #-----------------------------------------------------------581 if isinstance(health_issue, cHealthIssue): 582 pk = health_issue['pk_health_issue'] 583 else: 584 pk = int(health_issue) 585 586 try: 587 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.health_issue where pk=%(pk)s', 'args': {'pk': pk}}]) 588 except gmPG2.dbapi.IntegrityError: 589 # should be parsing pgcode/and or error message 590 _log.exception('cannot delete health issue') 591 raise gmExceptions.DatabaseObjectInUseError('cannot delete health issue, it is in use')592 #------------------------------------------------------------ 593 # use as dummy for unassociated episodes595 issue = { 596 'pk_health_issue': None, 597 'description': _('Unattributed episodes'), 598 'age_noted': None, 599 'laterality': u'na', 600 'is_active': True, 601 'clinically_relevant': True, 602 'is_confidential': None, 603 'is_cause_of_death': False, 604 'is_dummy': True, 605 'grouping': None 606 } 607 return issue608 #-----------------------------------------------------------610 return cProblem ( 611 aPK_obj = { 612 'pk_patient': health_issue['pk_patient'], 613 'pk_health_issue': health_issue['pk_health_issue'], 614 'pk_episode': None 615 }, 616 try_potential_problems = allow_irrelevant 617 )618 #============================================================ 619 # episodes API 620 #============================================================622 """Represents one clinical episode. 623 """ 624 _cmd_fetch_payload = u"select * from clin.v_pat_episodes where pk_episode=%s" 625 _cmds_store_payload = [ 626 u"""update clin.episode set 627 fk_health_issue = %(pk_health_issue)s, 628 is_open = %(episode_open)s::boolean, 629 description = %(description)s, 630 summary = gm.nullify_empty_string(%(summary)s), 631 diagnostic_certainty_classification = gm.nullify_empty_string(%(diagnostic_certainty_classification)s) 632 where 633 pk = %(pk_episode)s and 634 xmin = %(xmin_episode)s""", 635 u"""select xmin_episode from clin.v_pat_episodes where pk_episode = %(pk_episode)s""" 636 ] 637 _updatable_fields = [ 638 'pk_health_issue', 639 'episode_open', 640 'description', 641 'summary', 642 'diagnostic_certainty_classification' 643 ] 644 #--------------------------------------------------------1152 #============================================================645 - def __init__(self, aPK_obj=None, id_patient=None, name='xxxDEFAULTxxx', health_issue=None, row=None, encounter=None):646 pk = aPK_obj 647 if pk is None and row is None: 648 649 where_parts = [u'description = %(desc)s'] 650 651 if id_patient is not None: 652 where_parts.append(u'pk_patient = %(pat)s') 653 654 if health_issue is not None: 655 where_parts.append(u'pk_health_issue = %(issue)s') 656 657 if encounter is not None: 658 where_parts.append(u'pk_patient = (select fk_patient from clin.encounter where pk = %(enc)s)') 659 660 args = { 661 'pat': id_patient, 662 'issue': health_issue, 663 'enc': encounter, 664 'desc': name 665 } 666 667 cmd = u"select * from clin.v_pat_episodes where %s" % u' and '.join(where_parts) 668 669 rows, idx = gmPG2.run_ro_queries( 670 queries = [{'cmd': cmd, 'args': args}], 671 get_col_idx=True 672 ) 673 674 if len(rows) == 0: 675 raise gmExceptions.NoSuchBusinessObjectError, 'no episode for [%s:%s:%s:%s]' % (id_patient, name, health_issue, encounter) 676 677 r = {'idx': idx, 'data': rows[0], 'pk_field': 'pk_episode'} 678 gmBusinessDBObject.cBusinessDBObject.__init__(self, row=r) 679 680 else: 681 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk, row=row)682 #-------------------------------------------------------- 683 # external API 684 #--------------------------------------------------------686 """Get earliest and latest access to this episode. 687 688 Returns a tuple(earliest, latest). 689 """ 690 cmd = u""" 691 select 692 min(earliest), 693 max(latest) 694 from ( 695 (select 696 (case when clin_when < modified_when 697 then clin_when 698 else modified_when 699 end) as earliest, 700 (case when clin_when > modified_when 701 then clin_when 702 else modified_when 703 end) as latest 704 from 705 clin.clin_root_item 706 where 707 fk_episode = %(pk)s 708 709 ) union all ( 710 711 select 712 modified_when as earliest, 713 modified_when as latest 714 from 715 clin.episode 716 where 717 pk = %(pk)s 718 ) 719 ) as ranges""" 720 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': {'pk': self.pk_obj}}]) 721 if len(rows) == 0: 722 return (gmNull.cNull(warn=False), gmNull.cNull(warn=False)) 723 return (rows[0][0], rows[0][1])724 #-------------------------------------------------------- 727 #--------------------------------------------------------729 return gmClinNarrative.get_narrative ( 730 soap_cats = soap_cats, 731 encounters = encounters, 732 episodes = [self.pk_obj], 733 order_by = order_by 734 )735 #--------------------------------------------------------737 """Method for episode editing, that is, episode renaming. 738 739 @param description 740 - the new descriptive name for the encounter 741 @type description 742 - a string instance 743 """ 744 # sanity check 745 if description.strip() == '': 746 _log.error('<description> must be a non-empty string instance') 747 return False 748 # update the episode description 749 old_description = self._payload[self._idx['description']] 750 self._payload[self._idx['description']] = description.strip() 751 self._is_modified = True 752 successful, data = self.save_payload() 753 if not successful: 754 _log.error('cannot rename episode [%s] to [%s]' % (self, description)) 755 self._payload[self._idx['description']] = old_description 756 return False 757 return True758 #--------------------------------------------------------760 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 761 762 if pk_code in self._payload[self._idx['pk_generic_codes']]: 763 return 764 765 cmd = u""" 766 INSERT INTO clin.lnk_code2episode 767 (fk_item, fk_generic_code) 768 SELECT 769 %(item)s, 770 %(code)s 771 WHERE NOT EXISTS ( 772 SELECT 1 FROM clin.lnk_code2episode 773 WHERE 774 fk_item = %(item)s 775 AND 776 fk_generic_code = %(code)s 777 )""" 778 args = { 779 'item': self._payload[self._idx['pk_episode']], 780 'code': pk_code 781 } 782 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 783 return784 #--------------------------------------------------------786 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 787 cmd = u"DELETE FROM clin.lnk_code2episode WHERE fk_item = %(item)s AND fk_generic_code = %(code)s" 788 args = { 789 'item': self._payload[self._idx['pk_episode']], 790 'code': pk_code 791 } 792 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 793 return True794 #--------------------------------------------------------796 rows = gmClinNarrative.get_as_journal ( 797 episodes = (self.pk_obj,), 798 order_by = u'pk_encounter, clin_when, scr, src_table' 799 #order_by = u'pk_encounter, scr, clin_when, src_table' 800 ) 801 802 if len(rows) == 0: 803 return u'' 804 805 lines = [] 806 807 lines.append(_('Clinical data generated during encounters within this episode:')) 808 809 left_margin = u' ' * left_margin 810 811 prev_enc = None 812 for row in rows: 813 if row['pk_encounter'] != prev_enc: 814 lines.append(u'') 815 prev_enc = row['pk_encounter'] 816 817 when = row['clin_when'].strftime(date_format).decode(gmI18N.get_encoding()) 818 top_row = u'%s%s %s (%s) %s' % ( 819 gmTools.u_box_top_left_arc, 820 gmTools.u_box_horiz_single, 821 gmClinNarrative.soap_cat2l10n_str[row['real_soap_cat']], 822 when, 823 gmTools.u_box_horiz_single * 5 824 ) 825 soap = gmTools.wrap ( 826 text = row['narrative'], 827 width = 60, 828 initial_indent = u' ', 829 subsequent_indent = u' ' + left_margin 830 ) 831 row_ver = u'' 832 if row['row_version'] > 0: 833 row_ver = u'v%s: ' % row['row_version'] 834 bottom_row = u'%s%s %s, %s%s %s' % ( 835 u' ' * 40, 836 gmTools.u_box_horiz_light_heavy, 837 row['modified_by'], 838 row_ver, 839 row['date_modified'], 840 gmTools.u_box_horiz_heavy_light 841 ) 842 843 lines.append(top_row) 844 lines.append(soap) 845 lines.append(bottom_row) 846 847 eol_w_margin = u'\n%s' % left_margin 848 return left_margin + eol_w_margin.join(lines) + u'\n'849 #--------------------------------------------------------851 852 if patient.ID != self._payload[self._idx['pk_patient']]: 853 msg = '<patient>.ID = %s but episode %s belongs to patient %s' % ( 854 patient.ID, 855 self._payload[self._idx['pk_episode']], 856 self._payload[self._idx['pk_patient']] 857 ) 858 raise ValueError(msg) 859 860 lines = [] 861 862 # episode details 863 lines.append (_('Episode %s%s%s [#%s]') % ( 864 gmTools.u_left_double_angle_quote, 865 self._payload[self._idx['description']], 866 gmTools.u_right_double_angle_quote, 867 self._payload[self._idx['pk_episode']] 868 )) 869 870 enc = cEncounter(aPK_obj = self._payload[self._idx['pk_encounter']]) 871 lines.append (u' ' + _('Created during encounter: %s (%s - %s) [#%s]') % ( 872 enc['l10n_type'], 873 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 874 enc['last_affirmed_original_tz'].strftime('%H:%M'), 875 self._payload[self._idx['pk_encounter']] 876 )) 877 878 emr = patient.get_emr() 879 encs = emr.get_encounters(episodes = [self._payload[self._idx['pk_episode']]]) 880 first_encounter = None 881 last_encounter = None 882 if (encs is not None) and (len(encs) > 0): 883 first_encounter = emr.get_first_encounter(episode_id = self._payload[self._idx['pk_episode']]) 884 last_encounter = emr.get_last_encounter(episode_id = self._payload[self._idx['pk_episode']]) 885 if self._payload[self._idx['episode_open']]: 886 end = gmDateTime.pydt_now_here() 887 end_str = gmTools.u_ellipsis 888 else: 889 end = last_encounter['last_affirmed'] 890 end_str = last_encounter['last_affirmed'].strftime('%m/%Y') 891 age = gmDateTime.format_interval_medically(end - first_encounter['started']) 892 lines.append(_(' Duration: %s (%s - %s)') % ( 893 age, 894 first_encounter['started'].strftime('%m/%Y'), 895 end_str 896 )) 897 898 lines.append(u' ' + _('Status') + u': %s%s' % ( 899 gmTools.bool2subst(self._payload[self._idx['episode_open']], _('active'), _('finished')), 900 gmTools.coalesce ( 901 initial = diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']]), 902 instead = u'', 903 template_initial = u', %s', 904 none_equivalents = [None, u''] 905 ) 906 )) 907 908 if self._payload[self._idx['summary']] is not None: 909 lines.append(u'') 910 lines.append(gmTools.wrap ( 911 text = self._payload[self._idx['summary']], 912 width = 60, 913 initial_indent = u' ', 914 subsequent_indent = u' ' 915 ) 916 ) 917 918 # codes 919 codes = self.generic_codes 920 if len(codes) > 0: 921 lines.append(u'') 922 for c in codes: 923 lines.append(u' %s: %s (%s - %s)' % ( 924 c['code'], 925 c['term'], 926 c['name_short'], 927 c['version'] 928 )) 929 del codes 930 931 lines.append(u'') 932 933 # encounters 934 if encs is None: 935 lines.append(_('Error retrieving encounters for this episode.')) 936 elif len(encs) == 0: 937 lines.append(_('There are no encounters for this episode.')) 938 else: 939 lines.append(_('Last worked on: %s\n') % last_encounter['last_affirmed_original_tz'].strftime('%Y-%m-%d %H:%M')) 940 941 if len(encs) < 4: 942 line = _('%s encounter(s) (%s - %s):') 943 else: 944 line = _('1st and (up to 3) most recent (of %s) encounters (%s - %s):') 945 lines.append(line % ( 946 len(encs), 947 first_encounter['started'].strftime('%m/%Y'), 948 last_encounter['last_affirmed'].strftime('%m/%Y') 949 )) 950 951 lines.append(u' %s - %s (%s):%s' % ( 952 first_encounter['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 953 first_encounter['last_affirmed_original_tz'].strftime('%H:%M'), 954 first_encounter['l10n_type'], 955 gmTools.coalesce ( 956 first_encounter['assessment_of_encounter'], 957 gmTools.coalesce ( 958 first_encounter['reason_for_encounter'], 959 u'', 960 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE')) 961 ), 962 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE')) 963 ) 964 )) 965 966 if len(encs) > 4: 967 lines.append(_(' ... %s skipped ...') % (len(encs) - 4)) 968 969 for enc in encs[1:][-3:]: 970 lines.append(u' %s - %s (%s):%s' % ( 971 enc['started_original_tz'].strftime('%Y-%m-%d %H:%M'), 972 enc['last_affirmed_original_tz'].strftime('%H:%M'), 973 enc['l10n_type'], 974 gmTools.coalesce ( 975 enc['assessment_of_encounter'], 976 gmTools.coalesce ( 977 enc['reason_for_encounter'], 978 u'', 979 u' \u00BB%s\u00AB' + (u' (%s)' % _('RFE')) 980 ), 981 u' \u00BB%s\u00AB' + (u' (%s)' % _('AOE')) 982 ) 983 )) 984 del encs 985 986 # spell out last encounter 987 if last_encounter is not None: 988 lines.append('') 989 lines.append(_('Progress notes in most recent encounter:')) 990 lines.extend(last_encounter.format_soap ( 991 episodes = [ self._payload[self._idx['pk_episode']] ], 992 left_margin = left_margin, 993 soap_cats = 'soap', 994 emr = emr 995 )) 996 997 # documents 998 doc_folder = patient.get_document_folder() 999 docs = doc_folder.get_documents ( 1000 episodes = [ self._payload[self._idx['pk_episode']] ] 1001 ) 1002 1003 if len(docs) > 0: 1004 lines.append('') 1005 lines.append(_('Documents: %s') % len(docs)) 1006 1007 for d in docs: 1008 lines.append(u' %s %s:%s%s' % ( 1009 d['clin_when'].strftime('%Y-%m-%d'), 1010 d['l10n_type'], 1011 gmTools.coalesce(d['comment'], u'', u' "%s"'), 1012 gmTools.coalesce(d['ext_ref'], u'', u' (%s)') 1013 )) 1014 del docs 1015 1016 # hospital stays 1017 stays = emr.get_hospital_stays(episodes = [ self._payload[self._idx['pk_episode']] ]) 1018 if len(stays) > 0: 1019 lines.append('') 1020 lines.append(_('Hospital stays: %s') % len(stays)) 1021 for s in stays: 1022 lines.append(s.format(left_margin = (left_margin + 1))) 1023 del stays 1024 1025 # procedures 1026 procs = emr.get_performed_procedures(episodes = [ self._payload[self._idx['pk_episode']] ]) 1027 if len(procs) > 0: 1028 lines.append(u'') 1029 lines.append(_('Procedures performed: %s') % len(procs)) 1030 for p in procs: 1031 lines.append(p.format ( 1032 left_margin = (left_margin + 1), 1033 include_episode = False, 1034 include_codes = True 1035 )) 1036 del procs 1037 1038 # family history 1039 fhx = emr.get_family_history(episodes = [ self._payload[self._idx['pk_episode']] ]) 1040 if len(fhx) > 0: 1041 lines.append(u'') 1042 lines.append(_('Family History: %s') % len(fhx)) 1043 for f in fhx: 1044 lines.append(f.format ( 1045 left_margin = (left_margin + 1), 1046 include_episode = False, 1047 include_comment = True, 1048 include_codes = True 1049 )) 1050 del fhx 1051 1052 # test results 1053 tests = emr.get_test_results_by_date(episodes = [ self._payload[self._idx['pk_episode']] ]) 1054 1055 if len(tests) > 0: 1056 lines.append('') 1057 lines.append(_('Measurements and Results:')) 1058 1059 for t in tests: 1060 lines.extend(t.format ( 1061 with_review = False, 1062 with_comments = False, 1063 date_format = '%Y-%m-%d' 1064 )) 1065 del tests 1066 1067 # vaccinations 1068 vaccs = emr.get_vaccinations ( 1069 episodes = [ self._payload[self._idx['pk_episode']] ], 1070 order_by = u'date_given DESC, vaccine' 1071 ) 1072 1073 if len(vaccs) > 0: 1074 lines.append(u'') 1075 lines.append(_('Vaccinations:')) 1076 1077 for vacc in vaccs: 1078 lines.extend(vacc.format ( 1079 with_indications = True, 1080 with_comment = True, 1081 with_reaction = True, 1082 date_format = '%Y-%m-%d' 1083 )) 1084 del vaccs 1085 1086 left_margin = u' ' * left_margin 1087 eol_w_margin = u'\n%s' % left_margin 1088 return left_margin + eol_w_margin.join(lines) + u'\n'1089 #-------------------------------------------------------- 1090 # properties 1091 #--------------------------------------------------------1093 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])1094 1095 diagnostic_certainty_description = property(_get_diagnostic_certainty_description, lambda x:x) 1096 #--------------------------------------------------------1098 if len(self._payload[self._idx['pk_generic_codes']]) == 0: 1099 return [] 1100 1101 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s' 1102 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])} 1103 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1104 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]11051107 queries = [] 1108 # remove all codes 1109 if len(self._payload[self._idx['pk_generic_codes']]) > 0: 1110 queries.append ({ 1111 'cmd': u'DELETE FROM clin.lnk_code2episode WHERE fk_item = %(epi)s AND fk_generic_code IN %(codes)s', 1112 'args': { 1113 'epi': self._payload[self._idx['pk_episode']], 1114 'codes': tuple(self._payload[self._idx['pk_generic_codes']]) 1115 } 1116 }) 1117 # add new codes 1118 for pk_code in pk_codes: 1119 queries.append ({ 1120 'cmd': u'INSERT INTO clin.lnk_code2episode (fk_item, fk_generic_code) VALUES (%(epi)s, %(pk_code)s)', 1121 'args': { 1122 'epi': self._payload[self._idx['pk_episode']], 1123 'pk_code': pk_code 1124 } 1125 }) 1126 if len(queries) == 0: 1127 return 1128 # run it all in one transaction 1129 rows, idx = gmPG2.run_rw_queries(queries = queries) 1130 return1131 1132 generic_codes = property(_get_generic_codes, _set_generic_codes) 1133 #--------------------------------------------------------1135 cmd = u"""SELECT EXISTS ( 1136 SELECT 1 FROM clin.clin_narrative 1137 WHERE 1138 fk_episode = %(epi)s 1139 AND 1140 fk_encounter IN ( 1141 SELECT pk FROM clin.encounter WHERE fk_patient = %(pat)s 1142 ) 1143 )""" 1144 args = { 1145 u'pat': self._payload[self._idx['pk_patient']], 1146 u'epi': self._payload[self._idx['pk_episode']] 1147 } 1148 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = False) 1149 return rows[0][0]1150 1151 has_narrative = property(_get_has_narrative, lambda x:x)1153 -def create_episode(pk_health_issue=None, episode_name=None, is_open=False, allow_dupes=False, encounter=None):1154 """Creates a new episode for a given patient's health issue. 1155 1156 pk_health_issue - given health issue PK 1157 episode_name - name of episode 1158 """ 1159 if not allow_dupes: 1160 try: 1161 episode = cEpisode(name=episode_name, health_issue=pk_health_issue, encounter = encounter) 1162 if episode['episode_open'] != is_open: 1163 episode['episode_open'] = is_open 1164 episode.save_payload() 1165 return episode 1166 except gmExceptions.ConstructorError: 1167 pass 1168 1169 queries = [] 1170 cmd = u"insert into clin.episode (fk_health_issue, description, is_open, fk_encounter) values (%s, %s, %s::boolean, %s)" 1171 queries.append({'cmd': cmd, 'args': [pk_health_issue, episode_name, is_open, encounter]}) 1172 queries.append({'cmd': cEpisode._cmd_fetch_payload % u"currval('clin.episode_pk_seq')"}) 1173 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data=True, get_col_idx=True) 1174 1175 episode = cEpisode(row={'data': rows[0], 'idx': idx, 'pk_field': 'pk_episode'}) 1176 return episode1177 #-----------------------------------------------------------1179 if isinstance(episode, cEpisode): 1180 pk = episode['pk_episode'] 1181 else: 1182 pk = int(episode) 1183 1184 try: 1185 gmPG2.run_rw_queries(queries = [{'cmd': u'delete from clin.episode where pk=%(pk)s', 'args': {'pk': pk}}]) 1186 except gmPG2.dbapi.IntegrityError: 1187 # should be parsing pgcode/and or error message 1188 _log.exception('cannot delete episode') 1189 raise gmExceptions.DatabaseObjectInUseError('cannot delete episode, it is in use')1190 #-----------------------------------------------------------1192 return cProblem ( 1193 aPK_obj = { 1194 'pk_patient': episode['pk_patient'], 1195 'pk_episode': episode['pk_episode'], 1196 'pk_health_issue': episode['pk_health_issue'] 1197 }, 1198 try_potential_problems = allow_closed 1199 )1200 #============================================================ 1201 # encounter API 1202 #============================================================1204 """Represents one encounter.""" 1205 1206 _cmd_fetch_payload = u"select * from clin.v_pat_encounters where pk_encounter = %s" 1207 _cmds_store_payload = [ 1208 u"""update clin.encounter set 1209 started = %(started)s, 1210 last_affirmed = %(last_affirmed)s, 1211 fk_location = %(pk_location)s, 1212 fk_type = %(pk_type)s, 1213 reason_for_encounter = gm.nullify_empty_string(%(reason_for_encounter)s), 1214 assessment_of_encounter = gm.nullify_empty_string(%(assessment_of_encounter)s) 1215 where 1216 pk = %(pk_encounter)s and 1217 xmin = %(xmin_encounter)s""", 1218 u"""select xmin_encounter from clin.v_pat_encounters where pk_encounter=%(pk_encounter)s""" 1219 ] 1220 _updatable_fields = [ 1221 'started', 1222 'last_affirmed', 1223 'pk_location', 1224 'pk_type', 1225 'reason_for_encounter', 1226 'assessment_of_encounter' 1227 ] 1228 #--------------------------------------------------------1915 #-----------------------------------------------------------1230 """Set the encounter as the active one. 1231 1232 "Setting active" means making sure the encounter 1233 row has the youngest "last_affirmed" timestamp of 1234 all encounter rows for this patient. 1235 """ 1236 self['last_affirmed'] = gmDateTime.pydt_now_here() 1237 self.save()1238 #--------------------------------------------------------1240 """ 1241 Moves every element currently linked to the current encounter 1242 and the source_episode onto target_episode. 1243 1244 @param source_episode The episode the elements are currently linked to. 1245 @type target_episode A cEpisode intance. 1246 @param target_episode The episode the elements will be relinked to. 1247 @type target_episode A cEpisode intance. 1248 """ 1249 if source_episode['pk_episode'] == target_episode['pk_episode']: 1250 return True 1251 1252 queries = [] 1253 cmd = u""" 1254 UPDATE clin.clin_root_item 1255 SET fk_episode = %(trg)s 1256 WHERE 1257 fk_encounter = %(enc)s AND 1258 fk_episode = %(src)s 1259 """ 1260 rows, idx = gmPG2.run_rw_queries(queries = [{ 1261 'cmd': cmd, 1262 'args': { 1263 'trg': target_episode['pk_episode'], 1264 'enc': self.pk_obj, 1265 'src': source_episode['pk_episode'] 1266 } 1267 }]) 1268 self.refetch_payload() 1269 return True1270 #--------------------------------------------------------1272 1273 relevant_fields = [ 1274 'pk_location', 1275 'pk_type', 1276 'pk_patient', 1277 'reason_for_encounter', 1278 'assessment_of_encounter' 1279 ] 1280 for field in relevant_fields: 1281 if self._payload[self._idx[field]] != another_object[field]: 1282 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 1283 return False 1284 1285 relevant_fields = [ 1286 'started', 1287 'last_affirmed', 1288 ] 1289 for field in relevant_fields: 1290 if self._payload[self._idx[field]] is None: 1291 if another_object[field] is None: 1292 continue 1293 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 1294 return False 1295 1296 if another_object[field] is None: 1297 return False 1298 1299 # compares at minute granularity 1300 if self._payload[self._idx[field]].strftime('%Y-%m-%d %H:%M') != another_object[field].strftime('%Y-%m-%d %H:%M'): 1301 _log.debug('mismatch on [%s]: "%s" vs. "%s"', field, self._payload[self._idx[field]], another_object[field]) 1302 return False 1303 1304 # compare codes 1305 # 1) RFE 1306 if another_object['pk_generic_codes_rfe'] is None: 1307 if self._payload[self._idx['pk_generic_codes_rfe']] is not None: 1308 return False 1309 if another_object['pk_generic_codes_rfe'] is not None: 1310 if self._payload[self._idx['pk_generic_codes_rfe']] is None: 1311 return False 1312 if ( 1313 (another_object['pk_generic_codes_rfe'] is None) 1314 and 1315 (self._payload[self._idx['pk_generic_codes_rfe']] is None) 1316 ) is False: 1317 if set(another_object['pk_generic_codes_rfe']) != set(self._payload[self._idx['pk_generic_codes_rfe']]): 1318 return False 1319 # 2) AOE 1320 if another_object['pk_generic_codes_aoe'] is None: 1321 if self._payload[self._idx['pk_generic_codes_aoe']] is not None: 1322 return False 1323 if another_object['pk_generic_codes_aoe'] is not None: 1324 if self._payload[self._idx['pk_generic_codes_aoe']] is None: 1325 return False 1326 if ( 1327 (another_object['pk_generic_codes_aoe'] is None) 1328 and 1329 (self._payload[self._idx['pk_generic_codes_aoe']] is None) 1330 ) is False: 1331 if set(another_object['pk_generic_codes_aoe']) != set(self._payload[self._idx['pk_generic_codes_aoe']]): 1332 return False 1333 1334 return True1335 #--------------------------------------------------------1337 cmd = u""" 1338 select exists ( 1339 select 1 from clin.v_pat_items where pk_patient = %(pat)s and pk_encounter = %(enc)s 1340 union all 1341 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s 1342 )""" 1343 args = { 1344 'pat': self._payload[self._idx['pk_patient']], 1345 'enc': self.pk_obj 1346 } 1347 rows, idx = gmPG2.run_ro_queries ( 1348 queries = [{ 1349 'cmd': cmd, 1350 'args': args 1351 }] 1352 ) 1353 return rows[0][0]1354 #--------------------------------------------------------1356 cmd = u""" 1357 select exists ( 1358 select 1 from clin.v_pat_items where pk_patient=%(pat)s and pk_encounter=%(enc)s 1359 )""" 1360 args = { 1361 'pat': self._payload[self._idx['pk_patient']], 1362 'enc': self.pk_obj 1363 } 1364 rows, idx = gmPG2.run_ro_queries ( 1365 queries = [{ 1366 'cmd': cmd, 1367 'args': args 1368 }] 1369 ) 1370 return rows[0][0]1371 #--------------------------------------------------------1373 """soap_cats: <space> = admin category""" 1374 1375 if soap_cats is None: 1376 soap_cats = u'soap ' 1377 else: 1378 soap_cats = soap_cats.lower() 1379 1380 cats = [] 1381 for cat in soap_cats: 1382 if cat in u'soap': 1383 cats.append(cat) 1384 continue 1385 if cat == u' ': 1386 cats.append(None) 1387 1388 cmd = u""" 1389 SELECT EXISTS ( 1390 SELECT 1 FROM clin.clin_narrative 1391 WHERE 1392 fk_encounter = %(enc)s 1393 AND 1394 soap_cat IN %(cats)s 1395 LIMIT 1 1396 ) 1397 """ 1398 args = {'enc': self._payload[self._idx['pk_encounter']], 'cats': tuple(cats)} 1399 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd,'args': args}]) 1400 return rows[0][0]1401 #--------------------------------------------------------1403 cmd = u""" 1404 select exists ( 1405 select 1 from blobs.v_doc_med where pk_patient = %(pat)s and pk_encounter = %(enc)s 1406 )""" 1407 args = { 1408 'pat': self._payload[self._idx['pk_patient']], 1409 'enc': self.pk_obj 1410 } 1411 rows, idx = gmPG2.run_ro_queries ( 1412 queries = [{ 1413 'cmd': cmd, 1414 'args': args 1415 }] 1416 ) 1417 return rows[0][0]1418 #--------------------------------------------------------1420 1421 if soap_cat is not None: 1422 soap_cat = soap_cat.lower() 1423 1424 if episode is None: 1425 epi_part = u'fk_episode is null' 1426 else: 1427 epi_part = u'fk_episode = %(epi)s' 1428 1429 cmd = u""" 1430 select narrative 1431 from clin.clin_narrative 1432 where 1433 fk_encounter = %%(enc)s 1434 and 1435 soap_cat = %%(cat)s 1436 and 1437 %s 1438 order by clin_when desc 1439 limit 1 1440 """ % epi_part 1441 1442 args = {'enc': self.pk_obj, 'cat': soap_cat, 'epi': episode} 1443 1444 rows, idx = gmPG2.run_ro_queries ( 1445 queries = [{ 1446 'cmd': cmd, 1447 'args': args 1448 }] 1449 ) 1450 if len(rows) == 0: 1451 return None 1452 1453 return rows[0][0]1454 #--------------------------------------------------------1456 cmd = u""" 1457 SELECT * FROM clin.v_pat_episodes 1458 WHERE 1459 pk_episode IN ( 1460 1461 SELECT DISTINCT fk_episode 1462 FROM clin.clin_root_item 1463 WHERE fk_encounter = %%(enc)s 1464 1465 UNION 1466 1467 SELECT DISTINCT fk_episode 1468 FROM blobs.doc_med 1469 WHERE fk_encounter = %%(enc)s 1470 ) 1471 %s""" 1472 args = {'enc': self.pk_obj} 1473 if exclude is not None: 1474 cmd = cmd % u'AND pk_episode NOT IN %(excluded)s' 1475 args['excluded'] = tuple(exclude) 1476 else: 1477 cmd = cmd % u'' 1478 1479 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1480 1481 return [ cEpisode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_episode'}) for r in rows ]1482 #--------------------------------------------------------1484 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 1485 if field == u'rfe': 1486 cmd = u"INSERT INTO clin.lnk_code2rfe (fk_item, fk_generic_code) values (%(item)s, %(code)s)" 1487 elif field == u'aoe': 1488 cmd = u"INSERT INTO clin.lnk_code2aoe (fk_item, fk_generic_code) values (%(item)s, %(code)s)" 1489 else: 1490 raise ValueError('<field> must be one of "rfe" or "aoe", not "%s"', field) 1491 args = { 1492 'item': self._payload[self._idx['pk_encounter']], 1493 'code': pk_code 1494 } 1495 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1496 return True1497 #--------------------------------------------------------1499 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 1500 if field == u'rfe': 1501 cmd = u"DELETE FROM clin.lnk_code2rfe WHERE fk_item = %(item)s AND fk_generic_code = %(code)s" 1502 elif field == u'aoe': 1503 cmd = u"DELETE FROM clin.lnk_code2aoe WHERE fk_item = %(item)s AND fk_generic_code = %(code)s" 1504 else: 1505 raise ValueError('<field> must be one of "rfe" or "aoe", not "%s"', field) 1506 args = { 1507 'item': self._payload[self._idx['pk_encounter']], 1508 'code': pk_code 1509 } 1510 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 1511 return True1512 #--------------------------------------------------------1513 - def format_soap(self, episodes=None, left_margin=0, soap_cats='soap', emr=None, issues=None):1514 1515 lines = [] 1516 for soap_cat in soap_cats: 1517 soap_cat_narratives = emr.get_clin_narrative ( 1518 episodes = episodes, 1519 issues = issues, 1520 encounters = [self._payload[self._idx['pk_encounter']]], 1521 soap_cats = [soap_cat] 1522 ) 1523 if soap_cat_narratives is None: 1524 continue 1525 if len(soap_cat_narratives) == 0: 1526 continue 1527 1528 lines.append(u'%s%s %s %s' % ( 1529 gmTools.u_box_top_left_arc, 1530 gmTools.u_box_horiz_single, 1531 gmClinNarrative.soap_cat2l10n_str[soap_cat], 1532 gmTools.u_box_horiz_single * 5 1533 )) 1534 for soap_entry in soap_cat_narratives: 1535 txt = gmTools.wrap ( 1536 text = soap_entry['narrative'], 1537 width = 75, 1538 initial_indent = u'', 1539 subsequent_indent = (u' ' * left_margin) 1540 ) 1541 lines.append(txt) 1542 when = gmDateTime.pydt_strftime ( 1543 soap_entry['date'], 1544 format = '%Y-%m-%d %H:%M', 1545 accuracy = gmDateTime.acc_minutes 1546 ) 1547 txt = u'%s%s %.8s, %s %s' % ( 1548 u' ' * 40, 1549 gmTools.u_box_horiz_light_heavy, 1550 soap_entry['provider'], 1551 when, 1552 gmTools.u_box_horiz_heavy_light 1553 ) 1554 lines.append(txt) 1555 lines.append('') 1556 1557 return lines1558 #--------------------------------------------------------1560 1561 nothing2format = ( 1562 (self._payload[self._idx['reason_for_encounter']] is None) 1563 and 1564 (self._payload[self._idx['assessment_of_encounter']] is None) 1565 and 1566 (self.has_soap_narrative(soap_cats = u'soap') is False) 1567 ) 1568 if nothing2format: 1569 return u'' 1570 1571 if date_format is None: 1572 date_format = '%A, %B %d %Y' 1573 1574 tex = u'\\multicolumn{2}{l}{%s: %s ({\\footnotesize %s - %s})} \\tabularnewline \n' % ( 1575 gmTools.tex_escape_string(self._payload[self._idx['l10n_type']]), 1576 self._payload[self._idx['started']].strftime(date_format).decode(gmI18N.get_encoding()), 1577 self._payload[self._idx['started']].strftime('%H:%M'), 1578 self._payload[self._idx['last_affirmed']].strftime('%H:%M') 1579 ) 1580 tex += u'\\hline \\tabularnewline \n' 1581 1582 for epi in self.get_episodes(): 1583 soaps = epi.get_narrative(soap_cats = soap_cats, encounters = [self.pk_obj], order_by = soap_order) 1584 if len(soaps) == 0: 1585 continue 1586 tex += u'\\multicolumn{2}{l}{\\emph{%s: %s%s}} \\tabularnewline \n' % ( 1587 gmTools.tex_escape_string(_('Problem')), 1588 gmTools.tex_escape_string(epi['description']), 1589 gmTools.coalesce ( 1590 initial = diagnostic_certainty_classification2str(epi['diagnostic_certainty_classification']), 1591 instead = u'', 1592 template_initial = u' {\\footnotesize [%s]}', 1593 none_equivalents = [None, u''] 1594 ) 1595 ) 1596 if epi['pk_health_issue'] is not None: 1597 tex += u'\\multicolumn{2}{l}{\\emph{%s: %s%s}} \\tabularnewline \n' % ( 1598 gmTools.tex_escape_string(_('Health issue')), 1599 gmTools.tex_escape_string(epi['health_issue']), 1600 gmTools.coalesce ( 1601 initial = diagnostic_certainty_classification2str(epi['diagnostic_certainty_classification_issue']), 1602 instead = u'', 1603 template_initial = u' {\\footnotesize [%s]}', 1604 none_equivalents = [None, u''] 1605 ) 1606 ) 1607 for soap in soaps: 1608 tex += u'{\\small %s} & {\\small %s} \\tabularnewline \n' % ( 1609 gmClinNarrative.soap_cat2l10n[soap['soap_cat']], 1610 gmTools.tex_escape_string(soap['narrative'].strip(u'\n')) 1611 ) 1612 tex += u' & \\tabularnewline \n' 1613 1614 if self._payload[self._idx['reason_for_encounter']] is not None: 1615 tex += u'%s & %s \\tabularnewline \n' % ( 1616 gmTools.tex_escape_string(_('RFE')), 1617 gmTools.tex_escape_string(self._payload[self._idx['reason_for_encounter']]) 1618 ) 1619 if self._payload[self._idx['assessment_of_encounter']] is not None: 1620 tex += u'%s & %s \\tabularnewline \n' % ( 1621 gmTools.tex_escape_string(_('AOE')), 1622 gmTools.tex_escape_string(self._payload[self._idx['assessment_of_encounter']]) 1623 ) 1624 1625 tex += u'\\hline \\tabularnewline \n' 1626 tex += u' & \\tabularnewline \n' 1627 1628 return tex1629 #--------------------------------------------------------1630 - 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, with_co_encountlet_hints=False, with_rfe_aoe=False, with_family_history=True):1631 """Format an encounter. 1632 1633 with_co_encountlet_hints: 1634 - whether to include which *other* episodes were discussed during this encounter 1635 - (only makes sense if episodes != None) 1636 """ 1637 lines = [] 1638 1639 if fancy_header: 1640 lines.append(u'%s%s: %s - %s (@%s)%s [#%s]' % ( 1641 u' ' * left_margin, 1642 self._payload[self._idx['l10n_type']], 1643 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'), 1644 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'), 1645 self._payload[self._idx['source_time_zone']], 1646 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB'), 1647 self._payload[self._idx['pk_encounter']] 1648 )) 1649 1650 lines.append(_(' your time: %s - %s (@%s = %s%s)\n') % ( 1651 self._payload[self._idx['started']].strftime('%Y-%m-%d %H:%M'), 1652 self._payload[self._idx['last_affirmed']].strftime('%H:%M'), 1653 gmDateTime.current_local_iso_numeric_timezone_string, 1654 gmTools.bool2subst ( 1655 gmDateTime.dst_currently_in_effect, 1656 gmDateTime.py_dst_timezone_name, 1657 gmDateTime.py_timezone_name 1658 ), 1659 gmTools.bool2subst(gmDateTime.dst_currently_in_effect, u' - ' + _('daylight savings time in effect'), u'') 1660 )) 1661 1662 if self._payload[self._idx['reason_for_encounter']] is not None: 1663 lines.append(u'%s: %s' % (_('RFE'), self._payload[self._idx['reason_for_encounter']])) 1664 codes = self.generic_codes_rfe 1665 for c in codes: 1666 lines.append(u' %s: %s (%s - %s)' % ( 1667 c['code'], 1668 c['term'], 1669 c['name_short'], 1670 c['version'] 1671 )) 1672 if len(codes) > 0: 1673 lines.append(u'') 1674 1675 if self._payload[self._idx['assessment_of_encounter']] is not None: 1676 lines.append(u'%s: %s' % (_('AOE'), self._payload[self._idx['assessment_of_encounter']])) 1677 codes = self.generic_codes_aoe 1678 for c in codes: 1679 lines.append(u' %s: %s (%s - %s)' % ( 1680 c['code'], 1681 c['term'], 1682 c['name_short'], 1683 c['version'] 1684 )) 1685 if len(codes) > 0: 1686 lines.append(u'') 1687 del codes 1688 1689 else: 1690 lines.append(u'%s%s: %s - %s%s' % ( 1691 u' ' * left_margin, 1692 self._payload[self._idx['l10n_type']], 1693 self._payload[self._idx['started_original_tz']].strftime('%Y-%m-%d %H:%M'), 1694 self._payload[self._idx['last_affirmed_original_tz']].strftime('%H:%M'), 1695 gmTools.coalesce(self._payload[self._idx['assessment_of_encounter']], u'', u' \u00BB%s\u00AB') 1696 )) 1697 if with_rfe_aoe: 1698 if self._payload[self._idx['reason_for_encounter']] is not None: 1699 lines.append(u'%s: %s' % (_('RFE'), self._payload[self._idx['reason_for_encounter']])) 1700 codes = self.generic_codes_rfe 1701 for c in codes: 1702 lines.append(u' %s: %s (%s - %s)' % ( 1703 c['code'], 1704 c['term'], 1705 c['name_short'], 1706 c['version'] 1707 )) 1708 if len(codes) > 0: 1709 lines.append(u'') 1710 if self._payload[self._idx['assessment_of_encounter']] is not None: 1711 lines.append(u'%s: %s' % (_('AOE'), self._payload[self._idx['assessment_of_encounter']])) 1712 codes = self.generic_codes_aoe 1713 if len(codes) > 0: 1714 lines.append(u'') 1715 for c in codes: 1716 lines.append(u' %s: %s (%s - %s)' % ( 1717 c['code'], 1718 c['term'], 1719 c['name_short'], 1720 c['version'] 1721 )) 1722 if len(codes) > 0: 1723 lines.append(u'') 1724 del codes 1725 1726 if with_soap: 1727 lines.append(u'') 1728 1729 if patient.ID != self._payload[self._idx['pk_patient']]: 1730 msg = '<patient>.ID = %s but encounter %s belongs to patient %s' % ( 1731 patient.ID, 1732 self._payload[self._idx['pk_encounter']], 1733 self._payload[self._idx['pk_patient']] 1734 ) 1735 raise ValueError(msg) 1736 1737 emr = patient.get_emr() 1738 1739 lines.extend(self.format_soap ( 1740 episodes = episodes, 1741 left_margin = left_margin, 1742 soap_cats = 'soap', 1743 emr = emr, 1744 issues = issues 1745 )) 1746 1747 # # family history 1748 # if with_family_history: 1749 # if episodes is not None: 1750 # fhx = emr.get_family_history(episodes = episodes) 1751 # if len(fhx) > 0: 1752 # lines.append(u'') 1753 # lines.append(_('Family History: %s') % len(fhx)) 1754 # for f in fhx: 1755 # lines.append(f.format ( 1756 # left_margin = (left_margin + 1), 1757 # include_episode = False, 1758 # include_comment = True 1759 # )) 1760 # del fhx 1761 1762 # test results 1763 if with_tests: 1764 tests = emr.get_test_results_by_date ( 1765 episodes = episodes, 1766 encounter = self._payload[self._idx['pk_encounter']] 1767 ) 1768 if len(tests) > 0: 1769 lines.append('') 1770 lines.append(_('Measurements and Results:')) 1771 1772 for t in tests: 1773 lines.extend(t.format()) 1774 1775 del tests 1776 1777 # vaccinations 1778 if with_vaccinations: 1779 vaccs = emr.get_vaccinations ( 1780 episodes = episodes, 1781 encounters = [ self._payload[self._idx['pk_encounter']] ], 1782 order_by = u'date_given DESC, vaccine' 1783 ) 1784 1785 if len(vaccs) > 0: 1786 lines.append(u'') 1787 lines.append(_('Vaccinations:')) 1788 1789 for vacc in vaccs: 1790 lines.extend(vacc.format ( 1791 with_indications = True, 1792 with_comment = True, 1793 with_reaction = True, 1794 date_format = '%Y-%m-%d' 1795 )) 1796 del vaccs 1797 1798 # documents 1799 if with_docs: 1800 doc_folder = patient.get_document_folder() 1801 docs = doc_folder.get_documents ( 1802 episodes = episodes, 1803 encounter = self._payload[self._idx['pk_encounter']] 1804 ) 1805 1806 if len(docs) > 0: 1807 lines.append(u'') 1808 lines.append(_('Documents:')) 1809 1810 for d in docs: 1811 lines.append(u' %s %s:%s%s' % ( 1812 d['clin_when'].strftime('%Y-%m-%d'), 1813 d['l10n_type'], 1814 gmTools.coalesce(d['comment'], u'', u' "%s"'), 1815 gmTools.coalesce(d['ext_ref'], u'', u' (%s)') 1816 )) 1817 1818 del docs 1819 1820 # co-encountlets 1821 if with_co_encountlet_hints: 1822 if episodes is not None: 1823 other_epis = self.get_episodes(exclude = episodes) 1824 if len(other_epis) > 0: 1825 lines.append(u'') 1826 lines.append(_('%s other episodes touched upon during this encounter:') % len(other_epis)) 1827 for epi in other_epis: 1828 lines.append(u' %s%s%s%s' % ( 1829 gmTools.u_left_double_angle_quote, 1830 epi['description'], 1831 gmTools.u_right_double_angle_quote, 1832 gmTools.coalesce(epi['health_issue'], u'', u' (%s)') 1833 )) 1834 1835 eol_w_margin = u'\n%s' % (u' ' * left_margin) 1836 return u'%s\n' % eol_w_margin.join(lines)1837 #-------------------------------------------------------- 1838 # properties 1839 #--------------------------------------------------------1841 if len(self._payload[self._idx['pk_generic_codes_rfe']]) == 0: 1842 return [] 1843 1844 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s' 1845 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes_rfe']])} 1846 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1847 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]18481850 queries = [] 1851 # remove all codes 1852 if len(self._payload[self._idx['pk_generic_codes_rfe']]) > 0: 1853 queries.append ({ 1854 'cmd': u'DELETE FROM clin.lnk_code2rfe WHERE fk_item = %(enc)s AND fk_generic_code IN %(codes)s', 1855 'args': { 1856 'enc': self._payload[self._idx['pk_encounter']], 1857 'codes': tuple(self._payload[self._idx['pk_generic_codes_rfe']]) 1858 } 1859 }) 1860 # add new codes 1861 for pk_code in pk_codes: 1862 queries.append ({ 1863 'cmd': u'INSERT INTO clin.lnk_code2rfe (fk_item, fk_generic_code) VALUES (%(enc)s, %(pk_code)s)', 1864 'args': { 1865 'enc': self._payload[self._idx['pk_encounter']], 1866 'pk_code': pk_code 1867 } 1868 }) 1869 if len(queries) == 0: 1870 return 1871 # run it all in one transaction 1872 rows, idx = gmPG2.run_rw_queries(queries = queries) 1873 self.refetch_payload() 1874 return1875 1876 generic_codes_rfe = property(_get_generic_codes_rfe, _set_generic_codes_rfe) 1877 #--------------------------------------------------------1879 if len(self._payload[self._idx['pk_generic_codes_aoe']]) == 0: 1880 return [] 1881 1882 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s' 1883 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes_aoe']])} 1884 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 1885 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]18861888 queries = [] 1889 # remove all codes 1890 if len(self._payload[self._idx['pk_generic_codes_aoe']]) > 0: 1891 queries.append ({ 1892 'cmd': u'DELETE FROM clin.lnk_code2aoe WHERE fk_item = %(enc)s AND fk_generic_code IN %(codes)s', 1893 'args': { 1894 'enc': self._payload[self._idx['pk_encounter']], 1895 'codes': tuple(self._payload[self._idx['pk_generic_codes_aoe']]) 1896 } 1897 }) 1898 # add new codes 1899 for pk_code in pk_codes: 1900 queries.append ({ 1901 'cmd': u'INSERT INTO clin.lnk_code2aoe (fk_item, fk_generic_code) VALUES (%(enc)s, %(pk_code)s)', 1902 'args': { 1903 'enc': self._payload[self._idx['pk_encounter']], 1904 'pk_code': pk_code 1905 } 1906 }) 1907 if len(queries) == 0: 1908 return 1909 # run it all in one transaction 1910 rows, idx = gmPG2.run_rw_queries(queries = queries) 1911 self.refetch_payload() 1912 return1913 1914 generic_codes_aoe = property(_get_generic_codes_aoe, _set_generic_codes_aoe)1917 """Creates a new encounter for a patient. 1918 1919 fk_patient - patient PK 1920 fk_location - encounter location 1921 enc_type - type of encounter 1922 1923 FIXME: we don't deal with location yet 1924 """ 1925 if enc_type is None: 1926 enc_type = u'in surgery' 1927 # insert new encounter 1928 queries = [] 1929 try: 1930 enc_type = int(enc_type) 1931 cmd = u""" 1932 INSERT INTO clin.encounter ( 1933 fk_patient, fk_location, fk_type 1934 ) VALUES ( 1935 %(pat)s, 1936 -1, 1937 %(typ)s 1938 ) RETURNING pk""" 1939 except ValueError: 1940 enc_type = enc_type 1941 cmd = u""" 1942 insert into clin.encounter ( 1943 fk_patient, fk_location, fk_type 1944 ) values ( 1945 %(pat)s, 1946 -1, 1947 coalesce((select pk from clin.encounter_type where description = %(typ)s), 0) 1948 ) RETURNING pk""" 1949 args = {'pat': fk_patient, 'typ': enc_type} 1950 queries.append({'cmd': cmd, 'args': args}) 1951 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True, get_col_idx = False) 1952 encounter = cEncounter(aPK_obj = rows[0]['pk']) 1953 1954 return encounter1955 #-----------------------------------------------------------1957 1958 rows, idx = gmPG2.run_rw_queries( 1959 queries = [{ 1960 'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 1961 'args': {'desc': description, 'l10n_desc': l10n_description} 1962 }], 1963 return_data = True 1964 ) 1965 1966 success = rows[0][0] 1967 if not success: 1968 _log.warning('updating encounter type [%s] to [%s] failed', description, l10n_description) 1969 1970 return {'description': description, 'l10n_description': l10n_description}1971 #-----------------------------------------------------------1973 """This will attempt to create a NEW encounter type.""" 1974 1975 # need a system name, so derive one if necessary 1976 if description is None: 1977 description = l10n_description 1978 1979 args = { 1980 'desc': description, 1981 'l10n_desc': l10n_description 1982 } 1983 1984 _log.debug('creating encounter type: %s, %s', description, l10n_description) 1985 1986 # does it exist already ? 1987 cmd = u"select description, _(description) from clin.encounter_type where description = %(desc)s" 1988 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 1989 1990 # yes 1991 if len(rows) > 0: 1992 # both system and l10n name are the same so all is well 1993 if (rows[0][0] == description) and (rows[0][1] == l10n_description): 1994 _log.info('encounter type [%s] already exists with the proper translation') 1995 return {'description': description, 'l10n_description': l10n_description} 1996 1997 # or maybe there just wasn't a translation to 1998 # the current language for this type yet ? 1999 cmd = u"select exists (select 1 from i18n.translations where orig = %(desc)s and lang = i18n.get_curr_lang())" 2000 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}]) 2001 2002 # there was, so fail 2003 if rows[0][0]: 2004 _log.error('encounter type [%s] already exists but with another translation') 2005 return None 2006 2007 # else set it 2008 cmd = u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)" 2009 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2010 return {'description': description, 'l10n_description': l10n_description} 2011 2012 # no 2013 queries = [ 2014 {'cmd': u"insert into clin.encounter_type (description) values (%(desc)s)", 'args': args}, 2015 {'cmd': u"select i18n.upd_tx(%(desc)s, %(l10n_desc)s)", 'args': args} 2016 ] 2017 rows, idx = gmPG2.run_rw_queries(queries = queries) 2018 2019 return {'description': description, 'l10n_description': l10n_description}2020 #-----------------------------------------------------------2022 cmd = u""" 2023 SELECT 2024 _(description) AS l10n_description, 2025 description 2026 FROM 2027 clin.encounter_type 2028 ORDER BY 2029 l10n_description 2030 """ 2031 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd}]) 2032 return rows2033 #-----------------------------------------------------------2035 cmd = u"SELECT * from clin.encounter_type where description = %s" 2036 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': [description]}]) 2037 return rows2038 #-----------------------------------------------------------2040 cmd = u"delete from clin.encounter_type where description = %(desc)s" 2041 args = {'desc': description} 2042 try: 2043 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2044 except gmPG2.dbapi.IntegrityError, e: 2045 if e.pgcode == gmPG2.sql_error_codes.FOREIGN_KEY_VIOLATION: 2046 return False 2047 raise 2048 2049 return True2050 #============================================================2052 """Represents one problem. 2053 2054 problems are the aggregation of 2055 .clinically_relevant=True issues and 2056 .is_open=True episodes 2057 """ 2058 _cmd_fetch_payload = u'' # will get programmatically defined in __init__ 2059 _cmds_store_payload = [u"select 1"] 2060 _updatable_fields = [] 2061 2062 #--------------------------------------------------------2173 #-----------------------------------------------------------2064 """Initialize. 2065 2066 aPK_obj must contain the keys 2067 pk_patient 2068 pk_episode 2069 pk_health_issue 2070 """ 2071 if aPK_obj is None: 2072 raise gmExceptions.ConstructorError, 'cannot instatiate cProblem for PK: [%s]' % (aPK_obj) 2073 2074 # As problems are rows from a view of different emr struct items, 2075 # the PK can't be a single field and, as some of the values of the 2076 # composed PK may be None, they must be queried using 'is null', 2077 # so we must programmatically construct the SQL query 2078 where_parts = [] 2079 pk = {} 2080 for col_name in aPK_obj.keys(): 2081 val = aPK_obj[col_name] 2082 if val is None: 2083 where_parts.append('%s IS NULL' % col_name) 2084 else: 2085 where_parts.append('%s = %%(%s)s' % (col_name, col_name)) 2086 pk[col_name] = val 2087 2088 # try to instantiate from true problem view 2089 cProblem._cmd_fetch_payload = u""" 2090 SELECT *, False as is_potential_problem 2091 FROM clin.v_problem_list 2092 WHERE %s""" % u' AND '.join(where_parts) 2093 2094 try: 2095 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk) 2096 return 2097 except gmExceptions.ConstructorError: 2098 _log.exception('actual problem not found, trying "potential" problems') 2099 if try_potential_problems is False: 2100 raise 2101 2102 # try to instantiate from potential-problems view 2103 cProblem._cmd_fetch_payload = u""" 2104 SELECT *, True as is_potential_problem 2105 FROM clin.v_potential_problem_list 2106 WHERE %s""" % u' AND '.join(where_parts) 2107 gmBusinessDBObject.cBusinessDBObject.__init__(self, aPK_obj=pk)2108 #--------------------------------------------------------2110 """ 2111 Retrieve the cEpisode instance equivalent to this problem. 2112 The problem's type attribute must be 'episode' 2113 """ 2114 if self._payload[self._idx['type']] != 'episode': 2115 _log.error('cannot convert problem [%s] of type [%s] to episode' % (self._payload[self._idx['problem']], self._payload[self._idx['type']])) 2116 return None 2117 return cEpisode(aPK_obj = self._payload[self._idx['pk_episode']])2118 #--------------------------------------------------------2120 """ 2121 Retrieve the cHealthIssue instance equivalent to this problem. 2122 The problem's type attribute must be 'issue' 2123 """ 2124 if self._payload[self._idx['type']] != 'issue': 2125 _log.error('cannot convert problem [%s] of type [%s] to health issue' % (self._payload[self._idx['problem']], self._payload[self._idx['type']])) 2126 return None 2127 return cHealthIssue(aPK_obj = self._payload[self._idx['pk_health_issue']])2128 #--------------------------------------------------------2130 2131 if self._payload[self._idx['type']] == u'issue': 2132 episodes = [ cHealthIssue(aPK_obj = self._payload[self._idx['pk_health_issue']]).latest_episode ] 2133 #xxxxxxxxxxxxx 2134 2135 emr = patient.get_emr() 2136 2137 doc_folder = gmDocuments.cDocumentFolder(aPKey = patient.ID) 2138 return doc_folder.get_visual_progress_notes ( 2139 health_issue = self._payload[self._idx['pk_health_issue']], 2140 episode = self._payload[self._idx['pk_episode']] 2141 )2142 #-------------------------------------------------------- 2143 # properties 2144 #-------------------------------------------------------- 2145 # doubles as 'diagnostic_certainty_description' getter:2147 return diagnostic_certainty_classification2str(self._payload[self._idx['diagnostic_certainty_classification']])2148 2149 diagnostic_certainty_description = property(get_diagnostic_certainty_description, lambda x:x) 2150 #--------------------------------------------------------2152 if self._payload[self._idx['type']] == u'issue': 2153 cmd = u""" 2154 SELECT * FROM clin.v_linked_codes WHERE 2155 item_table = 'clin.lnk_code2h_issue'::regclass 2156 AND 2157 pk_item = %(item)s 2158 """ 2159 args = {'item': self._payload[self._idx['pk_health_issue']]} 2160 else: 2161 cmd = u""" 2162 SELECT * FROM clin.v_linked_codes WHERE 2163 item_table = 'clin.lnk_code2episode'::regclass 2164 AND 2165 pk_item = %(item)s 2166 """ 2167 args = {'item': self._payload[self._idx['pk_episode']]} 2168 2169 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2170 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]2171 2172 generic_codes = property(_get_generic_codes, lambda x:x)2175 """Retrieve the cEpisode instance equivalent to the given problem. 2176 2177 The problem's type attribute must be 'episode' 2178 2179 @param problem: The problem to retrieve its related episode for 2180 @type problem: A gmEMRStructItems.cProblem instance 2181 """ 2182 if isinstance(problem, cEpisode): 2183 return problem 2184 2185 exc = TypeError('cannot convert [%s] to episode' % problem) 2186 2187 if not isinstance(problem, cProblem): 2188 raise exc 2189 2190 if problem['type'] != 'episode': 2191 raise exc 2192 2193 return cEpisode(aPK_obj = problem['pk_episode'])2194 #-----------------------------------------------------------2196 """Retrieve the cIssue instance equivalent to the given problem. 2197 2198 The problem's type attribute must be 'issue'. 2199 2200 @param problem: The problem to retrieve the corresponding issue for 2201 @type problem: A gmEMRStructItems.cProblem instance 2202 """ 2203 if isinstance(problem, cHealthIssue): 2204 return problem 2205 2206 exc = TypeError('cannot convert [%s] to health issue' % problem) 2207 2208 if not isinstance(problem, cProblem): 2209 raise exc 2210 2211 if problem['type'] != 'issue': 2212 raise exc 2213 2214 return cHealthIssue(aPK_obj = problem['pk_health_issue'])2215 #-----------------------------------------------------------2217 """Transform given problem into either episode or health issue instance. 2218 """ 2219 if isinstance(problem, (cEpisode, cHealthIssue)): 2220 return problem 2221 2222 exc = TypeError('cannot reclass [%s] instance to either episode or health issue' % type(problem)) 2223 2224 if not isinstance(problem, cProblem): 2225 _log.debug(u'%s' % problem) 2226 raise exc 2227 2228 if problem['type'] == 'episode': 2229 return cEpisode(aPK_obj = problem['pk_episode']) 2230 2231 if problem['type'] == 'issue': 2232 return cHealthIssue(aPK_obj = problem['pk_health_issue']) 2233 2234 raise exc2235 #============================================================2237 2238 _cmd_fetch_payload = u"select * from clin.v_pat_hospital_stays where pk_hospital_stay = %s" 2239 _cmds_store_payload = [ 2240 u"""update clin.hospital_stay set 2241 clin_when = %(admission)s, 2242 discharge = %(discharge)s, 2243 narrative = gm.nullify_empty_string(%(hospital)s), 2244 fk_episode = %(pk_episode)s, 2245 fk_encounter = %(pk_encounter)s 2246 where 2247 pk = %(pk_hospital_stay)s and 2248 xmin = %(xmin_hospital_stay)s""", 2249 u"""select xmin_hospital_stay from clin.v_pat_hospital_stays where pk_hospital_stay = %(pk_hospital_stay)s""" 2250 ] 2251 _updatable_fields = [ 2252 'admission', 2253 'discharge', 2254 'hospital', 2255 'pk_episode', 2256 'pk_encounter' 2257 ] 2258 #-------------------------------------------------------2277 #-----------------------------------------------------------2260 2261 if self._payload[self._idx['discharge']] is not None: 2262 dis = u' - %s' % self._payload[self._idx['discharge']].strftime('%Y %b %d').decode(gmI18N.get_encoding()) 2263 else: 2264 dis = u'' 2265 2266 line = u'%s%s%s%s: %s%s%s' % ( 2267 u' ' * left_margin, 2268 self._payload[self._idx['admission']].strftime('%Y %b %d').decode(gmI18N.get_encoding()), 2269 dis, 2270 gmTools.coalesce(self._payload[self._idx['hospital']], u'', u' (%s)'), 2271 gmTools.u_left_double_angle_quote, 2272 self._payload[self._idx['episode']], 2273 gmTools.u_right_double_angle_quote 2274 ) 2275 2276 return line2279 2280 queries = [{ 2281 'cmd': u'SELECT * FROM clin.v_pat_hospital_stays WHERE pk_patient = %(pat)s ORDER BY admission', 2282 'args': {'pat': patient} 2283 }] 2284 2285 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 2286 2287 return [ cHospitalStay(row = {'idx': idx, 'data': r, 'pk_field': 'pk_hospital_stay'}) for r in rows ]2288 #-----------------------------------------------------------2290 2291 queries = [{ 2292 'cmd': u'INSERT INTO clin.hospital_stay (fk_encounter, fk_episode) VALUES (%(enc)s, %(epi)s) RETURNING pk', 2293 'args': {'enc': encounter, 'epi': episode} 2294 }] 2295 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 2296 2297 return cHospitalStay(aPK_obj = rows[0][0])2298 #-----------------------------------------------------------2300 cmd = u'DELETE FROM clin.hospital_stay WHERE pk = %(pk)s' 2301 args = {'pk': stay} 2302 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2303 return True2304 #============================================================2306 2307 _cmd_fetch_payload = u"select * from clin.v_pat_procedures where pk_procedure = %s" 2308 _cmds_store_payload = [ 2309 u"""UPDATE clin.procedure SET 2310 soap_cat = 'p', 2311 clin_when = %(clin_when)s, 2312 clin_end = %(clin_end)s, 2313 is_ongoing = %(is_ongoing)s, 2314 clin_where = NULLIF ( 2315 COALESCE ( 2316 %(pk_hospital_stay)s::TEXT, 2317 gm.nullify_empty_string(%(clin_where)s) 2318 ), 2319 %(pk_hospital_stay)s::TEXT 2320 ), 2321 narrative = gm.nullify_empty_string(%(performed_procedure)s), 2322 fk_hospital_stay = %(pk_hospital_stay)s, 2323 fk_episode = %(pk_episode)s, 2324 fk_encounter = %(pk_encounter)s 2325 WHERE 2326 pk = %(pk_procedure)s AND 2327 xmin = %(xmin_procedure)s 2328 RETURNING xmin as xmin_procedure""" 2329 ] 2330 _updatable_fields = [ 2331 'clin_when', 2332 'clin_end', 2333 'is_ongoing', 2334 'clin_where', 2335 'performed_procedure', 2336 'pk_hospital_stay', 2337 'pk_episode', 2338 'pk_encounter' 2339 ] 2340 #-------------------------------------------------------2446 #-----------------------------------------------------------2342 2343 if (attribute == 'pk_hospital_stay') and (value is not None): 2344 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'clin_where', None) 2345 2346 if (attribute == 'clin_where') and (value is not None) and (value.strip() != u''): 2347 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, 'pk_hospital_stay', None) 2348 2349 gmBusinessDBObject.cBusinessDBObject.__setitem__(self, attribute, value)2350 #-------------------------------------------------------2352 2353 if self._payload[self._idx['is_ongoing']]: 2354 end = _(' (ongoing)') 2355 else: 2356 end = self._payload[self._idx['clin_end']] 2357 if end is None: 2358 end = u'' 2359 else: 2360 end = u' - %s' % end.strftime('%Y %b %d').decode(gmI18N.get_encoding()) 2361 2362 line = u'%s%s%s, %s: %s' % ( 2363 (u' ' * left_margin), 2364 self._payload[self._idx['clin_when']].strftime('%Y %b %d').decode(gmI18N.get_encoding()), 2365 end, 2366 self._payload[self._idx['clin_where']], 2367 self._payload[self._idx['performed_procedure']] 2368 ) 2369 if include_episode: 2370 line = u'%s (%s)' % (line, self._payload[self._idx['episode']]) 2371 2372 if include_codes: 2373 codes = self.generic_codes 2374 if len(codes) > 0: 2375 line += u'\n' 2376 for c in codes: 2377 line += u'%s %s: %s (%s - %s)\n' % ( 2378 (u' ' * left_margin), 2379 c['code'], 2380 c['term'], 2381 c['name_short'], 2382 c['version'] 2383 ) 2384 del codes 2385 2386 return line2387 #--------------------------------------------------------2389 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 2390 cmd = u"INSERT INTO clin.lnk_code2procedure (fk_item, fk_generic_code) values (%(issue)s, %(code)s)" 2391 args = { 2392 'issue': self._payload[self._idx['pk_procedure']], 2393 'code': pk_code 2394 } 2395 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2396 return True2397 #--------------------------------------------------------2399 """<pk_code> must be a value from ref.coding_system_root.pk_coding_system (clin.lnk_code2item_root.fk_generic_code)""" 2400 cmd = u"DELETE FROM clin.lnk_code2procedure WHERE fk_item = %(issue)s AND fk_generic_code = %(code)s" 2401 args = { 2402 'issue': self._payload[self._idx['pk_procedure']], 2403 'code': pk_code 2404 } 2405 rows, idx = gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2406 return True2407 #-------------------------------------------------------- 2408 # properties 2409 #--------------------------------------------------------2411 if len(self._payload[self._idx['pk_generic_codes']]) == 0: 2412 return [] 2413 2414 cmd = gmCoding._SQL_get_generic_linked_codes % u'pk_generic_code IN %(pks)s' 2415 args = {'pks': tuple(self._payload[self._idx['pk_generic_codes']])} 2416 rows, idx = gmPG2.run_ro_queries(queries = [{'cmd': cmd, 'args': args}], get_col_idx = True) 2417 return [ gmCoding.cGenericLinkedCode(row = {'data': r, 'idx': idx, 'pk_field': 'pk_lnk_code2item'}) for r in rows ]24182420 queries = [] 2421 # remove all codes 2422 if len(self._payload[self._idx['pk_generic_codes']]) > 0: 2423 queries.append ({ 2424 'cmd': u'DELETE FROM clin.lnk_code2procedure WHERE fk_item = %(proc)s AND fk_generic_code IN %(codes)s', 2425 'args': { 2426 'proc': self._payload[self._idx['pk_procedure']], 2427 'codes': tuple(self._payload[self._idx['pk_generic_codes']]) 2428 } 2429 }) 2430 # add new codes 2431 for pk_code in pk_codes: 2432 queries.append ({ 2433 'cmd': u'INSERT INTO clin.lnk_code2procedure (fk_item, fk_generic_code) VALUES (%(proc)s, %(pk_code)s)', 2434 'args': { 2435 'proc': self._payload[self._idx['pk_procedure']], 2436 'pk_code': pk_code 2437 } 2438 }) 2439 if len(queries) == 0: 2440 return 2441 # run it all in one transaction 2442 rows, idx = gmPG2.run_rw_queries(queries = queries) 2443 return2444 2445 generic_codes = property(_get_generic_codes, _set_generic_codes)2448 2449 queries = [ 2450 { 2451 'cmd': u'select * from clin.v_pat_procedures where pk_patient = %(pat)s order by clin_when', 2452 'args': {'pat': patient} 2453 } 2454 ] 2455 2456 rows, idx = gmPG2.run_ro_queries(queries = queries, get_col_idx = True) 2457 2458 return [ cPerformedProcedure(row = {'idx': idx, 'data': r, 'pk_field': 'pk_procedure'}) for r in rows ]2459 #-----------------------------------------------------------2460 -def create_performed_procedure(encounter=None, episode=None, location=None, hospital_stay=None, procedure=None):2461 2462 queries = [{ 2463 'cmd': u""" 2464 INSERT INTO clin.procedure ( 2465 fk_encounter, 2466 fk_episode, 2467 soap_cat, 2468 clin_where, 2469 fk_hospital_stay, 2470 narrative 2471 ) VALUES ( 2472 %(enc)s, 2473 %(epi)s, 2474 'p', 2475 gm.nullify_empty_string(%(loc)s), 2476 %(stay)s, 2477 gm.nullify_empty_string(%(proc)s) 2478 ) 2479 RETURNING pk""", 2480 'args': {'enc': encounter, 'epi': episode, 'loc': location, 'stay': hospital_stay, 'proc': procedure} 2481 }] 2482 2483 rows, idx = gmPG2.run_rw_queries(queries = queries, return_data = True) 2484 2485 return cPerformedProcedure(aPK_obj = rows[0][0])2486 #-----------------------------------------------------------2488 cmd = u'delete from clin.procedure where pk = %(pk)s' 2489 args = {'pk': procedure} 2490 gmPG2.run_rw_queries(queries = [{'cmd': cmd, 'args': args}]) 2491 return True2492 #============================================================ 2493 # main - unit testing 2494 #------------------------------------------------------------ 2495 if __name__ == '__main__': 2496 2497 if len(sys.argv) < 2: 2498 sys.exit() 2499 2500 if sys.argv[1] != 'test': 2501 sys.exit() 2502 2503 #-------------------------------------------------------- 2504 # define tests 2505 #--------------------------------------------------------2507 print "\nProblem test" 2508 print "------------" 2509 prob = cProblem(aPK_obj={'pk_patient': 12, 'pk_health_issue': 1, 'pk_episode': None}) 2510 print prob 2511 fields = prob.get_fields() 2512 for field in fields: 2513 print field, ':', prob[field] 2514 print '\nupdatable:', prob.get_updatable_fields() 2515 epi = prob.get_as_episode() 2516 print '\nas episode:' 2517 if epi is not None: 2518 for field in epi.get_fields(): 2519 print ' .%s : %s' % (field, epi[field])2520 #--------------------------------------------------------2522 print "\nhealth issue test" 2523 print "-----------------" 2524 h_issue = cHealthIssue(aPK_obj=2) 2525 print h_issue 2526 fields = h_issue.get_fields() 2527 for field in fields: 2528 print field, ':', h_issue[field] 2529 print "has open episode:", h_issue.has_open_episode() 2530 print "open episode:", h_issue.get_open_episode() 2531 print "updateable:", h_issue.get_updatable_fields() 2532 h_issue.close_expired_episode(ttl=7300) 2533 h_issue = cHealthIssue(encounter = 1, name = u'post appendectomy/peritonitis') 2534 print h_issue 2535 print h_issue.format_as_journal()2536 #--------------------------------------------------------2538 print "\nepisode test" 2539 print "------------" 2540 episode = cEpisode(aPK_obj=1) 2541 print episode 2542 fields = episode.get_fields() 2543 for field in fields: 2544 print field, ':', episode[field] 2545 print "updatable:", episode.get_updatable_fields() 2546 raw_input('ENTER to continue') 2547 2548 old_description = episode['description'] 2549 old_enc = cEncounter(aPK_obj = 1) 2550 2551 desc = '1-%s' % episode['description'] 2552 print "==> renaming to", desc 2553 successful = episode.rename ( 2554 description = desc 2555 ) 2556 if not successful: 2557 print "error" 2558 else: 2559 print "success" 2560 for field in fields: 2561 print field, ':', episode[field] 2562 2563 print "episode range:", episode.get_access_range() 2564 2565 raw_input('ENTER to continue')2566 2567 #--------------------------------------------------------2569 print "\nencounter test" 2570 print "--------------" 2571 encounter = cEncounter(aPK_obj=1) 2572 print encounter 2573 fields = encounter.get_fields() 2574 for field in fields: 2575 print field, ':', encounter[field] 2576 print "updatable:", encounter.get_updatable_fields()2577 #--------------------------------------------------------2579 encounter = cEncounter(aPK_obj=1) 2580 print encounter 2581 print "" 2582 print encounter.format_latex()2583 #--------------------------------------------------------2585 procs = get_performed_procedures(patient = 12) 2586 for proc in procs: 2587 print proc.format(left_margin=2)2588 #--------------------------------------------------------2590 stay = create_hospital_stay(encounter = 1, episode = 2) 2591 stay['hospital'] = u'Starfleet Galaxy General Hospital' 2592 stay.save_payload() 2593 print stay 2594 for s in get_patient_hospital_stays(12): 2595 print s 2596 delete_hospital_stay(stay['pk_hospital_stay']) 2597 stay = create_hospital_stay(encounter = 1, episode = 4)2598 #--------------------------------------------------------2600 tests = [None, 'A', 'B', 'C', 'D', 'E'] 2601 2602 for t in tests: 2603 print type(t), t 2604 print type(diagnostic_certainty_classification2str(t)), diagnostic_certainty_classification2str(t)2605 #-------------------------------------------------------- 2610 #-------------------------------------------------------- 2611 # run them 2612 #test_episode() 2613 #test_problem() 2614 #test_encounter() 2615 #test_health_issue() 2616 #test_hospital_stay() 2617 #test_performed_procedure() 2618 #test_diagnostic_certainty_classification_map() 2619 #test_encounter2latex() 2620 test_episode_codes() 2621 #============================================================ 2622
Home | Trees | Indices | Help |
|
---|
Generated by Epydoc 3.0.1 on Tue Oct 18 04:00:33 2011 | http://epydoc.sourceforge.net |