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